feat(kernel): KRNBUG-AUDIT-002 — multi-frame guest stack capture at handle creation
Adds `walk_guest_back_chain` (PPC EABI back-chain walker) and a
`record_create_with_stack` audit hook gated on `--trace-handles-focus`.
NtCreateEvent / NtCreateSemaphore / NtCreateTimer / XamTaskSchedule now
route through the new helper so focused handles capture up to 6 stack
frames at allocation time. Diagnostic-only, read-only memory access:
unfocused handles pay one HashSet lookup, focused ones pay six
back-chain dereferences. Lockstep determinism preserved.
End-to-end finding: handles 0x1004 (8-instance pool via static ctor at
0x8280F810), 0x100c (singleton built inside main()), 0x15e0 (singleton
in distinct cluster) are silph-framework dispatcher objects whose
producer code is unreached at -n 500M. The producer hunt now has class
ownership; vtable/RTTI readout is the next step.
Tests: 576 → 581 green. `--stable-digest -n 100M` instructions=100000002
unchanged. Master HEAD prior: 9d45efe.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,14 @@ pub struct HandleAuditTrail {
|
||||
pub kind: &'static str,
|
||||
/// When/who/where the handle was minted.
|
||||
pub created: HandleAuditEntry,
|
||||
/// KRNBUG-AUDIT-002 producer-trace. Captured frames at allocation
|
||||
/// time, only populated when the handle is in `HandleAudit::focus`
|
||||
/// AND the create site routed through the `_with_stack` variant.
|
||||
/// Frame layout: `(frame_pointer, saved_lr_for_caller_of_that_frame)`.
|
||||
/// Index 0 is the live frame: `(ctx.gpr[1], ctx.lr)`. Index 1+ comes
|
||||
/// 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)>,
|
||||
/// Bounded ring of signal events.
|
||||
pub signals: VecDeque<HandleAuditEntry>,
|
||||
/// Bounded ring of wait-entry events (one per `Wait*` call).
|
||||
@@ -64,6 +72,7 @@ impl HandleAuditTrail {
|
||||
Self {
|
||||
kind,
|
||||
created,
|
||||
created_stack: Vec::new(),
|
||||
signals: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
waits: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
wakes: VecDeque::with_capacity(AUDIT_RING_CAPACITY),
|
||||
@@ -121,6 +130,26 @@ impl HandleAudit {
|
||||
.insert(handle, HandleAuditTrail::new(kind, entry));
|
||||
}
|
||||
|
||||
/// Same as `record_create`, but additionally stores a captured guest
|
||||
/// stack trace on the trail (`created_stack`). Intended for handles
|
||||
/// in `focus` so the dump can name the actual subsystem caller of the
|
||||
/// kernel API rather than just the immediate wrapper return.
|
||||
#[inline]
|
||||
pub fn record_create_with_stack(
|
||||
&mut self,
|
||||
handle: u32,
|
||||
kind: &'static str,
|
||||
entry: HandleAuditEntry,
|
||||
stack: Vec<(u32, u32)>,
|
||||
) {
|
||||
if !self.enabled {
|
||||
return;
|
||||
}
|
||||
let mut trail = HandleAuditTrail::new(kind, entry);
|
||||
trail.created_stack = stack;
|
||||
self.trails.insert(handle, trail);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn record_signal(&mut self, handle: u32, entry: HandleAuditEntry) {
|
||||
if !self.enabled {
|
||||
@@ -268,4 +297,34 @@ mod tests {
|
||||
let a = HandleAudit::default();
|
||||
assert!(a.counts(0x10FC).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_with_stack_stores_frames() {
|
||||
let mut a = HandleAudit { enabled: true, ..HandleAudit::default() };
|
||||
let frames = vec![
|
||||
(0x7000_0100, 0x824a_9f6c),
|
||||
(0x7000_0200, 0x824a_b020),
|
||||
(0x7000_0300, 0x82bb_aa00),
|
||||
];
|
||||
a.record_create_with_stack(
|
||||
0x1004,
|
||||
"Event/Manual",
|
||||
entry(0, "NtCreateEvent"),
|
||||
frames.clone(),
|
||||
);
|
||||
let trail = &a.trails[&0x1004];
|
||||
assert_eq!(trail.created_stack, frames);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_with_stack_disabled_is_noop() {
|
||||
let mut a = HandleAudit::default();
|
||||
a.record_create_with_stack(
|
||||
0x1004,
|
||||
"Event/Manual",
|
||||
entry(0, "NtCreateEvent"),
|
||||
vec![(0x7000_0000, 0x8200_0000)],
|
||||
);
|
||||
assert!(a.trails.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user