Project

General

Profile

Feature #8606 » Suricata(2).md

Jason Ish, 05/28/2026 04:07 PM

 

Landlock V3/V5 coverage gap in Suricata's src/util-landlock.c

Affected file: src/util-landlock.c (master)
Issue class: Landlock policy declares an incomplete handled_access_fs mask; V3 (LANDLOCK_ACCESS_FS_TRUNCATE) and V5 (LANDLOCK_ACCESS_FS_IOCTL_DEV) access rights are never mediated, even on kernels that support them.
Severity: Moderate. The defect is a structural sandbox coverage gap rather than a memory-safety bug. On a default Fedora deployment no secondary defense layer mediates these access classes either (audit below). Fix is small and uncontroversial.
Suggested coordination window: standard 90 days.


1. The finding

src/util-landlock.c:77-83 declares the WRITE-access mask Suricata's Landlock policy will handle:

#define _LANDLOCK_ACCESS_FS_WRITE                                                                  \
    (LANDLOCK_ACCESS_FS_WRITE_FILE | LANDLOCK_ACCESS_FS_REMOVE_DIR |                               \
            LANDLOCK_ACCESS_FS_REMOVE_FILE | LANDLOCK_ACCESS_FS_MAKE_CHAR |                        \
            LANDLOCK_ACCESS_FS_MAKE_DIR | LANDLOCK_ACCESS_FS_MAKE_REG |                            \
            LANDLOCK_ACCESS_FS_MAKE_SOCK | LANDLOCK_ACCESS_FS_MAKE_FIFO |                          \
            LANDLOCK_ACCESS_FS_MAKE_BLOCK | LANDLOCK_ACCESS_FS_MAKE_SYM |                          \
            LANDLOCK_ACCESS_FS_REFER)

This includes V1 access bits + LANDLOCK_ACCESS_FS_REFER (V2). It does NOT include:

  • LANDLOCK_ACCESS_FS_TRUNCATE (V3, bit 14, kernel ≥ 5.19)
  • LANDLOCK_ACCESS_FS_IOCTL_DEV (V5, bit 15, kernel ≥ 6.10)

The narrower _LANDLOCK_SURI_ACCESS_FS_WRITE at lines 87-89 (the mask actually used for write-path rules in LandlockSandboxingWritePath) has the same gap.

The ABI gate at lines 107-120 is downgrade-only:

int abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
...
if (abi < 2) {
    ruleset->attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
}

The only conditional branch is if (abi < 2) — there is no upward branch for V3, V5, or V6, so on an ABI-7 kernel the handled_access_fs mask is still the V1+REFER subset.

What this means at runtime

Landlock's contract: if the kernel handles an access-right bit but the policy does not include it in handled_access_fs, that access right is not mediated by Landlock at all. So:

  • A sandboxed Suricata process can ftruncate() any file inside a path-beneath grant. The directory was granted "write" via LANDLOCK_ACCESS_FS_WRITE_FILE, but truncate() is governed by the separate LANDLOCK_ACCESS_FS_TRUNCATE bit since V3 — and that bit is unhandled, so the kernel passes the syscall straight through.
  • A sandboxed Suricata process can ioctl() on any device file inside a path-beneath grant. LANDLOCK_ACCESS_FS_IOCTL_DEV is unhandled, so device-ioctl mediation never happens.

2. Independent verification

Four cross-checks, all agreeing:

2.1 Kernel header (authoritative bit values)

/usr/include/linux/landlock.h on Fedora 43:

LANDLOCK_ACCESS_FS_REFER       (1ULL << 13)   /* V2 */
LANDLOCK_ACCESS_FS_TRUNCATE    (1ULL << 14)   /* V3 */
LANDLOCK_ACCESS_FS_IOCTL_DEV   (1ULL << 15)   /* V5 */

2.2 Symbol grep across src/

$ grep -rn 'LANDLOCK_ACCESS_FS_TRUNCATE\|LANDLOCK_ACCESS_FS_IOCTL_DEV\|LANDLOCK_SCOPED' src/
(no matches)

The bits are not referenced anywhere in the tree — not just absent from the WRITE masks.

2.3 Upstream verification

Diffed local clone of OISF/suricata (master @ 367ca7f430f85be01a6cdc65ddaa1636a50ddefc, 2026-05-16) against a live fetch of https://raw.githubusercontent.com/OISF/suricata/master/src/util-landlock.c:

$ diff -q util-landlock.c upstream-util-landlock.c
(no diff — byte-identical, 293 lines)

The defect is still present on master as of the verification date.

2.4 ABI-gate visual inspection

The only ABI-version conditional in the file is if (abi < 2) at line 112. There is no if (abi >= 3) handled_access_fs |= TRUNCATE branch and no if (abi >= 5) ... |= IOCTL_DEV. Even on an ABI-7 kernel (Fedora 43 ships this), the ruleset declares it handles only the V1+REFER subset.


3. Secondary-defense audit (Fedora 43, suricata-7.0.15-1.fc43)

The question: with the Landlock layer's gap on TRUNCATE and IOCTL_DEV, what other layers in the deployed environment mediate these access classes?

