Skip to content

skel: validate yytables_fload header buffer and bound table dimensions#731

Open
MarkLee131 wants to merge 1 commit intowestes:masterfrom
MarkLee131:fix/yytables-fload-validation
Open

skel: validate yytables_fload header buffer and bound table dimensions#731
MarkLee131 wants to merge 1 commit intowestes:masterfrom
MarkLee131:fix/yytables-fload-validation

Conversation

@MarkLee131
Copy link
Copy Markdown

Fix #730: The binary tables-file loader trusts attacker-controlled bytes from the file in three places.

  1. yytbl_hdr_read at cpp-flex.skl:4010 did th->th_name = th->th_version + strlen(th->th_version) + 1; immediately after fread'ing th_hsize - 14 bytes into th_version. If those bytes contain no NUL, strlen walks past the heap region. Fix: use strnlen() and require both the version and the name string to terminate with NULs inside the buffer; return -1 otherwise.

  2. yytbl_fload at cpp-flex.skl:4278 then did if (strcmp(th.th_name, key) != 0) even when th_name == th_version + bytes, where the buffer has no room for the name section. Fixed by the same validation in (1): th_name is only set when a name NUL is found inside the buffer.

  3. yytbl_data_load at cpp-flex.skl:4090-4092 computes bytes = sizeof(yy_trans_info) * td_lolen * td_hilen (or the equivalent non-STRUCT form) with td_lolen and td_hilen read directly from the file as flex_uint32_t. A 50-byte tables file with td_lolen near UINT32_MAX makes the loader call yyalloc(8 GB). Fix: reject td_lolen or td_hilen above YYTBL_MAX_DIM (1<<24) before the multiplication, mirroring 9c54eb6 which added the analogous guard to the build-time tool's allocators.

Verified with three minimum-size PoCs (16, 16, 50 bytes) that the unpatched loader trips ASAN on each, and the patched loader returns -1 cleanly. A valid tables file generated by flex --tables-file= still loads as before (regression check passed).

The binary tables-file loader trusts attacker-controlled bytes from the
file in three places.  Fix all three:

  1. yytbl_hdr_read at cpp-flex.skl:4010 did
        th->th_name = th->th_version + strlen(th->th_version) + 1;
     immediately after fread'ing th_hsize - 14 bytes into th_version.
     If those bytes contain no NUL, strlen walks past the heap region.
     Fix: use strnlen() and require both the version and the name string
     to terminate with NULs inside the buffer; return -1 otherwise.

  2. yytbl_fload at cpp-flex.skl:4278 then did
        if (strcmp(th.th_name, key) != 0)
     even when th_name == th_version + bytes, where the buffer has no
     room for the name section.  Fixed by the same validation in (1):
     th_name is only set when a name NUL is found inside the buffer.

  3. yytbl_data_load at cpp-flex.skl:4090-4092 computes
        bytes = sizeof(yy_trans_info) * td_lolen * td_hilen
     (or the equivalent non-STRUCT form) with td_lolen and td_hilen read
     directly from the file as flex_uint32_t.  A 50-byte tables file with
     td_lolen near UINT32_MAX makes the loader call yyalloc(8 GB).  Fix:
     reject td_lolen or td_hilen above YYTBL_MAX_DIM (1<<24) before the
     multiplication, mirroring 9c54eb6 which added the analogous guard
     to the build-time tool's allocators.

Verified with three minimum-size PoCs (16, 16, 50 bytes) that the
unpatched loader trips ASAN on each, and the patched loader returns -1
cleanly.  A valid tables file generated by `flex --tables-file=` still
loads as before (regression check passed).

Found via a libFuzzer harness against yytables_fload, expanding what
the upstream OSS-Fuzz projects/flex integration covers (their
flex-patch.diff neuters the m4 fork, which keeps the loader off the
fuzzed code path).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Three bugs in yytables_fload (binary tables-file loader)

1 participant