The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [RFC] IMA: periodic runtime re-measurement of process .text/GOT
@ 2026-08-17 10:23 Nicolai Kuntze
  2026-08-19 17:45 ` Mimi Zohar
  2026-08-21  9:12 ` James Bottomley
  0 siblings, 2 replies; 6+ messages in thread
From: Nicolai Kuntze @ 2026-08-17 10:23 UTC (permalink / raw)
  To: linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel

Hi,

This is a design RFC, not a patch submission -- I'd like feedback on
the approach before polishing the patch series further. A working
reference implementation exists today for demonstration; details and
links below.

Problem

Every existing IMA hook (FILE_CHECK, MMAP_CHECK, BPRM_CHECK,
MODULE_CHECK, ...) measures once, at load time: file open, exec,
mmap, module load, or an explicit ima_measure_critical_data() call.
Nothing in IMA today re-measures an object after it's already
resident. A process's .text or GOT can be modified in memory after
load (ptrace(), a kernel exploit writing through /proc/pid/mem, or
similar) without IMA ever re-examining it -- the load-time
measurement stays valid in the log forever, even after the live
content has diverged from it.

This is exactly the gap Andre Rein's DRIVE paper (ASIA CCS '17,
https://doi.org/10.1145/3052973.3052975) addresses: periodic
re-hashing of a process's executable memory against reference values
derived from its on-disk ELF, anchored in a TPM PCR via the same
extend-chain model IMA already uses. This RFC proposes bringing that
capability into IMA's own policy-driven measurement model, rather
than as a separate, out-of-tree mechanism.

Scenario / Rational

Load-time-only measurement is a materially weaker guarantee on
systems that stay up for a long time between reboots than on systems
that cycle power regularly. Every reboot is itself a fresh
measured-boot event -- a natural bound on how long an in-memory
tamper that bypassed load-time measurement can persist undetected,
since the next boot re-measures everything from scratch.

Railway signalling and interlocking equipment is a concrete example
of the opposite case: field devices are routinely designed to run
for months or years without a reboot, both for safety-availability
reasons (a signalling function restarting is itself an operationally
disruptive event) and because physical access for a controlled
maintenance reboot is often scheduled, not on-demand. On that kind
of system, load-time IMA measurement establishes a trustworthy
baseline at boot and then provides no further guarantee for the
entire multi-month uptime that follows -- an attacker who achieves a
one-time in-memory modification (no on-disk change, so FILE_CHECK
never fires again) can persist for as long as the device stays up,
which on this class of hardware is the normal case, not an edge
case. The same shape of argument applies to other long-uptime
industrial-control and embedded contexts more generally; railway is
simply where this is currently being evaluated. Periodic
re-measurement turns "trustworthy at boot" into "trustworthy for as
long as it keeps checking," which is the property this class of
system actually needs.

Proposed design

Two pieces:

1. Policy grammar: a new interval= option (milliseconds), together
   with three new func= hook names -- RUNTIME_TEXT_CHECK,
   RUNTIME_GOT_CHECK, KERNEL_TEXT_CHECK -- added to the existing
   __ima_hooks() X-macro in ima.h, so the enum, the measuring-string
   table, and the policy-readback token array all stay in sync
   automatically, the same as every existing hook. interval= is
   required for, and only accepted with, those three hooks, enforced
   in ima_validate_rule() the same way IMA already enforces every
   other func-specific option. Example rule this grammar accepts:

       measure func=RUNTIME_TEXT_CHECK interval=30000 pcr=16

