Reject non-native ABIs in ML seccomp BPF filter#3080
Conversation
The Linux seccomp filter matched only on seccomp_data.nr, so an x86_64 process could issue int 0x80 (i386) and hit number collisions — notably i386 socketcall (102) vs allowlisted x86_64 getuid (102). That opened a socket surface the policy intended to block (elastic/security#12621). Require seccomp_data.arch to match the native ABI (AUDIT_ARCH_X86_64 / AUDIT_ARCH_AARCH64) with an immediate deny on mismatch, before any nr allowlist matching. The arch check is self-contained so existing relative jump offsets in the nr allowlist are unchanged. Co-authored-by: Cursor <[email protected]>
Co-authored-by: Cursor <[email protected]>
There was a problem hiding this comment.
Pull request overview
This PR hardens the Linux seccomp-BPF syscall filter used by ML processes by rejecting non-native/compat ABIs via seccomp_data.arch before any syscall-number allowlist checks, preventing syscall-number collisions (e.g. i386 socketcall vs x86_64 getuid).
Changes:
- Add an ABI/arch gate at the start of the Linux seccomp BPF program (x86_64/aarch64).
- Update public header documentation to describe the new
seccomp_data.archrequirement. - Add a changelog entry and clarify unit-test intent/limitations.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| lib/seccomp/CSystemCallFilter_Linux.cc | Adds seccomp_data.arch check prefix to deny foreign/compat ABIs before syscall allowlist matching. |
| lib/seccomp/unittest/CSystemCallFilterTest.cc | Documents that the compat-ABI path isn’t currently exercised by the unit test. |
| include/seccomp/CSystemCallFilter.h | Documents the new native-ABI requirement for the Linux filter. |
| docs/changelog/3080.yaml | Adds changelog entry for the security hardening. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Use offsetof for seccomp_data field loads and add an x86_64-only int 0x80 denial test, soft-skipped when IA32 emulation is unavailable. Co-authored-by: Cursor <[email protected]>
- Fix spelling in CSystemCallFilter.h doc comment (macOS, restrict/restricted). - Declare the 'cc' clobber on the int $0x80 i386-ABI probe asm, since the syscall entry path may modify the condition-code flags. Co-authored-by: Cursor <[email protected]>
|
Pinging @elastic/ml-core (Team:ML) |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (2)
lib/seccomp/unittest/CSystemCallFilterTest.cc:219
- Same as above: constrain/zero i386 argument registers for the int $0x80 call so the test remains deterministic and doesn't depend on whatever happened to be in caller-saved registers.
asm volatile("int $0x80" : "=a"(ret) : "a"(nr) : "memory", "cc");
lib/seccomp/unittest/CSystemCallFilterTest.cc:254
- The IA32 emulation probe currently treats any non-faulting int $0x80 as "i386 entry available". On some kernels/configs, int $0x80 from a 64-bit process can execute the native x86_64 syscall table instead of the i386 compat table; in that case this test would incorrectly fail (expecting socketcall denial) even though no compat ABI is in use. Consider tightening the probe by also issuing syscall number 102 and requiring it to behave like i386 socketcall (i.e. return a negative errno with zeroed args), otherwise skip the compat-ABI assertion.
long i386GetuidResult{0};
const bool i386EntryAvailable{tryI386Syscall(I386_NR_GETUID, i386GetuidResult)};
if (i386EntryAvailable) {
BOOST_TEST_REQUIRE(i386GetuidResult >= 0);
LOG_INFO(<< "i386 syscall entry available; will assert arch denial after filter install");
Test plan item 2 — i386
|
- Use plain // comments (not Doxygen //! / \p) in the .cc test file. - Zero the i386 argument registers (ebx/ecx/edx/esi/edi) in the int $0x80 probes so they are deterministic and side-effect free in the failure mode. - Harden the IA32 detection: a compat entry that returns -ENOSYS (stubbed, not truly usable) now skips the assertion instead of failing. - Fix the run-on DESCRIPTION sentence in the header. Co-authored-by: Cursor <[email protected]>
|
Addressed Copilot's latest review in e8c932b: plain // test comments; both int 0x80 probes zero the i386 arg registers (covers suppressed line 219); IA32 detection now skips (not fails) when the compat entry returns -ENOSYS (covers suppressed line 254); header run-on reworded. Inline asm still compiles clean with -Wall -Wextra for x86_64. |
|
buildkite run_pytorch_tests |
valeriy42
left a comment
There was a problem hiding this comment.
LGTM. Make sure to backport this and other security changes to 9.5.1, 9.4.5, and 8.19.20.
Co-authored-by: Cursor <[email protected]>
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation and see the Github Action logs for details |
* Reject non-native ABIs in ML seccomp BPF filter The Linux seccomp filter matched only on seccomp_data.nr, so an x86_64 process could issue int 0x80 (i386) and hit number collisions — notably i386 socketcall (102) vs allowlisted x86_64 getuid (102). That opened a socket surface the policy intended to block (elastic/security#12621). Require seccomp_data.arch to match the native ABI (AUDIT_ARCH_X86_64 / AUDIT_ARCH_AARCH64) with an immediate deny on mismatch, before any nr allowlist matching. The arch check is self-contained so existing relative jump offsets in the nr allowlist are unchanged. * Add changelog for #3080 * Address Copilot review on seccomp arch gate Use offsetof for seccomp_data field loads and add an x86_64-only int 0x80 denial test, soft-skipped when IA32 emulation is unavailable. * [ML] Address remaining Copilot review comments on seccomp arch gate - Fix spelling in CSystemCallFilter.h doc comment (macOS, restrict/restricted). - Declare the 'cc' clobber on the int $0x80 i386-ABI probe asm, since the syscall entry path may modify the condition-code flags. * [ML] Address Copilot review on seccomp arch-gate test/docs - Use plain // comments (not Doxygen //! / \p) in the .cc test file. - Zero the i386 argument registers (ebx/ecx/edx/esi/edi) in the int $0x80 probes so they are deterministic and side-effect free in the failure mode. - Harden the IA32 detection: a compat entry that returns -ENOSYS (stubbed, not truly usable) now skips the assertion instead of failing. - Fix the run-on DESCRIPTION sentence in the header. * [ML] Remove public-to-private security-tracker references --------- (cherry picked from commit 184a65a) Co-authored-by: Ed Savage <[email protected]> Co-authored-by: Cursor <[email protected]>
* Reject non-native ABIs in ML seccomp BPF filter The Linux seccomp filter matched only on seccomp_data.nr, so an x86_64 process could issue int 0x80 (i386) and hit number collisions — notably i386 socketcall (102) vs allowlisted x86_64 getuid (102). That opened a socket surface the policy intended to block (elastic/security#12621). Require seccomp_data.arch to match the native ABI (AUDIT_ARCH_X86_64 / AUDIT_ARCH_AARCH64) with an immediate deny on mismatch, before any nr allowlist matching. The arch check is self-contained so existing relative jump offsets in the nr allowlist are unchanged. * Add changelog for #3080 * Address Copilot review on seccomp arch gate Use offsetof for seccomp_data field loads and add an x86_64-only int 0x80 denial test, soft-skipped when IA32 emulation is unavailable. * [ML] Address remaining Copilot review comments on seccomp arch gate - Fix spelling in CSystemCallFilter.h doc comment (macOS, restrict/restricted). - Declare the 'cc' clobber on the int $0x80 i386-ABI probe asm, since the syscall entry path may modify the condition-code flags. * [ML] Address Copilot review on seccomp arch-gate test/docs - Use plain // comments (not Doxygen //! / \p) in the .cc test file. - Zero the i386 argument registers (ebx/ecx/edx/esi/edi) in the int $0x80 probes so they are deterministic and side-effect free in the failure mode. - Harden the IA32 detection: a compat entry that returns -ENOSYS (stubbed, not truly usable) now skips the assertion instead of failing. - Fix the run-on DESCRIPTION sentence in the header. * [ML] Remove public-to-private security-tracker references --------- (cherry picked from commit 184a65a) Co-authored-by: Ed Savage <[email protected]> Co-authored-by: Cursor <[email protected]>
* Reject non-native ABIs in ML seccomp BPF filter The Linux seccomp filter matched only on seccomp_data.nr, so an x86_64 process could issue int 0x80 (i386) and hit number collisions — notably i386 socketcall (102) vs allowlisted x86_64 getuid (102). That opened a socket surface the policy intended to block (elastic/security#12621). Require seccomp_data.arch to match the native ABI (AUDIT_ARCH_X86_64 / AUDIT_ARCH_AARCH64) with an immediate deny on mismatch, before any nr allowlist matching. The arch check is self-contained so existing relative jump offsets in the nr allowlist are unchanged. * Add changelog for #3080 * Address Copilot review on seccomp arch gate Use offsetof for seccomp_data field loads and add an x86_64-only int 0x80 denial test, soft-skipped when IA32 emulation is unavailable. * [ML] Address remaining Copilot review comments on seccomp arch gate - Fix spelling in CSystemCallFilter.h doc comment (macOS, restrict/restricted). - Declare the 'cc' clobber on the int $0x80 i386-ABI probe asm, since the syscall entry path may modify the condition-code flags. * [ML] Address Copilot review on seccomp arch-gate test/docs - Use plain // comments (not Doxygen //! / \p) in the .cc test file. - Zero the i386 argument registers (ebx/ecx/edx/esi/edi) in the int $0x80 probes so they are deterministic and side-effect free in the failure mode. - Harden the IA32 detection: a compat entry that returns -ENOSYS (stubbed, not truly usable) now skips the assertion instead of failing. - Fix the run-on DESCRIPTION sentence in the header. * [ML] Remove public-to-private security-tracker references --------- (cherry picked from commit 184a65a) Co-authored-by: Ed Savage <[email protected]> Co-authored-by: Cursor <[email protected]>
Summary
Follow-up to a privately reported security finding (details tracked internally): the Linux
CSystemCallFiltermatched only onseccomp_data.nr, so an x86_64 process could issueint 0x80(i386) and hit syscall-number collisions — notably i386socketcall(102) vs allowlisted x86_64getuid(102). That opened a socket surface the policy intended to block, enabling later HotSpot Attach / JVM escalation in the reported chain.Fix
Require
seccomp_data.archto match the native ABI (AUDIT_ARCH_X86_64/AUDIT_ARCH_AARCH64) before any nr allowlist matching. Mismatch returnsEACCESimmediately. The arch check is a self-contained prefix so existing relative jump offsets in the nr allowlist are unchanged.Test plan
CSystemCallFilterTeststill passes (allowlisted ops work;std::systemblocked). Confirmed on CI build 2843 —ml_test_seccomp(test [ML] Reduce peak memory usage and improve estimation for boosted tree training #781) passed on Linux x86_64 and aarch64. The suite was also extended to soft-probe IA32 emulation (int 0x80) and, when available, assert the arch gate denies i386socketcallafter install.int 0x80syscalls are denied under the new filter. Verified by evaluating the exact BPF program's decision logic against attack vectors (see the verification comment below): the pre-fix filter ALLOWs i386socketcall(102)(the getuid collision), while the arch-gated filter returns EACCES for it — and for any i386/foreign-arch/x32 syscall — with all native x86_64 allowlisted calls unaffected.Related: #3078 (
__setstate__pre-load), #3081 (controllerPR_SET_DUMPABLE).