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

Commit 6a2651b5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'libnvdimm-fixes-5.0-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm

Pull libnvdimm fixes from Dan Williams:
 "A fix for namespace label support for non-Intel NVDIMMs that implement
  the ACPI standard label method.

  This has apparently never worked and could wait for v5.1. However it
  has enough visibility with hardware vendors [1] and distro bug
  trackers [2], and low enough risk that I decided it should go in for
  -rc4. The other fixups target the new, for v5.0, nvdimm security
  functionality. The larger init path fixup closes a memory leak and a
  potential userspace lockup due to missed notifications.

    [1] https://github.com/pmem/ndctl/issues/78
    [2] https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1811785

  These have all soaked in -next for a week with no reported issues.

  Summary:

   - Fix support for NVDIMMs that implement the ACPI standard label
     methods.

   - Fix error handling for security overwrite (memory leak / userspace
     hang condition), and another one-line security cleanup"

* tag 'libnvdimm-fixes-5.0-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  acpi/nfit: Fix command-supported detection
  acpi/nfit: Block function zero DSMs
  libnvdimm/security: Require nvdimm_security_setup_events() to succeed
  nfit_test: fix security state pull for nvdimm security nfit_test
parents 78e372e6 11189c10
Loading
Loading
Loading
Loading
+47 −19
Original line number Original line Diff line number Diff line
@@ -409,6 +409,32 @@ static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func)
	return true;
	return true;
}
}


static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
		struct nd_cmd_pkg *call_pkg)
{
	if (call_pkg) {
		int i;

		if (nfit_mem->family != call_pkg->nd_family)
			return -ENOTTY;

		for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
			if (call_pkg->nd_reserved2[i])
				return -EINVAL;
		return call_pkg->nd_command;
	}

	/* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */
	if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
		return cmd;

	/*
	 * Force function number validation to fail since 0 is never
	 * published as a valid function in dsm_mask.
	 */
	return 0;
}