Layer Status Catches TRUNCATE / IOCTL_DEV?
Upstream Landlock policy (when enabled) Present in source Nohandled_access_fs missing both bits (this finding)
do_setuid / do_setgid privilege drop Yes — drops to suricata user No — LSM-level bit, not POSIX-perms-based
PR_SET_NO_NEW_PRIVS Only inside the Landlock path (util-landlock.c:133) No
Fedora systemd unit hardening Four directives shipped: MemoryDenyWriteExecute=true, LockPersonality=true, ProtectControlGroups=true, ProtectKernelModules=true No — none of these mediate ftruncate() or ioctl(). No SystemCallFilter=, no PrivateDevices=, no ProtectSystem=, no NoNewPrivileges=
Fedora SELinux suricata_t domain Not shipped. Verified: rpm -ql selinux-policy-targeted | grep suri → empty. seinfo -t | grep suricata → empty. No .fc/.te in the suricata RPM. Suricata runs in unconfined_service_t — no SELinux confinement
seccomp filter inside Suricata Zero referencesgrep -rn seccomp src/ → no matches No
AppArmor profile N/A on Fedora (Fedora is SELinux-only); Debian/Ubuntu untested

Compounding observations

Two adjacent issues compound with the V3/V5 gap on the default Fedora deployment story:

  • Default-disabled sandbox: util-landlock.c:186-192security.landlock.enabled defaults to 0. An admin who doesn't explicitly opt in gets zero Landlock at all, not just zero V3/V5 mediation.
  • Silent-failure mode: util-landlock.c:131-140LandlockEnforceRuleset logs on prctl(PR_SET_NO_NEW_PRIVS) or landlock_restrict_self() failure and returns silently. The enclosing LandlockSandboxing is void-returning, so failure doesn't reach an operational error path.

Conclusion (default Fedora deployment)

A compromised Suricata process can:

  1. ftruncate() any file inside a _LANDLOCK_SURI_ACCESS_FS_WRITE-granted directory. No Landlock mediation, no seccomp belt (no SystemCallFilter=), no SELinux confinement. Anti-forensics implication: live eve.json, fast.log, and rotated log files are all reachable for truncation.
  2. ioctl() on device files in any allowed path. Suricata typically holds an open file descriptor on /dev/<nic> or equivalent for packet capture; ioctl on that fd is unrestricted. No Landlock mediation, no PrivateDevices=true in the unit file.

4. Suggested patches

Upstream

  1. Add the missing bits to _LANDLOCK_ACCESS_FS_WRITE, conditionally, gated by the probed ABI:

    /* after the existing `if (abi < 2)` downgrade: */
    if (abi >= 3) {
        ruleset->attr.handled_access_fs |= LANDLOCK_ACCESS_FS_TRUNCATE;
    }
    if (abi >= 5) {
        ruleset->attr.handled_access_fs |= LANDLOCK_ACCESS_FS_IOCTL_DEV;
    }
    

    This needs fallback #ifndef LANDLOCK_ACCESS_FS_TRUNCATE / #ifndef LANDLOCK_ACCESS_FS_IOCTL_DEV shims for build environments older than the relevant kernel headers, mirroring the existing #ifndef LANDLOCK_ACCESS_FS_REFER shim at line 73.

    While in the area, V6 scope rules (LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, LANDLOCK_SCOPE_SIGNAL) are also worth adopting — they close a separate class of escape via the systemd user bus on user-mode sandboxes (see GHSA-27vp-2mmc-vmh3 for the published shape).

  2. Flip security.landlock.enabled to default-on, or document the implication prominently in the deployment guide.

  3. Make LandlockSandboxing return an error and propagate it. Operational failure to apply the sandbox should be visible to operators, not buried in a log line.

Downstream-distro (worth raising with packagers)

  1. Fedora spec file: ask packagers to add SystemCallFilter=@system-service, PrivateDevices=true, RestrictAddressFamilies=AF_INET AF_INET6 AF_PACKET AF_NETLINK, NoNewPrivileges=true to /usr/lib/systemd/system/suricata.service. These are independent of Landlock; they catch ftruncate/ioctl and many other syscalls via systemd's own seccomp/namespace machinery.

  2. Fedora SELinux: ship a suricata_t confined domain.

  3. Debian/Ubuntu (untested): confirm the AppArmor profile (if any) covers TRUNCATE/IOCTL on the log dir and device files.


5. Provenance

  • Upstream repository: OISF/suricata, branch master, commit 367ca7f430f85be01a6cdc65ddaa1636a50ddefc.
  • Upstream live-fetch URL: https://raw.githubusercontent.com/OISF/suricata/master/src/util-landlock.c (byte-identical to commit above as of verification date).
  • Verification dates: 2026-05-22 (initial), 2026-05-24 (secondary-defense audit).
  • Kernel headers consulted: /usr/include/linux/landlock.h on Fedora 43.
  • Tested Fedora package: suricata-7.0.15-1.fc43.x86_64 (downloaded via dnf download suricata, not installed).
  • Tested SELinux policy: selinux-policy-targeted-43.7-1.fc43.noarch.

6. Deliberately out of scope

  • No exploit PoC binary included. The compromise primitive ("a compromised Suricata parser") is taken as given — that is the threat model Suricata's own Landlock effort presumes. Building a parser-exploit PoC would shift the framing from "policy gap" to "offensive showcase," which is not the intent of this report.
  • No proof-of-anti-forensics demo included. The ftruncate(eve.json) exploit is feasible but not implemented; this report points to the gap, not at a working anti-forensics tool.
  • No Debian/Ubuntu reproduction. This audit is Fedora-only. If desirable, a follow-up Debian/Ubuntu pass should check the AppArmor profile shipped under /etc/apparmor.d/usr.sbin.suricata (if any) and the Debian systemd unit's hardening directives.
    (1-1/1)