The tool said there was no evidence.
It was reading stdin.
ausearch -k <key> — the canonical documented
invocation — returns no matches and exits 0. Reading the same
configured log explicitly returned 186 parsed matches; a raw grep found
192 keyed lines because the operations count different representations.
Result
| invocation | records returned |
|---|---|
| ausearch -k ghalvera_exec | 0 — “no matches”, exit 0 |
| ausearch -m SYSCALL | 0 |
| ausearch --start today / --start recent / no bound | 0 |
| aureport --summary | time range 01/01/1970 – 01/01/1970 |
| ausearch --input-logs -k ghalvera_exec | 186 |
| ausearch -if <log> -m SYSCALL | 771 |
| aureport --input-logs --summary | 08/15/2026 – 08/17/2026 |
| grep on the same file, same moment | 192 |
Mechanism
Reading the configured audit log is opt-in. The tool's own documentation says so:
--input-logs— Use the log file location from auditd.conf as input for searching.The ausearch utility can also take input from stdin as long as the input is the raw log data.
With neither --input-logs nor -if, the tool
consumes stdin. With no redirect it reaches EOF
immediately and reports an absence of matches rather than an
absence of input.
aureport behaves identically. Its 1970 time range is the
tell — minimum and maximum timestamps never move off initialisation
when zero records parse.
Why it is a forensic trap
No error. No warning. Exit status 0. Meanwhile
auditctl -s reports enabled 1
and lost 0 — the subsystem genuinely is
healthy, which corroborates the wrong conclusion.
An investigator concluding no execve was recorded, therefore no process ran would be wrong, with every available diagnostic agreeing. This is the difference between absence of evidence and absence of retrieval, and the tool does not distinguish them for you.
How it was found
Three hypotheses were eliminated first, each costing a controlled run. They are recorded because the next person should not repeat them.
| cell | -a task,never | audit=1 | ausearch | raw log |
|---|---|---|---|---|
| A | present | absent | 0 | present |
| B | removed | absent | 0 | present |
| C | present | added | 0 | present |
Cell B removed the distribution's default rule, verified absent by a
setup guard that aborts if it is still loaded. Cell C added
audit=1 with grubby and rebooted the guest,
confirmed in /proc/cmdline. Clock skew, time windows, the
arch=b64 filter and log rotation were all ruled out
separately.
The control that cracked it is the one that should have come first: does the tool find anything at all?
ausearch -m SYSCALL 0 → not key-specific aureport time range 1970 → not ausearch-specific with --input-logs 771 → the log was always readable
Not determined
- Whether this is a change in audit 4.x or long-standing upstream behaviour. Not established — it would be easy to call this a regression, and that claim has not been checked.
- Whether Fedora 44 ships a configuration that affects it, or it is purely upstream.
- Whether it reproduces on x86-64, on bare metal, or outside Lima/vz.
- How common the bare invocation is in published runbooks. Anecdotally it is the usual form — that is an impression, not a measurement.
Corrections
This finding was published wrong twice before it was published right. Both superseded versions remain in the repository history rather than being edited away.
-
1
“Linux Audit recorded nothing.” Wrong. It recorded everything. The error was inferring absence from a retrieval tool's silence — which is precisely what this finding is about.
-
2
“The cause is unknown.” Honest at the time, now superseded. Three hypotheses had been eliminated and the control that would have cracked it had not yet been run.
Consequence
Any procedure reading audit evidence must pass --input-logs
or -if explicitly, and must verify a non-empty result
against the raw log before treating an empty one as evidence of
absence.
Both range scenarios now do this: observe.sh counts
raw-log hits alongside the tool's answer, so a discrepancy of this
class appears in the artifact rather than being discovered three runs
later.