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); | ||
388 | selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc); | ||
389 | rc = of_property_read_string_index(np, "phandle-list-names", 2, strings); | ||
390 | selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc); | ||
391 | strings[0] = NULL; | ||
392 | rc = of_property_read_string_index(np, "phandle-list-names", 3, strings); | ||
393 | selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); | ||
394 | strings[0] = NULL; | ||
395 | rc = of_property_read_string_index(np, "unterminated-string", 0, strings); | ||
396 | selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); | ||
397 | rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings); | ||
398 | selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc); | ||
399 | strings[0] = NULL; | ||
400 | rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */ | ||
401 | selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc); | ||
402 | strings[1] = NULL; | ||
403 | |||
404 | /* of_property_read_string_array() tests */ | ||
405 | rc = of_property_read_string_array(np, "string-property", strings, 4); | ||
406 | selftest(rc == 1, "Incorrect string count; rc=%i\n", rc); | ||
407 | rc = of_property_read_string_array(np, "phandle-list-names", strings, 4); | ||
408 | selftest(rc == 3, "Incorrect string count; rc=%i\n", rc); | ||
409 | rc = of_property_read_string_array(np, "unterminated-string", strings, 4); | ||
410 | selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc); | ||
411 | /* -- An incorrectly formed string should cause a failure */ | ||
412 | rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4); | ||
413 | selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc); | ||
414 | /* -- parsing the correctly formed strings should still work: */ | ||
415 | strings[2] = NULL; | ||
416 | rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2); | ||
417 | selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc); | ||
418 | strings[1] = NULL; | ||
419 | rc = of_property_read_string_array(np, "phandle-list-names", strings, 1); | ||
420 | selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]); | ||
367 | } | 421 | } |
368 | 422 | ||
369 | #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ | 423 | #define propcmp(p1, p2) (((p1)->length == (p2)->length) && \ |
@@ -881,7 +935,7 @@ static int __init of_selftest(void) | |||
881 | of_selftest_find_node_by_name(); | 935 | of_selftest_find_node_by_name(); |
882 | of_selftest_dynamic(); | 936 | of_selftest_dynamic(); |
883 | of_selftest_parse_phandle_with_args(); | 937 | of_selftest_parse_phandle_with_args(); |
884 | of_selftest_property_match_string(); | 938 | of_selftest_property_string(); |
885 | of_selftest_property_copy(); | 939 | of_selftest_property_copy(); |
886 | of_selftest_changeset(); | 940 | of_selftest_changeset(); |
887 | of_selftest_parse_interrupts(); | 941 | of_selftest_parse_interrupts(); |
diff --git a/drivers/of/testcase-data/tests-phandle.dtsi b/drivers/of/testcase-data/tests-phandle.dtsi index ce0fe083d406..5b1527e8a7fb 100644 --- a/drivers/of/testcase-data/tests-phandle.dtsi +++ b/drivers/of/testcase-data/tests-phandle.dtsi | |||
@@ -39,7 +39,9 @@ | |||
39 | phandle-list-bad-args = <&provider2 1 0>, | 39 | phandle-list-bad-args = <&provider2 1 0>, |
40 | <&provider3 0>; | 40 | <&provider3 0>; |
41 | empty-property; | 41 | empty-property; |
42 | string-property = "foobar"; | ||
42 | unterminated-string = [40 41 42 43]; | 43 | unterminated-string = [40 41 42 43]; |
44 | unterminated-string-list = "first", "second", [40 41 42 43]; | ||
43 | }; | 45 | }; |
44 | }; | 46 | }; |
45 | }; | 47 | }; |
diff --git a/include/linux/of.h b/include/linux/of.h index 6545e7aec7bb..29f0adc5f3e4 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
@@ -267,14 +267,12 @@ extern int of_property_read_u64(const struct device_node *np, | |||
267 | extern int of_property_read_string(struct device_node *np, | 267 | extern int of_property_read_string(struct device_node *np, |
268 | const char *propname, | 268 | const char *propname, |
269 | const char **out_string); | 269 | const char **out_string); |
270 | extern int of_property_read_string_index(struct device_node *np, | ||
271 | const char *propname, | ||
272 | int index, const char **output); | ||
273 | extern int of_property_match_string(struct device_node *np, | 270 | extern int of_property_match_string(struct device_node *np, |
274 | const char *propname, | 271 | const char *propname, |
275 | const char *string); | 272 | const char *string); |
276 | extern int of_property_count_strings(struct device_node *np, | 273 | extern int of_property_read_string_helper(struct device_node *np, |
277 | const char *propname); | 274 | const char *propname, |
275 | const char **out_strs, size_t sz, int index); | ||
278 | extern int of_device_is_compatible(const struct device_node *device, | 276 | extern int of_device_is_compatible(const struct device_node *device, |
279 | const char *); | 277 | const char *); |
280 | extern int of_device_is_available(const struct device_node *device); | 278 | extern int of_device_is_available(const struct device_node *device); |
@@ -486,15 +484,9 @@ static inline int of_property_read_string(struct device_node *np, | |||
486 | return -ENOSYS; | 484 | return -ENOSYS; |
487 | } | 485 | } |
488 | 486 | ||
489 | static inline int of_property_read_string_index(struct device_node *np, | 487 | static inline int of_property_read_string_helper(struct device_node *np, |
490 | const char *propname, int index, | 488 | const char *propname, |
491 | const char **out_string) | 489 | const char **out_strs, size_t sz, int index) |
492 | { | ||
493 | return -ENOSYS; | ||
494 | } | ||
495 | |||
496 | static inline int of_property_count_strings(struct device_node *np, | ||
497 | const char *propname) | ||
498 | { | 490 | { |
499 | return -ENOSYS; | 491 | return -ENOSYS; |
500 | } | 492 | } |
@@ -668,6 +660,70 @@ static inline int of_property_count_u64_elems(const struct device_node *np, | |||
668 | } | 660 | } |
669 | 661 | ||
670 | /** | 662 | /** |
663 | * of_property_read_string_array() - Read an array of strings from a multiple | ||
664 | * strings property. | ||
665 | * @np: device node from which the property value is to be read. | ||
666 | * @propname: name of the property to be searched. | ||
667 | * @out_strs: output array of string pointers. | ||
668 | * @sz: number of array elements to read. | ||
669 | * | ||
670 | * Search for a property in a device tree node and retrieve a list of | ||
671 | * terminated string values (pointer to data, not a copy) in that property. | ||
672 | * | ||
673 | * If @out_strs is NULL, the number of strings in the property is returned. | ||
674 | */ | ||
675 | static inline int of_property_read_string_array(struct device_node *np, | ||
676 | const char *propname, const char **out_strs, | ||
677 | size_t sz) | ||
678 | { | ||
679 | return of_property_read_string_helper(np, propname, out_strs, sz, 0); | ||
680 | } | ||
681 | |||
682 | /** | ||
683 | * of_property_count_strings() - Find and return the number of strings from a | ||
684 | * multiple strings property. | ||
685 | * @np: device node from which the property value is to be read. | ||
686 | * @propname: name of the property to be searched. | ||
687 | * | ||
688 | * Search for a property in a device tree node and retrieve the number of null | ||
689 | * terminated string contain in it. Returns the number of strings on | ||
690 | * success, -EINVAL if the property does not exist, -ENODATA if property | ||
691 | * does not have a value, and -EILSEQ if the string is not null-terminated | ||
692 | * within the length of the property data. | ||
693 | */ | ||
694 | static inline int of_property_count_strings(struct device_node *np, | ||
695 | const char *propname) | ||
696 | { | ||
697 | return of_property_read_string_helper(np, propname, NULL, 0, 0); | ||
698 | } | ||
699 | |||
700 | /** | ||
701 | * of_property_read_string_index() - Find and read a string from a multiple | ||
702 | * strings property. | ||
703 | * @np: device node from which the property value is to be read. | ||
704 | * @propname: name of the property to be searched. | ||
705 | * @index: index of the string in the list of strings | ||
706 | * @out_string: pointer to null terminated return string, modified only if | ||
707 | * return value is 0. | ||
708 | * | ||
709 | * Search for a property in a device tree node and retrieve a null | ||
710 | * terminated string value (pointer to data, not a copy) in the list of strings | ||
711 | * contained in that property. | ||
712 | * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if | ||
713 | * property does not have a value, and -EILSEQ if the string is not | ||
714 | * null-terminated within the length of the property data. | ||
715 | * | ||
716 | * The out_string pointer is modified only if a valid string can be decoded. | ||
717 | */ | ||
718 | static inline int of_property_read_string_index(struct device_node *np, | ||
719 | const char *propname, | ||
720 | int index, const char **output) | ||
721 | { | ||
722 | int rc = of_property_read_string_helper(np, propname, output, 1, index); | ||
723 | return rc < 0 ? rc : 0; | ||
724 | } | ||
725 | |||
726 | /** | ||
671 | * of_property_read_bool - Findfrom a property | 727 | * of_property_read_bool - Findfrom a property |
672 | * @np: device node from which the property value is to be read. | 728 | * @np: device node from which the property value is to be read. |
673 | * @propname: name of the property to be searched. | 729 | * @propname: name of the property to be searched. |