Skip to content

Commit a87fa1d

Browse files
committed
of: Fix overflow bug in string property parsing functions
The string property read helpers will run off the end of the buffer if it is handed a malformed string property. Rework the parsers to make sure that doesn't happen. At the same time add new test cases to make sure the functions behave themselves. The original implementations of of_property_read_string_index() and of_property_count_strings() both open-coded the same block of parsing code, each with it's own subtly different bugs. The fix here merges functions into a single helper and makes the original functions static inline wrappers around the helper. One non-bugfix aspect of this patch is the addition of a new wrapper, of_property_read_string_array(). The new wrapper is needed by the device_properties feature that Rafael is working on and planning to merge for v3.19. The implementation is identical both with and without the new static inline wrapper, so it just got left in to reduce the churn on the header file. Signed-off-by: Grant Likely <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Cc: Mika Westerberg <[email protected]> Cc: Rob Herring <[email protected]> Cc: Arnd Bergmann <[email protected]> Cc: Darren Hart <[email protected]> Cc: <[email protected]> # v3.3+: Drop selftest hunks that don't apply
1 parent 0df1f24 commit a87fa1d

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)