Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2cc7b4ca authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull pstore updates from Kees Cook:
 "Various fixes and tweaks for the pstore subsystem.

  Highlights:

   - use memdup_user() instead of open-coded copies (Geliang Tang)

   - fix record memory leak during initialization (Douglas Anderson)

   - avoid confused compressed record warning (Ankit Kumar)

   - prepopulate record timestamp and remove redundant logic from
     backends"

* tag 'pstore-v4.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  powerpc/nvram: use memdup_user
  pstore: use memdup_user
  pstore: Fix format string to use %u for record id
  pstore: Populate pstore record->time field
  pstore: Create common record initializer
  efi-pstore: Refactor erase routine
  pstore: Avoid potential infinite loop
  pstore: Fix leaked pstore_record in pstore_get_backend_records()
  pstore: Don't warn if data is uncompressed and type is not PSTORE_TYPE_DMESG
parents e24dd9ee 0752e402
Loading
Loading
Loading
Loading
+5 −9
Original line number Original line Diff line number Diff line
@@ -792,21 +792,17 @@ static ssize_t dev_nvram_write(struct file *file, const char __user *buf,
	count = min_t(size_t, count, size - *ppos);
	count = min_t(size_t, count, size - *ppos);
	count = min(count, PAGE_SIZE);
	count = min(count, PAGE_SIZE);


	ret = -ENOMEM;
	tmp = memdup_user(buf, count);
	tmp = kmalloc(count, GFP_KERNEL);
	if (IS_ERR(tmp)) {
	if (!tmp)
		ret = PTR_ERR(tmp);
		goto out;

	ret = -EFAULT;
	if (copy_from_user(tmp, buf, count))
		goto out;
		goto out;
	}


	ret = ppc_md.nvram_write(tmp, count, ppos);
	ret = ppc_md.nvram_write(tmp, count, ppos);


out:
	kfree(tmp);
	kfree(tmp);
out:
	return ret;
	return ret;

}
}


static long dev_nvram_ioctl(struct file *file, unsigned int cmd,
static long dev_nvram_ioctl(struct file *file, unsigned int cmd,
+38 −49
Original line number Original line Diff line number Diff line
@@ -4,7 +4,7 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/ucs2_string.h>
#include <linux/ucs2_string.h>


#define DUMP_NAME_LEN 52
#define DUMP_NAME_LEN 66


static bool efivars_pstore_disable =
static bool efivars_pstore_disable =
	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
	IS_ENABLED(CONFIG_EFI_VARS_PSTORE_DEFAULT_DISABLE);
@@ -244,12 +244,12 @@ static int efi_pstore_write(struct pstore_record *record)
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	int i, ret = 0;
	int i, ret = 0;


	record->time.tv_sec = get_seconds();
	record->time.tv_nsec = 0;

	record->id = generic_id(record->time.tv_sec, record->part,
	record->id = generic_id(record->time.tv_sec, record->part,
				record->count);
				record->count);


	/* Since we copy the entire length of name, make sure it is wiped. */
	memset(name, 0, sizeof(name));

	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu-%c",
		 record->type, record->part, record->count,
		 record->type, record->part, record->count,
		 record->time.tv_sec, record->compressed ? 'C' : 'D');
		 record->time.tv_sec, record->compressed ? 'C' : 'D');
@@ -267,44 +267,20 @@ static int efi_pstore_write(struct pstore_record *record)
	return ret;
	return ret;
};
};


struct pstore_erase_data {
	struct pstore_record *record;
	efi_char16_t *name;
};

/*
/*
 * Clean up an entry with the same name
 * Clean up an entry with the same name
 */
 */
