Skip to content

Commit a315780

Browse files
committed
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
2 parents c4c23fb + a87fa1d commit a315780

File tree

4 files changed

+154
-86
lines changed

4 files changed

+154
-86
lines changed

drivers/of/base.c

Lines changed: 22 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,52 +1279,6 @@ int of_property_read_string(struct device_node *np, const char *propname,
12791279
}
12801280
EXPORT_SYMBOL_GPL(of_property_read_string);
12811281

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-
13281282
/**
13291283
* of_property_match_string() - Find string in a list and return index
13301284
* @np: pointer to node containing string list property
@@ -1351,7 +1305,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
13511305
end = p + prop->length;
13521306

13531307
for (i = 0; p < end; i++, p += l) {
1354-
l = strlen(p) + 1;
1308+
l = strnlen(p, end - p) + 1;
13551309
if (p + l > end)
13561310
return -EILSEQ;
13571311
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,
13631317
EXPORT_SYMBOL_GPL(of_property_match_string);
13641318

13651319
/**
1366-
* of_property_count_strings - Find and return the number of strings from a
1367-
* multiple strings property.
1320+
* of_property_read_string_util() - Utility helper for parsing string properties
13681321
* @np: device node from which the property value is to be read.
13691322
* @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.
13701326
*
1371-
* Search for a property in a device tree node and retrieve the number of null
1372-
* terminated string contain in it. Returns the number of strings on
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.
1327+
* Don't call this function directly. It is a utility helper for the
1328+
* of_property_read_string*() family of functions.
13761329
*/
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)
13781332
{
13791333
struct property *prop = of_find_property(np, propname, NULL);
1380-
int i = 0;
1381-
size_t l = 0, total = 0;
1382-
const char *p;
1334+
int l = 0, i = 0;
1335+
const char *p, *end;
13831336

13841337
if (!prop)
13851338
return -EINVAL;
13861339
if (!prop->value)
13871340
return -ENODATA;
1388-
if (strnlen(prop->value, prop->length) >= prop->length)
1389-
return -EILSEQ;
1390-
13911341
p = prop->value;
1342+
end = p + prop->length;
13921343

1393-
for (i = 0; total < prop->length; total += l, p += l, i++)
1394-
l = strlen(p) + 1;
1395-
1396-
return i;
1344+
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
1345+
l = strnlen(p, end - p) + 1;
1346+
if (p + l > end)
1347+
return -EILSEQ;
1348+
if (out_strs && i >= skip)
1349+
*out_strs++ = p;
1350+
}
1351+
i -= skip;
1352+
return i <= 0 ? -ENODATA : i;
13971353
}
1398-
EXPORT_SYMBOL_GPL(of_property_count_strings);
1354+
EXPORT_SYMBOL_GPL(of_property_read_string_helper);
13991355

14001356
void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
14011357
{

drivers/of/selftest.c

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,8 +339,9 @@ static void __init of_selftest_parse_phandle_with_args(void)
339339
selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
340340
}
341341

342-
static void __init of_selftest_property_match_string(void)
342+
static void __init of_selftest_property_string(void)
343343
{
344+
const char *strings[4];
344345
struct device_node *np;
345346
int rc;
346347

@@ -357,13 +358,66 @@ static void __init of_selftest_property_match_string(void)
357358
rc = of_property_match_string(np, "phandle-list-names", "third");
358359
selftest(rc == 2, "third expected:0 got:%i\n", rc);
359360
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);
361362
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);
363364
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);
365366
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]);
367421
}
368422

369423
#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
@@ -881,7 +935,7 @@ static int __init of_selftest(void)
881935
of_selftest_find_node_by_name();
882936
of_selftest_dynamic();
883937
of_selftest_parse_phandle_with_args();
884-
of_selftest_property_match_string();
938+
of_selftest_property_string();
885939
of_selftest_property_copy();
886940
of_selftest_changeset();
887941
of_selftest_parse_interrupts();

drivers/of/testcase-data/tests-phandle.dtsi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
phandle-list-bad-args = <&provider2 1 0>,
4040
<&provider3 0>;
4141
empty-property;
42+
string-property = "foobar";
4243
unterminated-string = [40 41 42 43];
44+
unterminated-string-list = "first", "second", [40 41 42 43];
4345
};
4446
};
4547
};

include/linux/of.h

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,12 @@ extern int of_property_read_u64(const struct device_node *np,
267267
extern int of_property_read_string(struct device_node *np,
268268
const char *propname,
269269
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);
273270
extern int of_property_match_string(struct device_node *np,
274271
const char *propname,
275272
const char *string);
276-
extern int of_property_count_strings(struct device_node *np,
277-
const char *propname);
273+
extern int of_property_read_string_helper(struct device_node *np,
274+
const char *propname,
275+
const char **out_strs, size_t sz, int index);
278276
extern int of_device_is_compatible(const struct device_node *device,
279277
const char *);
280278
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,
486484
return -ENOSYS;
487485
}
488486

489-
static inline int of_property_read_string_index(struct device_node *np,
490-
const char *propname, int index,
491-
const char **out_string)
492-
{
493-
return -ENOSYS;
494-
}
495-
496-
static inline int of_property_count_strings(struct device_node *np,
497-
const char *propname)
487+
static inline int of_property_read_string_helper(struct device_node *np,
488+
const char *propname,
489+
const char **out_strs, size_t sz, int index)
498490
{
499491
return -ENOSYS;
500492
}
@@ -667,6 +659,70 @@ static inline int of_property_count_u64_elems(const struct device_node *np,
667659
return of_property_count_elems_of_size(np, propname, sizeof(u64));
668660
}
669661

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+
670726
/**
671727
* of_property_read_bool - Findfrom a property
672728
* @np: device node from which the property value is to be read.

0 commit comments

Comments
 (0)