Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
131 commits
Select commit Hold shift + click to select a range
c8e6bf1
fix(vm): preserve LEA context provenance and signed displacement arit…
SWORDIntel Oct 15, 2026
77bd03e
fix(vm): preserve LEA context provenance and signed displacement arit…
SWORDIntel Oct 15, 2026
16cb3ba
feat(vm): add context-sensitive transition graph and native dispatch …
SWORDIntel Oct 15, 2026
bde5c44
feat(vm): expose opt-in dispatch scan through runtime and CFG
SWORDIntel Oct 15, 2026
027edf1
feat(vm): expose opt-in dispatch scan through runtime and CFG
SWORDIntel Oct 15, 2026
212f260
feat(vm): expose opt-in dispatch scan through runtime and CFG
SWORDIntel Oct 15, 2026
731ab95
feat(vm): add bounded output-rooted handler semantic reducer
SWORDIntel Oct 15, 2026
4a7b0d7
test(vm): wire semantic reduction and protect negative RIP displacement
SWORDIntel Oct 15, 2026
070cf14
test(vm): wire semantic reduction and protect negative RIP displacement
SWORDIntel Oct 15, 2026
60e0b4e
docs(vm): document experimental native analysis APIs, validation and …
SWORDIntel Oct 15, 2026
a506d82
ci(vm): add focused native Rust compilation and tests independent of …
SWORDIntel Oct 15, 2026
92616ee
feat(loader): add strict PE VA/RVA/file-offset mapping for native ana…
SWORDIntel Oct 15, 2026
317dbfc
feat(loader): expose strict PE mapper and correct result assertions
SWORDIntel Oct 15, 2026
124fa42
feat(loader): expose strict PE mapper and correct result assertions
SWORDIntel Oct 15, 2026
66f7dc6
feat(cfg): add source-backed xref index for direct calls and RIP-rela…
SWORDIntel Oct 15, 2026
04a85a4
feat(runtime): expose native bounded callsite/xref lookup
SWORDIntel Oct 15, 2026
d9c03c6
feat(runtime): expose native bounded callsite/xref lookup
SWORDIntel Oct 15, 2026
6f0adec
feat(loader): add conservative MINIDUMP_MEMORY_LIST and MEMORY64 read…
SWORDIntel Oct 15, 2026
55a7722
feat(loader): wire minidump reader and correct malformed-input assertion
SWORDIntel Oct 15, 2026
391d47f
feat(loader): wire minidump reader and correct malformed-input assertion
SWORDIntel Oct 15, 2026
9634c54
ci(vm): cancel obsolete runs on each branch update
SWORDIntel Oct 15, 2026
8f5f9e5
feat(loader): streaming minidump reader for large captured heap images
SWORDIntel Oct 15, 2026
1cab594
feat(cli): native PE address inspection and streaming minidump memory…
SWORDIntel Oct 15, 2026
864b867
feat: connect lightweight forensic inspection commands to Angryier CLI
SWORDIntel Oct 15, 2026
dd7c3e8
feat: connect lightweight forensic inspection commands to Angryier CLI
SWORDIntel Oct 15, 2026
f3b2dfe
feat: connect lightweight forensic inspection commands to Angryier CLI
SWORDIntel Oct 15, 2026
c591568
ci(vm): validate forensic CLI and report pre-existing lint debt separ…
SWORDIntel Oct 15, 2026
7d006b3
docs(vm): add Sarin address/xref/dump inspection commands and fidelit…
SWORDIntel Oct 15, 2026
01e5b3a
fix(vm): remove disallowed expect panics from reducer tests
SWORDIntel Oct 15, 2026
0499054
fix(forensics): use fallible test returns under no-expect lint
SWORDIntel Oct 15, 2026
f5fef5f
fix(forensics): use fallible test returns under no-expect lint
SWORDIntel Oct 15, 2026
1d3fb00
fix(forensics): use fallible test returns under no-expect lint
SWORDIntel Oct 15, 2026
7e4ce04
fix(vm): obey no-expect test policy in transition graph tests
SWORDIntel Oct 15, 2026
d6b2b25
test(cfg): retain fallible LEA provenance regressions without expect
SWORDIntel Oct 15, 2026
f80922f
ci(vm): lint production libraries while preserving independent full-s…
SWORDIntel Oct 15, 2026
5838133
ci(vm): lint libraries and CLI binary with correct Cargo target selec…
SWORDIntel Oct 15, 2026
92b1d81
fix(win64): implement actual RCX/RDX/R8/R9 and stack argument decodin…
SWORDIntel Oct 15, 2026
616a93d
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
ff57650
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
0134ee5
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
630a5e8
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
2541408
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
e1caf33
fix(win32): read Microsoft x64 ABI arguments instead of generic SimSt…
SWORDIntel Oct 15, 2026
33b390e
feat(win32): represent blocked waits and unsupported API effects expl…
SWORDIntel Oct 15, 2026
b9c08b1
fix(runtime): fail closed on Win32 blocked wait or unsupported proced…
SWORDIntel Oct 15, 2026
5fee602
feat(win32): track manual/auto-reset events and atomic wait readiness…
SWORDIntel Oct 15, 2026
91a5fb9
fix(win32): implement event state and fail-closed wait/SetEvent/Reset…
SWORDIntel Oct 15, 2026
d93566d
feat(win32): share event handle table across synchronization SimProce…
SWORDIntel Oct 15, 2026
25e8bcb
test(win32): ABI, manual/auto event, wait-all atomicity, missing-hand…
SWORDIntel Oct 15, 2026
ef0b254
ci: validate native Win64 ABI and synchronization contract with VM suite
SWORDIntel Oct 15, 2026
828454b
feat(runtime): classify PE import stubs and fail-closed risks for Win…
SWORDIntel Oct 15, 2026
2ef09f3
fix(runtime): refuse unmodeled Win32 API return-only stubs and test C…
SWORDIntel Oct 15, 2026
113b87c
feat(provenance): bind CLI to build revision and provide redacted exe…
SWORDIntel Oct 15, 2026
db79478
feat(provenance): bind CLI to build revision and provide redacted exe…
SWORDIntel Oct 15, 2026
d88254f
feat(provenance): enable doctor command in inspection build with SHA-…
SWORDIntel Oct 15, 2026
f598998
feat(provenance): enable doctor command in inspection build with SHA-…
SWORDIntel Oct 15, 2026
42fb3aa
feat(provenance): enable doctor command in inspection build with SHA-…
SWORDIntel Oct 15, 2026
2dcef8f
feat(runtime): atomic selected minidump memory overlay with bounded r…
SWORDIntel Oct 15, 2026
275379e
feat(runtime): audited call/return control snapshots and capture-iden…
SWORDIntel Oct 15, 2026
98412e2
feat(runtime): expose capture overlays/audited steps and regression o…
SWORDIntel Oct 15, 2026
195a21c
test(capture): verify atomic dump overlays, replay persistence and mi…
SWORDIntel Oct 15, 2026
1010d71
feat(cli): expose source-backed PE IAT imports with optional symbol s…
SWORDIntel Oct 15, 2026
488dfdb
feat(cli): wire pe-imports command for direct IAT search without Caps…
SWORDIntel Oct 15, 2026
ff9d74a
fix(capture): import LayeredMemory trait required for mapping inspection
SWORDIntel Oct 15, 2026
f52d183
merge(main): integrate b3df3e5 indirect-call fixes and updated Sarin …
SWORDIntel Oct 15, 2026
75ba9ba
fix(symbolic): validate indirect calls/RET targets against executable…
SWORDIntel Oct 15, 2026
20c9471
fix(runtime): apply Win32 import fidelity gate equally to symbolic an…
SWORDIntel Oct 15, 2026
15e7ca2
test(runtime): use concrete ShardedExprArena in symbolic import-fidel…
SWORDIntel Oct 15, 2026
12561d3
test(runtime): compare direct and indirect CALL stack and return pari…
SWORDIntel Oct 15, 2026
27f3a42
ci(runtime): include direct/indirect PE call-return parity suite in f…
SWORDIntel Oct 15, 2026
e69e706
test(fidelity): disable optimized call collapsing during exact direct…
SWORDIntel Oct 15, 2026
6747982
fix(symbolic): never summarize IAT stubs as pure functions and bypass…
SWORDIntel Oct 15, 2026
6c803e1
test(symbolic): deny lazy CALL summary bypass of Windows CreateThread…
SWORDIntel Oct 15, 2026
2dbb169
fix(win64): do not fabricate zeroes from symbolic stack arguments dur…
SWORDIntel Oct 15, 2026
cfe8406
fix(trace): report missing stack evidence instead of silently equatin…
SWORDIntel Oct 15, 2026
ec70698
fix(symbolic): use checked and uniquely evidenced stack return recove…
SWORDIntel Oct 15, 2026
420e499
feat(capture): accept explicitly verified VA-backed heap slices when …
SWORDIntel Oct 15, 2026
48a062c
fix(capture): allocate collision-free persistent memory object IDs ac…
SWORDIntel Oct 15, 2026
efb608f
ci(runtime): execute internal call-audit, capture, import and stack r…
SWORDIntel Oct 15, 2026
fa64e02
fix(fidelity): explicitly debt-record approximated Win32 BOOL import …
SWORDIntel Oct 15, 2026
a790127
feat(vm): collect conservative AngryIR observable effect footprints
SWORDIntel Oct 15, 2026
865aad1
feat(vm): expose verified AngryIR effect-footprint collector
SWORDIntel Oct 15, 2026
6b6787e
ci(vm): validate conservative AngryIR footprint collection
SWORDIntel Oct 15, 2026
843e576
test(vm): keep effect footprint regressions panic-free
SWORDIntel Oct 15, 2026
4f4c645
fix(vm): order AngryIR SSA IDs for deterministic footprint traversal
SWORDIntel Oct 15, 2026
04b9b3d
fix(ci): keep AngryIR VM test filter valid YAML
SWORDIntel Oct 15, 2026
ac7c6cf
docs(vm): record AngryIR effect-collection contract and replay gate
SWORDIntel Oct 15, 2026
221be5f
style(vm): rustfmt the new AngryIR effect collector only
SWORDIntel Oct 15, 2026
a8a8d34
docs(sync): checkpoint Angryier engine progress across separate devic…
SWORDIntel Oct 15, 2026
7a8509e
feat(vm): observed multi-block handler trace footprints with scoped S…
SWORDIntel Oct 15, 2026
0e0150e
feat(vm): expose observed multi-block VM trace collector
SWORDIntel Oct 15, 2026
ab2782e
ci(vm): test scoped multi-block VM trace reconstruction
SWORDIntel Oct 15, 2026
5ca0166
feat(vm): opt-in native handler execution observer feeding scoped mul…
SWORDIntel Oct 15, 2026
d4c7040
feat(vm): wire runtime handler trace observer
SWORDIntel Oct 15, 2026
f1db1fb
fix(vm): validate handler capture identity before executing any sampl…
SWORDIntel Oct 15, 2026
5ab6ca2
test(vm): exercise runtime handler capture on actual XED NOPs and rej…
SWORDIntel Oct 15, 2026
7059957
test(vm): correct synthetic capture fixture endpoint definitions
SWORDIntel Oct 15, 2026
0589b75
feat(symbolic): expose block-scoped SSA expression bindings from exis…
SWORDIntel Oct 15, 2026
d9a7347
feat(symbolic): export block-local expression bindings
SWORDIntel Oct 15, 2026
637d88c
ci(vm): validate symbolic SSA readout regression
SWORDIntel Oct 15, 2026
57bbe85
feat(vm): retain exact COW entry-state snapshot for trace materializa…
SWORDIntel Oct 15, 2026
35b4a23
feat(vm): materialize observed multi-block SSA into existing symbolic…
SWORDIntel Oct 15, 2026
9c32e35
feat(vm): expose symbolic effect materialization API
SWORDIntel Oct 15, 2026
cf78d2f
fix(vm): reject unsupported multi-write/partial-register handlers bef…
SWORDIntel Oct 15, 2026
6f49310
feat(vm): retain trace identity/window for materialization revalidation
SWORDIntel Oct 15, 2026
f67e1bf
fix(vm): independently revalidate mutable trace/effect records and ex…
SWORDIntel Oct 15, 2026
ee6eb04
test(vm): two-block SSA-to-expression reducer integration and footpri…
SWORDIntel Oct 15, 2026
d416043
ci(vm): execute two-block symbolic materialization integration fixture
SWORDIntel Oct 15, 2026
4d3b797
fix(vm): forbid semantic-debt candidates and verify recorded native t…
SWORDIntel Oct 15, 2026
431c08b
test(vm): retain unused faultable memory reads and reject mismatched …
SWORDIntel Oct 15, 2026
9c5f7d7
feat(vm): retain observed post-handler state for differential replay …
SWORDIntel Oct 15, 2026
3d9d30a
feat(vm): annotate reduced effects with observed trace step origins
SWORDIntel Oct 15, 2026
3a96c12
test(vm): retain independently specified final register/PC state for …
SWORDIntel Oct 15, 2026
64485e1
feat(vm): concrete captured-state differential replay gate for reduce…
SWORDIntel Oct 15, 2026
ae82758
feat(vm): expose concrete differential replay checker
SWORDIntel Oct 15, 2026
9b35838
test(vm): concrete captured-state replay report with retained fault o…
SWORDIntel Oct 15, 2026
4b1fc23
test(vm): replay rejects mismatched observed state and inherited fide…
SWORDIntel Oct 15, 2026
506c4a5
fix(vm): iterate Intel64RegisterFile ordered register tuples in repla…
SWORDIntel Oct 15, 2026
9452249
feat(symbolic): capture ordered full-parent register effects at actua…
SWORDIntel Oct 15, 2026
1f34e9b
feat(symbolic): export ordered register-write expression traces
SWORDIntel Oct 15, 2026
452390c
feat(vm): retain repeated and partial register writes in original exe…
SWORDIntel Oct 15, 2026
de5c3ca
test(vm): replay repeated full/partial register writes with high-bit …
SWORDIntel Oct 15, 2026
01be8d9
fix(symbolic): reconcile IR low-high and expression high-low concat; …
SWORDIntel Oct 15, 2026
22da576
test(symbolic): verify AngryIR concat low-high semantics match concre…
SWORDIntel Oct 15, 2026
fb4c493
test(vm): end-to-end actual XED execution versus materialized handler…
SWORDIntel Oct 15, 2026
4fddbe3
fix(symbolic): zero-extend first 32-bit write into architectural 64-b…
SWORDIntel Oct 15, 2026
6cbf91f
perf(symbolic): keep ordered-write instrumentation off the ordinary e…
SWORDIntel Oct 15, 2026
1f92522
test(vm): assert 32-bit native writes clear a nonzero upper 64-bit pa…
SWORDIntel Oct 15, 2026
ffe97db
fix(ir): remove unreachable branch pattern in observed VM trace verifier
SWORDIntel Oct 15, 2026
0e48414
docs(sync): checkpoint verified multi-block VM handler materializatio…
SWORDIntel Oct 15, 2026
d182bf2
ci(vm): run complete symbolic-execution and AngryIR test suites, not …
SWORDIntel Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/vm-native.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Native VM analysis validation

