aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libfdt/fdt_ro.c61
-rw-r--r--libfdt/libfdt.h18
-rw-r--r--libfdt/version.lds2
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile.tests2
-rwxr-xr-xtests/run_tests.sh1
-rw-r--r--tests/testdata.h1
-rw-r--r--tests/testutils.c11
-rw-r--r--tests/trees.S26
-rw-r--r--tests/truncated_string.c79
10 files changed, 193 insertions, 9 deletions
diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
index 4f4ef44..347aa7b 100644
--- a/libfdt/fdt_ro.c
+++ b/libfdt/fdt_ro.c
@@ -76,17 +76,72 @@ static int fdt_nodename_eq_(const void *fdt, int offset,
return 0;
}
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp)
+{
+ uint32_t absoffset = stroffset + fdt_off_dt_strings(fdt);
+ size_t len;
+ int err;
+ const char *s, *n;
+
+ err = fdt_ro_probe_(fdt);
+ if (err != 0)
+ goto fail;
+
+ err = -FDT_ERR_BADOFFSET;
+ if (absoffset >= fdt_totalsize(fdt))
+ goto fail;
+ len = fdt_totalsize(fdt) - absoffset;
+
+ if (fdt_magic(fdt) == FDT_MAGIC) {
+ if (stroffset < 0)
+ goto fail;
+ if (fdt_version(fdt) >= 17) {
+ if (stroffset >= fdt_size_dt_strings(fdt))
+ goto fail;
+ if ((fdt_size_dt_strings(fdt) - stroffset) < len)
+ len = fdt_size_dt_strings(fdt) - stroffset;
+ }
+ } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
+ if ((stroffset >= 0)
+ || (stroffset < -fdt_size_dt_strings(fdt)))
+ goto fail;
+ if ((-stroffset) < len)
+ len = -stroffset;
+ } else {
+ err = -FDT_ERR_INTERNAL;
+ goto fail;
+ }
+
+ s = (const char *)fdt + absoffset;
+ n = memchr(s, '\0', len);
+ if (!n) {
+ /* missing terminating NULL */
+ err = -FDT_ERR_TRUNCATED;
+ goto fail;
+ }
+
+ if (lenp)
+ *lenp = n - s;
+ return s;
+
+fail:
+ if (lenp)
+ *lenp = err;
+ return NULL;
+}
+
const char *fdt_string(const void *fdt, int stroffset)
{
- return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset;
+ return fdt_get_string(fdt, stroffset, NULL);
}
static int fdt_string_eq_(const void *fdt, int stroffset,
const char *s, int len)
{
- const char *p = fdt_string(fdt, stroffset);
+ int slen;
+ const char *p = fdt_get_string(fdt, stroffset, &slen);
- return (strlen(p) == len) && (memcmp(p, s, len) == 0);
+ return p && (slen == len) && (memcmp(p, s, len) == 0);
}
uint32_t fdt_get_max_phandle(const void *fdt)
diff --git a/libfdt/libfdt.h b/libfdt/libfdt.h
index 837c5e2..b50af54 100644
--- a/libfdt/libfdt.h
+++ b/libfdt/libfdt.h
@@ -288,6 +288,22 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
/**********************************************************************/
/**
+ * fdt_get_string - retrieve a string from the strings block of a device tree
+ * @fdt: pointer to the device tree blob
+ * @stroffset: offset of the string within the strings block (native endian)
+ * @lenp: optional pointer to return the string's length
+ *
+ * fdt_get_string() retrieves a pointer to a single string from the
+ * strings block of the device tree blob at fdt, and optionally also
+ * returns the string's length in *lenp.
+ *
+ * returns:
+ * a pointer to the string, on success
+ * NULL, if stroffset is out of bounds, or doesn't point to a valid string
+ */
+const char *fdt_get_string(const void *fdt, int stroffset, int *lenp);
+
+/**
* fdt_string - retrieve a string from the strings block of a device tree
* @fdt: pointer to the device tree blob
* @stroffset: offset of the string within the strings block (native endian)
@@ -297,7 +313,7 @@ int fdt_move(const void *fdt, void *buf, int bufsize);
*
* returns:
* a pointer to the string, on success
- * NULL, if stroffset is out of bounds
+ * NULL, if stroffset is out of bounds, or doesn't point to a valid string
*/
const char *fdt_string(const void *fdt, int stroffset);
diff --git a/libfdt/version.lds b/libfdt/version.lds
index 18fb69f..9f5d708 100644
--- a/libfdt/version.lds
+++ b/libfdt/version.lds
@@ -65,7 +65,7 @@ LIBFDT_1.2 {
fdt_stringlist_get;
fdt_resize;
fdt_overlay_apply;
-
+ fdt_get_string;
local:
*;
};
diff --git a/tests/.gitignore b/tests/.gitignore
index 62be2ec..0db6d78 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -61,5 +61,6 @@ tmp.*
/sw_tree1
/sw_states
/truncated_property
+/truncated_string
/utilfdt_test
/value-labels
diff --git a/tests/Makefile.tests b/tests/Makefile.tests
index c670100..97f7f8d 100644
--- a/tests/Makefile.tests
+++ b/tests/Makefile.tests
@@ -29,7 +29,7 @@ LIB_TESTS_L = get_mem_rsv \
check_path check_header
LIB_TESTS = $(LIB_TESTS_L:%=$(TESTS_PREFIX)%)
-LIBTREE_TESTS_L = truncated_property
+LIBTREE_TESTS_L = truncated_property truncated_string
LIBTREE_TESTS = $(LIBTREE_TESTS_L:%=$(TESTS_PREFIX)%)
DL_LIB_TESTS_L = asm_tree_dump value-labels
diff --git a/tests/run_tests.sh b/tests/run_tests.sh
index dc7f1ef..bc9070e 100755
--- a/tests/run_tests.sh
+++ b/tests/run_tests.sh
@@ -415,6 +415,7 @@ libfdt_tests () {
# Tests for behaviour on various sorts of corrupted trees
run_test truncated_property
+ run_test truncated_string
# Check aliases support in fdt_path_offset
run_dtc_test -I dts -O dtb -o aliases.dtb aliases.dts
diff --git a/tests/testdata.h b/tests/testdata.h
index c30f0c8..b257011 100644
--- a/tests/testdata.h
+++ b/tests/testdata.h
@@ -47,4 +47,5 @@ extern struct fdt_header bad_node_char;
extern struct fdt_header bad_node_format;
extern struct fdt_header bad_prop_char;
extern struct fdt_header ovf_size_strings;
+extern struct fdt_header truncated_string;
#endif /* ! __ASSEMBLY */
diff --git a/tests/testutils.c b/tests/testutils.c
index 521f4f1..f03140e 100644
--- a/tests/testutils.c
+++ b/tests/testutils.c
@@ -88,7 +88,7 @@ void check_property(void *fdt, int nodeoffset, const char *name,
int len, const void *val)
{
const struct fdt_property *prop;
- int retlen;
+ int retlen, namelen;
uint32_t tag, nameoff, proplen;
const char *propname;
@@ -106,8 +106,13 @@ void check_property(void *fdt, int nodeoffset, const char *name,
if (tag != FDT_PROP)
FAIL("Incorrect tag 0x%08x on property \"%s\"", tag, name);
- propname = fdt_string(fdt, nameoff);
- if (!propname || !streq(propname, name))
+ propname = fdt_get_string(fdt, nameoff, &namelen);
+ if (!propname)
+ FAIL("Couldn't get property name: %s", fdt_strerror(namelen));
+ if (namelen != strlen(propname))
+ FAIL("Incorrect prop name length: %d instead of %zd",
+ namelen, strlen(propname));
+ if (!streq(propname, name))
FAIL("Property name mismatch \"%s\" instead of \"%s\"",
propname, name);
if (proplen != retlen)
diff --git a/tests/trees.S b/tests/trees.S
index 6898cf9..7ae1bde 100644
--- a/tests/trees.S
+++ b/tests/trees.S
@@ -39,6 +39,9 @@ tree##_rsvmap_end: ;
FDTLONG(len) ; \
FDTLONG(tree##_##name - tree##_strings) ;
+#define PROP_EMPTY(tree, name) \
+ PROPHDR(tree, name, 0) ;
+
#define PROP_INT(tree, name, val) \
PROPHDR(tree, name, 4) \
FDTLONG(val) ;
@@ -233,3 +236,26 @@ ovf_size_strings_strings:
ovf_size_strings_bad_string = ovf_size_strings_strings + 0x10000000
ovf_size_strings_strings_end:
ovf_size_strings_end:
+
+
+ /* truncated_string */
+ TREE_HDR(truncated_string)
+ EMPTY_RSVMAP(truncated_string)
+
+truncated_string_struct:
+ BEGIN_NODE("")
+ PROP_EMPTY(truncated_string, good_string)
+ PROP_EMPTY(truncated_string, bad_string)
+ END_NODE
+ FDTLONG(FDT_END)
+truncated_string_struct_end:
+
+truncated_string_strings:
+ STRING(truncated_string, good_string, "good")
+truncated_string_bad_string:
+ .byte 'b'
+ .byte 'a'
+ .byte 'd'
+ /* NOTE: terminating \0 deliberately missing */
+truncated_string_strings_end:
+truncated_string_end:
diff --git a/tests/truncated_string.c b/tests/truncated_string.c
new file mode 100644
index 0000000..63214d2
--- /dev/null
+++ b/tests/truncated_string.c
@@ -0,0 +1,79 @@
+/*
+ * libfdt - Flat Device Tree manipulation
+ * Testcase for misbehaviour on a truncated string
+ * Copyright (C) 2018 David Gibson, IBM Corporation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <libfdt.h>
+
+#include "tests.h"
+#include "testdata.h"
+
+int main(int argc, char *argv[])
+{
+ void *fdt = &truncated_string;
+ const struct fdt_property *good, *bad;
+ int off, len;
+ const char *name;
+
+ test_init(argc, argv);
+
+ off = fdt_first_property_offset(fdt, 0);
+ good = fdt_get_property_by_offset(fdt, off, NULL);
+
+ off = fdt_next_property_offset(fdt, off);
+ bad = fdt_get_property_by_offset(fdt, off, NULL);
+
+ if (fdt32_to_cpu(good->len) != 0)
+ FAIL("Unexpected length for good property");
+ name = fdt_get_string(fdt, fdt32_to_cpu(good->nameoff), &len);
+ if (!name)
+ FAIL("fdt_get_string() failed on good property: %s",
+ fdt_strerror(len));
+ if (len != 4)
+ FAIL("fdt_get_string() returned length %d (not 4) on good property",
+ len);
+ if (!streq(name, "good"))
+ FAIL("fdt_get_string() returned \"%s\" (not \"good\") on good property",
+ name);
+
+ if (fdt32_to_cpu(bad->len) != 0)
+ FAIL("Unexpected length for bad property\n");
+ name = fdt_get_string(fdt, fdt32_to_cpu(bad->nameoff), &len);
+ if (name)
+ FAIL("fdt_get_string() succeeded incorrectly on bad property");
+ else if (len != -FDT_ERR_TRUNCATED)
+ FAIL("fdt_get_string() gave unexpected error on bad property: %s",
+ fdt_strerror(len));
+
+ /* Make sure the 'good' property breaks correctly if we
+ * truncate the strings section */
+ fdt_set_size_dt_strings(fdt, fdt32_to_cpu(good->nameoff) + 4);
+ name = fdt_get_string(fdt, fdt32_to_cpu(good->nameoff), &len);
+ if (name)
+ FAIL("fdt_get_string() succeeded incorrectly on mangled property");
+ else if (len != -FDT_ERR_TRUNCATED)
+ FAIL("fdt_get_string() gave unexpected error on mangled property: %s",
+ fdt_strerror(len));
+
+ PASS();
+}