feat(kernel): KRNBUG-AUDIT-003 — vtable/RTTI class probe at handle creation + wait
Adds a read-only MSVC RTTI traversal helper (`read_class_at_this`)
and a `probe_create_stack_classes` integration that walks each
captured back-chain frame for handle creates in `--trace-handles-focus`
and probes each frame's most-likely `this` candidate (live r31/r30/r3
for frame 0; saved-r31/r30 from the prologue spill area at [fp-12]/
[fp-16] for deeper frames). False-positive guard rejects the CRT
static-init iterator pattern (vtable's first two slots must be image-
range function pointers — PPC instruction words like `mflr r12` are
not in 0x82xxxxxx).
`dump_thread_diagnostic` now takes `&GuestMemory` so the FOCUS report
prints, for each parked waiter, a WAIT-THREAD block with full back-
chain frames and per-slot saved-register dump for offline lookup.
End-to-end finding (-n 500M producer-trace):
* Handle 0x100c dispatcher = 0x828F3D08 (image rdata; verified by
sub_82181750 disasm + xref table). [this+0] = -1 sentinel — POD
job queue, NOT a C++ polymorphic class.
* Handle 0x15e0 dispatcher = 0x828F4070 (same shape).
* Handle 0x1004's 8-instance pool members still TBD (MSVC ctors
didn't preserve `this` in r31).
* 0x42450b5c is a separate audit class (heap-allocated, parks via
non-`do_wait_single` path).
Decisive xref audit: every reference to 0x828F3D08 / 0x828F4070 in
the static analysis is in a ctor or the CRT init driver. NO producer
code references either dispatcher base. Confirms `signal_attempts=0`
is unreachable-producer, not broken-producer.
Tests: 581 → 586 green (+5: RTTI-intact / RTTI-stripped / non-object
/ cstring / probe_create_stack integration). `--stable-digest -n
100M` instructions=100000002 unchanged. Master HEAD prior: 6440261.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,14 @@ pub struct HandleAuditTrail {
|
||||
/// from walking the PPC back-chain. An empty vec means either the
|
||||
/// handle wasn't in focus or the create site didn't capture a stack.
|
||||
pub created_stack: Vec<(u32, u32)>,
|
||||
/// KRNBUG-AUDIT-003 class probes. Each entry is one already-formatted
|
||||
/// "frame=N r31=0x... vtable=0x... class=..." line, captured at
|
||||
/// allocation time from the live PPC context (frame 0: ctx.gpr[31] /
|
||||
/// r30 / r3) and the standard prologue spill area at `[fp - 12]` /
|
||||
/// `[fp - 16]` for deeper frames. Pre-formatted because the source
|
||||
/// memory is overwritten once tid=1 leaves the static-init phase, so
|
||||
/// the probe must run at the create call site, not at end-of-run.
|
||||
pub created_class_probes: Vec<String>,
|
||||
/// Bounded ring of signal events.
|
||||
pub signals: VecDeque<HandleAuditEntry>,
|
||||
/// Bounded ring of wait-entry events (one per `Wait*` call).
|
||||
@@ -73,6 +81,7 @@ impl HandleAuditTrail {
|
||||
kind,
|
||||
created,
|
||||
created_stack: Vec::new(),
|
||||
created_class_probes: Vec::new(),
|
||||
signals: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
waits: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
wakes: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
@@ -141,12 +150,30 @@ impl HandleAudit {
|
||||
kind: &'static str,
|
||||
entry: HandleAuditEntry,
|
||||
stack: Vec<(u32, u32)>,
|
||||
) {
|
||||
self.record_create_with_stack_and_probes(handle, kind, entry, stack, Vec::new());
|
||||
}
|
||||
|
||||
/// Variant of `record_create_with_stack` that also accepts pre-
|
||||
/// formatted class-probe strings (KRNBUG-AUDIT-003). Each string is
|
||||
/// one frame's RTTI/vtable readout: `frame=N candidate=r31 this=0x...
|
||||
/// vtable=0x... class=...` or the RTTI-stripped fallback. Caller
|
||||
/// formats them so this module remains memory-layout-agnostic.
|
||||
#[inline]
|
||||
pub fn record_create_with_stack_and_probes(
|
||||
&mut self,
|
||||
handle: u32,
|
||||
kind: &'static str,
|
||||
entry: HandleAuditEntry,
|
||||
stack: Vec<(u32, u32)>,
|
||||
class_probes: Vec<String>,
|
||||
) {
|
||||
if !self.enabled {
|
||||
return;
|
||||
}
|
||||
let mut trail = HandleAuditTrail::new(kind, entry);
|
||||
trail.created_stack = stack;
|
||||
trail.created_class_probes = class_probes;
|
||||
self.trails.insert(handle, trail);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user