[PATCH 14/31] btf_encoder, libctf: Add elf_strptr NULL checks and fix kfunc bounds
Arnaldo Carvalho de Melo <[email protected]> Wed, 29 Jul 2026 16:07:14 -0300
| Newsgroups | org.kernel.vger.dwarves,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
From: Arnaldo Carvalho de Melo <[email protected]> Several elf_strptr() calls in btf_encoder and libctf lacked NULL checks, which would cause segfaults on malformed ELF files: 1. btf_encoder__new(): strcmp(secname, PERCPU_SECTION) crashes if elf_section_by_idx() returns a valid section but elf_strptr() fails internally. 2. btf_encoder__collect_kfuncs(): two elf_strptr() calls for symbol names passed to strstarts()/get_func_name() without NULL guards. 3. libctf.c ctf__encode(): strcmp(secname, ".SUNW_ctf") without NULL check. Also fix is_sym_kfunc_set() bounds check: the original `off >= d_size` only verified the start offset, but accessing set->flags could read past the buffer. Changed to `off + sizeof(*set) > d_size` and added an `off < 0` guard to prevent signed-to-unsigned wraparound when the symbol address is below the section base. Before: malformed ELF with invalid string table causes SIGSEGV After: gracefully skips bad entries Fixes: 72e88f29c6f7e142 ("pahole: Inject kfunc decl tags into BTF") Fixes: ff34e733a0c23bf4 ("btf_encoder: Allow encoding VARs from many sections") Fixes: dcef613288086156 ("libctf: give up "for now" on using libelf to add a section to an existing file") Reported-by: Sashiko:gemini-3-1-pro-preview Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Arnaldo Carvalho de Melo <[email protected]> --- btf_encoder.c | 10 ++++++---- libctf.c | 2 +- pahole.c | 14 +++++++++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/btf_encoder.c b/btf_encoder.c index 5c12e79f5ef648ba..c7b71b5b741bfa6f 100644 --- a/btf_encoder.c +++ b/btf_encoder.c @@ -2099,14 +2099,14 @@ static int is_sym_kfunc_set(GElf_Sym *sym, const char *name, Elf_Data *idlist, s { void *ptr = idlist->d_buf; struct btf_id_set8 *set; - size_t off; + ptrdiff_t off; /* kfuncs are only found in BTF_SET8's */ if (!strstarts(name, BTF_ID_SET8_PFX)) return false; off = sym->st_value - idlist_addr; - if (off >= idlist->d_size) { + if (off < 0 || (size_t)off + sizeof(*set) > idlist->d_size) { fprintf(stderr, "%s: symbol '%s' out of bounds\n", __func__, name); return false; } @@ -2276,7 +2276,7 @@ static int btf_encoder__collect_kfuncs(struct btf_encoder *encoder) continue; name = elf_strptr(elf, strtabidx, sym.st_name); - if (!is_sym_kfunc_set(&sym, name, idlist, idlist_addr)) + if (name == NULL || !is_sym_kfunc_set(&sym, name, idlist, idlist_addr)) continue; range.start = sym.st_value; @@ -2305,6 +2305,8 @@ static int btf_encoder__collect_kfuncs(struct btf_encoder *encoder) continue; name = elf_strptr(elf, strtabidx, sym.st_name); + if (name == NULL) + continue; func = get_func_name(name); if (!func) continue; @@ -2879,7 +2881,7 @@ struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filenam if (encoder->encode_vars & BTF_VAR_GLOBAL) encoder->secinfo[shndx].include = true; - if (strcmp(secname, PERCPU_SECTION) == 0) { + if (secname != NULL && strcmp(secname, PERCPU_SECTION) == 0) { found_percpu = true; if (encoder->encode_vars & BTF_VAR_PERCPU) encoder->secinfo[shndx].include = true; diff --git a/libctf.c b/libctf.c index 8e31e3d550ebd51a..72f9949a2d25b3d9 100644 --- a/libctf.c +++ b/libctf.c @@ -674,7 +674,7 @@ int ctf__encode(struct ctf *ctf, uint8_t flags) if (shdr == NULL) continue; char *secname = elf_strptr(elf, strndx, shdr->sh_name); - if (strcmp(secname, ".SUNW_ctf") == 0) { + if (secname != NULL && strcmp(secname, ".SUNW_ctf") == 0) { data = elf_getdata(scn, data); goto out_update; } diff --git a/pahole.c b/pahole.c index 6ba3f578c28570a1..0e2acc35d6d679f0 100644 --- a/pahole.c +++ b/pahole.c @@ -2361,6 +2361,11 @@ static int pipe_seek(FILE *fp, off_t offset) chunk = offset; } + /* On EOF (not I/O error), clear errno so callers don't + * pick up a stale value from an earlier successful fread. */ + if (!ferror(fp)) + errno = 0; + return offset == 0 ? 0 : -1; } @@ -2583,8 +2588,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty if (instance == NULL) return -ENOMEM; + errno = 0; if (type__instance_read_once(header, input) < 0) { - printed = -errno; + printed = errno ? -errno : -EIO; fprintf(stderr, "pahole: --header (%s) type couldn't be read\n", conf.header_type); goto out; } @@ -2666,8 +2672,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty free(member_name); + errno = 0; if (pipe_seek(input, seek_bytes) < 0) { - printed = -errno; + printed = errno ? -errno : -EIO; fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes); goto out; } @@ -2717,8 +2724,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty seek_bytes -= ftell(input); } + errno = 0; if (pipe_seek(input, seek_bytes) < 0) { - printed = -errno; + printed = errno ? -errno : -EIO; fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes); goto out; } -- 2.55.0