Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT

Nicolai Kuntze <[email protected]>
Newsgroups org.kernel.vger.linux-integrity,org.kernel.vger.linux-kernel,org.kernel.vger.linux-security-module
Message-ID <[email protected]>
James, Mimi -- thank you both, these landed on real gaps, not just
missing explanation. Replies inline.

@James (Bottomley):

> A processes text segment is mapped ro and cannot change... if
> you're looking to fix something like the copy fail cache
> corruption, then a strengthened ROX mechanism would be way better

Agreed without reservation for the case ROX/W^X actually cover: any
tampering that goes through the normal page-table-enforced write
path with the page's permission bits left alone. Prevention beats
bounded-delay detection there every time, and this proposal isn't
meant to compete with it.

> The GOT ... are also often constructed lazily ... practically every
> measurement will be different

You're right, and this exposed a genuine bug rather than a
misunderstanding on your end. The reference implementation's kernel
side has always recorded each GOT slot's access permissions
specifically to distinguish "still being lazily populated" (rw-)
from "RELRO-locked, safe to compare" (r--) -- but the userspace
verifier was silently dropping that field and comparing every slot
regardless. Fixed: a slot is now only checked against its predicted
value once measured r--; a slot still rw- is reported separately
("unlocked") rather than flagged tampered. This doesn't require
BIND_NOW/non-lazy linking -- a binary that never RELRO-locks its GOT
simply never gets that slot verified, rather than false-flagged.

> ptrace()/kernel exploit through /proc/pid/mem are privileged
> operations, so what is the threat model?

The primary case is more mundane than either of us initially framed
it: an attacker who already has ordinary code execution inside the
process can call mprotect() on their own mapping to make a page
writable-then-executable, entirely through the kernel's normal,
page-table-consistent path -- no privilege escalation, no hardware
access needed. Plain W^X doesn't prevent that by itself (it's a
load-time property, not a runtime invariant); only additional
hardening most systems don't run (PaX MPROTECT, SELinux execmem, a
seccomp filter on mprotect()'s flags) closes it. This is exactly why
the design already includes an mprotect()-triggered re-scan alongside
the periodic one -- that's the case being targeted, not an exotic
bypass. A stronger, much rarer case -- an attacker who can write
physical memory or rewrite page tables directly -- also exists and is
covered the same way (this mechanism never consults permission bits,
only resident content), but it's a secondary point, not the main one.
For a deployment whose threat model stops at ordinary privileged code
execution with no mprotect()-hardening gap and no physical-memory
access, dm-verity + a hardened mprotect() policy may well already be
the complete, cheaper answer -- this isn't proposed as a universal
replacement for that.

@Mimi:

> Systems that aren't rebooted frequently ... periodically clearing
> the iint cached info ... good first kernel project

Agreed this is worth doing on its own merits, and I'd support it --
but want to flag it's solving an adjacent problem, not this one:
clearing iint forces a stale file to be re-measured on its next
open()/exec(), which does nothing for a process that's already open,
already measured, and whose live memory has diverged with no new
event to hook. Complementary, not a substitute.

> This feature is continuous/periodic ... a different execution
> model, not an incremental addition to a hook-based architecture.
> The reference implementation ... bypasses IMA's measurement list
> and dedup entirely

This was the strongest objection, and rather than argue it away I
went back and changed the design -- v2 patches attached/linked below,
not just a response in prose. You were right that a self-scheduling
timer inside ima_main.c's dispatch is a materially different execution
model, not an incremental addition to a hook-based one; the fix wasn't
to defend the timer, it was to notice the actual requirement was never
"recheck on a schedule," it was "answer a verifier's request," the
same shape a TPM Quote already has. So v2 drops the timer and
`interval=` entirely: RUNTIME_TEXT_CHECK/RUNTIME_GOT_CHECK/
KERNEL_TEXT_CHECK are now dispatched from a one-shot trigger, a new
write-only securityfs file (`/sys/kernel/security/ima/runtime_measure`,
CAP_SYS_ADMIN-gated), the same event-triggered shape every other IMA
hook already has -- the event is just "a verifier asked" instead of
"a file was opened." Boot-tested under QEMU/KVM: a 5-second idle
window with no write to `runtime_measure` produces zero measurement
log lines; one write produces exactly one correctly-scoped batch;
a further idle wait leaves the count unchanged, confirming it's
genuinely one-shot and not a disguised timer restart.

The measurement-list/dedup bypass you also flagged is a separate
point from the execution-model one, and v2 does *not* change it: it's
still deliberate, for the reason given in the original RFC -- an
external verifier replaying the full PCR-extend chain needs every
extend the TPM actually performed, including repeats of unchanged
content, which is exactly what IMA's own digest-based dedup would
throw away. v1's dedicated per-mapping dedup table (built to bound log
growth from timer ticks nobody asked for) is dropped in v2 for the
same reason it's no longer needed: growth is now bounded by how often
a verifier actually requests a measurement, not by an on-device clock.
It stays available, unchanged, for anyone who still wants periodic
mode -- v1 is kept as sent-and-tested history, not deleted, just not
the default.

Does this address the concern, or is there a reason a request-driven
hook still doesn't belong in ima_main.c's dispatch at all? Genuinely
asking, not rhetorically -- if the list still prefers the
separate-mechanism route (kthread/LSM-adjacent/eBPF, reusing IMA's
measurement list and PCR extend chain as a pure data sink without
adopting its hook model), I'd rather hear that now than after more
patches build on the current shape.

> W^X, hardware CFI, and page-fault-based monitoring all fully
> prevent ... DRIVE ... only bounded-time detection ... The RFC
> should be explicit about that distinction

Agreed, and the corrected version of what I said to James above is
exactly that distinction, made explicit rather than left implicit as
it was in the original RFC.

Thanks again, both -- v2 (0001-v2 through 0004-v2, replacing the
attached v1 series and now also covering RUNTIME_GOT_CHECK dispatch,
which v1 left grammar-only) is up at the same repo if you want to look
before replying: <https://gitlab.rlp.net/nicolai.kuntze/memory_attestation>.
Holding off on submitting any of this for formal review until I hear
whether v2 actually resolves the execution-model question for you,
since a "no, still the wrong shape" answer would change more than a
patch series built on the current shape would survive.

Nicolai (with a lot of help of AI for a swift answer)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.