Skip to content

Commit 9d99bff

Browse files
fix(log2f): infinite recursion / stack overflow in shipped libc.so (bd-2g7oyh.370)
core::math::float32::log2f used x.log2() (the std f32::log2). On x86-64 that lowers to an INDIRECT call through the log2f symbol; in the shipped libc.so that symbol is our OWN interposed log2f, so the call binds to itself => infinite recursion / stack overflow. Any program linking the real libc.so and calling log2f (or log2f32 / __log2f_finite, which route through core::log2f) crashes. CONFIRMED via the built cdylib's GOT relocation: the indirect call target in log2f was R_X86_64_GLOB_DAT 'log2f@@base' (= log2f's own entry). After the fix the disassembly shows no self-call (libm::log2f inlines). The bench NEVER caught this: glibc_baseline_bench is a normal Rust binary that links glibc, so x.log2() there binds to glibc's log2f (and even benched 'faster than glibc', which is how it slipped in as a perf change — an artifact, not a real win; the shipped path stack-overflows). Fix: use libm::log2f(x) (pure-Rust, recursion-safe), consistent with EVERY other f32 transcendental in this file (expf->libm::exp2f, log10f->libm::log10f, ...), which all use libm::* for exactly this reason. ULP parity preserved (libm::log2f was the prior path); core math::float32 (35) + abi conformance_diff_math (17) green. log2f reverts to ~1.13x-vs-glibc — a real but acceptable gap, vs a crash. LESSON: never use std f32/f64 .log2()/.exp()/.ln() etc. in interposed-symbol code — they lower to libm symbol calls that self-recurse in libc.so. The bench links glibc and cannot detect this; verify via cdylib GOT relocations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ef09373 commit 9d99bff

1 file changed

Lines changed: 8 additions & 1 deletion

File tree

crates/frankenlibc-core/src/math/float32.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,14 @@ pub fn logf(x: f32) -> f32 {
7171

7272
#[inline]
7373
pub fn log2f(x: f32) -> f32 {
74-
x.log2()
74+
// MUST use the pure-Rust libm implementation, NOT `x.log2()`. The std
75+
// `f32::log2` lowers to an indirect call through the `log2f` symbol; in the
76+
// shipped `libc.so` that symbol is our OWN interposed `log2f`, so `x.log2()`
77+
// here recurses infinitely (stack overflow). Verified via the cdylib GOT
78+
// relocation: the indirect target binds to `log2f@@Base` (self). The bench
79+
// never caught it because the bench binary links glibc's `log2f`. Every other
80+
// f32 transcendental here uses `libm::*` for exactly this reason.
81+
libm::log2f(x)
7582
}
7683

7784
#[inline]

0 commit comments

Comments
 (0)