Re-enable CONFIG_SHADOW_CALL_STACK once GCC 15 arm_neon.h pragma issue is fixed #20

Open
opened 2026-05-18 13:24:27 +00:00 by marfrit · 0 comments
Owner

Summary

linux-pinetab2-danctnix-besser pkgrel=2 ships with CONFIG_SHADOW_CALL_STACK=n as a temporary workaround for a GCC 15.2.1 toolchain issue. This issue tracks re-enabling SCS once GCC upstream is fixed so we don't carry the security-hardening regression indefinitely.

What SCS is + why we want it back

CONFIG_SHADOW_CALL_STACK is an arm64 kernel-hardening feature. The compiler reserves register x18 as a pointer to a parallel "shadow stack" that holds only return addresses. Stack-buffer-overflow / ROP attacks that rewrite the saved return address on the main stack trigger a mismatch and oops instead of jumping to attacker-controlled code. Cost: one register lost to general use, ~1-2% perf hit, ~10 KB per task. Cheap protection; the danctnix default has it on.

What's broken right now

Building this kernel on boltzmann (GCC 15.2.1) with CONFIG_SHADOW_CALL_STACK=y fails in arch/arm64/lib/xor-neon.c:

/usr/lib/gcc/aarch64-unknown-linux-gnu/15.2.1/include/arm_neon.h:7261:24:
   error: '-fsanitize=shadow-call-stack' requires '-ffixed-x18'
   #pragma GCC pop_options

The error fires inside arm_neon.h at every #pragma GCC pop_options that closes a #pragma GCC target("+nothing+aes")-style block. GCC 15's validator strict-checks SCS↔x18 compatibility after the pop, but the surrounding kernel-file CFLAGS already have both -ffixed-x18 and -fsanitize=shadow-call-stack set correctly (verified in the .xor-neon.o.cmd cache).

The target(...) pragma seemingly doesn't carry -ffixed-x18 through the push/pop boundary, so on pop GCC thinks the option pair is inconsistent.

The bug surfaces on arch/arm64/lib/xor-neon.c (the only file in the arm64 lib that includes <asm/neon-intrinsics.h>); kernel-side it's #include-only — no clever pragma manipulation on our part.

