An invalid BTF.ext subsection length can wrap the pointer addition used by btf_ext_parse_sec_info() on 32-bit builds. The wrapped pointer passes the bounds check and parsing then reads beyond the copied BTF.ext data. Validate the offset and length with subtraction before forming the section pointer. Fixes: ae4ab4b4117d ("btf: expose API to work with raw btf_ext data") Closes: https://issues.oss-fuzz.com/issues/477315119 Signed-off-by: Darren Carreras --- Changes in v4: - Drop the selftest because the malformed length is already rejected by the old check on 64-bit CI; the behavioral divergence is specific to 32-bit. - Correct the Fixes tag to the commit that introduced the pointer-based check. Changes in v3: - Remove the nested mbox envelope and mail headers from the Gmail attachment so Patchwork's generated mbox applies with git am. Changes in v2: - Resend as plain text because Gmail mangled the v1 diff and Patchwork reported "Patch is empty." - Include the authorized DCO Signed-off-by line. tools/lib/bpf/btf.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index 8417de9..744f33e 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -3364,7 +3364,7 @@ static int btf_ext_parse_sec_info(struct btf_ext *btf_ext, { const struct btf_ext_info_sec *sinfo; struct btf_ext_info *ext_info; - __u32 info_left, record_size; + __u32 data_left, info_left, record_size; size_t sec_cnt = 0; void *info; @@ -3377,16 +3377,17 @@ static int btf_ext_parse_sec_info(struct btf_ext *btf_ext, return -EINVAL; } - /* The start of the info sec (including the __u32 record_size). */ - info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off; - info_left = ext_sec->len; - - if (btf_ext->data + btf_ext->data_size < info + ext_sec->len) { + data_left = btf_ext->data_size - btf_ext->hdr->hdr_len; + if (ext_sec->off > data_left || ext_sec->len > data_left - ext_sec->off) { pr_debug("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", ext_sec->desc, ext_sec->off, ext_sec->len); return -EINVAL; } + /* The start of the info sec (including the __u32 record_size). */ + info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off; + info_left = ext_sec->len; + /* At least a record size */ if (info_left < sizeof(__u32)) { pr_debug(".BTF.ext %s record size not found\n", ext_sec->desc);