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)

Reply via email to