nmarshall commited on
Commit
b3cc53b
·
1 Parent(s): f0c42d4

Recover registered entity arithmetic and area query boundaries

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -4,6 +4,8 @@ Purpose: analyse **every file/member variant**, then produce reproducible assets
4
 
5
  The [entity-clipping loop collector](research/COLLISION_LOOP.md) extends binary inspection with bounded count/order/sentinel evidence. Reproduce with `python3 scripts/analyze_collision_loop.py`; its fixtures inform future collision sidecar validation without executing original geometry.
6
 
 
 
7
  ## Planned tool catalogue
8
 
9
  Names and output formats below are proposals to refine after the relevant research gates. They may become Python scripts, Rust CLI subcommands, or both. Runtime-critical parsers and shared schemas should have one authoritative implementation; orchestration scripts must not diverge from it.
 
4
 
5
  The [entity-clipping loop collector](research/COLLISION_LOOP.md) extends binary inspection with bounded count/order/sentinel evidence. Reproduce with `python3 scripts/analyze_collision_loop.py`; its fixtures inform future collision sidecar validation without executing original geometry.
6
 
7
+ The [area-query collector](research/AREA_QUERIES.md) adds inclusive-overlap and strict-split fixtures for future converted collision validation; full node placement and map query equivalence remain open. Reproduce with `python3 scripts/analyze_area_queries.py`.
8
+
9
  ## Planned tool catalogue
10
 
11
  Names and output formats below are proposals to refine after the relevant research gates. They may become Python scripts, Rust CLI subcommands, or both. Runtime-critical parsers and shared schemas should have one authoritative implementation; orchestration scripts must not diverge from it.
docs/FINDINGS.md CHANGED
@@ -306,3 +306,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
306
  **F-083 — Observed clipping scheduling invariant:** only lane 0 checks incremented counter == count; the paired and peeled continuations rely on parity and stop on nonzero status0. Supplied compositions visit consecutive four-byte array entries through counts 1023/1024. Count ownership, candidate bodies/callees and runtime safety remain open. [LOOP-002](research/COLLISION_LOOP.md).
307
 
308
  **F-084 — Observed related sentinel normalization:** pass 1023 skips entity lookup and writes 0xffffffff; other passes request lookup, whose supplied object +0x200 word is independently normalized from 1023 to 0xffffffff. Complete entity bounds, ownership semantics and runtime validity remain unverified. [LOOP-003](research/COLLISION_LOOP.md).
 
 
 
 
 
 
 
306
  **F-083 — Observed clipping scheduling invariant:** only lane 0 checks incremented counter == count; the paired and peeled continuations rely on parity and stop on nonzero status0. Supplied compositions visit consecutive four-byte array entries through counts 1023/1024. Count ownership, candidate bodies/callees and runtime safety remain open. [LOOP-002](research/COLLISION_LOOP.md).
307
 
308
  **F-084 — Observed related sentinel normalization:** pass 1023 skips entity lookup and writes 0xffffffff; other passes request lookup, whose supplied object +0x200 word is independently normalized from 1023 to 0xffffffff. Complete entity bounds, ownership semantics and runtime validity remain unverified. [LOOP-003](research/COLLISION_LOOP.md).