static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
{
{
	struct pstore_erase_data *ed = data;
	efi_char16_t *efi_name = data;
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
	efi_char16_t efi_name_old[DUMP_NAME_LEN];
	unsigned long ucs2_len = ucs2_strlen(efi_name);
	efi_char16_t *efi_name = ed->name;
	unsigned long ucs2_len = ucs2_strlen(ed->name);
	char name_old[DUMP_NAME_LEN];
	int i;


	if (efi_guidcmp(entry->var.VendorGuid, vendor))
	if (efi_guidcmp(entry->var.VendorGuid, vendor))
		return 0;
		return 0;


	if (ucs2_strncmp(entry->var.VariableName,
	if (ucs2_strncmp(entry->var.VariableName, efi_name, (size_t)ucs2_len))
			  efi_name, (size_t)ucs2_len)) {
		/*
		 * Check if an old format, which doesn't support
		 * holding multiple logs, remains.
		 */
		snprintf(name_old, sizeof(name_old), "dump-type%u-%u-%lu",
			ed->record->type, ed->record->part,
			ed->record->time.tv_sec);

		for (i = 0; i < DUMP_NAME_LEN; i++)
			efi_name_old[i] = name_old[i];

		if (ucs2_strncmp(entry->var.VariableName, efi_name_old,
				  ucs2_strlen(efi_name_old)))
		return 0;
		return 0;
	}


	if (entry->scanning) {
	if (entry->scanning) {
		/*
		/*
@@ -321,35 +297,48 @@ static int efi_pstore_erase_func(struct efivar_entry *entry, void *data)
	return 1;
	return 1;
}
}


static int efi_pstore_erase(struct pstore_record *record)
static int efi_pstore_erase_name(const char *name)
{
{
	struct pstore_erase_data edata;
	struct efivar_entry *entry = NULL;
	struct efivar_entry *entry = NULL;
	char name[DUMP_NAME_LEN];
	efi_char16_t efi_name[DUMP_NAME_LEN];
	efi_char16_t efi_name[DUMP_NAME_LEN];
	int found, i;
	int found, i;


	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu",
	for (i = 0; i < DUMP_NAME_LEN; i++) {
		 record->type, record->part, record->count,
		 record->time.tv_sec);

	for (i = 0; i < DUMP_NAME_LEN; i++)
		efi_name[i] = name[i];
		efi_name[i] = name[i];

		if (name[i] == '\0')
	edata.record = record;
			break;
	edata.name = efi_name;
	}


	if (efivar_entry_iter_begin())
	if (efivar_entry_iter_begin())
		return -EINTR;
		return -EINTR;
	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);


	if (found && !entry->scanning) {
	found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list,
				    efi_name, &entry);
	efivar_entry_iter_end();
	efivar_entry_iter_end();

	if (found && !entry->scanning)
		efivar_unregister(entry);
		efivar_unregister(entry);
	} else
		efivar_entry_iter_end();


	return 0;
	return found ? 0 : -ENOENT;
}

static int efi_pstore_erase(struct pstore_record *record)
{
	char name[DUMP_NAME_LEN];
	int ret;

	snprintf(name, sizeof(name), "dump-type%u-%u-%d-%lu",
		 record->type, record->part, record->count,
		 record->time.tv_sec);
	ret = efi_pstore_erase_name(name);
	if (ret != -ENOENT)
		return ret;

	snprintf(name, sizeof(name), "dump-type%u-%u-%lu",
		record->type, record->part, record->time.tv_sec);
	ret = efi_pstore_erase_name(name);

	return ret;
}
}


static struct pstore_info efi_pstore_info = {
static struct pstore_info efi_pstore_info = {
+11 −11
Original line number Original line Diff line number Diff line
@@ -349,48 +349,48 @@ int pstore_mkfile(struct dentry *root, struct pstore_record *record)


	switch (record->type) {
	switch (record->type) {
	case PSTORE_TYPE_DMESG:
	case PSTORE_TYPE_DMESG:
		scnprintf(name, sizeof(name), "dmesg-%s-%lld%s",
		scnprintf(name, sizeof(name), "dmesg-%s-%llu%s",
			  record->psi->name, record->id,
			  record->psi->name, record->id,
			  record->compressed ? ".enc.z" : "");
			  record->compressed ? ".enc.z" : "");
		break;
		break;
	case PSTORE_TYPE_CONSOLE:
	case PSTORE_TYPE_CONSOLE:
		scnprintf(name, sizeof(name), "console-%s-%lld",
		scnprintf(name, sizeof(name), "console-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_FTRACE:
	case PSTORE_TYPE_FTRACE:
		scnprintf(name, sizeof(name), "ftrace-%s-%lld",
		scnprintf(name, sizeof(name), "ftrace-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_MCE:
	case PSTORE_TYPE_MCE:
		scnprintf(name, sizeof(name), "mce-%s-%lld",
		scnprintf(name, sizeof(name), "mce-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_PPC_RTAS:
	case PSTORE_TYPE_PPC_RTAS:
		scnprintf(name, sizeof(name), "rtas-%s-%lld",
		scnprintf(name, sizeof(name), "rtas-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_PPC_OF:
	case PSTORE_TYPE_PPC_OF:
		scnprintf(name, sizeof(name), "powerpc-ofw-%s-%lld",
		scnprintf(name, sizeof(name), "powerpc-ofw-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_PPC_COMMON:
	case PSTORE_TYPE_PPC_COMMON:
		scnprintf(name, sizeof(name), "powerpc-common-%s-%lld",
		scnprintf(name, sizeof(name), "powerpc-common-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_PMSG:
	case PSTORE_TYPE_PMSG:
		scnprintf(name, sizeof(name), "pmsg-%s-%lld",
		scnprintf(name, sizeof(name), "pmsg-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_PPC_OPAL:
	case PSTORE_TYPE_PPC_OPAL:
		scnprintf(name, sizeof(name), "powerpc-opal-%s-%lld",
		scnprintf(name, sizeof(name), "powerpc-opal-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	case PSTORE_TYPE_UNKNOWN:
	case PSTORE_TYPE_UNKNOWN:
		scnprintf(name, sizeof(name), "unknown-%s-%lld",
		scnprintf(name, sizeof(name), "unknown-%s-%llu",
			  record->psi->name, record->id);
			  record->psi->name, record->id);
		break;
		break;
	default:
	default:
		scnprintf(name, sizeof(name), "type%d-%s-%lld",
		scnprintf(name, sizeof(name), "type%d-%s-%llu",
			  record->type, record->psi->name, record->id);
			  record->type, record->psi->name, record->id);
		break;
		break;
	}
	}
+2 −0
Original line number Original line Diff line number Diff line
@@ -30,5 +30,7 @@ extern void pstore_get_backend_records(struct pstore_info *psi,
extern int	pstore_mkfile(struct dentry *root,
extern int	pstore_mkfile(struct dentry *root,
			      struct pstore_record *record);
			      struct pstore_record *record);
extern bool	pstore_is_mounted(void);
extern bool	pstore_is_mounted(void);
extern void	pstore_record_init(struct pstore_record *record,
				   struct pstore_info *psi);


#endif
#endif
+44 −25
Original line number Original line Diff line number Diff line
@@ -474,6 +474,20 @@ static size_t copy_kmsg_to_buffer(int hsize, size_t len)
	return total_len;
	return total_len;
}
}


void pstore_record_init(struct pstore_record *record,
			struct pstore_info *psinfo)
{
	memset(record, 0, sizeof(*record));

	record->psi = psinfo;

	/* Report zeroed timestamp if called before timekeeping has resumed. */
	if (__getnstimeofday(&record->time)) {
		record->time.tv_sec = 0;
		record->time.tv_nsec = 0;
	}
}

/*
/*
 * callback from kmsg_dump. (s2,l2) has the most recently
 * callback from kmsg_dump. (s2,l2) has the most recently
 * written bytes, older bytes are in (s1,l1). Save as much
 * written bytes, older bytes are in (s1,l1). Save as much
@@ -509,15 +523,14 @@ static void pstore_dump(struct kmsg_dumper *dumper,
		int header_size;
		int header_size;
		int zipped_len = -1;
		int zipped_len = -1;
		size_t dump_size;
		size_t dump_size;
		struct pstore_record record = {
		struct pstore_record record;
			.type = PSTORE_TYPE_DMESG,

			.count = oopscount,
		pstore_record_init(&record, psinfo);
			.reason = reason,
		record.type = PSTORE_TYPE_DMESG;
			.part = part,
		record.count = oopscount;
			.compressed = false,
		record.reason = reason;
			.buf = psinfo->buf,
		record.part = part;
			.psi = psinfo,
		record.buf = psinfo->buf;
		};


		if (big_oops_buf && is_locked) {
		if (big_oops_buf && is_locked) {
			dst = big_oops_buf;
			dst = big_oops_buf;
@@ -587,12 +600,12 @@ static void pstore_console_write(struct console *con, const char *s, unsigned c)
	const char *e = s + c;
	const char *e = s + c;


	while (s < e) {
	while (s < e) {
		struct pstore_record record = {
		struct pstore_record record;
			.type = PSTORE_TYPE_CONSOLE,
			.psi = psinfo,
		};
		unsigned long flags;
		unsigned long flags;


		pstore_record_init(&record, psinfo);
		record.type = PSTORE_TYPE_CONSOLE;

		if (c > psinfo->bufsize)
		if (c > psinfo->bufsize)
			c = psinfo->bufsize;
			c = psinfo->bufsize;


@@ -640,19 +653,16 @@ static int pstore_write_user_compat(struct pstore_record *record,
	if (record->buf)
	if (record->buf)
		return -EINVAL;
		return -EINVAL;


	record->buf = kmalloc(record->size, GFP_KERNEL);
	record->buf = memdup_user(buf, record->size);
	if (!record->buf)
	if (unlikely(IS_ERR(record->buf))) {
		return -ENOMEM;
		ret = PTR_ERR(record->buf);

	if (unlikely(copy_from_user(record->buf, buf, record->size))) {
		ret = -EFAULT;
		goto out;
		goto out;
	}
	}


	ret = record->psi->write(record);
	ret = record->psi->write(record);


out:
	kfree(record->buf);
	kfree(record->buf);
out:
	record->buf = NULL;
	record->buf = NULL;


	return unlikely(ret < 0) ? ret : record->size;
	return unlikely(ret < 0) ? ret : record->size;
@@ -770,8 +780,11 @@ static void decompress_record(struct pstore_record *record)
	int unzipped_len;
	int unzipped_len;
	char *decompressed;
	char *decompressed;


	if (!record->compressed)
		return;

	/* Only PSTORE_TYPE_DMESG support compression. */
	/* Only PSTORE_TYPE_DMESG support compression. */
	if (!record->compressed || record->type != PSTORE_TYPE_DMESG) {
	if (record->type != PSTORE_TYPE_DMESG) {
		pr_warn("ignored compressed record type %d\n", record->type);
		pr_warn("ignored compressed record type %d\n", record->type);
		return;
		return;
	}
	}
@@ -819,6 +832,7 @@ void pstore_get_backend_records(struct pstore_info *psi,
				struct dentry *root, int quiet)
				struct dentry *root, int quiet)
{
{
	int failed = 0;
	int failed = 0;
	unsigned int stop_loop = 65536;


	if (!psi || !root)
	if (!psi || !root)
		return;
		return;
@@ -832,7 +846,7 @@ void pstore_get_backend_records(struct pstore_info *psi,
	 * may reallocate record.buf. On success, pstore_mkfile() will keep
	 * may reallocate record.buf. On success, pstore_mkfile() will keep
	 * the record.buf, so free it only on failure.
	 * the record.buf, so free it only on failure.
	 */
	 */
	for (;;) {
	for (; stop_loop; stop_loop--) {
		struct pstore_record *record;
		struct pstore_record *record;
		int rc;
		int rc;


@@ -841,13 +855,15 @@ void pstore_get_backend_records(struct pstore_info *psi,
			pr_err("out of memory creating record\n");
			pr_err("out of memory creating record\n");
			break;
			break;
		}
		}
		record->psi = psi;
		pstore_record_init(record, psi);


		record->size = psi->read(record);
		record->size = psi->read(record);


		/* No more records left in backend? */
		/* No more records left in backend? */
		if (record->size <= 0)
		if (record->size <= 0) {
			kfree(record);
			break;
			break;
		}


		decompress_record(record);
		decompress_record(record);
		rc = pstore_mkfile(root, record);
		rc = pstore_mkfile(root, record);
@@ -865,8 +881,11 @@ void pstore_get_backend_records(struct pstore_info *psi,
	mutex_unlock(&psi->read_mutex);
	mutex_unlock(&psi->read_mutex);


	if (failed)
	if (failed)
		pr_warn("failed to load %d record(s) from '%s'\n",
		pr_warn("failed to create %d record(s) from '%s'\n",
			failed, psi->name);
			failed, psi->name);
	if (!stop_loop)
		pr_err("looping? Too many records seen from '%s'\n",
			psi->name);
}
}


static void pstore_dowork(struct work_struct *work)
static void pstore_dowork(struct work_struct *work)
Loading