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
This commit is contained in:
commit
a315780977
|
@ -1279,52 +1279,6 @@ int of_property_read_string(struct device_node *np, const char *propname,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(of_property_read_string);
|
||||
|
||||
/**
|
||||
* of_property_read_string_index - Find and read a string from a multiple
|
||||
* strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @index: index of the string in the list of strings
|
||||
* @out_string: pointer to null terminated return string, modified only if
|
||||
* return value is 0.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve a null
|
||||
* terminated string value (pointer to data, not a copy) in the list of strings
|
||||
* contained in that property.
|
||||
* Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
|
||||
* property does not have a value, and -EILSEQ if the string is not
|
||||
* null-terminated within the length of the property data.
|
||||
*
|
||||
* The out_string pointer is modified only if a valid string can be decoded.
|
||||
*/
|
||||
int of_property_read_string_index(struct device_node *np, const char *propname,
|
||||
int index, const char **output)
|
||||
{
|
||||
struct property *prop = of_find_property(np, propname, NULL);
|
||||
int i = 0;
|
||||
size_t l = 0, total = 0;
|
||||
const char *p;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
if (strnlen(prop->value, prop->length) >= prop->length)
|
||||
return -EILSEQ;
|
||||
|
||||
p = prop->value;
|
||||
|
||||
for (i = 0; total < prop->length; total += l, p += l) {
|
||||
l = strlen(p) + 1;
|
||||
if (i++ == index) {
|
||||
*output = p;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENODATA;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_property_read_string_index);
|
||||
|
||||
/**
|
||||
* of_property_match_string() - Find string in a list and return index
|
||||
* @np: pointer to node containing string list property
|
||||
|
@ -1351,7 +1305,7 @@ int of_property_match_string(struct device_node *np, const char *propname,
|
|||
end = p + prop->length;
|
||||
|
||||
for (i = 0; p < end; i++, p += l) {
|
||||
l = strlen(p) + 1;
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
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,
|
|||
EXPORT_SYMBOL_GPL(of_property_match_string);
|
||||
|
||||
/**
|
||||
* of_property_count_strings - Find and return the number of strings from a
|
||||
* multiple strings property.
|
||||
* of_property_read_string_util() - Utility helper for parsing string properties
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @out_strs: output array of string pointers.
|
||||
* @sz: number of array elements to read.
|
||||
* @skip: Number of strings to skip over at beginning of list.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve the number of null
|
||||
* terminated string contain in it. Returns the number of strings on
|
||||
* success, -EINVAL if the property does not exist, -ENODATA if property
|
||||
* does not have a value, and -EILSEQ if the string is not null-terminated
|
||||
* within the length of the property data.
|
||||
* Don't call this function directly. It is a utility helper for the
|
||||
* of_property_read_string*() family of functions.
|
||||
*/
|
||||
int of_property_count_strings(struct device_node *np, const char *propname)
|
||||
int of_property_read_string_helper(struct device_node *np, const char *propname,
|
||||
const char **out_strs, size_t sz, int skip)
|
||||
{
|
||||
struct property *prop = of_find_property(np, propname, NULL);
|
||||
int i = 0;
|
||||
size_t l = 0, total = 0;
|
||||
const char *p;
|
||||
int l = 0, i = 0;
|
||||
const char *p, *end;
|
||||
|
||||
if (!prop)
|
||||
return -EINVAL;
|
||||
if (!prop->value)
|
||||
return -ENODATA;
|
||||
if (strnlen(prop->value, prop->length) >= prop->length)
|
||||
return -EILSEQ;
|
||||
|
||||
p = prop->value;
|
||||
end = p + prop->length;
|
||||
|
||||
for (i = 0; total < prop->length; total += l, p += l, i++)
|
||||
l = strlen(p) + 1;
|
||||
|
||||
return i;
|
||||
for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
|
||||
l = strnlen(p, end - p) + 1;
|
||||
if (p + l > end)
|
||||
return -EILSEQ;
|
||||
if (out_strs && i >= skip)
|
||||
*out_strs++ = p;
|
||||
}
|
||||
i -= skip;
|
||||
return i <= 0 ? -ENODATA : i;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_property_count_strings);
|
||||
EXPORT_SYMBOL_GPL(of_property_read_string_helper);
|
||||
|
||||
void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
|
||||
{
|
||||
|
|
|
@ -339,8 +339,9 @@ static void __init of_selftest_parse_phandle_with_args(void)
|
|||
selftest(rc == -EINVAL, "expected:%i got:%i\n", -EINVAL, rc);
|
||||
}
|
||||
|
||||
static void __init of_selftest_property_match_string(void)
|
||||
static void __init of_selftest_property_string(void)
|
||||
{
|
||||
const char *strings[4];
|
||||
struct device_node *np;
|
||||
int rc;
|
||||
|
||||
|
@ -357,13 +358,66 @@ static void __init of_selftest_property_match_string(void)
|
|||
rc = of_property_match_string(np, "phandle-list-names", "third");
|
||||
selftest(rc == 2, "third expected:0 got:%i\n", rc);
|
||||
rc = of_property_match_string(np, "phandle-list-names", "fourth");
|
||||
selftest(rc == -ENODATA, "unmatched string; rc=%i", rc);
|
||||
selftest(rc == -ENODATA, "unmatched string; rc=%i\n", rc);
|
||||
rc = of_property_match_string(np, "missing-property", "blah");
|
||||
selftest(rc == -EINVAL, "missing property; rc=%i", rc);
|
||||
selftest(rc == -EINVAL, "missing property; rc=%i\n", rc);
|
||||
rc = of_property_match_string(np, "empty-property", "blah");
|
||||
selftest(rc == -ENODATA, "empty property; rc=%i", rc);
|
||||
selftest(rc == -ENODATA, "empty property; rc=%i\n", rc);
|
||||
rc = of_property_match_string(np, "unterminated-string", "blah");
|
||||
selftest(rc == -EILSEQ, "unterminated string; rc=%i", rc);
|
||||
selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
|
||||
|
||||
/* of_property_count_strings() tests */
|
||||
rc = of_property_count_strings(np, "string-property");
|
||||
selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
|
||||
rc = of_property_count_strings(np, "phandle-list-names");
|
||||
selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
|
||||
rc = of_property_count_strings(np, "unterminated-string");
|
||||
selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
|
||||
rc = of_property_count_strings(np, "unterminated-string-list");
|
||||
selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
|
||||
|
||||
/* of_property_read_string_index() tests */
|
||||
rc = of_property_read_string_index(np, "string-property", 0, strings);
|
||||
selftest(rc == 0 && !strcmp(strings[0], "foobar"), "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
strings[0] = NULL;
|
||||
rc = of_property_read_string_index(np, "string-property", 1, strings);
|
||||
selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
rc = of_property_read_string_index(np, "phandle-list-names", 0, strings);
|
||||
selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
rc = of_property_read_string_index(np, "phandle-list-names", 1, strings);
|
||||
selftest(rc == 0 && !strcmp(strings[0], "second"), "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
rc = of_property_read_string_index(np, "phandle-list-names", 2, strings);
|
||||
selftest(rc == 0 && !strcmp(strings[0], "third"), "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
strings[0] = NULL;
|
||||
rc = of_property_read_string_index(np, "phandle-list-names", 3, strings);
|
||||
selftest(rc == -ENODATA && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
strings[0] = NULL;
|
||||
rc = of_property_read_string_index(np, "unterminated-string", 0, strings);
|
||||
selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
rc = of_property_read_string_index(np, "unterminated-string-list", 0, strings);
|
||||
selftest(rc == 0 && !strcmp(strings[0], "first"), "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
strings[0] = NULL;
|
||||
rc = of_property_read_string_index(np, "unterminated-string-list", 2, strings); /* should fail */
|
||||
selftest(rc == -EILSEQ && strings[0] == NULL, "of_property_read_string_index() failure; rc=%i\n", rc);
|
||||
strings[1] = NULL;
|
||||
|
||||
/* of_property_read_string_array() tests */
|
||||
rc = of_property_read_string_array(np, "string-property", strings, 4);
|
||||
selftest(rc == 1, "Incorrect string count; rc=%i\n", rc);
|
||||
rc = of_property_read_string_array(np, "phandle-list-names", strings, 4);
|
||||
selftest(rc == 3, "Incorrect string count; rc=%i\n", rc);
|
||||
rc = of_property_read_string_array(np, "unterminated-string", strings, 4);
|
||||
selftest(rc == -EILSEQ, "unterminated string; rc=%i\n", rc);
|
||||
/* -- An incorrectly formed string should cause a failure */
|
||||
rc = of_property_read_string_array(np, "unterminated-string-list", strings, 4);
|
||||
selftest(rc == -EILSEQ, "unterminated string array; rc=%i\n", rc);
|
||||
/* -- parsing the correctly formed strings should still work: */
|
||||
strings[2] = NULL;
|
||||
rc = of_property_read_string_array(np, "unterminated-string-list", strings, 2);
|
||||
selftest(rc == 2 && strings[2] == NULL, "of_property_read_string_array() failure; rc=%i\n", rc);
|
||||
strings[1] = NULL;
|
||||
rc = of_property_read_string_array(np, "phandle-list-names", strings, 1);
|
||||
selftest(rc == 1 && strings[1] == NULL, "Overwrote end of string array; rc=%i, str='%s'\n", rc, strings[1]);
|
||||
}
|
||||
|
||||
#define propcmp(p1, p2) (((p1)->length == (p2)->length) && \
|
||||
|
@ -881,7 +935,7 @@ static int __init of_selftest(void)
|
|||
of_selftest_find_node_by_name();
|
||||
of_selftest_dynamic();
|
||||
of_selftest_parse_phandle_with_args();
|
||||
of_selftest_property_match_string();
|
||||
of_selftest_property_string();
|
||||
of_selftest_property_copy();
|
||||
of_selftest_changeset();
|
||||
of_selftest_parse_interrupts();
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
phandle-list-bad-args = <&provider2 1 0>,
|
||||
<&provider3 0>;
|
||||
empty-property;
|
||||
string-property = "foobar";
|
||||
unterminated-string = [40 41 42 43];
|
||||
unterminated-string-list = "first", "second", [40 41 42 43];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -267,14 +267,12 @@ extern int of_property_read_u64(const struct device_node *np,
|
|||
extern int of_property_read_string(struct device_node *np,
|
||||
const char *propname,
|
||||
const char **out_string);
|
||||
extern int of_property_read_string_index(struct device_node *np,
|
||||
const char *propname,
|
||||
int index, const char **output);
|
||||
extern int of_property_match_string(struct device_node *np,
|
||||
const char *propname,
|
||||
const char *string);
|
||||
extern int of_property_count_strings(struct device_node *np,
|
||||
const char *propname);
|
||||
extern int of_property_read_string_helper(struct device_node *np,
|
||||
const char *propname,
|
||||
const char **out_strs, size_t sz, int index);
|
||||
extern int of_device_is_compatible(const struct device_node *device,
|
||||
const char *);
|
||||
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,
|
|||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int of_property_read_string_index(struct device_node *np,
|
||||
const char *propname, int index,
|
||||
const char **out_string)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
static inline int of_property_count_strings(struct device_node *np,
|
||||
const char *propname)
|
||||
static inline int of_property_read_string_helper(struct device_node *np,
|
||||
const char *propname,
|
||||
const char **out_strs, size_t sz, int index)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -667,6 +659,70 @@ static inline int of_property_count_u64_elems(const struct device_node *np,
|
|||
return of_property_count_elems_of_size(np, propname, sizeof(u64));
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_read_string_array() - Read an array of strings from a multiple
|
||||
* strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @out_strs: output array of string pointers.
|
||||
* @sz: number of array elements to read.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve a list of
|
||||
* terminated string values (pointer to data, not a copy) in that property.
|
||||
*
|
||||
* If @out_strs is NULL, the number of strings in the property is returned.
|
||||
*/
|
||||
static inline int of_property_read_string_array(struct device_node *np,
|
||||
const char *propname, const char **out_strs,
|
||||
size_t sz)
|
||||
{
|
||||
return of_property_read_string_helper(np, propname, out_strs, sz, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_count_strings() - Find and return the number of strings from a
|
||||
* multiple strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve the number of null
|
||||
* terminated string contain in it. Returns the number of strings on
|
||||
* success, -EINVAL if the property does not exist, -ENODATA if property
|
||||
* does not have a value, and -EILSEQ if the string is not null-terminated
|
||||
* within the length of the property data.
|
||||
*/
|
||||
static inline int of_property_count_strings(struct device_node *np,
|
||||
const char *propname)
|
||||
{
|
||||
return of_property_read_string_helper(np, propname, NULL, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_read_string_index() - Find and read a string from a multiple
|
||||
* strings property.
|
||||
* @np: device node from which the property value is to be read.
|
||||
* @propname: name of the property to be searched.
|
||||
* @index: index of the string in the list of strings
|
||||
* @out_string: pointer to null terminated return string, modified only if
|
||||
* return value is 0.
|
||||
*
|
||||
* Search for a property in a device tree node and retrieve a null
|
||||
* terminated string value (pointer to data, not a copy) in the list of strings
|
||||
* contained in that property.
|
||||
* Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
|
||||
* property does not have a value, and -EILSEQ if the string is not
|
||||
* null-terminated within the length of the property data.
|
||||
*
|
||||
* The out_string pointer is modified only if a valid string can be decoded.
|
||||
*/
|
||||
static inline int of_property_read_string_index(struct device_node *np,
|
||||
const char *propname,
|
||||
int index, const char **output)
|
||||
{
|
||||
int rc = of_property_read_string_helper(np, propname, output, 1, index);
|
||||
return rc < 0 ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_property_read_bool - Findfrom a property
|
||||
* @np: device node from which the property value is to be read.
|
||||
|
|
Loading…
Reference in New Issue