309
+
310
+ **F-085 — Observed registered entity arithmetic:** SV_GentityNum multiplies index/registered stride in 32 bits, sign-extends the product and adds the base without a local bounds check. Exact server-record fixtures corroborate stride 312 and modular inverse index recovery. Registered ABI/lifetime/validity and runtime/platform behavior remain open. [AREA-001](research/AREA_QUERIES.md).
311
+
312
+ **F-086 — Observed area overlap/capacity boundaries:** finite candidate-box comparisons include touching faces/edges/corners. Accepted entries test count == capacity before a four-byte index write and count increment; deliberately invalid counts expose the guard's equality-only nature. Count/array ownership and runtime safety remain unverified. [AREA-002](research/AREA_QUERIES.md).
313
+
314
+ **F-087 — Observed area ordering and split rules:** local linked records precede children; maximum > split requests child 0 before minimum < split tail-selects child 1. Axis -1 returns. Plane equality excludes each corresponding child, so completeness depends on node placement; full trees/callees/runtime remain open. [AREA-003](research/AREA_QUERIES.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -33,7 +33,7 @@ For implementation items, the minimum done condition is a documented contract, a
33
 
34
  ### First research batch
35
 
36
- Progress: [RE-232–234 / TIME-001–003](research/FRAME_TIMING.md) now cover Linux ELF64 elapsed modification, event sources and pause predicates. The [RE-237–239 follow-up](research/EVENT_QUEUES.md) resolves the pause-field address/bot chain and adds queue/journal evidence. The [RE-241–243 announcement pass](research/AUTH_ANNOUNCEMENTS.md) recovers label tables and recipient paths; The [RE-245–247 cvar pass](research/GAME_CVARS.md) adds all 126 main registration rows and resolves auth_verbosity/callers. The [RE-249–251 change pass](research/CVAR_CHANGES.md) adds cache/notification behavior, four numeric correction branches and direct registration coverage. The [RE-253–255 query pass](research/AUTH_QUERIES.md) adds selector helpers, command-role routing and internal authentication modes. The [RE-257–259 bounds/entry pass](research/SELECTOR_BOUNDS.md) adds integer parsing, selector storage/count evidence and native/QVM/chat route connections. The [RE-261–263 chat pass](research/CHAT_PATHS.md) adds native command metadata/filter evidence and bounded message/recipient paths, including the documented argument-mapping correction. The [RE-265–267 delivery pass](research/CHAT_DELIVERY.md) adds bounded dead-chat predicates, formatter argument routing and echo/fanout calls. The [RE-269–271 expansion pass](research/CHAT_EXPANSION.md) adds marker states, twelve key comparisons and health/momo branches. The [RE-273–275 item/time pass](research/CHAT_ITEMS_TIME.md) adds packed ammunition fields, sampled names and countdown branches. The [RE-277–279 location pass](research/CHAT_LOCATIONS.md) adds leader selection, cache/format boundaries and location-list/spatial evidence. The [RE-281–283 crosshair pass](research/CHAT_CROSSHAIR.md) adds finite geometry fixtures, trace arguments and result routing. The [RE-285–287 native spatial pass](research/NATIVE_SPATIAL.md) confirms Linux trig/spatial services, VM pointer branches and collision/query evidence. The [RE-289–291 collision pass](research/NATIVE_COLLISION.md) adds native candidate filters, result decisions and model/field evidence. The [RE-293–295 composition pass](research/COLLISION_COMPOSITION.md) adds three-lane byte-copy/status agreement and world-result routing. The [RE-297–299 loop pass](research/COLLISION_LOOP.md) adds signed count/status gates, parity scheduling and related sentinel normalization. PROV-002 remains open for full output/reset coverage. PROV-001 remains open for complete scheduling and platform coverage.
37
 
38
  Start with the open questions raised by the current binary evidence. These tasks depend on the linked reports, not on a working rewrite.
39
 
 
33
 
34
  ### First research batch
35
 
36
+ Progress: [RE-232–234 / TIME-001–003](research/FRAME_TIMING.md) now cover Linux ELF64 elapsed modification, event sources and pause predicates. The [RE-237–239 follow-up](research/EVENT_QUEUES.md) resolves the pause-field address/bot chain and adds queue/journal evidence. The [RE-241–243 announcement pass](research/AUTH_ANNOUNCEMENTS.md) recovers label tables and recipient paths; The [RE-245–247 cvar pass](research/GAME_CVARS.md) adds all 126 main registration rows and resolves auth_verbosity/callers. The [RE-249–251 change pass](research/CVAR_CHANGES.md) adds cache/notification behavior, four numeric correction branches and direct registration coverage. The [RE-253–255 query pass](research/AUTH_QUERIES.md) adds selector helpers, command-role routing and internal authentication modes. The [RE-257–259 bounds/entry pass](research/SELECTOR_BOUNDS.md) adds integer parsing, selector storage/count evidence and native/QVM/chat route connections. The [RE-261–263 chat pass](research/CHAT_PATHS.md) adds native command metadata/filter evidence and bounded message/recipient paths, including the documented argument-mapping correction. The [RE-265–267 delivery pass](research/CHAT_DELIVERY.md) adds bounded dead-chat predicates, formatter argument routing and echo/fanout calls. The [RE-269–271 expansion pass](research/CHAT_EXPANSION.md) adds marker states, twelve key comparisons and health/momo branches. The [RE-273–275 item/time pass](research/CHAT_ITEMS_TIME.md) adds packed ammunition fields, sampled names and countdown branches. The [RE-277–279 location pass](research/CHAT_LOCATIONS.md) adds leader selection, cache/format boundaries and location-list/spatial evidence. The [RE-281–283 crosshair pass](research/CHAT_CROSSHAIR.md) adds finite geometry fixtures, trace arguments and result routing. The [RE-285–287 native spatial pass](research/NATIVE_SPATIAL.md) confirms Linux trig/spatial services, VM pointer branches and collision/query evidence. The [RE-289–291 collision pass](research/NATIVE_COLLISION.md) adds native candidate filters, result decisions and model/field evidence. The [RE-293–295 composition pass](research/COLLISION_COMPOSITION.md) adds three-lane byte-copy/status agreement and world-result routing. The [RE-297–299 loop pass](research/COLLISION_LOOP.md) adds signed count/status gates, parity scheduling and related sentinel normalization. The [RE-301–303 area pass](research/AREA_QUERIES.md) adds registered address arithmetic, inclusive overlap and count/split ordering boundaries. PROV-002 remains open for full output/reset coverage. PROV-001 remains open for complete scheduling and platform coverage.
37
 
38
  Start with the open questions raised by the current binary evidence. These tasks depend on the linked reports, not on a working rewrite.
39
 
docs/RESEARCH_LEDGER.md CHANGED
@@ -161,3 +161,7 @@ Completed bounded RE-293–295 with `analyze_collision_composition.py`: paired c
161
  ## 2026-10-11 — Entity-clipping loop and sentinel normalization
162
 
163
  Completed bounded RE-297–299 with `analyze_collision_loop.py`: paired verified graphs, 72 entry/20 normalization/360 continuation slices and 70 scheduling compositions, including supplied counts 1023/1024. LOOP-001–003/F-082–084 distinguish equality-only count guards from a proven runtime safety invariant and pass lookup requests from executed callees. Added restricted signed TEST/CMP branches, simple LEA/ADDQ/low-byte tests and opt-in register capture to the byte evaluator. Eight tests independently check parity/order/counter/pointer/status/sentinel behavior, paired outputs, flag preservation and ambiguous/unmodeled inputs. The full suite passes 206 tests; old/new collectors regenerate byte-identical reports, documentation links and storage policy checks pass. RE-296 remains open; RE-300 tracks count/order/lookup ownership, candidate/callee state and full collision/runtime/platform coverage. No original process, area query, entity lookup or collision callee executes.
 
 
 
 
 
161
  ## 2026-10-11 — Entity-clipping loop and sentinel normalization
162
 
163
  Completed bounded RE-297–299 with `analyze_collision_loop.py`: paired verified graphs, 72 entry/20 normalization/360 continuation slices and 70 scheduling compositions, including supplied counts 1023/1024. LOOP-001–003/F-082–084 distinguish equality-only count guards from a proven runtime safety invariant and pass lookup requests from executed callees. Added restricted signed TEST/CMP branches, simple LEA/ADDQ/low-byte tests and opt-in register capture to the byte evaluator. Eight tests independently check parity/order/counter/pointer/status/sentinel behavior, paired outputs, flag preservation and ambiguous/unmodeled inputs. The full suite passes 206 tests; old/new collectors regenerate byte-identical reports, documentation links and storage policy checks pass. RE-296 remains open; RE-300 tracks count/order/lookup ownership, candidate/callee state and full collision/runtime/platform coverage. No original process, area query, entity lookup or collision callee executes.
164
+
165
+ ## 2026-10-11 — Entity addressing and area-query boundaries
166
+
167
+ Completed bounded/static RE-301–303 with `analyze_area_queries.py`: ten verified native graphs, 64 direct/24 reverse address traces, two registration slices, 432 overlap/48 insertion slices and 48 split compositions. AREA-001–003/F-085–087 distinguish registered game stride from fixed server stride, closed box overlap from strict tree splits, and equality guards from established safety. Eight tests check independent arithmetic/geometry/capacity/order oracles, registration widths, paired results and unknown/exceptional flag/address domains. The byte evaluator adds RIP/scaled addresses, multiply/sign-extension/shift/subtract/qword-test and finite comparison branches, with unmodeled flags rejected. The full suite passes 214 tests; new and prior collision reports regenerate byte-identically, and storage policy/documentation link checks pass. RE-300 remains open; RE-304 tracks complete registration/node/list/callee/count ownership, geometry/runtime and platform behavior. No original process, recursive query or recorded native callee executes.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -357,3 +357,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
357
  - [x] RE-298 Recover three continuation count/pointer/status rules and bounded scheduling compositions through capacity boundary fixtures ([LOOP-002](research/COLLISION_LOOP.md)).
358
  - [x] RE-299 Recover pass/related sentinel normalization and isolated lookup request boundaries ([LOOP-003](research/COLLISION_LOOP.md)).
359
  - [ ] RE-300 Recover area-query count/order ownership and legal bounds, entity lookup arithmetic/validity, all candidate body/callee state effects, collision-produced status reachability and full geometry/original-runtime/all-platform scheduling conformance.
 
 
 
 
 
 
357
  - [x] RE-298 Recover three continuation count/pointer/status rules and bounded scheduling compositions through capacity boundary fixtures ([LOOP-002](research/COLLISION_LOOP.md)).
358
  - [x] RE-299 Recover pass/related sentinel normalization and isolated lookup request boundaries ([LOOP-003](research/COLLISION_LOOP.md)).
359
  - [ ] RE-300 Recover area-query count/order ownership and legal bounds, entity lookup arithmetic/validity, all candidate body/callee state effects, collision-produced status reachability and full geometry/original-runtime/all-platform scheduling conformance.
360
+
361
+ - [x] RE-301 Recover paired registered entity-address arithmetic, registration slot writes and exact stride-312 server-record correspondence ([AREA-001](research/AREA_QUERIES.md)).
362
+ - [x] RE-302 Recover finite inclusive candidate-box overlap and equality-only capacity/index insertion slices ([AREA-002](research/AREA_QUERIES.md)).
363
+ - [x] RE-303 Recover static local-list-before-children order and bounded strict split/leaf/child-order compositions ([AREA-003](research/AREA_QUERIES.md)).
364
+ - [ ] RE-304 Recover registered ABI callers/sizes/counts/lifetime and pointer/index validation, area node/list construction/relinking/order/duplicates, full recursive count/capacity propagation and callee state effects, exceptional floats and original geometry/runtime/all-platform query fixtures.
docs/research/AREA_QUERIES.md ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Native entity addressing and area-query boundaries
2
+
3
+ Static/bounded pass, 2026-10-11. Reproduce with `python3 scripts/analyze_area_queries.py`; check with `python3 scripts/test_area_queries.py`. [Generated evidence](area-queries.json) verifies both original Linux ELF64 engines and their disassembly, retains five graphs per engine, and records **64 direct entity-address traces, 24 server-to-game address traces, two registration slices, 432 overlap slices, 48 insertion slices and 48 split-traversal compositions**. This extends the [clipping count/sentinel work](COLLISION_LOOP.md).
4
+
5
+ No original process or native callee executes. Registered addresses/strides, bounds, count/capacity and recursive-return state are synthetic inputs. Comparisons accept only finite binary32 values. Negative/out-of-capacity examples demonstrate raw instruction behavior; they are not observed valid game states.
6
+
7
+ ## AREA-001: registered game records and server-record addressing
8
+
9
+ SV_LocateGameData stores its incoming arguments into five adjacent native globals. The report's isolated registration slice supplies illustrative values rather than executing a QVM registration call:
10
+
11
+ | Global relative to entity base slot | Width | Incoming register | Supplied fixture value |
12
+ | --- | --- | --- | --- |
13
+ | +0 | 8 | rdi | 0x100000000 |
14
+ | +8 | 4 | edx | 880 |
15
+ | +12 | 4 | esi | 1024 |
16
+ | +16 | 8 | rcx | 0x200000000 |
17
+ | +24 | 4 | r8d | 256 |
18
+
19
+ The first two slots are used directly by SV_GentityNum. The helper computes `product = low32(index * registered_stride)`, sign-extends product to 64 bits, then adds the registered base modulo 2^64. **It has no range/null/stride check in its four-instruction body.** Multiplication occurs before sign extension; widening index/stride first in Rust would differ on overflow. The 32 cases per engine cover indices 0, 1, 7, 1023, 1024, -1 and signed extremes with stride bits 880, 312, 0 and -1. These raw inputs do not establish legal entities, actual registered sizes or valid memory.
20
+
21
+ SV_GEntityForSvEntity subtracts the fixed server-record base, arithmetic-shifts the byte difference right by three, then uses low-width multiplication by registered game stride and `0x96f96f97` before sign extension/base addition. The area insertion path similarly shifts the difference by three and multiplies by `0x6f96f96f96f96f97` before storing its low 32 bits. The constants are modular inverses of 39 in their respective widths: **312 = 8 * 39**. For tested pointers exactly `server_base + 312*i`, the recovered game pointer is `game_base + registered_stride*i` and the emitted index is i. No general signed division, misaligned-pointer validation or full array extent is inferred from these exact-record fixtures.
22
+
23
+ The dedicated globals are base slot **0x86da30**, stride slot **0x86da38**, fixed server-record base **0x81fa28**. Client addresses are preserved separately in the report. Native registration callers, QVM-provided record identities/counts, allocation lifetime and all-platform arithmetic remain open.
24
+
25
+ ## AREA-002: inclusive overlap and equality-only capacity guard
26
+
27
+ SV_AreaEntities statically constructs a 32-byte query record: min pointer +0, max pointer +8, output pointer +16, count +24 initialized zero, and capacity +28 from incoming ecx. It passes that record to SV_AreaEntities_r, then returns its count. The clipping caller builds the same field sequence in its larger stack frame with capacity 1024. These are code/data relationships, not executed allocation or traversal proofs.
28
+
29
+ For a supplied returned game object, the recursive helper rejects if any object +0x1d0/+0x1d4/+0x1d8 component is **greater than** the corresponding query maximum, or any +0x1dc/+0x1e0/+0x1e4 component is **less than** the corresponding query minimum. The observed finite overlap therefore includes faces, edges and corners at equality. All 216 combinations per engine use a query [-1,1]^3 and six intervals per axis, covering interior, crossing, exact touching and separation on each axis. The field names min/max are supported by these predicates; full structure/producers remain open.
30
+
31
+ An accepted candidate reads count and checks **count == capacity** before computing/storing the index. Equality enters the MAXCOUNT warning path without output or count writes. Otherwise it writes one four-byte index to output +4*sign_extend(count), then increments count modulo 2^32. Twenty-four insertion cases per engine include equality, just below capacity, and deliberately invalid above-capacity/negative-count fixtures. Above capacity does not trigger the guard; negative count can select storage before the supplied output base. This is not a claim those states occur normally.
32
+
33
+ The guard bounds writes under a valid initial count, nonnegative capacity, valid array and unchanged query-state invariant. Recover those owners and mutation paths before claiming runtime memory safety. The warning path returns from the current recursive invocation; it does not supply a global abort status to its caller, which can still consider another child after return. Full capacity propagation and warning multiplicity remain open.
34
+
35
+ ## AREA-003: local list order and strict split traversal
36
+
37
+ At each visited tree node, the native code starts with linked-list head node +0x18. Before requesting SV_GEntityForSvEntity for a record, it saves record +8 as the next pointer; after overlap/insertion it advances to that saved pointer. Static order is therefore **node's linked records before child traversal**, with no sort in this helper. Link creation, insertion direction, deletion/relinking, duplicate membership and actual map candidate order remain unresolved.
38
+
39
+ After the local list, node word +0 is interpreted as split axis. **-1** returns immediately. Supplied axes 0/1/2 address query components with a four-byte scale; arbitrary axes are not validated in these slices.
40
+
41
+ - Query maximum > node float +4 requests a recursive call on child pointer +8 (child 0).
42
+ - After that supplied return, or when maximum <= split, query minimum < split follows child pointer +16 (child 1) through a tail loop.
43
+ - Minimum >= split skips child 1. Equality does not visit that child; maximum equality does not visit child 0.
44
+
45
+ The 24 compositions per engine cover leaf/three axes and below, touching, degenerate-on-plane, crossing and above bounds around split zero. If both children qualify, child 0 precedes child 1. Each recursion is stopped before its call; a separate post-call slice supplies the preserved node/query registers. This is a **split-decision composition**, not execution of an entire tree or proof of callee state preservation. A query exactly on the split skips both children after processing the local list; its correctness depends on unrecovered entity-to-node placement rules.
46
+
47
+ ## Reproduction limits and Rust implications
48
+
49
+ The byte-addressed evaluator now supports RIP-relative addresses using the next instruction address, scaled indexed addresses, low-width multiplication, sign extension, arithmetic right shift, subtraction, qword tests and finite greater/less branches. Unknown memory/registers, malformed addressing, nonfinite comparisons, unmodeled flags and step escape fail closed. Multiply/shift invalidate flag knowledge; tests reject subsequent unmodeled conditional branches. Old collision reports regenerate unchanged.
50
+
51
+ Independent fixtures check modular address arithmetic, the registered field write widths, overlap equations, capacity writes, split order/leaf stop, paired engine outputs and evaluator failure boundaries. The collector retains source hashes and exact graphs for each binary; it does not infer Windows/macOS/Linux32 equivalence.
52
+
53
+ For the Rust collision contract, keep broad-phase box overlap distinct from split traversal, define candidate ordering explicitly, validate registered record/index/array bounds, and preserve observed 32-bit arithmetic only where required by verified behavior. RE-304 tracks complete node/list construction and query composition, full registered ABI/lifetime/callee effects, exceptional floats, original geometry/runtime fixtures and other-platform verification. No broad game/asset/port coverage task is completed by these slices alone.
docs/research/README.md CHANGED
@@ -105,3 +105,5 @@ The extension collector adds five focused tests for native tail calls, command-r
105
  [Collision copy/status composition and world decisions](COLLISION_COMPOSITION.md) adds RE-293–295: all three bounded merge/copy lanes, byte-addressed payload/status preservation and world sentinel/zero-fraction routing. Reproduce with `python3 scripts/analyze_collision_composition.py`; check with `python3 scripts/test_collision_composition.py`. Whole-loop/geometry/exception/runtime/platform behavior remains open.
106
 
107
  [Entity-clipping loop and sentinel normalization](COLLISION_LOOP.md) adds RE-297–299: signed count/status entry gates, parity-selected lanes, bounded continuation/scheduling compositions and pass/related sentinel normalization. Reproduce with `python3 scripts/analyze_collision_loop.py`; check with `python3 scripts/test_collision_loop.py`. Count/order/lookup ownership, candidate/callee/geometry behavior and runtime/platform conformance remain open.
 
 
 
105
  [Collision copy/status composition and world decisions](COLLISION_COMPOSITION.md) adds RE-293–295: all three bounded merge/copy lanes, byte-addressed payload/status preservation and world sentinel/zero-fraction routing. Reproduce with `python3 scripts/analyze_collision_composition.py`; check with `python3 scripts/test_collision_composition.py`. Whole-loop/geometry/exception/runtime/platform behavior remains open.
106
 
107
  [Entity-clipping loop and sentinel normalization](COLLISION_LOOP.md) adds RE-297–299: signed count/status entry gates, parity-selected lanes, bounded continuation/scheduling compositions and pass/related sentinel normalization. Reproduce with `python3 scripts/analyze_collision_loop.py`; check with `python3 scripts/test_collision_loop.py`. Count/order/lookup ownership, candidate/callee/geometry behavior and runtime/platform conformance remain open.
108
+
109
+ [Entity addressing and area-query boundaries](AREA_QUERIES.md) adds RE-301–303: registered low-width addressing, inclusive overlap/capacity insertion and static list/strict split ordering. Reproduce with `python3 scripts/analyze_area_queries.py`; check with `python3 scripts/test_area_queries.py`. Complete node/list/count/ABI ownership, query composition and runtime/platform conformance remain open.
docs/research/area-queries.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_area_queries.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Bounded entity addressing, area overlap/insertion and split traversal evidence."""
3
+ import gzip
4
+ import itertools
5
+ import json
6
+ from pathlib import Path
7
+ import re
8
+ from analyze_chat_crosshair import bits
9
+ from analyze_collision_composition import evaluate, get_word, put_word
10
+ from analyze_engine_control import control_flow
11
+ from analyze_qvm import write_json
12
+ from analyze_vm_extensions import native_functions, verified
13
+
14
+
15
+ def site(g,op,args):
16
+ matches=[x['address'] for x in g['instructions'] if x['opcode']==op and x['operands'].split('#')[0].strip()==args]
17
+ if len(matches)!=1:raise ValueError('ambiguous area site '+args)
18
+ return matches[0]
19
+
20
+
21
+ def run(g,start,stops,m,regs):return evaluate(g,start,stops,m,regs,capture_registers=True)
22
+
23
+
24
+ def main():
25
+ out={'native':{},'limitations':'Bounded synthetic instructions with supplied pointers, registered fields, '
26
+ 'bounds/counts and recursive-return state. No original process/native callee executes. Finite floats '
27
+ 'only; linked-list/tree construction, actual query composition, pointer validity, ABI lifetimes, '
28
+ 'geometry and original-runtime/other-platform behavior remain open.'}
29
+ for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
30
+ r=json.loads(Path(f'docs/research/native/{label}.json').read_text())
31
+ verified((Path('.cache/UrbanTerror43')/r['source']['path']).read_bytes(),r['source']['sha256'])
32
+ a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode())
33
+ graphs={n:control_flow(fs[n]) for n in ['SV_GentityNum','SV_GEntityForSvEntity','SV_LocateGameData','SV_AreaEntities','SV_AreaEntities_r']}
34
+ def global_address(n,op):
35
+ x=next(x for x in graphs[n]['instructions'] if x['opcode']==op and '(%rip)' in x['operands'])
36
+ return int(re.search(r'#\s*0x([0-9a-f]+)',x['operands'])[1],16)
37
+ stride_address=global_address('SV_GentityNum','imull');base_address=global_address('SV_GentityNum','addq')
38
+ g=graphs['SV_GEntityForSvEntity'];svbase=int(next(x for x in g['instructions'] if x['opcode']=='subq')['operands'].split(',')[0][1:],0)
39
+ entity=[]
40
+ for index,stride in itertools.product([0,1,7,1023,1024,0xffffffff,0x7fffffff,0x80000000],[880,312,0,0xffffffff]):
41
+ m={};put_word(m,stride_address,stride);put_word(m,base_address,0x100000000,8)
42
+ g=graphs['SV_GentityNum'];stop=next(x['address'] for x in g['instructions'] if x['opcode']=='retq')
43
+ t=run(g,g['entry'],[stop],m,{'%rdi':index})
44
+ entity.append({'index_bits':index,'stride_bits':stride,'base':0x100000000,'result':t['registers']['rax'],'trace':t['trace']})
45
+ reverse=[]
46
+ g=graphs['SV_GEntityForSvEntity'];stop=next(x['address'] for x in g['instructions'] if x['opcode']=='retq')
47
+ for index,stride in itertools.product([-1,0,1,7,1023,1024],[880,312]):
48
+ m={};put_word(m,stride_address,stride);put_word(m,base_address,0x100000000,8)
49
+ t=run(g,g['entry'],[stop],m,{'%rdi':svbase+312*index})
50
+ reverse.append({'sv_record_index':index,'sv_pointer':svbase+312*index,'stride':stride,'base':0x100000000,'result':t['registers']['rax'],'trace':t['trace']})
51
+ g=graphs['SV_LocateGameData'];stop=next(x['address'] for x in g['instructions'] if x['opcode']=='retq')
52
+ registration=run(g,g['entry'],[stop],{}, {'%rdi':0x100000000,'%rsi':1024,'%rdx':880,'%rcx':0x200000000,'%r8':256})
53
+ g=graphs['SV_AreaEntities_r'];rows=g['instructions']
54
+ overlap_start=site(g,'movq','0x8(%rbp), %rdx');accept=site(g,'movl','0x18(%rbp), %eax');reject=site(g,'testq','%r12, %r12')
55
+ count_cmp=site(g,'cmpl','0x1c(%rbp), %eax');i=next(i for i,x in enumerate(rows) if x['address']==count_cmp)
56
+ if rows[i+1]['opcode']!='je':raise ValueError('capacity branch changed')
57
+ warning=int(rows[i+1]['operands'].split()[0],16)
58
+ split_sites=[i for i,x in enumerate(rows[:-1]) if x['opcode']=='movl' and x['operands']=='(%r13), %ecx' and rows[i+1]['operands']=='$-0x1, %ecx']
59
+ if len(split_sites)!=1:raise ValueError('ambiguous split site')
60
+ i=split_sites[0];split_start=rows[i]['address']
61
+ if rows[i+2]['opcode']!='je':raise ValueError('leaf branch changed')
62
+ leaf_exit=int(rows[i+2]['operands'].split()[0],16)
63
+ tail=site(g,'movq','0x18(%r13), %rbx')
64
+ recursive=[i for i,x in enumerate(rows) if '<SV_AreaEntities_r>' in x['operands'] and x['opcode']=='callq']
65
+ if len(recursive)!=1:raise ValueError('recursive site changed')
66
+ call=rows[recursive[0]]['address'];after_call=rows[recursive[0]+1]['address']
67
+ query,obj,lo,hi,output,node=0x20000,0x30000,0x40000,0x40020,0x50000,0x60000
68
+ def query_memory(minimum,maximum):
69
+ m={};put_word(m,query,lo,8);put_word(m,query+8,hi,8);put_word(m,query+16,output,8)
70
+ for axis in range(3):put_word(m,lo+4*axis,bits(minimum[axis]));put_word(m,hi+4*axis,bits(maximum[axis]))
71
+ return m
72
+ overlap=[]
73
+ choices=[(-2,-1),(-1,1),(1,2),(1.01,2),(-2,-1.01),(-.5,.5)]
74
+ for bounds in itertools.product(choices,repeat=3):
75
+ m=query_memory([-1]*3,[1]*3)
76
+ for axis,(minimum,maximum) in enumerate(bounds):put_word(m,obj+0x1d0+4*axis,bits(minimum));put_word(m,obj+0x1dc+4*axis,bits(maximum))
77
+ t=run(g,overlap_start,[accept,reject],m,{'%rbp':query,'%rax':obj})
78
+ overlap.append({'entity_min':[x[0] for x in bounds],'entity_max':[x[1] for x in bounds], 'accepted':t['stop']==accept,'trace':t['trace']})
79
+ insertion=[]
80
+ magic=next(x for x in rows if x['opcode']=='movabsq')['operands'].split(',')[0][1:]
81
+ for count,capacity in [(0,0),(0,1),(1,1),(1,2),(1023,1024),(1024,1024),(1025,1024),(-1,1024)]:
82
+ for index in [0,1,1023]:
83
+ m={output-1:0xab,output+4096:0xcd};put_word(m,query+16,output,8);put_word(m,query+24,count);put_word(m,query+28,capacity)
84
+ t=run(g,accept,[reject,warning],m,{'%rbp':query,'%rbx':svbase+312*index,'%r14':int(magic,0)})
85
+ insertion.append({'count_before':count,'capacity':capacity,'sv_record_index':index,'warning_path':t['stop']==warning,
86
+ 'count_after':get_word(t['memory'],query+24),'output_writes':[{'address':w['address'],'bytes':w['bytes'],'value':get_word(t['memory'],w['address'],w['bytes'])} for w in t['writes'] if w['address']!=query+24],
87
+ 'trace':t['trace']})
88
+ traversal=[]
89
+ for axis in [-1,0,1,2]:
90
+ for minimum,maximum in [(-2,-1),(-1,0),(0,0),(0,1),(-1,1),(1,2)]:
91
+ m=query_memory([minimum]*3,[maximum]*3);put_word(m,node,axis);put_word(m,node+4,bits(0));put_word(m,node+8,node+0x100,8);put_word(m,node+16,node+0x200,8)
92
+ regs={'%r13':node,'%rbp':query};t=run(g,split_start,[call,tail,leaf_exit],m,regs);trace=t['trace'];children=[]
93
+ if t['stop']==call:
94
+ children.append(0)
95
+ # Isolated return boundary: no recursive callee runs; preserved node/query are supplied.
96
+ t=run(g,after_call,[tail,leaf_exit],m,{'%r13':node,'%rbp':query});trace+=t['trace']
97
+ if t['stop']==tail:children.append(1)
98
+ traversal.append({'axis':axis,'query_min':minimum,'query_max':maximum,'split':0,'children':children,'trace':trace})
99
+ out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],'graphs':graphs,
100
+ 'globals':{'entity_base':base_address,'entity_stride':stride_address,'sv_record_base':svbase},
101
+ 'entity_address_traces':entity,'sv_to_game_address_traces':reverse,
102
+ 'registration':{'registers':registration['registers'],'trace':registration['trace'],'writes':[{'address':w['address'],'bytes':w['bytes'],'value':get_word(registration['memory'],w['address'],w['bytes'])} for w in registration['writes']]},
103
+ 'sites':{'overlap':overlap_start,'accept':accept,'reject':reject,'capacity_warning':warning,'split':split_start,'child0_call':call,'after_child0_call':after_call,'child1_tail':tail,'leaf_exit':leaf_exit},
104
+ 'overlap_traces':overlap,'insertion_traces':insertion,'split_traversal_compositions':traversal}
105
+ write_json(Path('docs/research/area-queries.json'),out)
106
+ print('Recovered 64 entity/24 reverse-address, 432 overlap, 48 insertion and 48 split cases')
107
+
108
+
109
+ if __name__=='__main__':main()
scripts/analyze_collision_composition.py CHANGED
@@ -17,7 +17,7 @@ def register(r):
17
  r=r.lstrip('%')
18
  if r in aliases:return aliases[r]
19
  if re.fullmatch(r'r\d+d',r):return r[:-1],32
20
- if re.fullmatch(r'r(?:ax|di|si|dx|cx|bp|bx|sp|\d+)',r):return r,64
21
  if re.fullmatch(r'xmm\d+',r):return r,32
22
  raise ValueError('unsupported register '+r)
23
 
@@ -33,15 +33,15 @@ def get_word(m,address,width=4):
33
 
34
  def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=False):
35
  rows=graph['instructions'];indices={x['address']:i for i,x in enumerate(rows)}
36
- regs={register(k)[0]:v for k,v in registers.items()};m=dict(memory);equal=ge=False;signed_le=None;pc=start;trace=[];writes=[]
37
  def read_reg(name):
38
  key,size=register(name)
39
  if key not in regs:raise ValueError('unknown register')
40
  return regs[key]&((1<<size)-1)
41
  def address(arg):
42
- match=re.fullmatch(r'(-?0x[0-9a-f]+)?\((%\w+)\)',arg)
43
  if not match:raise ValueError('unsupported memory operand')
44
- return read_reg(match[2])+int(match[1] or '0',0)
45
  def read(arg,width):
46
  if arg.startswith('$'):return int(arg[1:],0)&((1<<(width*8))-1)
47
  if arg.startswith('%'):return read_reg(arg)
@@ -58,39 +58,56 @@ def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=Fals
58
  if pc not in indices or len(trace)>=limit:raise ValueError('composition slice escaped bound')
59
  x=rows[indices[pc]];trace.append(pc);op=x['opcode'];args=x['operands'].split('#')[0].strip()
60
  i=indices[pc];pc=rows[i+1]['address'] if i+1<len(rows) else None
 
61
  if op=='nop':pass
62
- elif op in ['movl','movq','movss']:
63
- a,b=args.split(', ');width=8 if op=='movq' else 4;write(b,read(a,width),width)
 
 
64
  elif op in ['leal','leaq']:
65
  a,b=args.split(', ');write(b,address(a),4 if op=='leal' else 8)
66
- elif op in ['testl','testb','cmpl']:
67
- width=1 if op=='testb' else 4
68
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
69
  def signed(value):
70
  sign=1<<(width*8-1)
71
  return (value^sign)-sign
72
  equal=v==w if op=='cmpl' else (v&w)==0
73
  signed_le=signed(w)<=signed(v) if op=='cmpl' else signed(v&w)<=0
74
- elif op in ['xorl','orl','addl','addq']:
75
- width=8 if op=='addq' else 4
 
 
 
 
 
 
 
 
 
 
76
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
77
- result=v^w if op=='xorl' else v|w if op=='orl' else v+w
78
  write(b,result,width);equal=(result&((1<<(width*8))-1))==0
79
- signed_le=None
80
  elif op=='xorps':
81
  a,b=args.split(', ')
82
  if a!=b:raise ValueError('unsupported SIMD xor')
83
  write(b,0,4) # Does not change integer flags.
84
  elif op=='comiss':
85
- a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4));equal=v==w;ge=w>=v;signed_le=None
86
- elif op=='sete':write(args,int(equal),1)
87
- elif op in ['je','jne','jae','jle','jmp']:
 
 
 
88
  if op=='jle' and signed_le is None:raise ValueError('unmodeled signed flags')
89
- taken=op=='jmp' or (equal if op=='je' else not equal if op=='jne' else signed_le if op=='jle' else ge)
 
90
  if taken:pc=int(args.split()[0],16)
91
  else:raise ValueError('unsupported composition opcode '+op)
92
  result={'trace':trace,'stop':pc,'memory':m,'writes':writes}
93
- if capture_registers:result['registers']=regs
94
  return result
95
 
96
 
 
17
  r=r.lstrip('%')
18
  if r in aliases:return aliases[r]
19
  if re.fullmatch(r'r\d+d',r):return r[:-1],32
20
+ if re.fullmatch(r'r(?:ax|di|si|dx|cx|bp|bx|sp|ip|\d+)',r):return r,64
21
  if re.fullmatch(r'xmm\d+',r):return r,32
22
  raise ValueError('unsupported register '+r)
23
 
 
33
 
34
  def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=False):
35
  rows=graph['instructions'];indices={x['address']:i for i,x in enumerate(rows)}
36
+ regs={register(k)[0]:v for k,v in registers.items()};m=dict(memory);equal=None;ge=greater=below=None;signed_le=None;pc=start;trace=[];writes=[]
37
  def read_reg(name):
38
  key,size=register(name)
39
  if key not in regs:raise ValueError('unknown register')
40
  return regs[key]&((1<<size)-1)
41
  def address(arg):
42
+ match=re.fullmatch(r'(-?0x[0-9a-f]+)?\((%\w+)(?:,(%\w+),([1248]))?\)',arg)
43
  if not match:raise ValueError('unsupported memory operand')
44
+ return (read_reg(match[2])+int(match[1] or '0',0)+(read_reg(match[3])*int(match[4]) if match[3] else 0))&((1<<64)-1)
45
  def read(arg,width):
46
  if arg.startswith('$'):return int(arg[1:],0)&((1<<(width*8))-1)
47
  if arg.startswith('%'):return read_reg(arg)
 
58
  if pc not in indices or len(trace)>=limit:raise ValueError('composition slice escaped bound')
59
  x=rows[indices[pc]];trace.append(pc);op=x['opcode'];args=x['operands'].split('#')[0].strip()
60
  i=indices[pc];pc=rows[i+1]['address'] if i+1<len(rows) else None
61
+ regs['rip']=pc
62
  if op=='nop':pass
63
+ elif op in ['movl','movq','movss','movabsq']:
64
+ a,b=args.split(', ');width=8 if op in ['movq','movabsq'] else 4;write(b,read(a,width),width)
65
+ elif op=='movslq':
66
+ a,b=args.split(', ');v=read(a,4);write(b,v if v<2**31 else v-2**32,8)
67
  elif op in ['leal','leaq']:
68
  a,b=args.split(', ');write(b,address(a),4 if op=='leal' else 8)
69
+ elif op in ['testl','testb','testq','cmpl']:
70
+ width=1 if op=='testb' else 8 if op=='testq' else 4
71
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
72
  def signed(value):
73
  sign=1<<(width*8-1)
74
  return (value^sign)-sign
75
  equal=v==w if op=='cmpl' else (v&w)==0
76
  signed_le=signed(w)<=signed(v) if op=='cmpl' else signed(v&w)<=0
77
+ ge=greater=below=None
78
+ elif op in ['imull','imulq']:
79
+ width=8 if op=='imulq' else 4;parts=args.split(', ')
80
+ if len(parts)==2:a,b=parts;v,w=read(a,width),read(b,width);dest=b
81
+ elif len(parts)==3:a,b,dest=parts;v,w=read(a,width),read(b,width)
82
+ else:raise ValueError('unsupported multiply operands')
83
+ write(dest,v*w,width);equal=None;signed_le=None;ge=greater=below=None
84
+ elif op=='sarq':
85
+ a,b=args.split(', ');v=read(b,8);v=v if v<2**63 else v-2**64
86
+ write(b,v>>(read(a,8)&63),8);equal=None;signed_le=None;ge=greater=below=None
87
+ elif op in ['xorl','orl','addl','addq','subq']:
88
+ width=8 if op in ['addq','subq'] else 4
89
  a,b=args.split(', ');v,w=read(a,width),read(b,width)
90
+ result=v^w if op=='xorl' else v|w if op=='orl' else w-v if op=='subq' else v+w
91
  write(b,result,width);equal=(result&((1<<(width*8))-1))==0
92
+ signed_le=None;ge=greater=below=None
93
  elif op=='xorps':
94
  a,b=args.split(', ')
95
  if a!=b:raise ValueError('unsupported SIMD xor')
96
  write(b,0,4) # Does not change integer flags.
97
  elif op=='comiss':
98
+ a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4));equal=v==w;ge=w>=v;greater=w>v;below=w<v;signed_le=None
99
+ elif op=='sete':
100
+ if equal is None:raise ValueError('unmodeled zero flag')
101
+ write(args,int(equal),1)
102
+ elif op in ['je','jne','jae','ja','jb','jle','jmp']:
103
+ if op in ['je','jne'] and equal is None:raise ValueError('unmodeled zero flag')
104
  if op=='jle' and signed_le is None:raise ValueError('unmodeled signed flags')
105
+ if op in ['jae','ja','jb'] and ge is None:raise ValueError('unmodeled unsigned flags')
106
+ taken=op=='jmp' or (equal if op=='je' else not equal if op=='jne' else signed_le if op=='jle' else greater if op=='ja' else below if op=='jb' else ge)
107
  if taken:pc=int(args.split()[0],16)
108
  else:raise ValueError('unsupported composition opcode '+op)
109
  result={'trace':trace,'stop':pc,'memory':m,'writes':writes}
110
+ if capture_registers:result['registers']={k:v for k,v in regs.items() if k!='rip'}
111
  return result
112
 
113
 
scripts/test_area_queries.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Independent finite geometry/address/count oracles for the bounded area slices."""
3
+ import json
4
+ from pathlib import Path
5
+ import unittest
6
+ from analyze_collision_composition import evaluate,get_word
7
+ from test_collision_composition import graph
8
+
9
+
10
+ def signed32(x):return x if x<2**31 else x-2**32
11
+
12
+
13
+ class AreaQueryTests(unittest.TestCase):
14
+ @classmethod
15
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/area-queries.json').read_text())
16
+
17
+ def test_registered_entity_address_uses_wrapped_signed_product(self):
18
+ for n in self.r['native'].values():
19
+ self.assertEqual(len(n['entity_address_traces']),32)
20
+ for x in n['entity_address_traces']:
21
+ expected=(x['base']+signed32((x['index_bits']*x['stride_bits'])&0xffffffff))&((1<<64)-1)
22
+ self.assertEqual(x['result'],expected)
23
+ self.assertTrue(any(x['index_bits']==1024 for x in n['entity_address_traces']))
24
+
25
+ def test_stride312_reverse_address_and_registration_fields(self):
26
+ self.assertEqual((39*0x96f96f97)%(2**32),1)
27
+ self.assertEqual((39*0x6f96f96f96f96f97)%(2**64),1)
28
+ for n in self.r['native'].values():
29
+ self.assertEqual(len(n['sv_to_game_address_traces']),12)
30
+ for x in n['sv_to_game_address_traces']:
31
+ self.assertEqual(x['sv_pointer'],n['globals']['sv_record_base']+312*x['sv_record_index'])
32
+ self.assertEqual(x['result'],x['base']+x['stride']*x['sv_record_index'])
33
+ writes=n['registration']['writes'];base=n['globals']['entity_base']
34
+ self.assertEqual([(x['address']-base,x['bytes'],x['value']) for x in writes],
35
+ [(0,8,0x100000000),(8,4,880),(12,4,1024),(16,8,0x200000000),(24,4,256)])
36
+
37
+ def test_closed_overlap_including_faces_edges_and_corners(self):
38
+ for n in self.r['native'].values():
39
+ self.assertEqual(len(n['overlap_traces']),216)
40
+ for x in n['overlap_traces']:
41
+ expected=all(low<=1 and high>=-1 for low,high in zip(x['entity_min'],x['entity_max']))
42
+ self.assertEqual(x['accepted'],expected)
43
+ touching=next(x for x in n['overlap_traces'] if x['entity_min']==[1,1,1])
44
+ self.assertTrue(touching['accepted'])
45
+
46
+ def test_capacity_equality_and_four_byte_index_insertion(self):
47
+ for n in self.r['native'].values():
48
+ self.assertEqual(len(n['insertion_traces']),24)
49
+ for x in n['insertion_traces']:
50
+ stopped=x['count_before']==x['capacity']
51
+ self.assertEqual(x['warning_path'],stopped)
52
+ self.assertEqual(x['count_after'],(x['count_before']+int(not stopped))&0xffffffff)
53
+ expected=[] if stopped else [{'address':0x50000+4*x['count_before'],'bytes':4,'value':x['sv_record_index']}]
54
+ self.assertEqual(x['output_writes'],expected)
55
+
56
+ def test_strict_split_boundaries_child_order_and_leaf_stop(self):
57
+ for n in self.r['native'].values():
58
+ self.assertEqual(len(n['split_traversal_compositions']),24)
59
+ for x in n['split_traversal_compositions']:
60
+ expected=[] if x['axis']==-1 else ([0] if x['query_max']>0 else [])+([1] if x['query_min']<0 else [])
61
+ self.assertEqual(x['children'],expected)
62
+ if expected==[0,1]:self.assertIn(n['sites']['after_child0_call'],x['trace'])
63
+ rows=n['graphs']['SV_AreaEntities_r']['instructions']
64
+ call=next(i for i,x in enumerate(rows) if '<SV_GEntityForSvEntity>' in x['operands'])
65
+ self.assertEqual(rows[call-1]['operands'],'0x8(%rbx), %r12')
66
+ self.assertLess(n['sites']['overlap'],n['sites']['split'])
67
+
68
+ def test_paired_engines_agree_on_bounded_outputs(self):
69
+ a,b=self.r['native'].values()
70
+ for key in ['entity_address_traces','sv_to_game_address_traces','overlap_traces','insertion_traces','split_traversal_compositions']:
71
+ def outputs(n):return [{k:v for k,v in x.items() if k not in ['trace','sv_pointer']} for x in n[key]]
72
+ self.assertEqual(outputs(a),outputs(b))
73
+
74
+ def test_indexed_address_sign_extension_and_shift_boundaries(self):
75
+ g=graph(('movslq','%edi, %rax'),('sarq','$0x3, %rax'),('movl','$0x123, (%rbx,%rax,4)'))
76
+ t=evaluate(g,1,[4],{}, {'%rdi':0xfffffff8,'%rbx':0x10000},capture_registers=True)
77
+ self.assertEqual(get_word(t['memory'],0xfffc),0x123)
78
+ self.assertEqual(t['registers']['rax'],2**64-1)
79
+ g=graph(('imull','$0x80000000, %edi, %eax'),('movslq','%eax, %rax'))
80
+ t=evaluate(g,1,[3],{}, {'%rdi':3},capture_registers=True)
81
+ self.assertEqual(t['registers']['rax'],0xffffffff80000000)
82
+
83
+ def test_unknown_reads_unmodeled_flags_and_nonfinite_fail_closed(self):
84
+ with self.assertRaises(ValueError):evaluate(graph(('movq','0x1(%rip), %rax')),1,[2],{}, {})
85
+ with self.assertRaises(ValueError):evaluate(graph(('sete','%al')),1,[2],{}, {})
86
+ with self.assertRaises(ValueError):evaluate(graph(('imull','$0x1, %eax'),('je','0x3')),1,[3],{}, {'%rax':0})
87
+ with self.assertRaises(ValueError):evaluate(graph(('cmpl','$0x1, %eax'),('ja','0x3')),1,[3],{}, {'%rax':0})
88
+ with self.assertRaises(ValueError):evaluate(graph(('comiss','%xmm0, %xmm1')),1,[2],{}, {'%xmm0':0x7f800000,'%xmm1':0})
89
+ with self.assertRaises(ValueError):evaluate(graph(('movl','(%rbx,%rax,3), %edi')),1,[2],{}, {'%rbx':0,'%rax':0})
90
+
91
+
92
+ if __name__=='__main__':unittest.main()