From 2c0b843e710aa1e2da25c2592e6dbe5d0b0ab7da Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 6 Feb 2009 14:01:56 +1100 Subject: [PATCH 1/4] libfdt: Rework fdt_next_node() Currently fdt_next_node() will find the next node in the blob regardless of whether it is above, below or at the same level in the tree as the starting node - the depth parameter is updated to indicate which is the case. When a depth parameter is supplied, this patch makes it instead terminate immediately when it finds the END_NODE tag for a node at depth 0. In this case it returns the offset immediately past the END_NODE tag. This has a couple of advantages. First, this slightly simplifies fdt_subnode_offset(), which no longer needs to explicitly check that fdt_next_node()'s iteration hasn't left the starting node. Second, this allows fdt_next_node() to be used to implement _fdt_node_end_offset() considerably simplifying the latter function. The other users of fdt_next_node() either don't need to iterate out of the starting node, or don't pass a depth parameter at all. Any callers that really need to iterate out of the starting node, but keep tracking depth can do so by biasing the initial depth value. This is a semantic change, but I think it's very unlikely to break any existing library users. Signed-off-by: David Gibson --- libfdt/fdt.c | 4 ++-- libfdt/fdt_ro.c | 20 ++++++++------------ libfdt/fdt_wip.c | 37 +++++-------------------------------- 3 files changed, 15 insertions(+), 46 deletions(-) diff --git a/libfdt/fdt.c b/libfdt/fdt.c index a59a518b0..940cee8b1 100644 --- a/libfdt/fdt.c +++ b/libfdt/fdt.c @@ -166,8 +166,8 @@ int fdt_next_node(const void *fdt, int offset, int *depth) break; case FDT_END_NODE: - if (depth) - (*depth)--; + if (depth && ((--(*depth)) < 0)) + return nextoffset; break; case FDT_END: diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c index fdc9c29d2..d682a40c1 100644 --- a/libfdt/fdt_ro.c +++ b/libfdt/fdt_ro.c @@ -112,24 +112,20 @@ int fdt_num_mem_rsv(const void *fdt) int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) { - int depth = 0; + int depth; FDT_CHECK_HEADER(fdt); - for (depth = 0, offset = fdt_next_node(fdt, offset, &depth); - (offset >= 0) && (depth > 0); - offset = fdt_next_node(fdt, offset, &depth)) { - if (depth < 0) - return -FDT_ERR_NOTFOUND; - else if ((depth == 1) - && _fdt_nodename_eq(fdt, offset, name, namelen)) + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node(fdt, offset, &depth)) + if ((depth == 1) + && _fdt_nodename_eq(fdt, offset, name, namelen)) return offset; - } - if (offset < 0) - return offset; /* error */ - else + if (depth < 0) return -FDT_ERR_NOTFOUND; + return offset; /* error */ } int fdt_subnode_offset(const void *fdt, int parentoffset, diff --git a/libfdt/fdt_wip.c b/libfdt/fdt_wip.c index e30c81d91..e373677c5 100644 --- a/libfdt/fdt_wip.c +++ b/libfdt/fdt_wip.c @@ -98,41 +98,14 @@ int fdt_nop_property(void *fdt, int nodeoffset, const char *name) return 0; } -int _fdt_node_end_offset(void *fdt, int nodeoffset) +int _fdt_node_end_offset(void *fdt, int offset) { - int level = 0; - uint32_t tag; - int offset, nextoffset; + int depth = 0; - tag = fdt_next_tag(fdt, nodeoffset, &nextoffset); - if (tag != FDT_BEGIN_NODE) - return -FDT_ERR_BADOFFSET; - do { - offset = nextoffset; - tag = fdt_next_tag(fdt, offset, &nextoffset); + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node(fdt, offset, &depth); - switch (tag) { - case FDT_END: - return offset; - - case FDT_BEGIN_NODE: - level++; - break; - - case FDT_END_NODE: - level--; - break; - - case FDT_PROP: - case FDT_NOP: - break; - - default: - return -FDT_ERR_BADSTRUCTURE; - } - } while (level >= 0); - - return nextoffset; + return offset; } int fdt_nop_node(void *fdt, int nodeoffset) From a22d9cfbb5bcfb3dc6ffd64d391b568e8a0ce383 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Fri, 6 Feb 2009 14:03:24 +1100 Subject: [PATCH 2/4] libfdt: Rework/cleanup fdt_next_tag() Currently, callers of fdt_next_tag() must usually follow the call with some sort of call to fdt_offset_ptr() to verify that the blob isn't truncated in the middle of the tag data they're going to process. This is a bit silly, since fdt_next_tag() generally has to call fdt_offset_ptr() on at least some of the data following the tag for its own operation. This patch alters fdt_next_tag() to always use fdt_offset_ptr() to verify the data between its starting offset and the offset it returns in nextoffset. This simplifies fdt_get_property() which no longer has to verify itself that the property data is all present. At the same time, I neaten and clarify the error handling for fdt_next_tag(). Previously, fdt_next_tag() could return -1 instead of a tag value in some circumstances - which almost none of the callers checked for. Also, fdt_next_tag() could return FDT_END either because it encountered an FDT_END tag, or because it reached the end of the structure block - no way was provided to tell between these cases. With this patch, fdt_next_tag() always returns FDT_END with a negative value in nextoffset for an error. This means the several places which loop looking for FDT_END will still work correctly - they only need to check for errors at the end. The errors which fdt_next_tag() can report are: - -FDT_ERR_TRUNCATED if it reached the end of the structure block instead of finding a tag. - -FDT_BADSTRUCTURE if a bad tag was encountered, or if the tag data couldn't be verified with fdt_offset_ptr(). This patch also updates the callers of fdt_next_tag(), where appropriate, to make use of the new error reporting. Finally, the prototype for the long gone _fdt_next_tag() is removed from libfdt_internal.h. Signed-off-by: David Gibson --- libfdt/fdt.c | 46 +++++++++++++++++++++++++--------------- libfdt/fdt_ro.c | 33 ++++++++-------------------- libfdt/fdt_rw.c | 2 ++ libfdt/fdt_sw.c | 9 ++++---- libfdt/libfdt_internal.h | 1 - 5 files changed, 44 insertions(+), 47 deletions(-) diff --git a/libfdt/fdt.c b/libfdt/fdt.c index 940cee8b1..b09ea6f04 100644 --- a/libfdt/fdt.c +++ b/libfdt/fdt.c @@ -94,42 +94,53 @@ const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) return p; } -uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset) +uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) { const uint32_t *tagp, *lenp; uint32_t tag; + int offset = startoffset; const char *p; - if (offset % FDT_TAGSIZE) - return -1; - + *nextoffset = -FDT_ERR_TRUNCATED; tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); - if (! tagp) + if (!tagp) return FDT_END; /* premature end */ tag = fdt32_to_cpu(*tagp); offset += FDT_TAGSIZE; + *nextoffset = -FDT_ERR_BADSTRUCTURE; switch (tag) { case FDT_BEGIN_NODE: /* skip name */ do { p = fdt_offset_ptr(fdt, offset++, 1); } while (p && (*p != '\0')); - if (! p) - return FDT_END; + if (!p) + return FDT_END; /* premature end */ break; + case FDT_PROP: lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); - if (! lenp) - return FDT_END; - /* skip name offset, length and value */ - offset += 2*FDT_TAGSIZE + fdt32_to_cpu(*lenp); + if (!lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu(*lenp); break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; } - if (nextoffset) - *nextoffset = FDT_TAGALIGN(offset); + if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + *nextoffset = FDT_TAGALIGN(offset); return tag; } @@ -171,10 +182,11 @@ int fdt_next_node(const void *fdt, int offset, int *depth) break; case FDT_END: - return -FDT_ERR_NOTFOUND; - - default: - return -FDT_ERR_BADSTRUCTURE; + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; } } while (tag != FDT_BEGIN_NODE); diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c index d682a40c1..1e1e32209 100644 --- a/libfdt/fdt_ro.c +++ b/libfdt/fdt_ro.c @@ -205,7 +205,6 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt, { uint32_t tag; const struct fdt_property *prop; - int namestroff; int offset, nextoffset; int err; @@ -220,38 +219,24 @@ const struct fdt_property *fdt_get_property_namelen(const void *fdt, tag = fdt_next_tag(fdt, offset, &nextoffset); switch (tag) { case FDT_END: - err = -FDT_ERR_TRUNCATED; + if (nextoffset < 0) + err = nextoffset; + else + /* FDT_END tag with unclosed nodes */ + err = -FDT_ERR_BADSTRUCTURE; goto fail; - case FDT_BEGIN_NODE: - case FDT_END_NODE: - case FDT_NOP: - break; - case FDT_PROP: - err = -FDT_ERR_BADSTRUCTURE; - prop = fdt_offset_ptr(fdt, offset, sizeof(*prop)); - if (! prop) - goto fail; - namestroff = fdt32_to_cpu(prop->nameoff); - if (_fdt_string_eq(fdt, namestroff, name, namelen)) { + prop = _fdt_offset_ptr(fdt, offset); + if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), + name, namelen)) { /* Found it! */ - int len = fdt32_to_cpu(prop->len); - prop = fdt_offset_ptr(fdt, offset, - sizeof(*prop)+len); - if (! prop) - goto fail; - if (lenp) - *lenp = len; + *lenp = fdt32_to_cpu(prop->len); return prop; } break; - - default: - err = -FDT_ERR_BADSTRUCTURE; - goto fail; } } while ((tag != FDT_BEGIN_NODE) && (tag != FDT_END_NODE)); diff --git a/libfdt/fdt_rw.c b/libfdt/fdt_rw.c index cd06178e7..5c27a677e 100644 --- a/libfdt/fdt_rw.c +++ b/libfdt/fdt_rw.c @@ -410,6 +410,8 @@ int fdt_open_into(const void *fdt, void *buf, int bufsize) struct_size = 0; while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) ; + if (struct_size < 0) + return struct_size; } if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) { diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index 698329e0c..2380b2750 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -82,7 +82,7 @@ static void *_fdt_grab_space(void *fdt, int len) return NULL; fdt_set_size_dt_struct(fdt, offset + len); - return fdt_offset_ptr_w(fdt, offset, len); + return _fdt_offset_ptr_w(fdt, offset); } int fdt_create(void *buf, int bufsize) @@ -237,18 +237,17 @@ int fdt_finish(void *fdt) while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { if (tag == FDT_PROP) { struct fdt_property *prop = - fdt_offset_ptr_w(fdt, offset, sizeof(*prop)); + _fdt_offset_ptr_w(fdt, offset); int nameoff; - if (! prop) - return -FDT_ERR_BADSTRUCTURE; - nameoff = fdt32_to_cpu(prop->nameoff); nameoff += fdt_size_dt_strings(fdt); prop->nameoff = cpu_to_fdt32(nameoff); } offset = nextoffset; } + if (nextoffset < 0) + return nextoffset; /* Finally, adjust the header */ fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); diff --git a/libfdt/libfdt_internal.h b/libfdt/libfdt_internal.h index 46eb93e4a..d2dcbd65e 100644 --- a/libfdt/libfdt_internal.h +++ b/libfdt/libfdt_internal.h @@ -62,7 +62,6 @@ return err; \ } -uint32_t _fdt_next_tag(const void *fdt, int startoffset, int *nextoffset); int _fdt_check_node_offset(const void *fdt, int offset); const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); int _fdt_node_end_offset(void *fdt, int nodeoffset); From 13d93f38e86818739317b0206d597265cf9e675e Mon Sep 17 00:00:00 2001 From: Emil Medve Date: Mon, 23 Feb 2009 10:43:36 -0600 Subject: [PATCH 3/4] Fix a possible overflow case detected by gcc 4.3.2 .../dtc/libfdt/fdt_sw.c: In function 'fdt_end_node': .../dtc/libfdt/fdt_sw.c:81: error: assuming signed overflow does not occur when assuming that (X + c) < X is always false Signed-off-by: Emil Medve --- libfdt/fdt_sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libfdt/fdt_sw.c b/libfdt/fdt_sw.c index 2380b2750..55ebebf1e 100644 --- a/libfdt/fdt_sw.c +++ b/libfdt/fdt_sw.c @@ -70,7 +70,7 @@ static int _fdt_sw_check_header(void *fdt) return err; \ } -static void *_fdt_grab_space(void *fdt, int len) +static void *_fdt_grab_space(void *fdt, size_t len) { int offset = fdt_size_dt_struct(fdt); int spaceleft; From 67b89c79e72fe86b0ea0199425d880630beb95d8 Mon Sep 17 00:00:00 2001 From: Laurent Gregoire Date: Tue, 3 Mar 2009 14:23:59 +0100 Subject: [PATCH 4/4] libfdt: Fix C++ compile-time cast error on gnu 4.2.1 Allow the inclusion of libfdt.h in C++ source. Signed-off-by: Laurent Gregoire Acked-by: David Gibson --- include/libfdt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libfdt.h b/include/libfdt.h index ce374fded..d23d40e07 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -156,7 +156,7 @@ int fdt_next_node(const void *fdt, int offset, int *depth); #define __fdt_set_hdr(name) \ static inline void fdt_set_##name(void *fdt, uint32_t val) \ { \ - struct fdt_header *fdth = fdt; \ + struct fdt_header *fdth = (struct fdt_header*)fdt; \ fdth->name = cpu_to_fdt32(val); \ } __fdt_set_hdr(magic);