]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
libnvdimm, nfit: move the check on nd_reserved2 to the endpoint
authorMeng Xu <mengxu.gatech@gmail.com>
Mon, 4 Sep 2017 15:34:33 +0000 (11:34 -0400)
committerDan Williams <dan.j.williams@intel.com>
Mon, 4 Sep 2017 18:02:21 +0000 (11:02 -0700)
Delay the check of nd_reserved2 to the actual endpoint (acpi_nfit_ctl)
that uses it, as a prevention of a potential double-fetch bug.

While examining the kernel source code, I found a dangerous operation that
could turn into a double-fetch situation (a race condition bug) where
the same userspace memory region are fetched twice into kernel with sanity
checks after the first fetch while missing checks after the second fetch.

In the case of _IOC_NR(ioctl_cmd) == ND_CMD_CALL:

1. The first fetch happens in line 935 copy_from_user(&pkg, p, sizeof(pkg)

2. subsequently `pkg.nd_reserved2` is asserted to be all zeroes
(line 984 to 986).

3. The second fetch happens in line 1022 copy_from_user(buf, p, buf_len)

4. Given that `p` can be fully controlled in userspace, an attacker can
race condition to override the header part of `p`, say,
`((struct nd_cmd_pkg *)p)->nd_reserved2` to arbitrary value
(say nine 0xFFFFFFFF for `nd_reserved2`) after the first fetch but before the
second fetch. The changed value will be copied to `buf`.

5. There is no checks on the second fetches until the use of it in
line 1034: nd_cmd_clear_to_send(nvdimm_bus, nvdimm, cmd, buf) and
line 1038: nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc)
which means that the assumed relation, `p->nd_reserved2` are all zeroes might
not hold after the second fetch. And once the control goes to these functions
we lose the context to assert the assumed relation.

6. Based on my manual analysis, `p->nd_reserved2` is not used in function
`nd_cmd_clear_to_send` and potential implementations of `nd_desc->ndctl`
so there is no working exploit against it right now. However, this could
easily turns to an exploitable one if careless developers start to use
`p->nd_reserved2` later and assume that they are all zeroes.

Move the validation of the nd_reserved2 field to the ->ndctl()
implementation where it has a stable buffer to evaluate.

Signed-off-by: Meng Xu <mengxu.gatech@gmail.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/acpi/nfit/core.c
drivers/nvdimm/bus.c

index c20124a6eb4964890e25edc50d0de2cacfd80bba..42221e785c47b9f52a51572214e41973a9b96cd0 100644 (file)
@@ -228,6 +228,10 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
        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) {
index 66586ce23f1b176a984b8024ba2d3323f337fc07..baf283986a7ec38ea4f2562a43b36e31e1743f95 100644 (file)
@@ -987,10 +987,6 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
                dev_dbg(dev, "%s:%s, idx: %llu, in: %u, out: %u, len %llu\n",
                                __func__, dimm_name, pkg.nd_command,
                                in_len, out_len, buf_len);
-
-               for (i = 0; i < ARRAY_SIZE(pkg.nd_reserved2); i++)
-                       if (pkg.nd_reserved2[i])
-                               return -EINVAL;
        }
 
        /* process an output envelope */