diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-11-09 17:33:49 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-11-09 17:33:49 -0500 |
| commit | a3157809772c4a5256ef68a4d5b21ea4ecc80ad4 (patch) | |
| tree | 1269b7442c98571c9a4bce5f02e2c7cc6e6d1d40 | |
| parent | c4c23fb6f2b17c922b493f79cf27a0d32397f461 (diff) | |
| parent | a87fa1d81a9fb5e9adca9820e16008c40ad09f33 (diff) | |
Merge branch 'devicetree/merge' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux
Pull devicetree bugfix from Grant Likely:
"One buffer overflow bug that shouldn't be left around"
* 'devicetree/merge' of git://git.kernel.org/pub/scm/linux/kernel/git/glikely/linux:
of: Fix overflow bug in string property parsing functions
| -rw-r--r-- | drivers/of/base.c | 88 | ||||
| -rw-r--r-- | drivers/of/selftest.c | 66 | ||||
| -rw-r--r-- | drivers/of/testcase-data/tests-phandle.dtsi | 2 | ||||
| -rw-r--r-- | include/linux/of.h | 84 |
4 files changed, 154 insertions, 86 deletions
diff --git a/drivers/of/base.c b/drivers/of/base.c index 2305dc0382bc..3823edf2d012 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c | |||
| @@ -1280,52 +1280,6 @@ int of_property_read_string(struct device_node *np, const char *propname, | |||
| 1280 | EXPORT_SYMBOL_GPL(of_property_read_string); | 1280 | EXPORT_SYMBOL_GPL(of_property_read_string); |
| 1281 | 1281 | ||
| 1282 | /** | 1282 | /** |
| 1283 | * of_property_read_string_index - Find and read a string from a multiple | ||
| 1284 | * strings property. | ||
| 1285 | * @np: device node from which the property value is to be read. | ||
| 1286 | * @propname: name of the property to be searched. | ||
| 1287 | * @index: index of the string in the list of strings | ||
| 1288 | * @out_string: pointer to null terminated return string, modified only if | ||
| 1289 | * return value is 0. | ||
| 1290 | * | ||
| 1291 | * Search for a property in a device tree node and retrieve a null | ||
| 1292 | * terminated string value (pointer to data, not a copy) in the list of strings | ||
| 1293 | * contained in that property. | ||
| 1294 | * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if | ||
| 1295 | * property does not have a value, and -EILSEQ if the string is not | ||
| 1296 | * null-terminated within the length of the property data. | ||
| 1297 | * | ||
| 1298 | * The out_string pointer is modified only if a valid string can be decoded. | ||
| 1299 | */ | ||
| 1300 | int of_property_read_string_index(struct device_node *np, const char *propname, | ||
| 1301 | int index, const char **output) | ||
| 1302 | { | ||
| 1303 | struct property *prop = of_find_property(np, propname, NULL); | ||
| 1304 | int i = 0; | ||
| 1305 | size_t l = 0, total = 0; | ||
| 1306 | const char *p; | ||
| 1307 | |||
| 1308 | if (!prop) | ||
| 1309 | return -EINVAL; | ||
| 1310 | if (!prop->value) | ||
| 1311 | return -ENODATA; | ||
| 1312 | if (strnlen(prop->value, prop->length) >= prop->length) | ||
| 1313 | return -EILSEQ; | ||
| 1314 | |||
| 1315 | p = prop->value; | ||
| 1316 | |||
| 1317 | for (i = 0; total < prop->length; total += l, p += l) { | ||
| 1318 | l = strlen(p) + 1; | ||
| 1319 | if (i++ == index) { | ||
| 1320 | *output = p; | ||
| 1321 | return 0; | ||
| 1322 | } | ||
| 1323 | } | ||
| 1324 | return -ENODATA; | ||
| 1325 | } | ||
| 1326 | EXPORT_SYMBOL_GPL(of_property_read_string_index); | ||
| 1327 | |||
| 1328 | /** | ||
| 1329 | * of_property_match_string() - Find string in a list and return index | 1283 | * of_property_match_string() - Find string in a list and return index |
| 1330 | * @np: pointer to node containing string list property | 1284 | * @np: pointer to node containing string list property |
| 1331 | * @propname: string list property name | 1285 | * @propname: string list property name |
| @@ -1351,7 +1305,7 @@ int of_property_match_string(struct device_node *np, const char *propname, | |||
| 1351 | end = p + prop->length; | 1305 | end = p + prop->length; |
| 1352 | 1306 | ||
| 1353 | for (i = 0; p < end; i++, p += l) { | 1307 | for (i = 0; p < end; i++, p += l) { |
| 1354 | l = strlen(p) + 1; | 1308 | l = strnlen(p, end - p) + 1; |
| 1355 | if (p + l > end) | 1309 | if (p + l > end) |
| 1356 | return -EILSEQ; | 1310 | return -EILSEQ; |
| 1357 | pr_debug("comparing %s with %s\n", string, p); | 1311 | pr_debug("comparing %s with %s\n", string, p); |
| @@ -1363,39 +1317,41 @@ int of_property_match_string(struct device_node *np, const char *propname, | |||
| 1363 | EXPORT_SYMBOL_GPL(of_property_match_string); | 1317 | EXPORT_SYMBOL_GPL(of_property_match_string); |
| 1364 | 1318 | ||
| 1365 | /** | 1319 | /** |
| 1366 | * of_property_count_strings - Find and return the number of strings from a | 1320 | * of_property_read_string_util() - Utility helper for parsing string properties |
| 1367 | * multiple strings property. | ||
| 1368 | * @np: device node from which the property value is to be read. | 1321 | * @np: device node from which the property value is to be read. |
| 1369 | * @propname: name of the property to be searched. | 1322 | * @propname: name of the property to be searched. |
| 1323 | * @out_strs: output array of string pointers. | ||
| 1324 | * @sz: number of array elements to read. | ||
| 1325 | * @skip: Number of strings to skip over at beginning of list. | ||
| 1370 | * | 1326 | * |
| 1371 | * Search for a property in a device tree node and retrieve the number of null | 1327 | * Don't call this function directly. It is a utility helper for the |
| 1372 | * terminated string contain in it. Returns the number of strings on | 1328 | * of_property_read_string*() family of functions. |
| 1373 | * success, -EINVAL if the property does not exist, -ENODATA if property | ||
| 1374 | * does not have a value, and -EILSEQ if the string is not null-terminated | ||
| 1375 | * within the length of the property data. | ||
| 1376 | */ | 1329 | */ |
| 1377 | int of_property_count_strings(struct device_node *np, const char *propname) | 1330 | int of_property_read_string_helper(struct device_node *np, const char *propname, |
| 1331 | const char **out_strs, size_t sz, int skip) | ||
| 1378 | { | 1332 | { |
| 1379 | struct property *prop = of_find_property(np, propname, NULL); | 1333 | struct property *prop = of_find_property(np, propname, NULL); |
| 1380 | int i = 0; | 1334 | int l = 0, i = 0; |
| 1381 | size_t l = 0, total = 0; | 1335 | const char *p, *end; |
| 1382 | const char *p; | ||
| 1383 | 1336 | ||
| 1384 | if (!prop) | 1337 | if (!prop) |
| 1385 | return -EINVAL; | 1338 | return -EINVAL; |
| 1386 | if (!prop->value) | 1339 | if (!prop->value) |
| 1387 | return -ENODATA; | 1340 | return -ENODATA; |
| 1388 | if (strnlen(prop->value, prop->length) >= prop->length) | ||
| 1389 | return -EILSEQ; | ||
| 1390 | |||
| 1391 | p = prop->value; | 1341 | p = prop->value; |
| 1342 | end = p + prop->length; | ||
| 1392 | 1343 | ||
| 1393 | for (i = 0; total < prop->length; total += l, p += l, i++) | 1344 | for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) { |
| 1394 | l = strlen(p) + 1; | 1345 | l = strnlen(p, end - p) + 1; |
| 1395 | 1346 | if (p + l > end) | |
| 1396 | return i; | 1347 | return -EILSEQ; |
| 1348 | if (out_strs && i >= skip) | ||
| 1349 | *out_strs++ = p; | ||
| 1350 | } | ||
| 1351 | i -= skip; | ||
| 1352 | return i <= 0 ? -ENODATA : i; | ||
| 1397 | } | 1353 | } |
| 1398 | EXPORT_SYMBOL_GPL(of_property_count_strings); | 1354 | EXPORT_SYMBOL_GPL(of_property_read_string_helper); |
| 1399 | 1355 | ||
| 1400 | void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) | 1356 | void of_print_phandle_args(const char *msg, const struct of_phandle_args *args) |
| 1401 | { | 1357 | { |
diff --git a/drivers/of/selftest.c b/drivers/of/selftest.c index 78001270a598..11b873c54a77 100644 --- a/drivers/of/selftest.c +++ b/drivers/of/selftest.c | |||
| @@ -339,8 +339,9 @@ static void __init of_selftest_parse_phandle_with_args(void) | |||
| 339 | selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); | 339 | selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc); |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | static void __init of_selftest_property_match_string(void) | 342 | static void __init of_selftest_property_string(void) |
| 343 | { | 343 | { |
| 344 | const char *strings[4]; | ||
| 344 | struct device_node *np; | 345 | struct device_node *np; |
| 345 | int rc; | 346 | int rc; |
| 346 | 347 | ||
| @@ -357,13 +358,66 @@ static void __init of_selftest_property_match_string(void) | |||
| 357 | rc = of_property_match_string(np, "phandle-list-names", "third"); | 358 | rc = of_property_match_string(np, "phandle-list-names", "third"); |
| 358 | selftest(rc == 2, "third expected:0 got:%i\n", rc); | 359 | selftest(rc == 2, "third expected:0 got:%i\n", rc); |
| 359 | rc = of_property_match_string(np, "phandle-list-names", "fourth"); | 360 | rc = of_property_match_string(np, "phandle-list-names", "fourth"); |
| 360 | selftest(rc == -ENODATA, "unmatched string; rc=%i", rc); | 361 | selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc); |
| 361 | rc = of_property_match_string(np, "missing-property", "blah"); | 362 | rc = of_property_match_string(np, "missing-property", "blah"); |
| 362 | selftest(rc == -EINVAL, "missing property; rc=%i", rc); | 363 | selftest(rc == -EINVAL, "missing property; rc=%i\n", rc); |
| 363 | rc = of_property_match_string(np, "empty-property", "blah"); | 364 | rc = of_property_match_string(np, "empty-property", "blah"); |
| 364 | selftest(rc == -ENODATA, "empty property; rc=%i", rc); | 365 | selftest(rc == -ENODATA, "empty property; rc=%i\n", rc); |
| 365 | rc = of_property_match_string(np, "unterminated-string", "blah"); | 366 | rc = of_property_match_string(np, "unterminated-string", "blah"); |
| 366 | selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc); | 367 | selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); |
| 368 | |||
| 369 | /* of_property_count_strings() tests */ | ||
| 370 | rc = of_property_count_strings(np, "string-property"); | ||
| 371 | selftest(rc == 1, "Incorrect string count; rc=%i\n", rc); | ||
| 372 | rc = of_property_count_strings(np, "phandle-list-names"); | ||
| 373 | selftest(rc == 3, "Incorrect string count; rc=%i\n", rc); | ||
| 374 | rc = of_property_count_strings(np, "unterminated-string"); | ||
| 375 | selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); | ||
| 376 | rc = of_property_count_strings(np, "unterminated-string-list"); | ||
| 377 | selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc); | ||
| 378 | |||
| 379 | /* of_property_read_string_index() tests */ | ||
| 380 | rc = of_property_read_string_index(np, "string-property", 0, strings); | ||
| 381 | selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc); | ||
| 382 | strings[0] = NULL; | ||
| 383 | rc = of_property_read_string_index(np, "string-property", 1, strings); | ||
| 384 | selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); | ||
| 385 | rc = of_property_read_string_index(np, "phandle-list-names", 0, strings); | ||
| 386 | selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc); | ||
| 387 | rc = of_property_read_string_index(np, "phandle-list-names", 1, strings); | ||
