Bug Summary

File:util/cbfstool/cbfs-mkstage.c
Warning:line 309, column 4
Potential leak of memory pointed to by 'phdrs'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cbfs-mkstage.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -pic-is-pie -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild -resource-dir /opt/xgcc/lib/clang/17 -include /home/coreboot/node-root/workspace/coreboot_scanbuild/src/commonlib/bsd/include/commonlib/bsd/compiler.h -include /home/coreboot/node-root/workspace/coreboot_scanbuild/src/commonlib/bsd/include/commonlib/bsd/compiler.h -D _DEFAULT_SOURCE -D _BSD_SOURCE -D _SVID_SOURCE -D _GNU_SOURCE -I /home/coreboot/node-root/workspace/coreboot_scanbuild/util/cbfstool/flashmap -I /home/coreboot/node-root/workspace/coreboot_scanbuild/util/cbfstool -I /cb-build/coreboot_scanbuild.0/sharedutils/cbfstool -I /home/coreboot/node-root/workspace/coreboot_scanbuild/src/commonlib/include -I /home/coreboot/node-root/workspace/coreboot_scanbuild/src/commonlib/bsd/include -I 3rdparty/vboot/firmware/include -I 3rdparty/vboot/firmware/2lib/include -I 3rdparty/vboot/host/include -I 3rdparty/vboot/host/lib/include -I /home/coreboot/node-root/workspace/coreboot_scanbuild/src -I /home/coreboot/node-root/workspace/coreboot_scanbuild/src/vendorcode/intel/edk2/uefi_2.4/MdePkg/Include -internal-isystem /opt/xgcc/lib/clang/17/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/13/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -source-date-epoch 1714465709 -O2 -Wwrite-strings -std=c11 -fconst-strings -fdebug-compilation-dir=/home/coreboot/node-root/workspace/coreboot_scanbuild -ferror-limit 19 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /cb-build/coreboot_scanbuild.0/sharedutils-scanbuildtmp/2024-05-02-073004-2299942-1 -x c /home/coreboot/node-root/workspace/coreboot_scanbuild/util/cbfstool/cbfs-mkstage.c
1/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <inttypes.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8#include "elfparsing.h"
9#include "common.h"
10#include "cbfs.h"
11#include "rmodule.h"
12
13/* Checks if program segment contains the ignored sections */
14static int is_phdr_ignored(Elf64_Phdr *phdr, Elf64_Shdr **shdrs)
15{
16 /* If no ignored section, return false. */
17 if (shdrs == NULL((void*)0))
18 return 0;
19
20 while (*shdrs) {
21 Elf64_Addr sh_start = (*shdrs)->sh_addr;
22 Elf64_Addr sh_end = (*shdrs)->sh_addr + (*shdrs)->sh_size;
23 Elf64_Addr ph_start = phdr->p_vaddr;
24 Elf64_Addr ph_end = phdr->p_vaddr + phdr->p_memsz;
25
26 /* Return true only if section occupies whole of segment. */
27 if ((sh_start == ph_start) && (sh_end == ph_end)) {
28 DEBUG("Ignoring program segment at 0x%" PRIx64 "\n", ph_start)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Ignoring program segment at 0x%"
"l" "x" "\n", ph_start); } while (0)
;
29 return 1;
30 }
31
32 /* If shdr intersects phdr at all, its a conflict */
33 if (((sh_start >= ph_start) && (sh_start <= ph_end)) ||
34 ((sh_end >= ph_start) && (sh_end <= ph_end))) {
35 ERROR("Conflicting sections in segment\n")fprintf(stderr, "E: " "Conflicting sections in segment\n");
36 exit(1);
37 }
38 shdrs++;
39 }
40
41 /* Program header doesn't need to be ignored. */
42 return 0;
43}
44
45/* Sections to be ignored are comma separated */
46static bool_Bool is_ignored_sections(const char *section_name,
47 const char *ignore_sections)
48{
49 const char *cur, *comma;
50
51 for (cur = ignore_sections; (comma = strchr(cur, ',')); cur = comma + 1)
52 if (!strncmp(cur, section_name, comma - cur))
53 return true1;
54 return !strcmp(cur, section_name);
55}
56
57/* Find section headers based on ignored section names.
58 * Returns a NULL-terminated list of section headers.
59 */
60static Elf64_Shdr **find_ignored_sections_header(struct parsed_elf *pelf,
61 const char *ignore_sections)
62{
63 int i;
64 const char *shstrtab;
65 Elf64_Shdr **headers = NULL((void*)0);
66 size_t size = 1;
67
68 /* No section needs to be ignored */
69 if (ignore_sections == NULL((void*)0))
70 return NULL((void*)0);
71
72 DEBUG("Sections to be ignored: %s\n", ignore_sections)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Sections to be ignored: %s\n"
, ignore_sections); } while (0)
;
73
74 /* Get pointer to string table */
75 shstrtab = buffer_get(pelf->strtabs[pelf->ehdr.e_shstrndx]);
76
77 for (i = 0; i < pelf->ehdr.e_shnum; i++) {
78 Elf64_Shdr *shdr;
79 const char *section_name;
80
81 shdr = &pelf->shdr[i];
82 section_name = &shstrtab[shdr->sh_name];
83
84 /* If section name matches ignored string, add to list */
85 if (is_ignored_sections(section_name, ignore_sections)) {
86 headers = realloc(headers, sizeof(*headers) * ++size);
87 if (!headers) {
88 ERROR("Memory allocation failed\n")fprintf(stderr, "E: " "Memory allocation failed\n");
89 exit(1);
90 }
91 headers[size - 2] = shdr;
92 }
93 }
94
95 if (headers)
96 headers[size - 1] = NULL((void*)0);
97 return headers;
98}
99
100static int fill_cbfs_stageheader(struct cbfs_file_attr_stageheader *stageheader,
101 uint64_t entry, uint64_t loadaddr,
102 uint32_t memsize)
103{
104 if (entry - loadaddr >= memsize) {
105 ERROR("stage entry point out of bounds!\n")fprintf(stderr, "E: " "stage entry point out of bounds!\n");
106 return -1;
107 }
108
109 stageheader->loadaddr = htobe64(loadaddr)__bswap_64 (loadaddr);
110 stageheader->memlen = htobe32(memsize)__bswap_32 (memsize);
111 stageheader->entry_offset = htobe32(entry - loadaddr)__bswap_32 (entry - loadaddr);
112
113 return 0;
114}
115
116/* returns size of result, or -1 if error.
117 * Note that, with the new code, this function
118 * works for all elf files, not just the restricted set.
119 */
120int parse_elf_to_stage(const struct buffer *input, struct buffer *output,
121 const char *ignore_section,
122 struct cbfs_file_attr_stageheader *stageheader)
123{
124 struct parsed_elf pelf;
125 Elf64_Phdr *phdr;
126 Elf64_Ehdr *ehdr;
127 Elf64_Shdr **shdrs_ignored;
128 Elf64_Addr virt_to_phys;
129 int ret = -1;
130
131 int headers;
132 int i;
133 uint64_t data_start, data_end, mem_end;
134
135 int flags = ELF_PARSE_PHDR(1 << 0) | ELF_PARSE_SHDR(1 << 1) | ELF_PARSE_STRTAB(1 << 3);
136
137 if (parse_elf(input, &pelf, flags)) {
138 ERROR("Couldn't parse ELF\n")fprintf(stderr, "E: " "Couldn't parse ELF\n");
139 return -1;
140 }
141
142 ehdr = &pelf.ehdr;
143 phdr = &pelf.phdr[0];
144
145 /* Find the section headers corresponding to ignored-sections */
146 shdrs_ignored = find_ignored_sections_header(&pelf, ignore_section);
147
148 if (ignore_section && (shdrs_ignored == NULL((void*)0)))
149 WARN("Ignore section(s) not found\n")fprintf(stderr, "W: " "Ignore section(s) not found\n");
150
151 headers = ehdr->e_phnum;
152
153 /* Ignore the program header containing ignored section */
154 for (i = 0; i < headers; i++) {
155 if (is_phdr_ignored(&phdr[i], shdrs_ignored))
156 phdr[i].p_type = PT_NULL0;
157 }
158
159 data_start = ~0;
160 data_end = 0;
161 mem_end = 0;
162 virt_to_phys = 0;
163
164 for (i = 0; i < headers; i++) {
165 uint64_t start, mend, rend;
166
167 if (phdr[i].p_type != PT_LOAD1)
168 continue;
169
170 /* Empty segments are never interesting */
171 if (phdr[i].p_memsz == 0)
172 continue;
173
174 /* BSS */
175
176 start = phdr[i].p_paddr;
177
178 mend = start + phdr[i].p_memsz;
179 rend = start + phdr[i].p_filesz;
180
181 if (start < data_start)
182 data_start = start;
183
184 if (rend > data_end)
185 data_end = rend;
186
187 if (mend > mem_end)
188 mem_end = mend;
189
190 if (virt_to_phys == 0)
191 virt_to_phys = phdr[i].p_paddr - phdr[i].p_vaddr;
192 }
193
194 if (data_end <= data_start) {
195 ERROR("data ends (%08lx) before it starts (%08lx). Make sure "fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure "
"the ELF file is correct and resides in ROM space.\n", (unsigned
long)data_end, (unsigned long)data_start)
196 "the ELF file is correct and resides in ROM space.\n",fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure "
"the ELF file is correct and resides in ROM space.\n", (unsigned
long)data_end, (unsigned long)data_start)
197 (unsigned long)data_end, (unsigned long)data_start)fprintf(stderr, "E: " "data ends (%08lx) before it starts (%08lx). Make sure "
"the ELF file is correct and resides in ROM space.\n", (unsigned
long)data_end, (unsigned long)data_start)
;
198 exit(1);
199 }
200
201 if (buffer_create(output, data_end - data_start, input->name) != 0) {
202 ERROR("Unable to allocate memory: %m\n")fprintf(stderr, "E: " "Unable to allocate memory: %m\n");
203 goto err;
204 }
205 memset(output->data, 0, output->size);
206
207 /* Copy the file data into the output buffer */
208
209 for (i = 0; i < headers; i++) {
210 if (phdr[i].p_type != PT_LOAD1)
211 continue;
212
213 if (phdr[i].p_memsz == 0)
214 continue;
215
216 /* A legal ELF file can have a program header with
217 * non-zero length but zero-length file size and a
218 * non-zero offset which, added together, are > than
219 * input->size (i.e. the total file size). So we need
220 * to not even test in the case that p_filesz is zero.
221 */
222 if (!phdr[i].p_filesz)
223 continue;
224 if (input->size < (phdr[i].p_offset + phdr[i].p_filesz)){
225 ERROR("Underflow copying out the segment."fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n"
, input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz
))
226 "File has %zu bytes left, segment end is %zu\n",fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n"
, input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz
))
227 input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz))fprintf(stderr, "E: " "Underflow copying out the segment." "File has %zu bytes left, segment end is %zu\n"
, input->size, (size_t)(phdr[i].p_offset + phdr[i].p_filesz
))
;
228 goto err;
229 }
230 memcpy(&output->data[phdr[i].p_paddr - data_start],
231 &input->data[phdr[i].p_offset],
232 phdr[i].p_filesz);
233 }
234
235 /* coreboot expects entry point to be physical address. Thus, adjust the
236 entry point accordingly. */
237 ret = fill_cbfs_stageheader(stageheader, ehdr->e_entry + virt_to_phys,
238 data_start, mem_end - data_start);
239err:
240 parsed_elf_destroy(&pelf);
241 return ret;
242}
243
244struct xip_context {
245 struct rmod_context rmodctx;
246 Elf64_Shdr **ignored_sections;
247};
248
249static int rmod_filter(struct reloc_filter *f, const Elf64_Rela *r)
250{
251 size_t symbol_index;
252 int reloc_type;
253 struct parsed_elf *pelf;
254 Elf64_Sym *sym;
255 struct xip_context *xipctx;
256 Elf64_Shdr **sections;
257
258 xipctx = f->context;
259 pelf = &xipctx->rmodctx.pelf;
260
261 /* Allow everything through if there isn't an ignored section. */
262 if (xipctx->ignored_sections == NULL((void*)0))
263 return 1;
264
265 reloc_type = ELF64_R_TYPE(r->r_info)((r->r_info) & 0xffffffff);
266 symbol_index = ELF64_R_SYM(r->r_info)((r->r_info) >> 32);
267 sym = &pelf->syms[symbol_index];
268
269 /* Nothing to filter. Relocation is not being applied to the
270 * ignored sections. */
271 for (sections = xipctx->ignored_sections; *sections; sections++)
272 if (sym->st_shndx == *sections - pelf->shdr)
273 break;
274 if (!*sections)
275 return 1;
276
277 /* If there is any relocation to the ignored section that isn't
278 * absolute fail as current assumptions are that all relocations
279 * are absolute. */
280 if ((reloc_type != R_386_321) &&
281 (reloc_type != R_AMD64_641) &&
282 (reloc_type != R_AMD64_3210)) {
283 ERROR("Invalid reloc to ignored section: %x\n", reloc_type)fprintf(stderr, "E: " "Invalid reloc to ignored section: %x\n"
, reloc_type)
;
284 return -1;
285 }
286
287 /* Relocation referencing ignored sections. Don't emit it. */
288 return 0;
289}
290
291/* Returns a NULL-terminated list of loadable segments. Returns NULL if no
292 * loadable segments were found or if two consecutive segments are not
293 * consecutive in their physical address space.
294 */
295static Elf64_Phdr **find_loadable_segments(struct parsed_elf *pelf)
296{
297 Elf64_Phdr **phdrs = NULL((void*)0);
298 Elf64_Phdr *prev = NULL((void*)0), *cur;
299 size_t size = 1, i;
300
301 for (i = 0; i < pelf->ehdr.e_phnum; i++, prev = cur) {
6
Assuming 'i' is < field 'e_phnum'
7
Loop condition is true. Entering loop body
16
Assuming 'i' is < field 'e_phnum'
17
Loop condition is true. Entering loop body
302 cur = &pelf->phdr[i];
303
304 if (cur->p_type != PT_LOAD1 || cur->p_memsz == 0)
8
Assuming field 'p_type' is equal to PT_LOAD
9
Assuming field 'p_memsz' is not equal to 0
10
Taking false branch
18
Assuming field 'p_type' is equal to PT_LOAD
19
Assuming field 'p_memsz' is not equal to 0
20
Taking false branch
305 continue;
306
307 phdrs = realloc(phdrs, sizeof(*phdrs) * ++size);
11
Memory is allocated
21
Attempt to reallocate memory
308 if (!phdrs) {
12
Assuming 'phdrs' is non-null
13
Taking false branch
22
Assuming 'phdrs' is null
23
Reallocation failed
24
Taking true branch
309 ERROR("Memory allocation failed\n")fprintf(stderr, "E: " "Memory allocation failed\n");
25
Potential leak of memory pointed to by 'phdrs'
310 return NULL((void*)0);
311 }
312 phdrs[size - 2] = cur;
313
314 if (!prev
13.1
'prev' is null
)
14
Taking true branch
315 continue;
15
Execution continues on line 301
316
317 if (prev->p_paddr + prev->p_memsz != cur->p_paddr ||
318 prev->p_filesz != prev->p_memsz) {
319 ERROR("Loadable segments physical addresses should "fprintf(stderr, "E: " "Loadable segments physical addresses should "
"be consecutive\n")
320 "be consecutive\n")fprintf(stderr, "E: " "Loadable segments physical addresses should "
"be consecutive\n")
;
321 free(phdrs);
322 return NULL((void*)0);
323 }
324 }
325
326 if (phdrs)
327 phdrs[size - 1] = NULL((void*)0);
328 return phdrs;
329}
330
331int parse_elf_to_xip_stage(const struct buffer *input, struct buffer *output,
332 uint32_t location, const char *ignore_sections,
333 struct cbfs_file_attr_stageheader *stageheader)
334{
335 struct xip_context xipctx;
336 struct rmod_context *rmodctx;
337 struct reloc_filter filter;
338 struct parsed_elf *pelf;
339 uint32_t adjustment, memsz = 0;
340 struct buffer binput;
341 struct buffer boutput;
342 Elf64_Phdr **toload, **phdr;
343 Elf64_Xword i;
344 int ret = -1;
345 size_t filesz = 0;
346
347 rmodctx = &xipctx.rmodctx;
348 pelf = &rmodctx->pelf;
349
350 if (rmodule_init(rmodctx, input))
1
Assuming the condition is false
351 return -1;
352
353 /* Only support x86 / x86_64 XIP currently. */
354 if ((rmodctx->pelf.ehdr.e_machine != EM_3863) &&
2
Assuming field 'e_machine' is equal to EM_386
355 (rmodctx->pelf.ehdr.e_machine != EM_X86_6462)) {
356 ERROR("Only support XIP stages for x86/x86_64\n")fprintf(stderr, "E: " "Only support XIP stages for x86/x86_64\n"
)
;
357 goto out;
358 }
359
360 xipctx.ignored_sections =
361 find_ignored_sections_header(pelf, ignore_sections);
362
363 filter.filter = rmod_filter;
364 filter.context = &xipctx;
365
366 if (rmodule_collect_relocations(rmodctx, &filter))
3
Assuming the condition is false
4
Taking false branch
367 goto out;
368
369 toload = find_loadable_segments(pelf);
5
Calling 'find_loadable_segments'
370 if (!toload)
371 goto out;
372
373 for (phdr = toload; *phdr; phdr++)
374 filesz += (*phdr)->p_filesz;
375 if (buffer_create(output, filesz, input->name) != 0) {
376 ERROR("Unable to allocate memory: %m\n")fprintf(stderr, "E: " "Unable to allocate memory: %m\n");
377 goto out;
378 }
379 buffer_clone(&boutput, output);
380 memset(buffer_get(&boutput), 0, filesz);
381 buffer_set_size(&boutput, 0);
382
383 /* The program segment moves to final location from based on virtual
384 * address of loadable segment. */
385 adjustment = location - pelf->phdr->p_vaddr;
386 DEBUG("Relocation adjustment: %08x\n", adjustment)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "Relocation adjustment: %08x\n"
, adjustment); } while (0)
;
387
388 for (phdr = toload; *phdr; phdr++)
389 memsz += (*phdr)->p_memsz;
390 fill_cbfs_stageheader(stageheader,
391 (uint32_t)pelf->ehdr.e_entry + adjustment,
392 (uint32_t)pelf->phdr->p_vaddr + adjustment,
393 memsz);
394 for (phdr = toload; *phdr; phdr++) {
395 /* Need an adjustable buffer. */
396 buffer_clone(&binput, input);
397 buffer_seek(&binput, (*phdr)->p_offset);
398 bputs(&boutput, buffer_get(&binput), (*phdr)->p_filesz);
399 }
400
401 buffer_clone(&boutput, output);
402
403 /* Make adjustments to all the relocations within the program. */
404 for (i = 0; i < rmodctx->nrelocs; i++) {
405 size_t reloc_offset;
406 uint32_t val;
407 struct buffer in, out;
408
409 /* The relocations represent in-program addresses of the
410 * linked program. Obtain the offset into the program to do
411 * the adjustment. */
412 reloc_offset = rmodctx->emitted_relocs[i] - pelf->phdr->p_vaddr;
413
414 buffer_clone(&out, &boutput);
415 buffer_seek(&out, reloc_offset);
416 buffer_clone(&in, &out);
417 /* Appease around xdr semantics: xdr decrements buffer
418 * size when get()ing and appends to size when put()ing. */
419 buffer_set_size(&out, 0);
420
421 val = xdr_le.get32(&in);
422 DEBUG("reloc %zx %08x -> %08x\n", reloc_offset, val,do { if (verbose > 1) fprintf(stderr, "DEBUG: " "reloc %zx %08x -> %08x\n"
, reloc_offset, val, val + adjustment); } while (0)
423 val + adjustment)do { if (verbose > 1) fprintf(stderr, "DEBUG: " "reloc %zx %08x -> %08x\n"
, reloc_offset, val, val + adjustment); } while (0)
;
424 xdr_le.put32(&out, val + adjustment);
425 }
426
427 ret = 0;
428
429out:
430 rmodule_cleanup(rmodctx);
431 return ret;
432}