https://sourceware.org/bugzilla/show_bug.cgi?id=34898

            Bug ID: 34898
           Summary: libctf: ctf_member_next with CTF_MN_RECURSE writes to
                    the iterator after freeing it (heap-use-after-free at
                    ctf-types.c:229)
           Product: binutils
           Version: 2.48 (HEAD)
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: libctf
          Assignee: unassigned at sourceware dot org
          Reporter: martin.mirchev at u dot nus.edu
  Target Milestone: ---

In ctf_member_next (libctf/ctf-types.c), when CTF_MN_RECURSE is set and the
recursive call for an unnamed sub-struct member fails with an error other than
ECTF_NEXT_END, the error path frees the iterator and then stores into it:

    227   ctf_next_destroy (i);
    228   *it = NULL;
    229   i->ctn_type = 0;

Line 229 is an 8-byte write into the freed 80-byte ctf_next_t.

Tested on binutils-gdb master 1ec1cee48577c7b1960ddce5a6054b3f9fab6ef5
(--enable-targets=all). Release: Ubuntu 22.04 gcc 11 default -g -O2.
ASan: -O1 -g -fsanitize=address,undefined.

Attached (all files in libctf-member-next-uaf.tar.gz):
  evil.ctf    121-byte raw CTF v3 dict: struct S { struct T; } where T's own
              unnamed member has an out-of-range type id (ECTF_BADID on
resolve)
  benign.ctf  same dict with a valid member type (control)
  craft.c     generator for both files
  harness.c   libctf consumer: ctf_simple_open() on the file, then
              ctf_member_next (fp, S, &it, ..., CTF_MN_RECURSE) in a loop, as
              libctf-lookup/struct-iteration.c does

  gcc -I include -o harness harness.c libctf/.libs/libctf.a bfd/.libs/libbfd.a
\
      libsframe/.libs/libsframe.a libiberty/libiberty.a -lzstd -lz -ldl
  ./harness evil.ctf

ASan:
  ERROR: AddressSanitizer: heap-use-after-free on address 0x507000000178
  WRITE of size 8 at 0x507000000178 thread T0
    #0 ctf_member_next libctf/ctf-types.c:229
    #1 main harness.c:89
  0x507000000178 is located 8 bytes inside of 80-byte region
[0x507000000170,0x5070000001c0)
  freed by thread T0 here:
    #1 ctf_next_destroy libctf/ctf-util.c:267
    #2 ctf_member_next libctf/ctf-types.c:227
  previously allocated by thread T0 here:
    #1 ctf_next_create libctf/ctf-util.c:250
    #2 ctf_member_next libctf/ctf-types.c:121

Release build: no crash. The second call returns -1 with ECTF_BADID and the
program exits 0. Valgrind on the release build: "Invalid write of size 8 at
ctf_member_next (ctf-types.c:229) ... 8 bytes inside a block of size 80 free'd
... by ctf_member_next (ctf-types.c:227)".

Controls: benign.ctf is clean under ASan and valgrind. evil.ctf iterated with
flags 0 is also clean, because the sub-struct branch needs CTF_MN_RECURSE. I
found no in-tree binutils tool that passes that flag, so this needs a libctf
API consumer.

The store at line 229 should probably be dropped or moved before
ctf_next_destroy, since the iterator no longer exists at that point.

This issue was found by an LLM-Enhanced Static Analysis Engine and verified
manually.

-- 
You are receiving this mail because:
You are on the CC list for the bug.

Reply via email to