# 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:

```c
#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**:

```c
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 | **No** — `handled_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 references** — `grep -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-192` — `security.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-140` — `LandlockEnforceRuleset` 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:

   ```c
   /* 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)

4. **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.

5. **Fedora SELinux:** ship a `suricata_t` confined domain.

6. **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.
