summaryrefslogtreecommitdiffstats
path: root/drivers/of
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2017-08-24 03:03:52 +0200
committerRob Herring <robh@kernel.org>2017-08-24 18:52:51 +0200
commit8c2a75e5687e0137ddbb35454c7a1a468079f790 (patch)
tree25d148d8ac6bd6e9c8f858e6a6645d073706cecd /drivers/of
parentof/device: Prevent buffer overflow in of_device_modalias() (diff)
downloadlinux-8c2a75e5687e0137ddbb35454c7a1a468079f790.tar.xz
linux-8c2a75e5687e0137ddbb35454c7a1a468079f790.zip
of/device: Fix of_device_get_modalias() buffer handling
of_device_request_module() calls of_device_get_modalias() with "len" 0, to calculate the size of the buffer needed to store the result, but due to integer promotion the ssize_t "len" will be compared as unsigned with strlen(compat) and the loop will generally never break. This results in a call to snprintf() with a negative len, which triggers below warning, followed by a dereference of a invalid pointer: [ 3.060067] WARNING: CPU: 0 PID: 51 at lib/vsprintf.c:2122 vsnprintf+0x348/0x6d8 ... [ 3.060301] [<ffffff800891ede8>] vsnprintf+0x348/0x6d8 [ 3.060308] [<ffffff800891f248>] snprintf+0x48/0x50 [ 3.060316] [<ffffff80086a7c80>] of_device_get_modalias+0x108/0x160 [ 3.060322] [<ffffff80086a7cf8>] of_device_request_module+0x20/0x88 ... Further more of_device_get_modalias() is supposed to return the number of bytes needed to store the entire modalias, so the loop needs to continue accumulate the total size even though the buffer is full. Finally the function is not expected to ensure space for the NUL, nor include it in the returned size, so only 1 should be added to the length of "compat" in the loop (to account for the character 'C'). Fixes: bc575064d688 ("of/device: use of_property_for_each_string to parse compatible strings") Cc: Rob Herring <robh@kernel.org> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org> Signed-off-by: Rob Herring <robh@kernel.org>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/device.c15
1 files changed, 10 insertions, 5 deletions
diff --git a/drivers/of/device.c b/drivers/of/device.c
index c5c06997fdd2..11a17582e568 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -195,10 +195,11 @@ EXPORT_SYMBOL(of_device_get_match_data);
static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len)
{
- const char *compat, *start = str;
+ const char *compat;
char *c;
struct property *p;
ssize_t csize;
+ ssize_t tsize;
if ((!dev) || (!dev->of_node))
return -ENODEV;
@@ -206,12 +207,16 @@ static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len
/* Name & Type */
csize = snprintf(str, len, "of:N%sT%s", dev->of_node->name,
dev->of_node->type);
+ tsize = csize;
len -= csize;
- str += csize;
+ if (str)
+ str += csize;
of_property_for_each_string(dev->of_node, "compatible", p, compat) {
- if (strlen(compat) + 2 > len)
- break;
+ csize = strlen(compat) + 1;
+ tsize += csize;
+ if (csize > len)
+ continue;
csize = snprintf(str, len, "C%s", compat);
for (c = str; c; ) {
@@ -223,7 +228,7 @@ static ssize_t of_device_get_modalias(struct device *dev, char *str, ssize_t len
str += csize;
}
- return str - start;
+ return tsize;
}
int of_device_request_module(struct device *dev)