Recover collision copy status and world-result composition
Browse files- docs/FINDINGS.md +6 -0
- docs/PROVISIONAL_TODO.md +1 -1
- docs/RESEARCH_LEDGER.md +4 -0
- docs/REVERSE_ENGINEERING_TODO.md +5 -0
- docs/research/COLLISION_COMPOSITION.md +51 -0
- docs/research/NATIVE_COLLISION.md +2 -0
- docs/research/README.md +4 -1
- docs/research/collision-composition.json +0 -0
- scripts/analyze_binaries.py +1 -1
- scripts/analyze_collision_composition.py +133 -0
- scripts/test_collision_composition.py +87 -0
docs/FINDINGS.md
CHANGED
|
@@ -294,3 +294,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 294 |
**F-077 — Observed trace decision separation:** candidate status updates precede finite strict candidate-float < current-float replacement; equal floats skip copy. Static copy paths preserve prior offset-4 status with OR and write candidate identity at result +52. Complete status/copy/world/lane/runtime composition remains open. [HIT-002](research/NATIVE_COLLISION.md).
|
| 295 |
|
| 296 |
**F-078 — Observed model/transform routes:** inline model selection uses candidate field 0x1b0 and index 0xa0; temporary models use bounds and byte-flag selection. Transformed trace routes preserve origin/orientation pointers. Full flags, ownership and brush/patch/capsule/runtime geometry remain unverified. [HIT-003](research/NATIVE_COLLISION.md).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 294 |
**F-077 — Observed trace decision separation:** candidate status updates precede finite strict candidate-float < current-float replacement; equal floats skip copy. Static copy paths preserve prior offset-4 status with OR and write candidate identity at result +52. Complete status/copy/world/lane/runtime composition remains open. [HIT-002](research/NATIVE_COLLISION.md).
|
| 295 |
|
| 296 |
**F-078 — Observed model/transform routes:** inline model selection uses candidate field 0x1b0 and index 0xa0; temporary models use bounds and byte-flag selection. Transformed trace routes preserve origin/orientation pointers. Full flags, ownership and brush/patch/capsule/runtime geometry remain unverified. [HIT-003](research/NATIVE_COLLISION.md).
|
| 297 |
+
|
| 298 |
+
**F-079 — Observed merge-lane agreement:** all three native clip result lanes give identical words across tested statuses, finite fractions and prior offset-4 bits. Equality/farther candidates retain the old payload/identifier despite possible status updates. Complete loop scheduling and geometry remain open. [MIX-001](research/COLLISION_COMPOSITION.md).
|
| 299 |
+
|
| 300 |
+
**F-080 — Observed byte-copy/status composition:** closer-hit paths copy 56 bytes, overwrite the candidate identifier from object word zero, and OR saved prior/intermediate status into result +4. Byte-addressed fixtures preserve copy boundaries and distinguish status from payload selection. Canonical field values and runtime conformance remain open. [MIX-002](research/COLLISION_COMPOSITION.md).
|
| 301 |
+
|
| 302 |
+
**F-081 — Observed world-result decisions:** fraction exactly one assigns identifier 1023; other tested finite values assign 1022. Positive/negative zero skips entity clipping toward result copying. These are supplied-result instruction slices, not executed world collisions or complete sentinel semantics. [MIX-003](research/COLLISION_COMPOSITION.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. 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. 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
|
@@ -153,3 +153,7 @@ Completed bounded/static RE-285–287 with `analyze_native_spatial.py`: ten comp
|
|
| 153 |
## 2026-10-11 — Native collision candidate and result decisions
|
| 154 |
|
| 155 |
Completed bounded/static RE-289–291 with `analyze_native_collision.py`: ten native graphs, CM_Trace windows, 256 first-lane filter and 64 result-decision instruction probes. HIT-001–003/F-076–078 distinguish content overlap from nonzero contents, status updates from strict hit replacement and static copy/model routes from executed geometry. Seven tests check paired raw predicates, sentinel/contents behavior, finite fraction ties/status effects, retained model/producer/copy evidence and probe failure domains. RE-288 remains open; RE-292 tracks all lanes, copy/status composition, layout/producers/geometry/runtime/platform requirements. No original process or native collision callee executes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
## 2026-10-11 — Native collision candidate and result decisions
|
| 154 |
|
| 155 |
Completed bounded/static RE-289–291 with `analyze_native_collision.py`: ten native graphs, CM_Trace windows, 256 first-lane filter and 64 result-decision instruction probes. HIT-001–003/F-076–078 distinguish content overlap from nonzero contents, status updates from strict hit replacement and static copy/model routes from executed geometry. Seven tests check paired raw predicates, sentinel/contents behavior, finite fraction ties/status effects, retained model/producer/copy evidence and probe failure domains. RE-288 remains open; RE-292 tracks all lanes, copy/status composition, layout/producers/geometry/runtime/platform requirements. No original process or native collision callee executes.
|
| 156 |
+
|
| 157 |
+
## 2026-10-11 — Collision copy/status and world-result composition
|
| 158 |
+
|
| 159 |
+
Completed bounded RE-293–295 with `analyze_collision_composition.py`: paired clip/trace graphs, 216 complete merge/copy slices spanning three lanes and twelve world-result decisions. MIX-001–003/F-079–081 retain supplied geometry results, raw noncanonical prior-bit fixtures and exceptional/runtime limits. Added a separate restricted byte-addressed evaluator for overlapping native 32/64-bit copies, OR/status branches and register alias behavior. Eight tests check final words, lane agreement, tie retention, copy canaries, world sentinels/zero decisions, register/byte aliasing and failure domains. RE-292 remains open; RE-296 tracks full loop scheduling, producer/layout/sentinel/geometry/runtime/platform behavior. No original process or collision callee executes.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -347,3 +347,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 347 |
- [x] RE-290 Recover finite first-lane status updates and strict closer-hit replacement decisions; retain copy/OR/result-field relationships ([HIT-002](research/NATIVE_COLLISION.md)).
|
| 348 |
- [x] RE-291 Recover static inline/temporary model selection and transformed-trace argument routes in both Linux ELF64 engines ([HIT-003](research/NATIVE_COLLISION.md)).
|
| 349 |
- [ ] RE-292 Validate every unrolled lane, existing-status/copy/OR composition, trace/entity layouts, owner/count producers, model/flag transforms, full geometry/order and original-runtime/all-platform fixtures.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
- [x] RE-290 Recover finite first-lane status updates and strict closer-hit replacement decisions; retain copy/OR/result-field relationships ([HIT-002](research/NATIVE_COLLISION.md)).
|
| 348 |
- [x] RE-291 Recover static inline/temporary model selection and transformed-trace argument routes in both Linux ELF64 engines ([HIT-003](research/NATIVE_COLLISION.md)).
|
| 349 |
- [ ] RE-292 Validate every unrolled lane, existing-status/copy/OR composition, trace/entity layouts, owner/count producers, model/flag transforms, full geometry/order and original-runtime/all-platform fixtures.
|
| 350 |
+
|
| 351 |
+
- [x] RE-293 Recover paired bounded result composition through all three unrolled lanes and test identical finite-input outcomes ([MIX-001](research/COLLISION_COMPOSITION.md)).
|
| 352 |
+
- [x] RE-294 Recover complete 56-byte copy, prior status OR, identifier/payload retention and register/byte-alias fixture semantics ([MIX-002](research/COLLISION_COMPOSITION.md)).
|
| 353 |
+
- [x] RE-295 Recover native world-result sentinel assignment and positive/negative-zero branch skipping entity clipping ([MIX-003](research/COLLISION_COMPOSITION.md)).
|
| 354 |
+
- [ ] RE-296 Validate whole multi-candidate/world/entity loops, prior status reachability and early exits, canonical result producers/layout/sentinels, exceptional floats and original-runtime/all-platform fixtures.
|
docs/research/COLLISION_COMPOSITION.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Collision copy/status composition and world-result decisions
|
| 2 |
+
|
| 3 |
+
Static pass, 2026-10-11. Reproduce with `python3 scripts/analyze_collision_composition.py`; check with `python3 scripts/test_collision_composition.py`. [Generated evidence](collision-composition.json) verifies original/disassembly hashes for both Linux ELF64 engines, retains clip/trace graphs, and records **216 complete bounded merge/copy slices plus twelve world-result decisions**. No original process, intersection generator or collision callee executes. This extends the [first-lane decision findings](NATIVE_COLLISION.md).
|
| 4 |
+
|
| 5 |
+
## MIX-001: three unrolled result lanes
|
| 6 |
+
|
| 7 |
+
SV_ClipMoveToEntities has three post-CM_TransformedBoxTrace lanes. The collector identifies each candidate status read and finite fraction comparison, then evaluates the original instructions through the lane's common continuation address. Lane 1 uses candidate register r12; lanes 0/2 use rbp. Register renaming and out-of-line branches are followed rather than treated as separate algorithms.
|
| 8 |
+
|
| 9 |
+
Each engine has 108 slices: all three lanes, candidate status words 0/1 at result offsets 0/4, candidate fraction **0.25/0.5/0.75**, prior current fraction **0.5**, and prior offset-4 word **0/1/4**. The input's other result words contain distinct old/new payload markers; old identifier is 42 and candidate object word zero is 7. Initial current offset-0 status is zero. Prior status value 4 is a synthetic raw-bit case, not a claim that normal engine status values include 4.
|
| 10 |
+
|
| 11 |
+
All three lanes produce the same final words for every tested input. They replace only when candidate fraction is **strictly smaller**. Equal and farther fractions preserve current fraction, payload and identifier. Candidate flags may still update the current status words before that comparison. These probes establish bounded lane agreement, not the entire unrolled loop's entry, scheduling or early-exit behavior.
|
| 12 |
+
|
| 13 |
+
## MIX-002: byte-addressed copies and accumulated status
|
| 14 |
+
|
| 15 |
+
The new restricted evaluator uses a byte-addressed memory map, so four-byte OR/store operations and overlapping eight-byte loads/stores share actual fixture bytes. It handles only the observed moves, register aliases, selected integer/finite SSE comparisons, status writes and branches. Unknown reads/operations, nonfinite comparisons and step limits fail closed. It models 32-bit register writes clearing upper bits, and AL writes preserving the remaining RAX bits; tests cover both.
|
| 16 |
+
|
| 17 |
+
Let C0/C4 denote candidate status words, P4 the prior current offset-4 word, and F the candidate fraction. In the tested domain, the status update before deciding on replacement is:
|
| 18 |
+
|
| 19 |
+
```
|
| 20 |
+
intermediate4 = P4 if C0 != 0
|
| 21 |
+
1 if C0 == 0 and C4 != 0
|
| 22 |
+
P4 otherwise
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
Candidate C0 nonzero separately sets current offset 0 to 1. It skips the candidate C4 test in that lane. Then:
|
| 26 |
+
|
| 27 |
+
- **F < current fraction:** candidate object word zero is written into candidate result offset **52**; seven eight-byte chunks copy the **56-byte** candidate into current. The saved intermediate4 is ORed into copied candidate offset 4. Final status4 is therefore **C4 OR intermediate4**, with payload and identifier from the candidate.
|
| 28 |
+
- **F >= current fraction:** no full copy occurs. The current payload/identifier remain unchanged; status updates made before the comparison survive.
|
| 29 |
+
|
| 30 |
+
A raw prior status4 of 4 plus candidate C4=1 can produce 5 after a closer hit. A zero candidate C4 does not erase a prior status4 on replacement. When C0 is nonzero, the candidate C4 contribution can enter through copying even though its separate pre-copy test was skipped. These differences matter when separating hit selection from status accumulation.
|
| 31 |
+
|
| 32 |
+
Distinct marker words verify complete payload transfer and retention. Canaries directly before/after the current result span stay unchanged in all slices, and each closer-hit path makes seven eight-byte writes totaling 56 bytes plus its status/identifier operations. This is an observed copy span, not proof of ABI member names, all object memory bounds or atomicity.
|
| 33 |
+
|
| 34 |
+
## MIX-003: world identifier and entity-clipping gate
|
| 35 |
+
|
| 36 |
+
A separate slice in SV_Trace begins after CM_BoxTrace and its file-backed 1.0 load, supplying the returned fraction and xmm2 constant explicitly. It follows the original single-precision compare, SETE low-byte write, integer add and subsequent zero comparison.
|
| 37 |
+
|
| 38 |
+
The world-result identifier at relative result offset **52** becomes:
|
| 39 |
+
|
| 40 |
+
```
|
| 41 |
+
1023 if returned fraction == 1.0
|
| 42 |
+
1022 otherwise
|
| 43 |
+
```
|
| 44 |
+
|
| 45 |
+
The fraction is then compared with zero. **+0.0 and -0.0** take the branch directly toward result copying, skipping the later entity-clipping path. Other tested finite values continue into swept-bound/entity clipping. Inputs 0.25/1.0/1.5/-1.0 exercise identifier and continuation distinctions; 1.5/-1.0 are synthetic out-of-normal-range cases, not evidence that CM_BoxTrace produces them in gameplay.
|
| 46 |
+
|
| 47 |
+
The graph labels and constants establish identifier assignment and continuation, without executing world collision or assigning every sentinel's complete lifecycle. The zero branch is separate from the clip helper's status-driven early exits. COMISS exceptional flags, NaN behavior, native libm/rounding domains and collision producer constraints remain unmodeled.
|
| 48 |
+
|
| 49 |
+
## Remaining work and Rust dependencies
|
| 50 |
+
|
| 51 |
+
RE-293–295 record three-lane composition, byte-copy/status behavior and world-result decisions. RE-292 remains open for complete clipping loops and geometry; RE-296 tracks full multi-candidate/world/entity composition, prior nonzero offset-0 reachability, every early-exit/count path, canonical status/fraction producers, final trace field/sentinel identities, exceptional floating-point behavior and original-runtime/all-platform fixtures. Rust collision results need independent payload selection and status accumulation, followed by the recovered world/entity scheduling rules; the tested slices alone are not a complete collision implementation.
|
docs/research/NATIVE_COLLISION.md
CHANGED
|
@@ -58,3 +58,5 @@ Three unrolled call sites and their related out-of-line branches are retained in
|
|
| 58 |
## Remaining work and Rust dependencies
|
| 59 |
|
| 60 |
RE-289–291 record first-lane filters, bounded result decisions and static model/result-field evidence. RE-288 remains open; RE-292 tracks every unrolled lane, existing-status and copy/OR composition, complete trace/entity fields, owner/count producers, model/flag/transform identities, collision internals/ordering and original-runtime/all-platform fixtures. A Rust collision contract must preserve exclusion relations, content overlap, status accumulation and strict hit replacement independently; a single nearest-hit operation would not capture the recovered status behavior.
|
|
|
|
|
|
|
|
|
| 58 |
## Remaining work and Rust dependencies
|
| 59 |
|
| 60 |
RE-289–291 record first-lane filters, bounded result decisions and static model/result-field evidence. RE-288 remains open; RE-292 tracks every unrolled lane, existing-status and copy/OR composition, complete trace/entity fields, owner/count producers, model/flag/transform identities, collision internals/ordering and original-runtime/all-platform fixtures. A Rust collision contract must preserve exclusion relations, content overlap, status accumulation and strict hit replacement independently; a single nearest-hit operation would not capture the recovered status behavior.
|
| 61 |
+
|
| 62 |
+
The [MIX-001–003 follow-up](COLLISION_COMPOSITION.md) evaluates full copy/status composition in all three result lanes and world sentinel/zero-fraction decisions. Full candidate-loop/geometry/runtime behavior remains open under RE-292/296.
|
docs/research/README.md
CHANGED
|
@@ -43,10 +43,11 @@ python3 scripts/test_chat_locations.py
|
|
| 43 |
python3 scripts/test_chat_crosshair.py
|
| 44 |
python3 scripts/test_native_spatial.py
|
| 45 |
python3 scripts/test_native_collision.py
|
|
|
|
| 46 |
python3 scripts/storage_policy.py check --require-setup
|
| 47 |
```
|
| 48 |
|
| 49 |
-
The pipeline runs `fetch_analysis_references.py`, `analyze_qvm.py`, `analyze_native.py`, `analyze_vm_abi.py`, `analyze_native_dispatch.py`, `analyze_vm_extensions.py`, `analyze_native_metadata.py`, `analyze_native_coverage.py`, `analyze_auth_drop.py`, `analyze_deferred_auth.py`, `analyze_engine_control.py`, `analyze_frame_timing.py`, `analyze_event_queues.py`, `analyze_auth_announcements.py`, `analyze_game_cvars.py`, `analyze_cvar_changes.py`, `analyze_auth_queries.py`, `analyze_selector_bounds.py`, `analyze_chat_paths.py`, `analyze_chat_delivery.py`, `analyze_chat_expansion.py`, `analyze_chat_items_time.py`, `analyze_chat_locations.py`, `analyze_chat_crosshair.py`, `analyze_native_spatial.py`, and `
|
| 50 |
|
| 51 |
Generated JSON/JSONL and compressed evidence are regenerated in place; keep prose notes in Markdown. Rerunning the four analysis stages reproduced every generated report and gzip artifact byte-for-byte on the same toolchain. gzip timestamps and stored filenames are fixed. Different objdump versions can produce different disassembly text, so tool version and uncompressed evidence hashes are recorded. Reports are UTF-8 normal Git text, with individual files below the storage policy's 2 MiB limit.
|
| 52 |
|
|
@@ -100,3 +101,5 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 100 |
[Native trig, spatial services and VM pointers](NATIVE_SPATIAL.md) adds RE-285–287: confirmed paired Linux service mappings/float transport, bounded VM_ArgPtr branches and native trace/PVS/area-query graphs. Reproduce with `python3 scripts/analyze_native_spatial.py`; check with `python3 scripts/test_native_spatial.py`. Complete geometry, layout, exceptional arithmetic and all-platform runtime contracts remain open.
|
| 101 |
|
| 102 |
[Native entity collision filters and decisions](NATIVE_COLLISION.md) adds RE-289–291: paired first-lane filter/result probes, result-field relationships and model/transform paths. Reproduce with `python3 scripts/analyze_native_collision.py`; check with `python3 scripts/test_native_collision.py`. Full collision geometry/lane composition/runtime/platform behavior remains open.
|
|
|
|
|
|
|
|
|
| 43 |
python3 scripts/test_chat_crosshair.py
|
| 44 |
python3 scripts/test_native_spatial.py
|
| 45 |
python3 scripts/test_native_collision.py
|
| 46 |
+
python3 scripts/test_collision_composition.py
|
| 47 |
python3 scripts/storage_policy.py check --require-setup
|
| 48 |
```
|
| 49 |
|
| 50 |
+
The pipeline runs `fetch_analysis_references.py`, `analyze_qvm.py`, `analyze_native.py`, `analyze_vm_abi.py`, `analyze_native_dispatch.py`, `analyze_vm_extensions.py`, `analyze_native_metadata.py`, `analyze_native_coverage.py`, `analyze_auth_drop.py`, `analyze_deferred_auth.py`, `analyze_engine_control.py`, `analyze_frame_timing.py`, `analyze_event_queues.py`, `analyze_auth_announcements.py`, `analyze_game_cvars.py`, `analyze_cvar_changes.py`, `analyze_auth_queries.py`, `analyze_selector_bounds.py`, `analyze_chat_paths.py`, `analyze_chat_delivery.py`, `analyze_chat_expansion.py`, `analyze_chat_items_time.py`, `analyze_chat_locations.py`, `analyze_chat_crosshair.py`, `analyze_native_spatial.py`, `analyze_native_collision.py`, and `analyze_collision_composition.py` in that order. Python 3.9+ and **LLVM objdump** with x86 ELF/COFF/Mach-O support are required. This pass used Apple LLVM 17.0.0 on macOS arm64; it disassembled x86 images without running them. Upstream references are downloaded only if absent, checked against the committed SHA-256 manifest, and cached under `.cache/research/upstream/`. A clone also needs `git lfs pull` to obtain the derived artifacts/extracted members.
|
| 51 |
|
| 52 |
Generated JSON/JSONL and compressed evidence are regenerated in place; keep prose notes in Markdown. Rerunning the four analysis stages reproduced every generated report and gzip artifact byte-for-byte on the same toolchain. gzip timestamps and stored filenames are fixed. Different objdump versions can produce different disassembly text, so tool version and uncompressed evidence hashes are recorded. Reports are UTF-8 normal Git text, with individual files below the storage policy's 2 MiB limit.
|
| 53 |
|
|
|
|
| 101 |
[Native trig, spatial services and VM pointers](NATIVE_SPATIAL.md) adds RE-285–287: confirmed paired Linux service mappings/float transport, bounded VM_ArgPtr branches and native trace/PVS/area-query graphs. Reproduce with `python3 scripts/analyze_native_spatial.py`; check with `python3 scripts/test_native_spatial.py`. Complete geometry, layout, exceptional arithmetic and all-platform runtime contracts remain open.
|
| 102 |
|
| 103 |
[Native entity collision filters and decisions](NATIVE_COLLISION.md) adds RE-289–291: paired first-lane filter/result probes, result-field relationships and model/transform paths. Reproduce with `python3 scripts/analyze_native_collision.py`; check with `python3 scripts/test_native_collision.py`. Full collision geometry/lane composition/runtime/platform behavior remains open.
|
| 104 |
+
|
| 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.
|
docs/research/collision-composition.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_binaries.py
CHANGED
|
@@ -14,7 +14,7 @@ def main():
|
|
| 14 |
'analyze_deferred_auth.py', 'analyze_engine_control.py', 'analyze_frame_timing.py',
|
| 15 |
'analyze_event_queues.py', 'analyze_auth_announcements.py', 'analyze_game_cvars.py',
|
| 16 |
'analyze_cvar_changes.py', 'analyze_auth_queries.py', 'analyze_selector_bounds.py',
|
| 17 |
-
'analyze_chat_paths.py', 'analyze_chat_delivery.py', 'analyze_chat_expansion.py', 'analyze_chat_items_time.py', 'analyze_chat_locations.py', 'analyze_chat_crosshair.py', 'analyze_native_spatial.py', 'analyze_native_collision.py'):
|
| 18 |
subprocess.run([sys.executable, str(Path('scripts')/script)], check=True)
|
| 19 |
|
| 20 |
|
|
|
|
| 14 |
'analyze_deferred_auth.py', 'analyze_engine_control.py', 'analyze_frame_timing.py',
|
| 15 |
'analyze_event_queues.py', 'analyze_auth_announcements.py', 'analyze_game_cvars.py',
|
| 16 |
'analyze_cvar_changes.py', 'analyze_auth_queries.py', 'analyze_selector_bounds.py',
|
| 17 |
+
'analyze_chat_paths.py', 'analyze_chat_delivery.py', 'analyze_chat_expansion.py', 'analyze_chat_items_time.py', 'analyze_chat_locations.py', 'analyze_chat_crosshair.py', 'analyze_native_spatial.py', 'analyze_native_collision.py', 'analyze_collision_composition.py'):
|
| 18 |
subprocess.run([sys.executable, str(Path('scripts')/script)], check=True)
|
| 19 |
|
| 20 |
|
scripts/analyze_collision_composition.py
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Bounded byte-addressed collision copies and world-result decisions."""
|
| 3 |
+
import gzip
|
| 4 |
+
import itertools
|
| 5 |
+
import json
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
import re
|
| 8 |
+
from analyze_chat_crosshair import bits,number
|
| 9 |
+
from analyze_engine_control import control_flow
|
| 10 |
+
from analyze_qvm import write_json
|
| 11 |
+
from analyze_vm_extensions import native_functions,verified
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def register(r):
|
| 15 |
+
aliases={'eax':('rax',32),'edi':('rdi',32),'esi':('rsi',32),'edx':('rdx',32),
|
| 16 |
+
'ecx':('rcx',32),'ebp':('rbp',32),'ebx':('rbx',32),'al':('rax',8)}
|
| 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 |
+
|
| 24 |
+
|
| 25 |
+
def put_word(m,address,value,width=4):
|
| 26 |
+
for i in range(width):m[address+i]=(value>>(8*i))&255
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def get_word(m,address,width=4):
|
| 30 |
+
if any(address+i not in m for i in range(width)):raise ValueError('unknown byte-addressed read')
|
| 31 |
+
return sum(m[address+i]<<(8*i) for i in range(width))
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def evaluate(graph,start,stops,memory,registers,limit=128):
|
| 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;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)
|
| 48 |
+
return get_word(m,address(arg),width)
|
| 49 |
+
def write(arg,val,width):
|
| 50 |
+
val&=(1<<(width*8))-1
|
| 51 |
+
if arg.startswith('%'):
|
| 52 |
+
key,size=register(arg)
|
| 53 |
+
if size==8:regs[key]=(regs.get(key,0)&~255)|val
|
| 54 |
+
else:regs[key]=val # 32-bit writes clear high bits.
|
| 55 |
+
else:
|
| 56 |
+
a=address(arg);put_word(m,a,val,width);writes.append({'address':a,'bytes':width})
|
| 57 |
+
while pc not in stops:
|
| 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 ['testl','cmpl']:
|
| 65 |
+
a,b=args.split(', ');v,w=read(a,4),read(b,4);equal=v==w if op=='cmpl' else (v&w)==0
|
| 66 |
+
elif op in ['xorl','orl','addl']:
|
| 67 |
+
a,b=args.split(', ');v,w=read(a,4),read(b,4)
|
| 68 |
+
result=v^w if op=='xorl' else v|w if op=='orl' else v+w
|
| 69 |
+
write(b,result,4);equal=(result&0xffffffff)==0
|
| 70 |
+
elif op=='xorps':
|
| 71 |
+
a,b=args.split(', ')
|
| 72 |
+
if a!=b:raise ValueError('unsupported SIMD xor')
|
| 73 |
+
write(b,0,4) # Does not change integer flags.
|
| 74 |
+
elif op=='comiss':
|
| 75 |
+
a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4));equal=v==w;ge=w>=v
|
| 76 |
+
elif op=='sete':write(args,int(equal),1)
|
| 77 |
+
elif op in ['je','jne','jae','jmp']:
|
| 78 |
+
taken=op=='jmp' or (equal if op=='je' else not equal if op=='jne' else ge)
|
| 79 |
+
if taken:pc=int(args.split()[0],16)
|
| 80 |
+
else:raise ValueError('unsupported composition opcode '+op)
|
| 81 |
+
return {'trace':trace,'stop':pc,'memory':m,'writes':writes}
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def lane_sites(graph):
|
| 85 |
+
rows=graph['instructions'];starts=[x['address'] for x in rows if x['opcode']=='movl' and x['operands']=='0x1030(%rsp), %r10d']
|
| 86 |
+
comparisons=[i for i,x in enumerate(rows) if x['opcode']=='comiss' and x['operands'].startswith('0x44(%rbx),')]
|
| 87 |
+
if len(starts)!=3 or len(comparisons)!=3:raise ValueError('lane count changed')
|
| 88 |
+
return [{'lane':lane,'start':start,'stop':int(rows[i+1]['operands'].split()[0],16),
|
| 89 |
+
'candidate_register':'%r12' if lane==1 else '%rbp'}
|
| 90 |
+
for lane,(start,i) in enumerate(zip(starts,comparisons))]
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def main():
|
| 94 |
+
out={'native':{},'limitations':'Bounded byte-addressed synthetic instruction evaluation, not original '
|
| 95 |
+
'native/runtime/callee execution. Supplied statuses/fractions/payloads omit collision generation. '
|
| 96 |
+
'Only finite comparisons and selected operations are modeled. Full entity-loop scheduling, '
|
| 97 |
+
'geometry, malformed/exceptional inputs and other-platform conformance remain open.'}
|
| 98 |
+
for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
|
| 99 |
+
r=json.loads(Path(f'docs/research/native/{label}.json').read_text())
|
| 100 |
+
verified((Path('.cache/UrbanTerror43')/r['source']['path']).read_bytes(),r['source']['sha256'])
|
| 101 |
+
a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode())
|
| 102 |
+
graphs={n:control_flow(fs[n]) for n in ['SV_ClipMoveToEntities','SV_Trace']};lanes=lane_sites(graphs['SV_ClipMoveToEntities'])
|
| 103 |
+
clip,entity,stack=0x10000,0x20000,0x40000;current=clip+0x3c;candidate=stack+0x1030
|
| 104 |
+
merges=[]
|
| 105 |
+
for lane,status0,status4,fraction,prior4 in itertools.product(lanes,[0,1],[0,1],[.25,.5,.75],[0,1,4]):
|
| 106 |
+
old=[0,prior4,bits(.5)]+[0x100+i for i in range(3,13)]+[42]
|
| 107 |
+
new=[status0,status4,bits(fraction)]+[0x200+i for i in range(3,13)]+[999]
|
| 108 |
+
m={current-1:0xab,current+56:0xcd};put_word(m,entity,7)
|
| 109 |
+
for i,(v,w) in enumerate(zip(old,new)):put_word(m,current+4*i,v);put_word(m,candidate+4*i,w)
|
| 110 |
+
t=evaluate(graphs['SV_ClipMoveToEntities'],lane['start'],[lane['stop']],m,
|
| 111 |
+
{'%rbx':clip,'%rsp':stack,lane['candidate_register']:entity})
|
| 112 |
+
merges.append({'lane':lane['lane'],'candidate_status0':status0,'candidate_status4':status4,
|
| 113 |
+
'candidate_fraction':fraction,'prior_status4':prior4,'old_words':old,'input_candidate_words':new,
|
| 114 |
+
'final_words':[get_word(t['memory'],current+4*i) for i in range(14)],
|
| 115 |
+
'canaries':[t['memory'][current-1],t['memory'][current+56]],'trace':t['trace'],'writes':t['writes']})
|
| 116 |
+
g=graphs['SV_Trace'];rows=g['instructions']
|
| 117 |
+
call=next(i for i,x in enumerate(rows) if '<CM_BoxTrace>' in x['operands'])
|
| 118 |
+
start=rows[call+2]['address'] # Start after file-backed 1.0 load; supply xmm2 explicitly.
|
| 119 |
+
branch=next(i for i in range(call,len(rows)) if rows[i]['opcode']=='je')
|
| 120 |
+
stop_yes=int(rows[branch]['operands'].split()[0],16);stop_no=rows[branch+1]['address']
|
| 121 |
+
world=[]
|
| 122 |
+
for fraction in [0.0,-0.0,.25,1.0,1.5,-1.0]:
|
| 123 |
+
m={};put_word(m,stack+0x64,bits(fraction))
|
| 124 |
+
t=evaluate(g,start,[stop_yes,stop_no],m,{'%rsp':stack,'%xmm2':bits(1), '%rax':0})
|
| 125 |
+
world.append({'fraction':fraction,'identifier_word':get_word(t['memory'],stack+0x90),
|
| 126 |
+
'skips_entity_clip_path':t['stop']==stop_yes,'trace':t['trace']})
|
| 127 |
+
out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],
|
| 128 |
+
'graphs':graphs,'lane_sites':lanes,'composition_traces':merges,'world_decision_traces':world}
|
| 129 |
+
write_json(Path('docs/research/collision-composition.json'),out)
|
| 130 |
+
print('Recovered 216 complete three-lane merge/copy slices and 12 world-result decisions')
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
if __name__=='__main__':main()
|
scripts/test_collision_composition.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Byte-copy, status accumulation, lane agreement and world-decision fixtures."""
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import unittest
|
| 6 |
+
from analyze_collision_composition import evaluate,get_word,put_word
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def graph(*ops):
|
| 10 |
+
return {'instructions':[{'address':i+1,'opcode':op,'operands':args} for i,(op,args) in enumerate(ops+(('nop',''),))]}
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class CollisionCompositionTests(unittest.TestCase):
|
| 14 |
+
@classmethod
|
| 15 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/collision-composition.json').read_text())
|
| 16 |
+
|
| 17 |
+
def test_complete_copy_and_status_or(self):
|
| 18 |
+
for n in self.r['native'].values():
|
| 19 |
+
self.assertEqual(len(n['composition_traces']),108)
|
| 20 |
+
for x in n['composition_traces']:
|
| 21 |
+
pre=x['prior_status4'] if x['candidate_status0'] else 1 if x['candidate_status4'] else x['prior_status4']
|
| 22 |
+
if x['candidate_fraction']<.5:
|
| 23 |
+
expected=list(x['input_candidate_words']);expected[1]|=pre;expected[13]=7
|
| 24 |
+
else:
|
| 25 |
+
expected=list(x['old_words']);expected[0]=int(bool(x['candidate_status0']));expected[1]=pre
|
| 26 |
+
self.assertEqual(x['final_words'],expected)
|
| 27 |
+
|
| 28 |
+
def test_all_three_lanes_have_identical_outcomes(self):
|
| 29 |
+
for n in self.r['native'].values():
|
| 30 |
+
by_input={}
|
| 31 |
+
for x in n['composition_traces']:
|
| 32 |
+
key=(x['candidate_status0'],x['candidate_status4'],x['candidate_fraction'],x['prior_status4'])
|
| 33 |
+
by_input.setdefault(key,[]).append((x['lane'],x['final_words']))
|
| 34 |
+
self.assertEqual(len(by_input),36)
|
| 35 |
+
for cases in by_input.values():
|
| 36 |
+
self.assertEqual([lane for lane,_ in cases],[0,1,2])
|
| 37 |
+
self.assertTrue(all(words==cases[0][1] for _,words in cases))
|
| 38 |
+
|
| 39 |
+
def test_equal_fraction_keeps_old_payload_and_identifier(self):
|
| 40 |
+
for n in self.r['native'].values():
|
| 41 |
+
for x in n['composition_traces']:
|
| 42 |
+
if x['candidate_fraction']==.5:
|
| 43 |
+
self.assertEqual(x['final_words'][2:],x['old_words'][2:])
|
| 44 |
+
self.assertEqual(x['final_words'][13],42)
|
| 45 |
+
|
| 46 |
+
def test_canaries_and_current_write_envelope(self):
|
| 47 |
+
for n in self.r['native'].values():
|
| 48 |
+
for x in n['composition_traces']:
|
| 49 |
+
self.assertEqual(x['canaries'],[0xab,0xcd])
|
| 50 |
+
for w in x['writes']:
|
| 51 |
+
if w['address']<0x20000:
|
| 52 |
+
self.assertGreaterEqual(w['address'],0x10000+0x3c)
|
| 53 |
+
self.assertLessEqual(w['address']+w['bytes'],0x10000+0x3c+56)
|
| 54 |
+
if x['candidate_fraction']<.5:
|
| 55 |
+
self.assertEqual(sum(w['bytes'] for w in x['writes'] if w['bytes']==8),56)
|
| 56 |
+
|
| 57 |
+
def test_world_sentinels_and_zero_fraction_skip(self):
|
| 58 |
+
for n in self.r['native'].values():
|
| 59 |
+
cases=n['world_decision_traces']
|
| 60 |
+
self.assertEqual([x['identifier_word'] for x in cases],[1022,1022,1022,1023,1022,1022])
|
| 61 |
+
self.assertEqual([x['skips_entity_clip_path'] for x in cases],[True,True,False,False,False,False])
|
| 62 |
+
|
| 63 |
+
def test_byte_aliasing_and_32bit_register_write(self):
|
| 64 |
+
m={};put_word(m,0x10000,0x1122334455667788,8)
|
| 65 |
+
g=graph(('movl','$0xffffffff, %eax'),('movq','%rax, 0x10000(%rbx)'))
|
| 66 |
+
t=evaluate(g,1,[3],m,{'%rbx':0,'%rax':2**63})
|
| 67 |
+
self.assertEqual(get_word(t['memory'],0x10000,8),0xffffffff)
|
| 68 |
+
g=graph(('movl','$0xaabbccdd, 0x10004(%rbx)'))
|
| 69 |
+
t=evaluate(g,1,[2],m,{'%rbx':0})
|
| 70 |
+
self.assertEqual(get_word(t['memory'],0x10000,8),0xaabbccdd55667788)
|
| 71 |
+
|
| 72 |
+
def test_low_byte_write_preserves_other_register_bits(self):
|
| 73 |
+
g=graph(('cmpl','%eax, %eax'),('sete','%al'),('movq','%rax, 0x10000(%rbx)'))
|
| 74 |
+
m={};put_word(m,0x10000,0,8)
|
| 75 |
+
t=evaluate(g,1,[4],m,{'%rbx':0,'%rax':0x12345678})
|
| 76 |
+
self.assertEqual(get_word(t['memory'],0x10000,8),0x12345601)
|
| 77 |
+
|
| 78 |
+
def test_unknown_memory_ops_nan_and_limits_fail_closed(self):
|
| 79 |
+
g=graph(('movq','0x10000(%rbx), %rax'))
|
| 80 |
+
with self.assertRaises(ValueError):evaluate(g,1,[2],{}, {'%rbx':0})
|
| 81 |
+
with self.assertRaises(ValueError):evaluate(graph(('callq','0x123')),1,[2],{}, {})
|
| 82 |
+
with self.assertRaises(ValueError):evaluate(graph(('comiss','%xmm0, %xmm1')),1,[2],{},
|
| 83 |
+
{'%xmm0':0x7fc00000,'%xmm1':0})
|
| 84 |
+
with self.assertRaises(ValueError):evaluate(graph(('jmp','0x1')),1,[2],{}, {},limit=3)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
if __name__=='__main__':unittest.main()
|