2. A dispatch engine: a new security/integrity/ima/ima_runtime.c,
   compiled into ima.o (not a separate loadable module), holding a
   delayed_work-based periodic worker that ima_update_policy()
   starts, reconfigures, or stops to track whatever
   RUNTIME_TEXT_CHECK rule is currently active. On each tick it
   walks every live task's file-backed executable VMAs under a
   locked snapshot, then hashes unlocked (a two-pass split that
   exists specifically to avoid a nested-mmap_read_lock self-
   deadlock hit during prototyping -- see "What's already been
   tested" below), reuses ima_calc_buffer_hash() for the hash
   itself, and PCR-extends via tpm_pcr_extend(ima_tpm_chip, ...)
   directly, deliberately bypassing ima_add_template_entry()'s
   measurement-list/htable path. That path dedups by (digest, pcr)
   alone and skips the PCR extend on a repeat digest -- which would
   silently break external verification, since an auditor replaying
   the log's extend chain needs to reproduce exactly what the TPM
   really did, including repeats of unchanged content across ticks.
   Log-growth from repeatedly re-measuring unchanged content is
   instead bounded by a small (pid, address)-keyed dedup cache,
   checked after every hash (never instead of hashing), so nothing
   is ever skipped without being freshly examined first.

RUNTIME_GOT_CHECK and KERNEL_TEXT_CHECK are accepted by the grammar
but not yet wired to the dispatch engine -- v1 measures .text only.
Kernel-image .text re-measurement in particular needs its own
reference-value handling (a statically-linked kernel image has
link-time-relocated content, unlike the userspace PIC/PIE case this
prototype already handles) and is intentionally left for a follow-up
rather than folded into this first round.

What's already been tested (reference implementation)

Public repo, with everything below runnable, not just described:

    https://gitlab.rlp.net/nicolai.kuntze/memory_attestation

- The full DRIVE pipeline -- runtime re-measurement, DML/SMAF chain,
  ELF-derived reference values (.text and per-GOT-slot), and an
  external OBS/VS verification split with TPM-Quote cross-checking
  -- is prototyped and validated end-to-end as a standalone
  out-of-tree module (drive_ima/) plus a Rust verification toolchain
  (drive-rs/), including real ptrace()-based tamper injection
  correctly detected in every case, and real PCR extends confirmed
  against a swtpm-backed TPM via tpm2_pcrread.

- The two mainline patches this RFC describes
  (kernel-patches/0001-ima-policy-add-interval-and-runtime-hook-
  funcs.patch and 0002-ima-runtime-dispatch-engine-v1.patch), plus a
  follow-on 0003-ima-runtime-per-mapping-dedup.patch adding the
  dedup cache, apply cleanly against v6.8, pass checkpatch.pl
  --no-tree with 0 errors/0 warnings, compile clean under W=1, link
  into a full vmlinux, and boot and dispatch for real under QEMU/KVM:
  writing a RUNTIME_TEXT_CHECK rule to /sys/kernel/security/ima/policy
  in a running kernel causes the worker to fire on the configured
  interval, correctly measure every live process each cycle, and
  correctly reconfigure after a policy rewrite -- all empirically
  timed, not just log-inspected.

- One thing not yet exercised: a genuine hardware PCR extend from
  this dispatch engine specifically. The test environment (QEMU
  8.2.2 + SeaBIOS 1.16.3) hits a reproducible ACPI early-boot fault
  whenever any TPM device is attached, confirmed unrelated to this
  patch (fault is at PID 0, well before IMA runs). With no TPM
  present, the code correctly takes its documented -ENODEV fallback
  rather than crashing; that path is tested, a real PCR write from
  this specific code path is not, in this environment. (The extend
  call itself, and the extend-chain format, are the same ones
  already confirmed working against real hardware in the standalone
  prototype above.)

Threat-model note, already accounted for

Read-write access to /sys/kernel/security/ima/policy at runtime
(CONFIG_IMA_WRITE_POLICY=on) lets anyone with CAP_SYS_ADMIN silently
remove or weaken an active RUNTIME_TEXT_CHECK rule without touching
any signed code -- which would defeat the "isolated from a post-boot
adversary" property this is meant to provide, and matters more, not
less, on the long-uptime deployments described above, since there's
no near-term reboot to force a policy re-load. This isn't a gap in
the proposed patches; it's a statement about deployment
configuration, and it's already documented as a hard requirement for
production use of this extension: CONFIG_IMA_WRITE_POLICY should be
off, making the policy write-once at boot the same way it already is
for every other IMA policy today.

Questions for the list

- Is a delayed_work periodic worker compiled into ima.o the right
  shape for a non-event-triggered hook class, or would the list
  prefer this live behind a separate mechanism (a kthread, an
  LSM-adjacent facility, something eBPF-driven) rather than inside
  IMA's own hook dispatch?

- Is bypassing ima_add_template_entry()'s measurement-list htable
  (for the reason given above -- external-verifier replay needs
  every real extend, not a deduped view) an acceptable precedent, or
  is there a preferred way to get an un-deduped extend through the
  existing measurement-list path instead of calling
  tpm_pcr_extend() directly?

- Any objection to the interval= grammar addition / the X-macro-
  based hook additions as scoped here, ahead of a full patch series?

- Kernel-image .text re-measurement (KERNEL_TEXT_CHECK) is
  deliberately left unimplemented in this round -- is that the
  right place to split the series, or would the list rather see it
  land together with RUNTIME_TEXT_CHECK?


Thanks,
Nicolai

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT
  2026-08-17 10:23 [RFC] IMA: periodic runtime re-measurement of process .text/GOT Nicolai Kuntze
@ 2026-08-19 17:45 ` Mimi Zohar
  2026-08-21  9:12 ` James Bottomley
  1 sibling, 0 replies; 6+ messages in thread
From: Mimi Zohar @ 2026-08-19 17:45 UTC (permalink / raw)
  To: Nicolai Kuntze, linux-integrity
  Cc: Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
	James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel

On Mon, 2026-08-17 at 12:23 +0200, Nicolai Kuntze wrote:
> Hi,
> 
> This is a design RFC, not a patch submission -- I'd like feedback on
> the approach before polishing the patch series further. A working
> reference implementation exists today for demonstration; details and
> links below.
> 
> Problem
> 
> Every existing IMA hook (FILE_CHECK, MMAP_CHECK, BPRM_CHECK,
> MODULE_CHECK, ...) measures once, at load time: file open, exec,
> mmap, module load, or an explicit ima_measure_critical_data() call.
> Nothing in IMA today re-measures an object after it's already
> resident. A process's .text or GOT can be modified in memory after
> load (ptrace(), a kernel exploit writing through /proc/pid/mem, or
> similar) without IMA ever re-examining it -- the load-time
> measurement stays valid in the log forever, even after the live
> content has diverged from it.
> 
> This is exactly the gap Andre Rein's DRIVE paper (ASIA CCS '17,
> https://doi.org/10.1145/3052973.3052975) addresses: periodic
> re-hashing of a process's executable memory against reference values
> derived from its on-disk ELF, anchored in a TPM PCR via the same
> extend-chain model IMA already uses. This RFC proposes bringing that
> capability into IMA's own policy-driven measurement model, rather
> than as a separate, out-of-tree mechanism.

There are two separate issues that need to be addressed:

- Systems that aren't rebooted frequently
- Long-running processes whose mmap'ed executable memory may have been tampered
with after load

The first issue can be addressed by periodically clearing the iint cached info.
It's something that should be added to IMA and is a relatively simple addition.
It would be a good first kernel project.

The second is verifying a long-running process's mmap'ed executable memory
(.text, GOT) hasn't diverged from what was loaded — via ptrace(), a kernel
exploit through /proc/pid/mem, or similar. DRIVE addresses this via periodic re-
hashing against the on-disk ELF, TPM-anchored.

Worth noting: W^X, hardware CFI, and page-fault-based monitoring all fully
prevent tampering that goes through normal page-table-enforced writes — no
window, no cost of periodic checking. None of them, however, cover an attacker
with an arbitrary physical-memory write or the ability to rewrite page tables
directly, since that bypasses MMU enforcement entirely. DRIVE is the one
mechanism here that says anything about that residual case — but only as
bounded-time detection (via the re-hash interval), not prevention. On bare
metal, nothing closes that gap outright; DRIVE narrows it rather than solving
it. The RFC should be explicit about that distinction.

I'm not convinced this feature belongs in IMA. IMA's existing model is event-
driven, once: measure at open/exec/mmap. This feature is continuous/periodic — a
timer walking live process state on its own schedule — which is a different
execution model, not an incremental addition to a hook-based architecture. The
reference implementation shows this mismatch concretely: it bypasses IMA's
measurement list and dedup entirely, because IMA's "measured once, log stays
valid" model doesn't fit a periodic check that needs to prove this specific tick
still matched.

Mimi (with AI's help)

> 
> Scenario / Rational
> 
> Load-time-only measurement is a materially weaker guarantee on
> systems that stay up for a long time between reboots than on systems
> that cycle power regularly. Every reboot is itself a fresh
> measured-boot event -- a natural bound on how long an in-memory
> tamper that bypassed load-time measurement can persist undetected,
> since the next boot re-measures everything from scratch.
> 
> Railway signalling and interlocking equipment is a concrete example
> of the opposite case: field devices are routinely designed to run
> for months or years without a reboot, both for safety-availability
> reasons (a signalling function restarting is itself an operationally
> disruptive event) and because physical access for a controlled
> maintenance reboot is often scheduled, not on-demand. On that kind
> of system, load-time IMA measurement establishes a trustworthy
> baseline at boot and then provides no further guarantee for the
> entire multi-month uptime that follows -- an attacker who achieves a
> one-time in-memory modification (no on-disk change, so FILE_CHECK
> never fires again) can persist for as long as the device stays up,
> which on this class of hardware is the normal case, not an edge
> case. The same shape of argument applies to other long-uptime
> industrial-control and embedded contexts more generally; railway is
> simply where this is currently being evaluated. Periodic
> re-measurement turns "trustworthy at boot" into "trustworthy for as
> long as it keeps checking," which is the property this class of
> system actually needs.
> 
> Proposed design
> 
> Two pieces:
> 
> 1. Policy grammar: a new interval= option (milliseconds), together
>    with three new func= hook names -- RUNTIME_TEXT_CHECK,
>    RUNTIME_GOT_CHECK, KERNEL_TEXT_CHECK -- added to the existing
>    __ima_hooks() X-macro in ima.h, so the enum, the measuring-string
>    table, and the policy-readback token array all stay in sync
>    automatically, the same as every existing hook. interval= is
>    required for, and only accepted with, those three hooks, enforced
>    in ima_validate_rule() the same way IMA already enforces every
>    other func-specific option. Example rule this grammar accepts:
> 
>        measure func=RUNTIME_TEXT_CHECK interval=30000 pcr=16
> 
> 2. A dispatch engine: a new security/integrity/ima/ima_runtime.c,
>    compiled into ima.o (not a separate loadable module), holding a
>    delayed_work-based periodic worker that ima_update_policy()
>    starts, reconfigures, or stops to track whatever
>    RUNTIME_TEXT_CHECK rule is currently active. On each tick it
>    walks every live task's file-backed executable VMAs under a
>    locked snapshot, then hashes unlocked (a two-pass split that
>    exists specifically to avoid a nested-mmap_read_lock self-
>    deadlock hit during prototyping -- see "What's already been
>    tested" below), reuses ima_calc_buffer_hash() for the hash
>    itself, and PCR-extends via tpm_pcr_extend(ima_tpm_chip, ...)
>    directly, deliberately bypassing ima_add_template_entry()'s
>    measurement-list/htable path. That path dedups by (digest, pcr)
>    alone and skips the PCR extend on a repeat digest -- which would
>    silently break external verification, since an auditor replaying
>    the log's extend chain needs to reproduce exactly what the TPM
>    really did, including repeats of unchanged content across ticks.
>    Log-growth from repeatedly re-measuring unchanged content is
>    instead bounded by a small (pid, address)-keyed dedup cache,
>    checked after every hash (never instead of hashing), so nothing
>    is ever skipped without being freshly examined first.
> 
> RUNTIME_GOT_CHECK and KERNEL_TEXT_CHECK are accepted by the grammar
> but not yet wired to the dispatch engine -- v1 measures .text only.
> Kernel-image .text re-measurement in particular needs its own
> reference-value handling (a statically-linked kernel image has
> link-time-relocated content, unlike the userspace PIC/PIE case this
> prototype already handles) and is intentionally left for a follow-up
> rather than folded into this first round.
> 
> What's already been tested (reference implementation)
> 
> Public repo, with everything below runnable, not just described:
> 
>     https://gitlab.rlp.net/nicolai.kuntze/memory_attestation
> 
> - The full DRIVE pipeline -- runtime re-measurement, DML/SMAF chain,
>   ELF-derived reference values (.text and per-GOT-slot), and an
>   external OBS/VS verification split with TPM-Quote cross-checking
>   -- is prototyped and validated end-to-end as a standalone
>   out-of-tree module (drive_ima/) plus a Rust verification toolchain
>   (drive-rs/), including real ptrace()-based tamper injection
>   correctly detected in every case, and real PCR extends confirmed
>   against a swtpm-backed TPM via tpm2_pcrread.
> 
> - The two mainline patches this RFC describes
>   (kernel-patches/0001-ima-policy-add-interval-and-runtime-hook-
>   funcs.patch and 0002-ima-runtime-dispatch-engine-v1.patch), plus a
>   follow-on 0003-ima-runtime-per-mapping-dedup.patch adding the
>   dedup cache, apply cleanly against v6.8, pass checkpatch.pl
>   --no-tree with 0 errors/0 warnings, compile clean under W=1, link
>   into a full vmlinux, and boot and dispatch for real under QEMU/KVM:
>   writing a RUNTIME_TEXT_CHECK rule to /sys/kernel/security/ima/policy
>   in a running kernel causes the worker to fire on the configured
>   interval, correctly measure every live process each cycle, and
>   correctly reconfigure after a policy rewrite -- all empirically
>   timed, not just log-inspected.
> 
> - One thing not yet exercised: a genuine hardware PCR extend from
>   this dispatch engine specifically. The test environment (QEMU
>   8.2.2 + SeaBIOS 1.16.3) hits a reproducible ACPI early-boot fault
>   whenever any TPM device is attached, confirmed unrelated to this
>   patch (fault is at PID 0, well before IMA runs). With no TPM
>   present, the code correctly takes its documented -ENODEV fallback
>   rather than crashing; that path is tested, a real PCR write from
>   this specific code path is not, in this environment. (The extend
>   call itself, and the extend-chain format, are the same ones
>   already confirmed working against real hardware in the standalone
>   prototype above.)
> 
> Threat-model note, already accounted for
> 
> Read-write access to /sys/kernel/security/ima/policy at runtime
> (CONFIG_IMA_WRITE_POLICY=on) lets anyone with CAP_SYS_ADMIN silently
> remove or weaken an active RUNTIME_TEXT_CHECK rule without touching
> any signed code -- which would defeat the "isolated from a post-boot
> adversary" property this is meant to provide, and matters more, not
> less, on the long-uptime deployments described above, since there's
> no near-term reboot to force a policy re-load. This isn't a gap in
> the proposed patches; it's a statement about deployment
> configuration, and it's already documented as a hard requirement for
> production use of this extension: CONFIG_IMA_WRITE_POLICY should be
> off, making the policy write-once at boot the same way it already is
> for every other IMA policy today.
> 
> Questions for the list
> 
> - Is a delayed_work periodic worker compiled into ima.o the right
>   shape for a non-event-triggered hook class, or would the list
>   prefer this live behind a separate mechanism (a kthread, an
>   LSM-adjacent facility, something eBPF-driven) rather than inside
>   IMA's own hook dispatch?
> 
> - Is bypassing ima_add_template_entry()'s measurement-list htable
>   (for the reason given above -- external-verifier replay needs
>   every real extend, not a deduped view) an acceptable precedent, or
>   is there a preferred way to get an un-deduped extend through the
>   existing measurement-list path instead of calling
>   tpm_pcr_extend() directly?
> 
> - Any objection to the interval= grammar addition / the X-macro-
>   based hook additions as scoped here, ahead of a full patch series?
> 
> - Kernel-image .text re-measurement (KERNEL_TEXT_CHECK) is
>   deliberately left unimplemented in this round -- is that the
>   right place to split the series, or would the list rather see it
>   land together with RUNTIME_TEXT_CHECK?
> 
> 
> Thanks,
> Nicolai

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT
  2026-08-17 10:23 [RFC] IMA: periodic runtime re-measurement of process .text/GOT Nicolai Kuntze
  2026-08-19 17:45 ` Mimi Zohar
@ 2026-08-21  9:12 ` James Bottomley
  2026-08-22 19:38   ` Nicolai Kuntze
  1 sibling, 1 reply; 6+ messages in thread
From: James Bottomley @ 2026-08-21  9:12 UTC (permalink / raw)
  To: Nicolai Kuntze, linux-integrity
  Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel

On Mon, 2026-08-17 at 12:23 +0200, Nicolai Kuntze wrote:
> Hi,
> 
> This is a design RFC, not a patch submission -- I'd like feedback on
> the approach before polishing the patch series further. A working
> reference implementation exists today for demonstration; details and
> links below.
> 
> Problem
> 
> Every existing IMA hook (FILE_CHECK, MMAP_CHECK, BPRM_CHECK,
> MODULE_CHECK, ...) measures once, at load time: file open, exec,
> mmap, module load, or an explicit ima_measure_critical_data() call.
> Nothing in IMA today re-measures an object after it's already
> resident. A process's .text

A processes text segment is mapped ro and cannot change.  We're looking
to apply the kernel ROX guarantees to user processes, which would make
it very hard even for someone with privilege to modify the text
segment, but if you're looking to fix something like the copy fail
cache corruption, then a strengthened ROX mechanism would be way better
than trying to detect it.

>  or GOT can be modified in memory after load

The GOT (and GOT.PLT) sections are updated by the dynamic linker
*after* the binary is measured.  They're also often constructed lazily
by just in time symbol resolution meaning they change throughout the
lifetime of the process ... therefore if you include them, practically
every measurement will be different and there'll be no way to
reconstruct what measurement you should have seen or relate it back to
the original measurement.  It is possible to do non-lazy linking and
then seal the GOT read only, but that incurs a huge startup cost, so
what is the actual threat model here?

>  (ptrace(), a kernel exploit writing through /proc/pid/mem, or
> similar) without IMA ever re-examining it

Those are all privileged operations, so what is the threat model you're
guarding against?  because if it's something like container mutations,
then using dm-verity and proper ROX handling is likely a better
guarantor.

Regards,

James

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT
  2026-08-21  9:12 ` James Bottomley
@ 2026-08-22 19:38   ` Nicolai Kuntze
  2026-08-24 13:17     ` Mimi Zohar
  0 siblings, 1 reply; 6+ messages in thread
From: Nicolai Kuntze @ 2026-08-22 19:38 UTC (permalink / raw)
  To: linux-integrity
  Cc: James Bottomley, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin,
	Eric Snowberg, Paul Moore, James Morris, Serge E. Hallyn,
	linux-security-module, linux-kernel

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)

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT
  2026-08-22 19:38   ` Nicolai Kuntze
@ 2026-08-24 13:17     ` Mimi Zohar
  2026-08-24 14:22       ` Mimi Zohar
  0 siblings, 1 reply; 6+ messages in thread
From: Mimi Zohar @ 2026-08-24 13:17 UTC (permalink / raw)
  To: Nicolai Kuntze, linux-integrity
  Cc: James Bottomley, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel

On Sat, 2026-08-22 at 21:38 +0200, Nicolai Kuntze wrote:
[...]

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

Thank you.

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

Extending the TPM is a performance concern — that's why IMA caches the result.
CONFIG_IMA_DISABLE_HTABLE addresses the A-B-A case, where without it, a repeat
of an earlier digest (A) gets skipped because it's already in the htable, making
the log misleadingly show only A-B. It doesn't change consecutive-repeat
behavior — if the last hash was B and the new hash is still B, there's still no
need to add another B.

Why does this case need to be different? With extends now triggered per verifier
request rather than per tick, why does an unchanged digest still need a fresh
extend on every request?


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

Retaining v1 in your GitHub repo is up to you, but it wouldn't be upstreamed.

Mimi

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC] IMA: periodic runtime re-measurement of process .text/GOT
  2026-08-24 13:17     ` Mimi Zohar
@ 2026-08-24 14:22       ` Mimi Zohar
  0 siblings, 0 replies; 6+ messages in thread
From: Mimi Zohar @ 2026-08-24 14:22 UTC (permalink / raw)
  To: Nicolai Kuntze, linux-integrity
  Cc: James Bottomley, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
	Paul Moore, James Morris, Serge E. Hallyn, linux-security-module,
	linux-kernel

On Mon, 2026-08-24 at 09:17 -0400, Mimi Zohar wrote:
> On Sat, 2026-08-22 at 21:38 +0200, Nicolai Kuntze wrote:
> [...]
> 
> > 
> > > 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.
> 
> Thank you.

Have you considered defining this as an IMA critical-data measurement, triggered
by the securityfs write?  That way the verifier wouldn't need any information
beyond what's already in the IMA measurement list.

Mimi

> 
> > 
> > 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.
> 
> Extending the TPM is a performance concern — that's why IMA caches the result.
> CONFIG_IMA_DISABLE_HTABLE addresses the A-B-A case, where without it, a repeat
> of an earlier digest (A) gets skipped because it's already in the htable, making
> the log misleadingly show only A-B. It doesn't change consecutive-repeat
> behavior — if the last hash was B and the new hash is still B, there's still no
> need to add another B.
> 
> Why does this case need to be different? With extends now triggered per verifier
> request rather than per tick, why does an unchanged digest still need a fresh
> extend on every request?
> 
> 
> > 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.
> 
> Retaining v1 in your GitHub repo is up to you, but it wouldn't be upstreamed.
> 
> Mimi
> 
> > 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.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-08-24 14:23 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-17 10:23 [RFC] IMA: periodic runtime re-measurement of process .text/GOT Nicolai Kuntze
2026-08-19 17:45 ` Mimi Zohar
2026-08-21  9:12 ` James Bottomley
2026-08-22 19:38   ` Nicolai Kuntze
2026-08-24 13:17     ` Mimi Zohar
2026-08-24 14:22       ` Mimi Zohar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox