From bbef46e7830c935ff7983f05d981a72b7b26e555 Mon Sep 17 00:00:00 2001 From: 2SinceJuly Date: Thu, 12 Nov 2026 06:19:57 +0800 Subject: [PATCH] fix(hardening): recognize the __chk_fail FORTIFY handler import (#10036) * fix(hardening): recognize the __chk_fail FORTIFY handler import glibc exports __chk_fail as the shared failure handler that most FORTIFY checks funnel into, and its dynamic import is a positive sign that the binary uses FORTIFY. The ELF FORTIFY matcher accepted __fortify_fail and __*_chk names, so an image whose only relevant import was __chk_fail was reported as fortify: unknown with no evidence location. * style(hardening): wrap the __chk_fail fixture call to the formatter's width format:check fails on the current head because the new fixture call fits on one line at the file's previous width but exceeds oxfmt's 80-column limit with the longer fortifyName literal. --------- Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com> Co-authored-by: morluto <76467478+morluto@users.noreply.github.com> --- src/native/hardening/BinaryHardeningParsers.test.ts | 13 +++++++++++++ src/native/hardening/ElfHardening.ts | 1 + 2 files changed, 14 insertions(+) diff --git a/src/native/hardening/BinaryHardeningParsers.test.ts b/src/native/hardening/BinaryHardeningParsers.test.ts index 5efc51d5ba..d92673743a 100644 --- a/src/native/hardening/BinaryHardeningParsers.test.ts +++ b/src/native/hardening/BinaryHardeningParsers.test.ts @@ -1108,6 +1108,19 @@ it("cites a non-ASCII import name by its source bytes", () => { expect(bytes.subarray(at, at + 7).toString("latin1")).toBe(name); }); +it("treats the __chk_fail handler import as a FORTIFY sign", () => { + const bytes = elfHardeningFixture({ + fortify: true, + fortifyName: "__chk_fail", + }); + const image = readElfHardening(bytes); + const row = findRow(image.rows, "fortify"); + expect(row).toMatchObject({ state: "enabled", location: { bytes: 10 } }); + expect(row.basis).toContain("__chk_fail"); + const at = row.location?.offset ?? 0; + expect(bytes.subarray(at, at + 10).toString("latin1")).toBe("__chk_fail"); +}); + it("keeps malformed name offsets malformed in both formats", async () => { await expect( readMachoHardening(machoHardeningFixture({ brokenStrx: true })), diff --git a/src/native/hardening/ElfHardening.ts b/src/native/hardening/ElfHardening.ts index 46f6d54b48..23a64065cf 100644 --- a/src/native/hardening/ElfHardening.ts +++ b/src/native/hardening/ElfHardening.ts @@ -279,6 +279,7 @@ export const readElfHardening = ( symbols, (name) => name === "__fortify_fail" || + name === "__chk_fail" || (name.startsWith("__") && name.endsWith("_chk")), "No fortify symbol was found in the dynamic symbol table. A static or stripped image can omit those imports while still using FORTIFY.", "The dynamic symbol table is absent, so FORTIFY use is unknown.",