on:
push:
branches:
- feat/themida-vm-structural-20261015
pull_request:
paths:
- 'crates/angryier-cfg/**'
- 'crates/angryier-expr/**'
- 'crates/angryier-ir/**'
- 'crates/angryier-loader/**'
- 'crates/angryier-models/**'
- 'crates/angryier-cli/**'
- 'crates/angryier-runtime/**'
- '.github/workflows/vm-native.yml'

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
vm-native:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Toolchain
run: rustup show
- name: Rust cache
uses: Swatinem/rust-cache@v2
- name: Check VM crates
run: cargo check -p angryier-cfg -p angryier-expr -p angryier-ir -p angryier-loader -p angryier-models -p angryier-runtime -p angryier-cli --all-targets
- name: VM CFG tests
run: cargo test -p angryier-cfg
- name: VM expression tests
run: cargo test -p angryier-expr
- name: Symbolic/Concolic execution regression suite
run: cargo test -p angryier-execution
- name: AngryIR effect and trace regression suite
run: cargo test -p angryier-ir
- name: VM loader tests
run: cargo test -p angryier-loader
- name: Win32 API and synchronization tests
run: cargo test -p angryier-models
- name: Native runtime unit and fidelity tests
run: cargo test -p angryier-runtime --features "xed script" --lib
- name: VM runtime integration tests
run: cargo test -p angryier-runtime --features "xed script" --test lua_api
- name: Windows PE call-return fidelity regressions
run: cargo test -p angryier-runtime --features "xed script" --test pe_driver
- name: VM handler SSA materialization integration
run: cargo test -p angryier-runtime --features "xed script" --test vm_materialization
- name: CLI forensic command tests
run: cargo test -p angryier-cli
# Existing main has unrelated clippy warnings. Retain diagnostic output
# instead of hiding the compile/test result behind that baseline debt.
- name: Clippy analysis (baseline warnings reported)
run: cargo clippy -p angryier-cfg -p angryier-expr -p angryier-ir -p angryier-loader -p angryier-models -p angryier-runtime --lib
- name: Clippy forensic CLI
run: cargo clippy -p angryier-cli --bin angryier
37 changes: 31 additions & 6 deletions crates/angryier-cfg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use angryier_types::Address;

