Skip to content

Fix destruct ub and ubsan ci#16

Merged
igudich merged 4 commits into
masterfrom
fix-destruct-ub-and-ubsan-ci
May 21, 2026
Merged

Fix destruct ub and ubsan ci#16
igudich merged 4 commits into
masterfrom
fix-destruct-ub-and-ubsan-ci

Conversation

@igudich
Copy link
Copy Markdown
Contributor

@igudich igudich commented May 19, 2026

Fix UB in GEN_ALLOC_CLONE_FREE and run tests under UBSan

Summary

  • name##_free called destruct_free(f, &name##_destruct), casting a typed
    destructor (e.g. void (*)(field_t *)) to void (*)(void *) and calling
    through the latter. C99/C11 §6.3.2.3¶8 makes this UB even when the calling
    convention happens to match; clang's -fsanitize=function flags it on
    every *_free. Inline the destructor call in the macro so the call goes
    through the function's actual type; destruct_free / destruct_cb_t
    become dead and are removed.
  • Build the test target under -fsanitize=undefined -fno-sanitize-recover=all,
    through the latter. C99/C11 §6.3.2.3¶8 makes this UB even when the calling
    convention happens to match; clang's -fsanitize=function flags it on
    every *_free. Inline the destructor call in the macro so the call goes
    through the function's actual type; destruct_free / destruct_cb_t
    become dead and are removed.
  • Build the test target under -fsanitize=undefined -fno-sanitize-recover=all,
    conditionally adding -fsanitize=function when the compiler supports it
    (clang). Any UB hit aborts the test and fails CI.
  • Extend the GitHub Actions workflow to a [gcc, clang] matrix so the
    function check actually runs.
  • Add test/test-alloc-free.cxx exercising every name##_free path
    (field / hdr / frame, including the recursive chain) so UBSan covers them.

@igudich igudich requested a review from jeso-mchp May 19, 2026 10:12
@igudich igudich force-pushed the fix-destruct-ub-and-ubsan-ci branch from 7ed69b1 to 07f98d8 Compare May 19, 2026 11:13
@jeso-mchp
Copy link
Copy Markdown
Contributor

Nice, I will add some more sanitizers to the fuzz targets. You can just rebase on master an merge,

Igor Gudich added 4 commits May 21, 2026 12:39
name##_free called destruct_free(f, &name##_destruct), which cast the
typed destructor (e.g. void (*)(field_t *)) to void (*)(void *) and
called through the latter. Per C99/C11 §6.3.2.3¶8, calling a function
through a pointer of an incompatible type is undefined behaviour, even
when the calling convention happens to match. clang's -fsanitize=function
flags this on every *_free.

Inline the destructor call in the macro body so the call goes through
the function's actual type. destruct_free and destruct_cb_t become dead;
remove them.
Build libef and ef-tests with -fsanitize=undefined -fno-sanitize-recover=all
under TEST_ENABLE so any UB hit at runtime aborts the test and fails CI.
Probe for -fsanitize=function (clang-only) and add it when available; this
catches indirect-call type mismatches like the destruct_free regression
fixed in the previous commit.

Add a [gcc, clang] matrix to the GitHub Actions workflow so the function
check actually runs in CI.

Add test/test-alloc-free.cxx exercising every name##_free path explicitly
(field, hdr, frame — including the recursive frame_free -> hdr_free ->
field_free chain on a populated frame) so UBSan covers them.
Default ON preserves existing behaviour. -DEF_USE_LIBPCAP=OFF skips
the detect-and-link block entirely so libef and ef do not pull in
libpcap or its transitive deps. Useful for sanitizer builds where a
transitive dependency (e.g. libnl-route-3 brought in by libpcap)
trips MSan on uninitialized DSO-init reads outside our control —
previously fixable only by carrying a downstream patch that
commented out the detection block.
frame_def's serialization loop has the same shape as hdr_copy_to_buf_
which 4d9ce68 just guarded: if a frame_fill_defaults hook zeroes a
field's bit_width while leaving its bit_offset past the new header
end, hdr_write_field would assert. Today no header sets a def_val
on a width-0 field, so this is purely defensive — but symmetric with
the call-site fix and harmless.

Also add a Catch2 regression test mirroring the ef-args.c sequence
that triggered the original mld_fill_defaults bug: parse an MLDv2
query (with qrv/qqic/ns set, no rresv/ng), call frame_to_buf to drive
mld_fill_defaults' shrink + bit_width-zeroing, then call
frame_mask_to_buf on the same now-mutated frame. Pre-fix the second
call aborted in hdr_write_field; post-fix the suite passes.
@igudich igudich force-pushed the fix-destruct-ub-and-ubsan-ci branch from 07f98d8 to fe12a52 Compare May 21, 2026 10:40
@igudich igudich merged commit a582474 into master May 21, 2026
2 checks passed
@igudich igudich deleted the fix-destruct-ub-and-ubsan-ci branch May 21, 2026 11:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants