#!/bin/bash # SPDX-License-Identifier: GPL-2.0-only # kselftest: linux-0002 — audit_filter_inodes O(F²×R) → O(F×R) # CWE-407: Algorithmic Complexity in kernel/auditsc.c # # Tests: # Unit: audit rules with AUDIT_INODE fields fire once per name, not F times # Integration: F=20 files, R=10 rules — verify rule matching is correct post-patch # Functional: measure syscall overhead with vs without audit watch at F=50, R=20 # # Requires: auditd running, auditctl in PATH, CAP_AUDIT_CONTROL (root) # Run from: tools/testing/selftests/audit/ # make && ./linux-0002-audit-kselftest.sh set -e source "$(dirname "$0")/../kselftest/runner.sh" 2>/dev/null || { # Minimal harness if runner not found PASS=0; FAIL=0; SKIP=0 ksft_pass() { echo "ok - $1"; ((PASS++)); } ksft_fail() { echo "not ok - $1"; ((FAIL++)); } ksft_skip() { echo "ok - $1 # SKIP $2"; ((SKIP++)); } ksft_exit() { echo "# Totals: pass=$PASS fail=$FAIL skip=$SKIP"; exit $((FAIL > 0)); } } TESTDIR=$(mktemp -d /tmp/linux-0002-audit-XXXXXX) WATCHDIR="$TESTDIR/watched" mkdir -p "$WATCHDIR" cleanup() { auditctl -W "$WATCHDIR" -p rwxa 2>/dev/null || true; rm -rf "$TESTDIR"; } trap cleanup EXIT # ── Prerequisites ───────────────────────────────────────────────────────────── if [ "$(id -u)" -ne 0 ]; then ksft_skip "linux-0002-audit unit" "requires root" ksft_skip "linux-0002-audit integration" "requires root" ksft_skip "linux-0002-audit functional" "requires root" ksft_exit fi if ! command -v auditctl &>/dev/null; then ksft_skip "linux-0002-audit" "auditctl not found (install audit package)" ksft_exit fi # ── Unit: add a watch, open a file, verify audit event fires once ───────────── auditctl -W "$WATCHDIR" -p rwxa -k cwe407_test echo "unit_data" > "$WATCHDIR/test_file" # Drain existing events, then open + check exactly one audit event logged ausearch -k cwe407_test --start recent -i 2>/dev/null | grep -c SYSCALL > /tmp/cwe407_before.txt || echo 0 > /tmp/cwe407_before.txt cat "$WATCHDIR/test_file" > /dev/null sleep 0.1 ausearch -k cwe407_test --start recent -i 2>/dev/null | grep -c SYSCALL > /tmp/cwe407_after.txt || echo 0 > /tmp/cwe407_after.txt BEFORE=$(cat /tmp/cwe407_before.txt) AFTER=$(cat /tmp/cwe407_after.txt) if [ "$AFTER" -gt "$BEFORE" ]; then ksft_pass "linux-0002-audit unit: audit event fired for watched file" else ksft_fail "linux-0002-audit unit: no audit event for watched file (BEFORE=$BEFORE AFTER=$AFTER)" fi auditctl -W "$WATCHDIR" -p rwxa -k cwe407_test # ── Integration: R rules, F files — verify all matching rules fire ───────────── R=10; F=20 for i in $(seq 1 $R); do auditctl -a always,exit -F dir="$WATCHDIR" -F perm=r -k cwe407_r$i done # Create F files and open them all for i in $(seq 1 $F); do echo "file$i" > "$WATCHDIR/file$i"; done EVENTS_BEFORE=$(ausearch -k cwe407_r1 --start recent -i 2>/dev/null | grep -c SYSCALL || echo 0) for i in $(seq 1 $F); do cat "$WATCHDIR/file$i" > /dev/null; done sleep 0.2 EVENTS_AFTER=$(ausearch -k cwe407_r1 --start recent -i 2>/dev/null | grep -c SYSCALL || echo 0) for i in $(seq 1 $R); do auditctl -d always,exit -F dir="$WATCHDIR" -F perm=r -k cwe407_r$i 2>/dev/null || true; done if [ "$EVENTS_AFTER" -gt "$EVENTS_BEFORE" ]; then ksft_pass "linux-0002-audit integration: audit rules matched (R=$R F=$F)" else ksft_fail "linux-0002-audit integration: no matches for R=$R F=$F rules" fi # ── Functional / complexity gate: timing open() with vs without audit watch ──── # # CWE-407 gate: with the patch, adding R=20 audit rules should not increase # per-syscall overhead superlinearly as F grows. # # Method: time 1000 open() calls with no watch, then with a watch + R rules. # The patched kernel should show overhead proportional to R×1 not R×F. # F_SCALE=50; R_SCALE=20; ITERS=1000 python3 - <