feat(kernel): KRNBUG-AUDIT-007 — --branch-probe instrumentation; sub_824A9710 exit gate identified
Sister to --pc-probe / --ctor-probe but emits a single compact one-line BRANCH-PROBE record per fire (pc, tid, hw, cycle, r3, lr, cr0/cr6 flags) with no back-chain. Designed for tracing every conditional-branch fire inside a candidate-gate function so the last PC reached before the function epilogue identifies the exit branch. Runtime trace at audit-runs/audit-007/sub_824A9710-trace.log decisively identifies the priv-11 gate: - Exit branch: 0x824a9944 (post bl sub_824ABD88 first call) - Responsible kernel call: NtDeviceIoControlFile, FsCtlCode=0x74004 (registered as stub_success at exports.rs:90) - Mechanical chain: stub returns 0/SUCCESS without writing OUT, game reads [out_buf+8], finds zero, assigns hardcoded 0xC0000034 (STATUS_OBJECT_NAME_NOT_FOUND) at sub_824ABD88:0x824abea8-ac, exits via 0x824a9944's lt branch before priv-11 site at 0x824a99a0. 592→592 tests; lockstep instructions=100000010, swaps=2, draws=0 deterministic across reruns. Read-only diagnostic — no fix this session. Next session: KRNBUG-IO-003 (real NtDeviceIoControlFile per canary NullDevice::IoControl for FsCtlCodes 0x70000 + 0x74004). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -215,6 +215,15 @@ pub struct KernelState {
|
||||
/// extended syntax of `--pc-probe` / `--ctor-probe`. Read-only
|
||||
/// load — does not mutate guest state.
|
||||
pub pc_probe_consumers: HashMap<u32, (u32, u32)>,
|
||||
/// Diagnostic. Comma-separated set of guest PCs that, when reached,
|
||||
/// emit a single compact one-line `BRANCH-PROBE` record. The line
|
||||
/// includes (pc, tid, hw, cycle, r3, lr, cr0.{lt,gt,eq}, cr6.{lt,gt,eq})
|
||||
/// — designed for tracing every conditional-branch fire inside a
|
||||
/// candidate-gate function (sub_824A9710 etc.) so the LAST PC
|
||||
/// reached before function epilogue identifies the exit branch.
|
||||
/// Distinct from `ctor_probe_pcs` because that helper emits 8
|
||||
/// frames of back-chain per hit — too noisy for branch tracing.
|
||||
pub branch_probe_pcs: std::collections::HashSet<u32>,
|
||||
/// Diagnostic. Guest addresses to dump (64 bytes each, hex + u32
|
||||
/// lanes) at end-of-run. Populated from `--dump-addr=0x828F3D08,
|
||||
/// 0x828F4070`. Used to inspect static dispatcher / job-queue /
|
||||
@@ -277,6 +286,7 @@ impl KernelState {
|
||||
parallel_active: false,
|
||||
ctor_probe_pcs: std::collections::HashSet::new(),
|
||||
pc_probe_consumers: HashMap::new(),
|
||||
branch_probe_pcs: std::collections::HashSet::new(),
|
||||
dump_addrs: Vec::new(),
|
||||
};
|
||||
crate::exports::register_exports(&mut state);
|
||||
@@ -620,6 +630,39 @@ impl KernelState {
|
||||
}
|
||||
}
|
||||
|
||||
/// Diagnostic. If the live PC for HW slot `hw_id` is in
|
||||
/// `self.branch_probe_pcs`, emit one compact `BRANCH-PROBE` line
|
||||
/// with (pc, tid, hw, cycle, r3, lr, cr0.{lt,gt,eq}, cr6.{lt,gt,eq}).
|
||||
/// No back-chain walk — designed for tracing every conditional
|
||||
/// branch fire inside a candidate-gate function. Read-only.
|
||||
/// Lockstep digest unaffected.
|
||||
pub fn fire_branch_probe_if_match(&self, hw_id: u8) {
|
||||
if self.branch_probe_pcs.is_empty() {
|
||||
return;
|
||||
}
|
||||
let ctx = self.scheduler.ctx(hw_id);
|
||||
let pc = ctx.pc;
|
||||
if !self.branch_probe_pcs.contains(&pc) {
|
||||
return;
|
||||
}
|
||||
let tid = self.scheduler.tid(hw_id).unwrap_or(0);
|
||||
let r3 = ctx.gpr[3] as u32;
|
||||
let lr = ctx.lr as u32;
|
||||
let cycle = ctx.cycle_count;
|
||||
let cr0 = &ctx.cr[0];
|
||||
let cr6 = &ctx.cr[6];
|
||||
println!(
|
||||
"BRANCH-PROBE pc={:#010x} tid={} hw={} cycle={} r3={:#010x} lr={:#010x} cr0={}{}{} cr6={}{}{}",
|
||||
pc, tid, hw_id, cycle, r3, lr,
|
||||
if cr0.lt { 'L' } else { '.' },
|
||||
if cr0.gt { 'G' } else { '.' },
|
||||
if cr0.eq { 'E' } else { '.' },
|
||||
if cr6.lt { 'L' } else { '.' },
|
||||
if cr6.gt { 'G' } else { '.' },
|
||||
if cr6.eq { 'E' } else { '.' },
|
||||
);
|
||||
}
|
||||
|
||||
/// Read a TLS slot for the currently running HW thread.
|
||||
pub fn tls_get(&self, index: u32) -> u64 {
|
||||
self.scheduler.tls_get(index)
|
||||
|
||||
Reference in New Issue
Block a user