Web-search verdict (2026-05-18)

  • PR 102768 added SCS-↔x18 strict-check to GCC ~12-era. So the check itself has been there a while. (https://patchwork.sourceware.org/project/gcc/patch/20220129150035.114602-1-ashimida@linux.alibaba.com/)
  • No public GCC bug found for the specific arm_neon.h + #pragma target + SCS interaction. Possibly because:
    • GCC ≤ 14 ran a looser validator that swallowed the inconsistency
    • GCC 15 either tightened that validator or arm_neon.h gained more push/target sequences that lose -ffixed-x18
    • Linux distros that hit it (or other kernel users) may have worked around silently
  • See also: Collabora's 2021 patch series "xor-neon: Remove GCC warn & pragmas" — touches this same file's pragma pain.

Workarounds tried during the Phase 6 build (2026-05-18)

Approach Outcome
Add -ffixed-x18 back to CFLAGS_xor-neon.o No — flag IS in the gcc cmdline (visible in .xor-neon.o.cmd) but still fails inside arm_neon.h pragma
Add -fno-sanitize=shadow-call-stack to CFLAGS_xor-neon.o Breaks include resolution (arm_neon.h "not found")
CFLAGS_REMOVE_xor-neon.o += -fsanitize=shadow-call-stack Same include resolution failure
Disable CONFIG_SHADOW_CALL_STACK in .config Works. Currently shipped in pkgrel=2. Security hardening regression.
Downgrade to GCC 14 Would work but Arch ARM ships only GCC 15.x; non-trivial dance. Deferred.
Patch arm_neon.h directly Modifies a GCC-shipped system header; too dirty for production.

Action items

  • Periodically (~monthly) re-test build with CONFIG_SHADOW_CALL_STACK=y against current Arch ARM GCC. Once the build succeeds, flip the config and re-deploy.
  • If a minimal arm_neon.h reproducer can be extracted, file upstream GCC bug with full target-pragma context. Look first at PR target/119099 territory or whatever the latest target-validator hardening was.
  • If linux-distro mailing lists (linux-arm-kernel, arm-build) post a kernel-side workaround we can pick (e.g. wrap arch/arm64/include/asm/neon-intrinsics.h's <arm_neon.h> include with explicit #pragma GCC target("+ffixed-x18") or similar), evaluate and adopt.
  • kernel-agent migration of this PKGBUILD (tracked separately in marfrit/kernel-agent#5) should carry forward the SCS-off override; remember to clear it once GCC is fixed so the manifest doesn't pin the regression.

References

  • Memory: reference_arm64_scs_arm_neon_gcc15 — verbose copy of this analysis + workaround history
  • Memory: project_bes2600_c5x_deployed — Patch I row, SCS-off caveat
  • PKGBUILD: marfrit/besser claude-noether-14 branch, danctnix-besser-pkgbuild/kernel/config has # CONFIG_SHADOW_CALL_STACK is not set as of pkgrel=2
  • Companion: marfrit/kernel-agent#5 (PKGBUILD migration into kernel-agent flow)
## Summary `linux-pinetab2-danctnix-besser` pkgrel=2 ships with `CONFIG_SHADOW_CALL_STACK=n` as a temporary workaround for a GCC 15.2.1 toolchain issue. This issue tracks **re-enabling SCS once GCC upstream is fixed** so we don't carry the security-hardening regression indefinitely. ## What SCS is + why we want it back `CONFIG_SHADOW_CALL_STACK` is an arm64 kernel-hardening feature. The compiler reserves register `x18` as a pointer to a parallel "shadow stack" that holds only return addresses. Stack-buffer-overflow / ROP attacks that rewrite the saved return address on the main stack trigger a mismatch and oops instead of jumping to attacker-controlled code. Cost: one register lost to general use, ~1-2% perf hit, ~10 KB per task. Cheap protection; the danctnix default has it on. ## What's broken right now Building this kernel on boltzmann (GCC 15.2.1) with `CONFIG_SHADOW_CALL_STACK=y` fails in `arch/arm64/lib/xor-neon.c`: ``` /usr/lib/gcc/aarch64-unknown-linux-gnu/15.2.1/include/arm_neon.h:7261:24: error: '-fsanitize=shadow-call-stack' requires '-ffixed-x18' #pragma GCC pop_options ``` The error fires inside arm_neon.h at every `#pragma GCC pop_options` that closes a `#pragma GCC target("+nothing+aes")`-style block. GCC 15's validator strict-checks SCS↔x18 compatibility *after* the pop, but the surrounding kernel-file CFLAGS already have both `-ffixed-x18` and `-fsanitize=shadow-call-stack` set correctly (verified in the `.xor-neon.o.cmd` cache). The `target(...)` pragma seemingly doesn't carry `-ffixed-x18` through the push/pop boundary, so on pop GCC thinks the option pair is inconsistent. The bug surfaces on `arch/arm64/lib/xor-neon.c` (the only file in the arm64 lib that includes `<asm/neon-intrinsics.h>`); kernel-side it's `#include`-only — no clever pragma manipulation on our part. ## Web-search verdict (2026-05-18) - **PR 102768** added SCS-↔x18 strict-check to GCC ~12-era. So the check itself has been there a while. (https://patchwork.sourceware.org/project/gcc/patch/20220129150035.114602-1-ashimida@linux.alibaba.com/) - **No public GCC bug** found for the specific `arm_neon.h` + `#pragma target` + SCS interaction. Possibly because: - GCC ≤ 14 ran a looser validator that swallowed the inconsistency - GCC 15 either tightened that validator or arm_neon.h gained more push/target sequences that lose `-ffixed-x18` - Linux distros that hit it (or other kernel users) may have worked around silently - See also: Collabora's 2021 patch series "[xor-neon: Remove GCC warn & pragmas](https://yhbt.net/lore/all/20210118105557.186614-1-adrian.ratiu@collabora.com/T/)" — touches this same file's pragma pain. ## Workarounds tried during the Phase 6 build (2026-05-18) | Approach | Outcome | |---|---| | Add `-ffixed-x18` back to `CFLAGS_xor-neon.o` | No — flag IS in the gcc cmdline (visible in `.xor-neon.o.cmd`) but still fails inside arm_neon.h pragma | | Add `-fno-sanitize=shadow-call-stack` to `CFLAGS_xor-neon.o` | Breaks include resolution (arm_neon.h "not found") | | `CFLAGS_REMOVE_xor-neon.o += -fsanitize=shadow-call-stack` | Same include resolution failure | | **Disable `CONFIG_SHADOW_CALL_STACK` in .config** | Works. **Currently shipped in pkgrel=2.** Security hardening regression. | | Downgrade to GCC 14 | Would work but Arch ARM ships only GCC 15.x; non-trivial dance. Deferred. | | Patch arm_neon.h directly | Modifies a GCC-shipped system header; too dirty for production. | ## Action items - [ ] Periodically (~monthly) re-test build with `CONFIG_SHADOW_CALL_STACK=y` against current Arch ARM GCC. Once the build succeeds, flip the config and re-deploy. - [ ] If a minimal `arm_neon.h` reproducer can be extracted, **file upstream GCC bug** with full target-pragma context. Look first at PR target/119099 territory or whatever the latest target-validator hardening was. - [ ] If linux-distro mailing lists (linux-arm-kernel, arm-build) post a kernel-side workaround we can pick (e.g. wrap `arch/arm64/include/asm/neon-intrinsics.h`'s `<arm_neon.h>` include with explicit `#pragma GCC target("+ffixed-x18")` or similar), evaluate and adopt. - [ ] kernel-agent migration of this PKGBUILD (tracked separately in `marfrit/kernel-agent#5`) should carry forward the SCS-off override; remember to clear it once GCC is fixed so the manifest doesn't pin the regression. ## References - Memory: `reference_arm64_scs_arm_neon_gcc15` — verbose copy of this analysis + workaround history - Memory: `project_bes2600_c5x_deployed` — Patch I row, SCS-off caveat - PKGBUILD: `marfrit/besser` `claude-noether-14` branch, `danctnix-besser-pkgbuild/kernel/config` has `# CONFIG_SHADOW_CALL_STACK is not set` as of pkgrel=2 - Companion: `marfrit/kernel-agent#5` (PKGBUILD migration into kernel-agent flow)
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marfrit/besser#20