Re: [PATCH 2/4] test: firmware_class: add asynchronous request trigger
flat view
From: Brian Norris <computersforpeace@gmail.com>
Date: 2015-12-09 21:48:49
Also in:
lkml
Hi Kees, On Wed, Dec 09, 2015 at 01:09:02PM -0800, Kees Cook wrote:
On Tue, Dec 8, 2015 at 6:38 PM, Brian Norris [off-list ref] wrote:quoted
diff --git a/lib/test_firmware.c b/lib/test_firmware.c index 841191061816..ba0a12d0301d 100644 --- a/lib/test_firmware.c +++ b/lib/test_firmware.c@@ -12,6 +12,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/printk.h> +#include <linux/completion.h> #include <linux/firmware.h> #include <linux/device.h> #include <linux/fs.h>@@ -81,6 +82,57 @@ out: } static DEVICE_ATTR_WO(trigger_request); +static DECLARE_COMPLETION(async_fw_done); + +static void trigger_async_request_cb(const struct firmware *fw, void *context) +{ + test_firmware = fw; + complete(&async_fw_done); +} + +static ssize_t trigger_async_request_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + char *name; + + name = kzalloc(count + 1, GFP_KERNEL); + if (!name) + return -ENOSPC; + memcpy(name, buf, count);It strikes me that this (and the existing code) should use kstrndup instead, since the request_firmware* interfaces will ignore \0 bytes in the name: name = kstrndup(buf, count, GFP_KERNEL); if (!name) return -ENOSPC;
Thought of that at some point, then for some reason I didn't do it. Probably laziness... Will do in a v2, along with the more important fix below.
quoted
+ + pr_info("loading '%s'\n", name); + + mutex_lock(&test_fw_mutex); + release_firmware(test_firmware); + test_firmware = NULL; + rc = request_firmware_nowait(THIS_MODULE, 1, name, dev, GFP_KERNEL, + NULL, trigger_async_request_cb); + /* Free 'name' ASAP, to test for race conditions */ + kfree(name); + if (rc) { + pr_info("async load of '%s' failed: %d\n", name, rc);Well, that's a little TOO soon. :) The pr_info uses it still.
Haha, yeah... nice catch.
I was also thinking, since use-after-free isn't necessarily immediately
obvious (this worked fine in my testing), that maybe we could poison the
buffer before kfree()'ing? Like:
name = ...;
len = strlen(name);
...
rc = request_firmware_nowait(...);
if (rc) {
pr_info("...");
kfree(name);
goto out;
}
/*
* Clear out the name, to test for race conditions with the
* async request
*/
memset(name, 0, len);
kfree(name);
quoted
+ goto out; + } + + wait_for_completion(&async_fw_done); + + if (test_firmware) { + pr_info("loaded: %zu\n", test_firmware->size); + rc = count; + } else { + pr_err("failed to async load firmware\n"); + rc = -ENODEV; + } + +out: + mutex_unlock(&test_fw_mutex); + + return rc; +} +static DEVICE_ATTR_WO(trigger_async_request); + static int __init test_firmware_init(void) { int rc;
... Brian