int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
		unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
		unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
{
{
@@ -422,30 +448,23 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
	unsigned long cmd_mask, dsm_mask;
	unsigned long cmd_mask, dsm_mask;
	u32 offset, fw_status = 0;
	u32 offset, fw_status = 0;
	acpi_handle handle;
	acpi_handle handle;
	unsigned int func;
	const guid_t *guid;
	const guid_t *guid;
	int rc, i;
	int func, rc, i;


	if (cmd_rc)
	if (cmd_rc)
		*cmd_rc = -EINVAL;
		*cmd_rc = -EINVAL;
	func = cmd;
	if (cmd == ND_CMD_CALL) {
		call_pkg = buf;
		func = call_pkg->nd_command;

		for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
			if (call_pkg->nd_reserved2[i])
				return -EINVAL;
	}


	if (nvdimm) {
	if (nvdimm) {
		struct acpi_device *adev = nfit_mem->adev;
		struct acpi_device *adev = nfit_mem->adev;


		if (!adev)
		if (!adev)
			return -ENOTTY;
			return -ENOTTY;
		if (call_pkg && nfit_mem->family != call_pkg->nd_family)
			return -ENOTTY;


		if (cmd == ND_CMD_CALL)
			call_pkg = buf;
		func = cmd_to_func(nfit_mem, cmd, call_pkg);
		if (func < 0)
			return func;
		dimm_name = nvdimm_name(nvdimm);
		dimm_name = nvdimm_name(nvdimm);
		cmd_name = nvdimm_cmd_name(cmd);
		cmd_name = nvdimm_cmd_name(cmd);
		cmd_mask = nvdimm_cmd_mask(nvdimm);
		cmd_mask = nvdimm_cmd_mask(nvdimm);
@@ -456,6 +475,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
	} else {
	} else {
		struct acpi_device *adev = to_acpi_dev(acpi_desc);
		struct acpi_device *adev = to_acpi_dev(acpi_desc);


		func = cmd;
		cmd_name = nvdimm_bus_cmd_name(cmd);
		cmd_name = nvdimm_bus_cmd_name(cmd);
		cmd_mask = nd_desc->cmd_mask;
		cmd_mask = nd_desc->cmd_mask;
		dsm_mask = cmd_mask;
		dsm_mask = cmd_mask;
@@ -470,7 +490,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
	if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
	if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
		return -ENOTTY;
		return -ENOTTY;


	if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask))
	/*
	 * Check for a valid command.  For ND_CMD_CALL, we also have to
	 * make sure that the DSM function is supported.
	 */
	if (cmd == ND_CMD_CALL && !test_bit(func, &dsm_mask))
		return -ENOTTY;
	else if (!test_bit(cmd, &cmd_mask))
		return -ENOTTY;
		return -ENOTTY;


	in_obj.type = ACPI_TYPE_PACKAGE;
	in_obj.type = ACPI_TYPE_PACKAGE;
@@ -1867,6 +1893,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
		return 0;
		return 0;
	}
	}


	/*
	 * Function 0 is the command interrogation function, don't
	 * export it to potential userspace use, and enable it to be
	 * used as an error value in acpi_nfit_ctl().
	 */
	dsm_mask &= ~1UL;

	guid = to_nfit_uuid(nfit_mem->family);
	guid = to_nfit_uuid(nfit_mem->family);
	for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
	for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
		if (acpi_check_dsm(adev_dimm->handle, guid,
		if (acpi_check_dsm(adev_dimm->handle, guid,
@@ -2042,11 +2075,6 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
		if (!nvdimm)
		if (!nvdimm)
			continue;
			continue;


		rc = nvdimm_security_setup_events(nvdimm);
		if (rc < 0)
			dev_warn(acpi_desc->dev,
				"security event setup failed: %d\n", rc);

		nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
		nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
		if (nfit_kernfs)
		if (nfit_kernfs)
			nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
			nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
+6 −0
Original line number Original line Diff line number Diff line
@@ -26,6 +26,12 @@ static int nvdimm_probe(struct device *dev)
	struct nvdimm_drvdata *ndd;
	struct nvdimm_drvdata *ndd;
	int rc;
	int rc;


	rc = nvdimm_security_setup_events(dev);
	if (rc < 0) {
		dev_err(dev, "security event setup failed: %d\n", rc);
		return rc;
	}

	rc = nvdimm_check_config_data(dev);
	rc = nvdimm_check_config_data(dev);
	if (rc) {
	if (rc) {
		/* not required for non-aliased nvdimm, ex. NVDIMM-N */
		/* not required for non-aliased nvdimm, ex. NVDIMM-N */
+17 −5
Original line number Original line Diff line number Diff line
@@ -578,13 +578,25 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
}
}
EXPORT_SYMBOL_GPL(__nvdimm_create);
EXPORT_SYMBOL_GPL(__nvdimm_create);


int nvdimm_security_setup_events(struct nvdimm *nvdimm)
static void shutdown_security_notify(void *data)
{
{
	nvdimm->sec.overwrite_state = sysfs_get_dirent(nvdimm->dev.kobj.sd,
	struct nvdimm *nvdimm = data;
			"security");

	if (!nvdimm->sec.overwrite_state)
	sysfs_put(nvdimm->sec.overwrite_state);
		return -ENODEV;
}

int nvdimm_security_setup_events(struct device *dev)
{
	struct nvdimm *nvdimm = to_nvdimm(dev);

	if (nvdimm->sec.state < 0 || !nvdimm->sec.ops
			|| !nvdimm->sec.ops->overwrite)
		return 0;
		return 0;
	nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
	if (!nvdimm->sec.overwrite_state)
		return -ENOMEM;

	return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm);
}
}
EXPORT_SYMBOL_GPL(nvdimm_security_setup_events);
EXPORT_SYMBOL_GPL(nvdimm_security_setup_events);


+1 −0
Original line number Original line Diff line number Diff line
@@ -250,6 +250,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
void nvdimm_set_aliasing(struct device *dev);
void nvdimm_set_aliasing(struct device *dev);
void nvdimm_set_locked(struct device *dev);
void nvdimm_set_locked(struct device *dev);
void nvdimm_clear_locked(struct device *dev);
void nvdimm_clear_locked(struct device *dev);
int nvdimm_security_setup_events(struct device *dev);
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
#if IS_ENABLED(CONFIG_NVDIMM_KEYS)
int nvdimm_security_unlock(struct device *dev);
int nvdimm_security_unlock(struct device *dev);
#else
#else
+0 −1
Original line number Original line Diff line number Diff line
@@ -235,7 +235,6 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus,
			cmd_mask, num_flush, flush_wpq, NULL, NULL);
			cmd_mask, num_flush, flush_wpq, NULL, NULL);
}
}


int nvdimm_security_setup_events(struct nvdimm *nvdimm);
const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd);
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd);
u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd,
Loading