diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-27 12:11:51 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-01-27 12:11:51 -0500 |
| commit | 6a2651b55bcf5b56177d4ccaeb52adeeadb142ea (patch) | |
| tree | 2c36e0a1e88fbf59adcb900494337bfc749a5734 | |
| parent | 78e372e6509bc2412e86afb11be65185f4c9c568 (diff) | |
| parent | 11189c1089da413aa4b5fd6be4c4d47c78968819 (diff) | |
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
| -rw-r--r-- | drivers/acpi/nfit/core.c | 66 | ||||
| -rw-r--r-- | drivers/nvdimm/dimm.c | 6 | ||||
| -rw-r--r-- | drivers/nvdimm/dimm_devs.c | 22 | ||||
| -rw-r--r-- | drivers/nvdimm/nd.h | 1 | ||||
| -rw-r--r-- | include/linux/libnvdimm.h | 1 | ||||
| -rw-r--r-- | tools/testing/nvdimm/dimm_devs.c | 4 |
6 files changed, 73 insertions, 27 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 5143e11e3b0f..e18ade5d74e9 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c | |||
| @@ -409,6 +409,32 @@ static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func) | |||
| 409 | return true; | 409 | return true; |
| 410 | } | 410 | } |
| 411 | 411 | ||
| 412 | static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd, | ||
| 413 | struct nd_cmd_pkg *call_pkg) | ||
| 414 | { | ||
| 415 | if (call_pkg) { | ||
| 416 | int i; | ||
| 417 | |||
| 418 | if (nfit_mem->family != call_pkg->nd_family) | ||
| 419 | return -ENOTTY; | ||
| 420 | |||
| 421 | for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) | ||
| 422 | if (call_pkg->nd_reserved2[i]) | ||
| 423 | return -EINVAL; | ||
| 424 | return call_pkg->nd_command; | ||
| 425 | } | ||
| 426 | |||
| 427 | /* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */ | ||
| 428 | if (nfit_mem->family == NVDIMM_FAMILY_INTEL) | ||
| 429 | return cmd; | ||
| 430 | |||
| 431 | /* | ||
| 432 | * Force function number validation to fail since 0 is never | ||
| 433 | * published as a valid function in dsm_mask. | ||
| 434 | */ | ||
| 435 | return 0; | ||
| 436 | } | ||
| 437 | |||
| 412 | int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, | 438 | int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, |
| 413 | unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) | 439 | unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) |
| 414 | { | 440 | { |
| @@ -422,30 +448,23 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, | |||
| 422 | unsigned long cmd_mask, dsm_mask; | 448 | unsigned long cmd_mask, dsm_mask; |
| 423 | u32 offset, fw_status = 0; | 449 | u32 offset, fw_status = 0; |
| 424 | acpi_handle handle; | 450 | acpi_handle handle; |
| 425 | unsigned int func; | ||
| 426 | const guid_t *guid; | 451 | const guid_t *guid; |
| 427 | int rc, i; | 452 | int func, rc, i; |
| 428 | 453 | ||
| 429 | if (cmd_rc) | 454 | if (cmd_rc) |
| 430 | *cmd_rc = -EINVAL; | 455 | *cmd_rc = -EINVAL; |
| 431 | func = cmd; | ||
| 432 | if (cmd == ND_CMD_CALL) { | ||
| 433 | call_pkg = buf; | ||
| 434 | func = call_pkg->nd_command; | ||
| 435 | |||
| 436 | for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++) | ||
| 437 | if (call_pkg->nd_reserved2[i]) | ||
| 438 | return -EINVAL; | ||
| 439 | } | ||
| 440 | 456 | ||
| 441 | if (nvdimm) { | 457 | if (nvdimm) { |
| 442 | struct acpi_device *adev = nfit_mem->adev; | 458 | struct acpi_device *adev = nfit_mem->adev; |
| 443 | 459 | ||
| 444 | if (!adev) | 460 | if (!adev) |
| 445 | return -ENOTTY; | 461 | return -ENOTTY; |
| 446 | if (call_pkg && nfit_mem->family != call_pkg->nd_family) | ||
| 447 | return -ENOTTY; | ||
| 448 | 462 | ||
| 463 | if (cmd == ND_CMD_CALL) | ||
| 464 | call_pkg = buf; | ||
| 465 | func = cmd_to_func(nfit_mem, cmd, call_pkg); | ||
| 466 | if (func < 0) | ||
| 467 | return func; | ||
| 449 | dimm_name = nvdimm_name(nvdimm); | 468 | dimm_name = nvdimm_name(nvdimm); |
| 450 | cmd_name = nvdimm_cmd_name(cmd); | 469 | cmd_name = nvdimm_cmd_name(cmd); |
| 451 | cmd_mask = nvdimm_cmd_mask(nvdimm); | 470 | cmd_mask = nvdimm_cmd_mask(nvdimm); |
| @@ -456,6 +475,7 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, | |||
| 456 | } else { | 475 | } else { |
| 457 | struct acpi_device *adev = to_acpi_dev(acpi_desc); | 476 | struct acpi_device *adev = to_acpi_dev(acpi_desc); |
| 458 | 477 | ||
| 478 | func = cmd; | ||
| 459 | cmd_name = nvdimm_bus_cmd_name(cmd); | 479 | cmd_name = nvdimm_bus_cmd_name(cmd); |
| 460 | cmd_mask = nd_desc->cmd_mask; | 480 | cmd_mask = nd_desc->cmd_mask; |
| 461 | dsm_mask = cmd_mask; | 481 | dsm_mask = cmd_mask; |
| @@ -470,7 +490,13 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, | |||
| 470 | if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) | 490 | if (!desc || (cmd && (desc->out_num + desc->in_num == 0))) |
| 471 | return -ENOTTY; | 491 | return -ENOTTY; |
| 472 | 492 | ||
| 473 | if (!test_bit(cmd, &cmd_mask) || !test_bit(func, &dsm_mask)) | 493 | /* |
| 494 | * Check for a valid command. For ND_CMD_CALL, we also have to | ||
| 495 | * make sure that the DSM function is supported. | ||
| 496 | */ | ||
| 497 | if (cmd == ND_CMD_CALL && !test_bit(func, &dsm_mask)) | ||
| 498 | return -ENOTTY; | ||
| 499 | else if (!test_bit(cmd, &cmd_mask)) | ||
| 474 | return -ENOTTY; | 500 | return -ENOTTY; |
| 475 | 501 | ||
| 476 | in_obj.type = ACPI_TYPE_PACKAGE; | 502 | in_obj.type = ACPI_TYPE_PACKAGE; |
| @@ -1867,6 +1893,13 @@ static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc, | |||
| 1867 | return 0; | 1893 | return 0; |
| 1868 | } | 1894 | } |
| 1869 | 1895 | ||
| 1896 | /* | ||
| 1897 | * Function 0 is the command interrogation function, don't | ||
| 1898 | * export it to potential userspace use, and enable it to be | ||
| 1899 | * used as an error value in acpi_nfit_ctl(). | ||
| 1900 | */ | ||
| 1901 | dsm_mask &= ~1UL; | ||
| 1902 | |||
| 1870 | guid = to_nfit_uuid(nfit_mem->family); | 1903 | guid = to_nfit_uuid(nfit_mem->family); |
| 1871 | for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) | 1904 | for_each_set_bit(i, &dsm_mask, BITS_PER_LONG) |
| 1872 | if (acpi_check_dsm(adev_dimm->handle, guid, | 1905 | 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 | if (!nvdimm) | 2075 | if (!nvdimm) |
| 2043 | continue; | 2076 | continue; |
| 2044 | 2077 | ||
| 2045 | rc = nvdimm_security_setup_events(nvdimm); | ||
| 2046 | if (rc < 0) | ||
| 2047 | dev_warn(acpi_desc->dev, | ||
| 2048 | "security event setup failed: %d\n", rc); | ||
| 2049 | |||
| 2050 | nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); | 2078 | nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit"); |
| 2051 | if (nfit_kernfs) | 2079 | if (nfit_kernfs) |
| 2052 | nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, | 2080 | nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs, |
diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 0cf58cabc9ed..3cf50274fadb 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c | |||
| @@ -26,6 +26,12 @@ static int nvdimm_probe(struct device *dev) | |||
| 26 | struct nvdimm_drvdata *ndd; | 26 | struct nvdimm_drvdata *ndd; |
| 27 | int rc; | 27 | int rc; |
| 28 | 28 | ||
| 29 | rc = nvdimm_security_setup_events(dev); | ||
| 30 | if (rc < 0) { | ||
| 31 | dev_err(dev, "security event setup failed: %d\n", rc); | ||
| 32 | return rc; | ||
| 33 | } | ||
| 34 | |||
| 29 | rc = nvdimm_check_config_data(dev); | 35 | rc = nvdimm_check_config_data(dev); |
| 30 | if (rc) { | 36 | if (rc) { |
| 31 | /* not required for non-aliased nvdimm, ex. NVDIMM-N */ | 37 | /* not required for non-aliased nvdimm, ex. NVDIMM-N */ |
diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 4890310df874..efe412a6b5b9 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c | |||
| @@ -578,13 +578,25 @@ struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus, | |||
| 578 | } | 578 | } |
| 579 | EXPORT_SYMBOL_GPL(__nvdimm_create); | 579 | EXPORT_SYMBOL_GPL(__nvdimm_create); |
| 580 | 580 | ||
| 581 | int nvdimm_security_setup_events(struct nvdimm *nvdimm) | 581 | static void shutdown_security_notify(void *data) |
| 582 | { | 582 | { |
| 583 | nvdimm->sec.overwrite_state = sysfs_get_dirent(nvdimm->dev.kobj.sd, | 583 | struct nvdimm *nvdimm = data; |
| 584 | "security"); | 584 | |
| 585 | sysfs_put(nvdimm->sec.overwrite_state); | ||
| 586 | } | ||
| 587 | |||
| 588 | int nvdimm_security_setup_events(struct device *dev) | ||
| 589 | { | ||
| 590 | struct nvdimm *nvdimm = to_nvdimm(dev); | ||
| 591 | |||
| 592 | if (nvdimm->sec.state < 0 || !nvdimm->sec.ops | ||
| 593 | || !nvdimm->sec.ops->overwrite) | ||
| 594 | return 0; | ||
| 595 | nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security"); | ||
| 585 | if (!nvdimm->sec.overwrite_state) | 596 | if (!nvdimm->sec.overwrite_state) |
| 586 | return -ENODEV; | 597 | return -ENOMEM; |
| 587 | return 0; | 598 | |
| 599 | return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm); | ||
| 588 | } | 600 | } |
| 589 | EXPORT_SYMBOL_GPL(nvdimm_security_setup_events); | 601 | EXPORT_SYMBOL_GPL(nvdimm_security_setup_events); |
| 590 | 602 | ||
diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index cfde992684e7..379bf4305e61 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h | |||
| @@ -250,6 +250,7 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys, | |||
| 250 | void nvdimm_set_aliasing(struct device *dev); | 250 | void nvdimm_set_aliasing(struct device *dev); |
| 251 | void nvdimm_set_locked(struct device *dev); | 251 | void nvdimm_set_locked(struct device *dev); |
| 252 | void nvdimm_clear_locked(struct device *dev); | 252 | void nvdimm_clear_locked(struct device *dev); |
| 253 | int nvdimm_security_setup_events(struct device *dev); | ||
| 253 | #if IS_ENABLED(CONFIG_NVDIMM_KEYS) | 254 | #if IS_ENABLED(CONFIG_NVDIMM_KEYS) |
| 254 | int nvdimm_security_unlock(struct device *dev); | 255 | int nvdimm_security_unlock(struct device *dev); |
| 255 | #else | 256 | #else |
diff --git a/include/linux/libnvdimm.h b/include/linux/libnvdimm.h index 7315977b64da..ad609617aeb8 100644 --- a/include/linux/libnvdimm.h +++ b/include/linux/libnvdimm.h | |||
| @@ -235,7 +235,6 @@ static inline struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, | |||
| 235 | cmd_mask, num_flush, flush_wpq, NULL, NULL); | 235 | cmd_mask, num_flush, flush_wpq, NULL, NULL); |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | int nvdimm_security_setup_events(struct nvdimm *nvdimm); | ||
| 239 | const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); | 238 | const struct nd_cmd_desc *nd_cmd_dimm_desc(int cmd); |
| 240 | const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); | 239 | const struct nd_cmd_desc *nd_cmd_bus_desc(int cmd); |
| 241 | u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, | 240 | u32 nd_cmd_in_size(struct nvdimm *nvdimm, int cmd, |
diff --git a/tools/testing/nvdimm/dimm_devs.c b/tools/testing/nvdimm/dimm_devs.c index e75238404555..2d4baf57822f 100644 --- a/tools/testing/nvdimm/dimm_devs.c +++ b/tools/testing/nvdimm/dimm_devs.c | |||
| @@ -18,8 +18,8 @@ ssize_t security_show(struct device *dev, | |||
| 18 | * For the test version we need to poll the "hardware" in order | 18 | * For the test version we need to poll the "hardware" in order |
| 19 | * to get the updated status for unlock testing. | 19 | * to get the updated status for unlock testing. |
| 20 | */ | 20 | */ |
| 21 | nvdimm->sec.state = nvdimm_security_state(nvdimm, false); | 21 | nvdimm->sec.state = nvdimm_security_state(nvdimm, NVDIMM_USER); |
| 22 | nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, true); | 22 | nvdimm->sec.ext_state = nvdimm_security_state(nvdimm, NVDIMM_MASTER); |
| 23 | 23 | ||
| 24 | switch (nvdimm->sec.state) { | 24 | switch (nvdimm->sec.state) { |
| 25 | case NVDIMM_SECURITY_DISABLED: | 25 | case NVDIMM_SECURITY_DISABLED: |
