source: rtems/cpukit/dtc/libfdt/fdt.c @ 8073f95e

5
Last change on this file since 8073f95e was 8073f95e, checked in by David Gibson <david@…>, on 10/23/17 at 03:45:56

libfdt: Make fdt_check_header() more thorough

Currently fdt_check_header() performs only some rudimentary checks, which
is not really what the name suggests. This strengthens fdt_check_header()
to check as much about the blob as is possible from the header alone: as
well as checking the magic number and version, it checks that the total
size is sane, and that all the sub-blocks within the blob lie within the
total size.

  • This broadens the meaning of FDT_ERR_TRUNCATED to cover all sorts of improperly terminated blocks as well as just a structure block without FDT_END.
  • This makes fdt_check_header() only succeed on "complete" blobs, not in-progress sequential write blobs. The only reason this didn't fail before was that this function used to be called by many RO functions which are supposed to also work on incomplete SW blobs.

Signed-off-by: David Gibson <david@…>
Tested-by: Alexey Kardashevskiy <aik@…>
Reviewed-by: Alexey Kardashevskiy <aik@…>
Reviewed-by: Simon Glass <sjg@…>

  • Property mode set to 100644
File size: 8.4 KB
Line 
1/*
2 * libfdt - Flat Device Tree manipulation
3 * Copyright (C) 2006 David Gibson, IBM Corporation.
4 *
5 * libfdt is dual licensed: you can use it either under the terms of
6 * the GPL, or the BSD license, at your option.
7 *
8 *  a) This library is free software; you can redistribute it and/or
9 *     modify it under the terms of the GNU General Public License as
10 *     published by the Free Software Foundation; either version 2 of the
11 *     License, or (at your option) any later version.
12 *
13 *     This library is distributed in the hope that it will be useful,
14 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *     GNU General Public License for more details.
17 *
18 *     You should have received a copy of the GNU General Public
19 *     License along with this library; if not, write to the Free
20 *     Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 *     MA 02110-1301 USA
22 *
23 * Alternatively,
24 *
25 *  b) Redistribution and use in source and binary forms, with or
26 *     without modification, are permitted provided that the following
27 *     conditions are met:
28 *
29 *     1. Redistributions of source code must retain the above
30 *        copyright notice, this list of conditions and the following
31 *        disclaimer.
32 *     2. Redistributions in binary form must reproduce the above
33 *        copyright notice, this list of conditions and the following
34 *        disclaimer in the documentation and/or other materials
35 *        provided with the distribution.
36 *
37 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
38 *     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39 *     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
40 *     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
41 *     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
42 *     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
43 *     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44 *     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
46 *     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
47 *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
48 *     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
49 *     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 */
51#include "libfdt_env.h"
52
53#include <fdt.h>
54#include <libfdt.h>
55
56#include "libfdt_internal.h"
57
58/*
59 * Minimal sanity check for a read-only tree. fdt_ro_probe_() checks
60 * that the given buffer contains what appears to be a flattened
61 * device tree with sane information in its header.
62 */
63int fdt_ro_probe_(const void *fdt)
64{
65        if (fdt_magic(fdt) == FDT_MAGIC) {
66                /* Complete tree */
67                if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
68                        return -FDT_ERR_BADVERSION;
69                if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION)
70                        return -FDT_ERR_BADVERSION;
71        } else if (fdt_magic(fdt) == FDT_SW_MAGIC) {
72                /* Unfinished sequential-write blob */
73                if (fdt_size_dt_struct(fdt) == 0)
74                        return -FDT_ERR_BADSTATE;
75        } else {
76                return -FDT_ERR_BADMAGIC;
77        }
78
79        return 0;
80}
81
82static int check_off_(uint32_t hdrsize, uint32_t totalsize, uint32_t off)
83{
84        return (off >= hdrsize) && (off <= totalsize);
85}
86
87static int check_block_(uint32_t hdrsize, uint32_t totalsize,
88                        uint32_t base, uint32_t size)
89{
90        if (!check_off_(hdrsize, totalsize, base))
91                return 0; /* block start out of bounds */
92        if ((base + size) < base)
93                return 0; /* overflow */
94        if (!check_off_(hdrsize, totalsize, base + size))
95                return 0; /* block end out of bounds */
96        return 1;
97}
98
99int fdt_check_header(const void *fdt)
100{
101        size_t hdrsize = FDT_V16_SIZE;
102
103        if (fdt_magic(fdt) != FDT_MAGIC)
104                return -FDT_ERR_BADMAGIC;
105        if ((fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION)
106            || (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION))
107                return -FDT_ERR_BADVERSION;
108        if (fdt_version(fdt) < fdt_last_comp_version(fdt))
109                return -FDT_ERR_BADVERSION;
110
111        if (fdt_version(fdt) >= 17)
112                hdrsize = FDT_V17_SIZE;
113
114        if ((fdt_totalsize(fdt) < hdrsize)
115            || (fdt_totalsize(fdt) > INT_MAX))
116                return -FDT_ERR_TRUNCATED;
117
118        /* Bounds check memrsv block */
119        if (!check_off_(hdrsize, fdt_totalsize(fdt), fdt_off_mem_rsvmap(fdt)))
120                return -FDT_ERR_TRUNCATED;
121
122        /* Bounds check structure block */
123        if (fdt_version(fdt) < 17) {
124                if (!check_off_(hdrsize, fdt_totalsize(fdt),
125                                fdt_off_dt_struct(fdt)))
126                        return -FDT_ERR_TRUNCATED;
127        } else {
128                if (!check_block_(hdrsize, fdt_totalsize(fdt),
129                                  fdt_off_dt_struct(fdt),
130                                  fdt_size_dt_struct(fdt)))
131                        return -FDT_ERR_TRUNCATED;
132        }
133
134        /* Bounds check strings block */
135        if (!check_block_(hdrsize, fdt_totalsize(fdt),
136                          fdt_off_dt_strings(fdt), fdt_size_dt_strings(fdt)))
137                return -FDT_ERR_TRUNCATED;
138
139        return 0;
140}
141
142const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
143{
144        unsigned absoffset = offset + fdt_off_dt_struct(fdt);
145
146        if ((absoffset < offset)
147            || ((absoffset + len) < absoffset)
148            || (absoffset + len) > fdt_totalsize(fdt))
149                return NULL;
150
151        if (fdt_version(fdt) >= 0x11)
152                if (((offset + len) < offset)
153                    || ((offset + len) > fdt_size_dt_struct(fdt)))
154                        return NULL;
155
156        return fdt_offset_ptr_(fdt, offset);
157}
158
159uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset)
160{
161        const fdt32_t *tagp, *lenp;
162        uint32_t tag;
163        int offset = startoffset;
164        const char *p;
165
166        *nextoffset = -FDT_ERR_TRUNCATED;
167        tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE);
168        if (!tagp)
169                return FDT_END; /* premature end */
170        tag = fdt32_to_cpu(*tagp);
171        offset += FDT_TAGSIZE;
172
173        *nextoffset = -FDT_ERR_BADSTRUCTURE;
174        switch (tag) {
175        case FDT_BEGIN_NODE:
176                /* skip name */
177                do {
178                        p = fdt_offset_ptr(fdt, offset++, 1);
179                } while (p && (*p != '\0'));
180                if (!p)
181                        return FDT_END; /* premature end */
182                break;
183
184        case FDT_PROP:
185                lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp));
186                if (!lenp)
187                        return FDT_END; /* premature end */
188                /* skip-name offset, length and value */
189                offset += sizeof(struct fdt_property) - FDT_TAGSIZE
190                        + fdt32_to_cpu(*lenp);
191                if (fdt_version(fdt) < 0x10 && fdt32_to_cpu(*lenp) >= 8 &&
192                    ((offset - fdt32_to_cpu(*lenp)) % 8) != 0)
193                        offset += 4;
194                break;
195
196        case FDT_END:
197        case FDT_END_NODE:
198        case FDT_NOP:
199                break;
200
201        default:
202                return FDT_END;
203        }
204
205        if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset))
206                return FDT_END; /* premature end */
207
208        *nextoffset = FDT_TAGALIGN(offset);
209        return tag;
210}
211
212int fdt_check_node_offset_(const void *fdt, int offset)
213{
214        if ((offset < 0) || (offset % FDT_TAGSIZE)
215            || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE))
216                return -FDT_ERR_BADOFFSET;
217
218        return offset;
219}
220
221int fdt_check_prop_offset_(const void *fdt, int offset)
222{
223        if ((offset < 0) || (offset % FDT_TAGSIZE)
224            || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP))
225                return -FDT_ERR_BADOFFSET;
226
227        return offset;
228}
229
230int fdt_next_node(const void *fdt, int offset, int *depth)
231{
232        int nextoffset = 0;
233        uint32_t tag;
234
235        if (offset >= 0)
236                if ((nextoffset = fdt_check_node_offset_(fdt, offset)) < 0)
237                        return nextoffset;
238
239        do {
240                offset = nextoffset;
241                tag = fdt_next_tag(fdt, offset, &nextoffset);
242
243                switch (tag) {
244                case FDT_PROP:
245                case FDT_NOP:
246                        break;
247
248                case FDT_BEGIN_NODE:
249                        if (depth)
250                                (*depth)++;
251                        break;
252
253                case FDT_END_NODE:
254                        if (depth && ((--(*depth)) < 0))
255                                return nextoffset;
256                        break;
257
258                case FDT_END:
259                        if ((nextoffset >= 0)
260                            || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth))
261                                return -FDT_ERR_NOTFOUND;
262                        else
263                                return nextoffset;
264                }
265        } while (tag != FDT_BEGIN_NODE);
266
267        return offset;
268}
269
270int fdt_first_subnode(const void *fdt, int offset)
271{
272        int depth = 0;
273
274        offset = fdt_next_node(fdt, offset, &depth);
275        if (offset < 0 || depth != 1)
276                return -FDT_ERR_NOTFOUND;
277
278        return offset;
279}
280
281int fdt_next_subnode(const void *fdt, int offset)
282{
283        int depth = 1;
284
285        /*
286         * With respect to the parent, the depth of the next subnode will be
287         * the same as the last.
288         */
289        do {
290                offset = fdt_next_node(fdt, offset, &depth);
291                if (offset < 0 || depth < 1)
292                        return -FDT_ERR_NOTFOUND;
293        } while (depth > 1);
294
295        return offset;
296}
297
298const char *fdt_find_string_(const char *strtab, int tabsize, const char *s)
299{
300        int len = strlen(s) + 1;
301        const char *last = strtab + tabsize - len;
302        const char *p;
303
304        for (p = strtab; p <= last; p++)
305                if (memcmp(p, s, len) == 0)
306                        return p;
307        return NULL;
308}
309
310int fdt_move(const void *fdt, void *buf, int bufsize)
311{
312        FDT_RO_PROBE(fdt);
313
314        if (fdt_totalsize(fdt) > bufsize)
315                return -FDT_ERR_NOSPACE;
316
317        memmove(buf, fdt, fdt_totalsize(fdt));
318        return 0;
319}
Note: See TracBrowser for help on using the repository browser.