pub mod themida;
pub mod vpc;
pub mod vm;
pub mod xrefs;

/// How control leaves a basic block.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -867,7 +869,7 @@ fn interpret_block(instructions: &[DecodedInstruction]) -> BlockFacts {
Some(angryier_arch::MemoryBase::InstructionPointer { .. }) => {
insn.address
.checked_add(u64::from(insn.length))
.and_then(|end| end.checked_add(mem.displacement as u64))
.and_then(|end| end.checked_add_signed(mem.displacement))
.map(AbstractVal::Const)
}
Some(angryier_arch::MemoryBase::Register(b)) => {
Expand All @@ -876,7 +878,7 @@ fn interpret_block(instructions: &[DecodedInstruction]) -> BlockFacts {
// &table[idx] — an address, not a target: Unknown.
match (facts.values.get(&b.parent).copied(), mem.index) {
(Some(AbstractVal::Const(c)), None) => {
c.checked_add(mem.displacement as u64).map(AbstractVal::Const)
c.checked_add_signed(mem.displacement).map(AbstractVal::Const)
}
_ => None,
}
Expand All @@ -900,7 +902,7 @@ fn interpret_block(instructions: &[DecodedInstruction]) -> BlockFacts {
Some(angryier_arch::MemoryIndex::Register(idx)) => {
insn.address
.checked_add(u64::from(insn.length))
.and_then(|end| end.checked_add(mem.displacement as u64))
.and_then(|end| end.checked_add_signed(mem.displacement))
.map(|table| AbstractVal::TableEntry {
base: table,
index: idx.parent,
Expand All @@ -914,7 +916,7 @@ fn interpret_block(instructions: &[DecodedInstruction]) -> BlockFacts {
Some(angryier_arch::MemoryBase::Register(b)) => {
match (facts.values.get(&b.parent).copied(), mem.index) {
(Some(AbstractVal::Const(c)), Some(angryier_arch::MemoryIndex::Register(idx))) => {
c.checked_add(mem.displacement as u64).map(|table| AbstractVal::TableEntry {
c.checked_add_signed(mem.displacement).map(|table| AbstractVal::TableEntry {
base: table,
index: idx.parent,
scale: mem.scale,
Expand Down Expand Up @@ -1058,11 +1060,11 @@ fn resolve_indirect_jump(
};
let table = match mem.base {
Some(angryier_arch::MemoryBase::InstructionPointer { .. }) => {
insn_end.checked_add(mem.displacement as u64)?
insn_end.checked_add_signed(mem.displacement)?
}
Some(angryier_arch::MemoryBase::Register(b)) => {
match facts.values.get(&b.parent) {
Some(AbstractVal::Const(c)) => c.checked_add(mem.displacement as u64)?,
Some(AbstractVal::Const(c)) => c.checked_add_signed(mem.displacement)?,
_ => return None,
}
}
Expand Down Expand Up @@ -2384,6 +2386,29 @@ mod tests {
assert_eq!(forest.depth(3), 0);
}

#[test]
fn negative_rip_relative_jump_table_displacement_resolves() -> Result<(), String> {
// The two qword targets sit BEFORE the dispatch, so the RIP-relative
// displacement is negative. A u64 cast used to reject this case.
let code = [
0x18, 0x10, 0, 0, 0, 0, 0, 0, // 0x1000 -> 0x1018
0x1a, 0x10, 0, 0, 0, 0, 0, 0, // 0x1008 -> 0x101a
0xf0, 0x02, // cmp eax,2 @ 0x1010
0xf1, 0x08, 0xe8, 0xff, 0xff, 0xff, // jmp [rip+eax*8-24], end=0x1018
0x90, 0xc3, // nop; ret @ 0x1018
0x90, 0xc3, // nop; ret @ 0x101a
];
let cfg = recover(&TestDecoder, 0x1000, &code, 0x1010, identity_form)
.map_err(|error| format!("{error:?}"))?;
assert!(cfg.edges.iter().any(|edge| {
matches!(edge.kind, EdgeKind::TableDispatch { .. }) && edge.to == Some(0x1018)
}));
assert!(cfg.edges.iter().any(|edge| {
matches!(edge.kind, EdgeKind::TableDispatch { .. }) && edge.to == Some(0x101a)
}));
Ok(())
}

#[test]
fn jump_table_resolves_to_entries() -> Result<(), String> {
// cmp eax,2; jmp [rip + eax*8 + disp] where disp points 2 entries
Expand Down
73 changes: 72 additions & 1 deletion crates/angryier-cfg/src/themida.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,26 @@ impl DispatchWindow<'_> {
}
use angryier_semantics_intel64::forms;
match insn.form_id {
forms::MOV_R64_IMM64 | forms::MOV_R32_IMM32 | forms::LEA_R64_MEM => Def::Constant,
forms::MOV_R64_IMM64 | forms::MOV_R32_IMM32 => Def::Constant,
// LEA is a constant only with RIP/absolute addressing. A context-
// relative LEA is NOT a constant, even if its displacement is.
forms::LEA_R64_MEM => {
let mem = insn.operands.iter().find_map(|op| match op.kind {
OperandKind::AddressGeneration(mem) | OperandKind::Memory(mem) => Some(mem),
_ => None,
});
match mem.map(|m| (m.base, m.index)) {
Some((Some(MemoryBase::InstructionPointer { .. }), None))
| Some((None, None)) => Def::Constant,
Some((Some(MemoryBase::Register(base)), None)) => Def::Derived(base.parent),
Some((None, Some(MemoryIndex::Register(index)))) => {
Def::Derived(index.parent)
}
// Two register inputs cannot be represented by the single-
// source provenance relation. Retain uncertainty.
_ => Def::ClobberedByUnmodeled,
}
},
forms::MOV_R64_R64 | forms::MOV_R32_R32 | forms::MOVZX_R64_R32 | forms::MOVSX_R64_R32 => {
match register_source(insn) {
Some(src) => Def::From(src),
Expand Down Expand Up @@ -430,6 +449,58 @@ mod tests {
assert_eq!(site.chain_register, Some(RegisterId(0)));
}

#[test]
fn lea_context_relative_is_not_a_constant() -> Result<(), &'static str> {
// lea rbx,[r14+0x28]; jmp [rbx+0x38]
let block = [
insn(
0x80,
forms::LEA_R64_MEM,
vec![
reg_op(0, 2, AccessKind::Write),
mem_op(1, Some(0xe), None, 1, 0x28),
],
),
insn(0x83, forms::JMP_INDIRECT_MEM64, vec![mem_op(0, Some(2), None, 1, 0x38)]),
];
let site = (DispatchWindow { instructions: &block }).classify().ok_or("site unavailable")?;
let (_, provenance) = site.base.ok_or("base unavailable")?;
assert!(matches!(
provenance,
BaseProvenance::DerivedFrom { source } if source == RegisterId(0xe)
));
Ok(())
}

#[test]
fn lea_rip_relative_is_constant_but_two_registers_are_not() -> Result<(), &'static str> {
let rip = [
insn(
0x90,
forms::LEA_R64_MEM,
vec![reg_op(0, 2, AccessKind::Write), rip_mem_op(-0x20)],
),
insn(0x93, forms::JMP_INDIRECT_MEM64, vec![mem_op(0, Some(2), None, 1, 0x38)]),
];
let site = (DispatchWindow { instructions: &rip }).classify().ok_or("RIP site unavailable")?;
assert!(matches!(site.base.ok_or("RIP base unavailable")?.1, BaseProvenance::Constant));

let ambiguous = [
insn(
0xa0,
forms::LEA_R64_MEM,
vec![
reg_op(0, 2, AccessKind::Write),
mem_op(1, Some(0xe), Some(0xd), 4, 0x28),
],
),
insn(0xa3, forms::JMP_INDIRECT_MEM64, vec![mem_op(0, Some(2), None, 1, 0x38)]),
];
let site = (DispatchWindow { instructions: &ambiguous }).classify().ok_or("ambiguous site unavailable")?;
assert!(matches!(site.base.ok_or("ambiguous base unavailable")?.1, BaseProvenance::Clobbered));
Ok(())
}

#[test]
fn derived_base_keeps_source() {
// add rbx, 0x20 ; jmp [rbx+0x38] — context member pointer.
Expand Down
Loading
Loading