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

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
...@@ -409,6 +409,32 @@ static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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,
......
...@@ -26,6 +26,12 @@ static int nvdimm_probe(struct device *dev) ...@@ -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 */
......
...@@ -578,13 +578,25 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, ...@@ -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);
......
...@@ -250,6 +250,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, ...@@ -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
......
...@@ -235,7 +235,6 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, ...@@ -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,
......
...@@ -18,8 +18,8 @@ ssize_t security_show(struct device *dev, ...@@ -18,8 +18,8 @@ ssize_t security_show(struct device *dev,
* For the test version we need to poll the "hardware" in order * For the test version we need to poll the "hardware" in order
* to get the updated status for unlock testing. * to get the updated status for unlock testing.
*/ */
nvdimm->sec.state = nvdimm_security_state(nvdimm, false); nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER);
nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, true); nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER);
switch (nvdimm->sec.state) { switch (nvdimm->sec.state) {
case NVDIMM_SECURITY_DISABLED: case NVDIMM_SECURITY_DISABLED:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment