Recover leaf references and bounded area portal state
Browse files- docs/ASSET_PIPELINE_TODO.md +2 -0
- docs/FINDINGS.md +6 -0
- docs/PROVISIONAL_TODO.md +2 -0
- docs/RESEARCH_LEDGER.md +4 -0
- docs/REVERSE_ENGINEERING_TODO.md +5 -0
- docs/research/BSP_LOADING.md +2 -0
- docs/research/BSP_REFERENCES.md +54 -0
- docs/research/README.md +2 -0
- docs/research/bsp-references.json +0 -0
- scripts/analyze_bsp_references.py +112 -0
- scripts/analyze_collision_composition.py +1 -1
- scripts/test_bsp_references.py +92 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -204,3 +204,5 @@ Names and output formats below are proposals to refine after the relevant resear
|
|
| 204 |
Native collision follow-up: [BSP planes](research/BSP_PLANES.md) records type/sign fields and 16-byte native node addressing. Connect these to file-lump conversion only after loading/type/sign producers and valid counts are recovered (RE-320); native in-memory offsets are not a disk-format specification.
|
| 205 |
|
| 206 |
Native loading follow-up: [BSP payload loading](research/BSP_LOADING.md) connects plane/node/leaf descriptors 2/3/4 to native conversion. Preserve original disk fields and every map variant; native collision projection omits fields that rendering or other consumers may need. Complete range/reference validation and actual-map conversion remain RE-324 work.
|
|
|
|
|
|
|
|
|
| 204 |
Native collision follow-up: [BSP planes](research/BSP_PLANES.md) records type/sign fields and 16-byte native node addressing. Connect these to file-lump conversion only after loading/type/sign producers and valid counts are recovered (RE-320); native in-memory offsets are not a disk-format specification.
|
| 205 |
|
| 206 |
Native loading follow-up: [BSP payload loading](research/BSP_LOADING.md) connects plane/node/leaf descriptors 2/3/4 to native conversion. Preserve original disk fields and every map variant; native collision projection omits fields that rendering or other consumers may need. Complete range/reference validation and actual-map conversion remain RE-324 work.
|
| 207 |
+
|
| 208 |
+
Reference follow-up: [leaf references and area state](research/BSP_REFERENCES.md) connects native leaf words to brush/surface index arrays and records standalone copy behavior. Validate every source reference and prove inline-loader equivalence before implementing conversion; retain disk bounds and every variant (RE-328).
|
docs/FINDINGS.md
CHANGED
|
@@ -342,3 +342,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 342 |
**F-101 — Observed node reconstruction:** 36-byte disk records become 16-byte native nodes containing plane_base + 20 * signed index and unchanged child words. Sampled conversion performs no local plane-index guard and omits the six trailing disk words. Complete file validation and native ownership/runtime behavior remain open. [LOAD-002](research/BSP_LOADING.md).
|
| 343 |
|
| 344 |
**F-102 — Observed leaf projection/state:** 48-byte disk records become 24-byte native leaves in word order 0,1,10,11,8,9. Zero-seeded running cluster/area words track tested maximum nonnegative IDs plus one; leaf storage reserves two spare records and later requests area-related storage. Reference semantics, initialization, count overflow and runtime conformance remain open. [LOAD-003](research/BSP_LOADING.md).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
**F-101 — Observed node reconstruction:** 36-byte disk records become 16-byte native nodes containing plane_base + 20 * signed index and unchanged child words. Sampled conversion performs no local plane-index guard and omits the six trailing disk words. Complete file validation and native ownership/runtime behavior remain open. [LOAD-002](research/BSP_LOADING.md).
|
| 343 |
|
| 344 |
**F-102 — Observed leaf projection/state:** 48-byte disk records become 24-byte native leaves in word order 0,1,10,11,8,9. Zero-seeded running cluster/area words track tested maximum nonnegative IDs plus one; leaf storage reserves two spare records and later requests area-related storage. Reference semantics, initialization, count overflow and runtime conformance remain open. [LOAD-003](research/BSP_LOADING.md).
|
| 345 |
+
|
| 346 |
+
**F-103 — Observed leaf reference consumers:** reference helpers preserve ordered four-byte IDs, reserving one spare brush word but no spare surface word. Bounded leaf address slices select 56-byte native brush records and surface pointer-table entries from signed IDs. Inline map loading, valid ranges and full collision bodies remain open. [REF-001](research/BSP_REFERENCES.md).
|
| 347 |
+
|
| 348 |
+
**F-104 — Observed connectivity priority:** nonzero cm_noAreas returns true before index checks; either negative index returns false before upper bounds; valid areas compare labels in eight-byte records. Supplied labels establish local outcomes, not proven flood generation or runtime connectivity. [REF-002](research/BSP_REFERENCES.md).
|
| 349 |
+
|
| 350 |
+
**F-105 — Observed portal references:** nonzero open increments both directed counters and zero decrements both; self-pairs mutate twice. Close-underflow is checked after writes, and successful updates increment the epoch before requesting flooding. Full flood/lifecycle/query composition and runtime safety remain open. [REF-003](research/BSP_REFERENCES.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -516,3 +516,5 @@ Before marking a broad subsystem complete, reconcile it with the canonical check
|
|
| 516 |
Research progress: [BSP plane boundaries and bounded node traversal](research/BSP_PLANES.md) completes bounded RE-317–319. Broad geometry/runtime/port milestones remain open; see RE-320 for full traversal and loading work.
|
| 517 |
|
| 518 |
Research progress: [native BSP payload loading](research/BSP_LOADING.md) completes bounded RE-321–323. Complete asset conversion and geometry milestones remain open; RE-324 tracks actual variant audits, validation, reference ownership and runtime fixtures.
|
|
|
|
|
|
|
|
|
| 516 |
Research progress: [BSP plane boundaries and bounded node traversal](research/BSP_PLANES.md) completes bounded RE-317–319. Broad geometry/runtime/port milestones remain open; see RE-320 for full traversal and loading work.
|
| 517 |
|
| 518 |
Research progress: [native BSP payload loading](research/BSP_LOADING.md) completes bounded RE-321–323. Complete asset conversion and geometry milestones remain open; RE-324 tracks actual variant audits, validation, reference ownership and runtime fixtures.
|
| 519 |
+
|
| 520 |
+
Research progress: [leaf references and area portal state](research/BSP_REFERENCES.md) completes bounded RE-325–327. Broad geometry/networking/runtime milestones remain open; RE-328 tracks complete flooding, reference ownership and snapshot consumers.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -185,3 +185,7 @@ Completed bounded RE-317–319 with `analyze_bsp_planes.py`: four hash-verified
|
|
| 185 |
## 2026-10-11 — Native BSP payload loading
|
| 186 |
|
| 187 |
Completed bounded RE-321–323 with `analyze_bsp_loading.py`: eight verified graphs, six header descriptor-routing slices, 72 supplied-allocation plane/node/leaf payload conversions and corresponding allocation-size slices. LOAD-001–003/F-100–102 distinguish disk/native records, +1 type priority from unit-normal validation, signed plane-pointer construction from valid indices, and projected leaf words/maxima from complete ownership. Eight independent tests cover field copies/type/sign/padding, signed pointers/children, leaf projection/maxima, allocation/routing arithmetic, exact byte writes, paired outputs and restricted byte/conditional/signed-branch evaluator semantics. The full suite passes 254 tests; current and preceding reports regenerate byte-identically, storage and documentation checks pass. RE-320 remains open; RE-324 tracks complete validation/reset/allocator/spare-record/reference/portal contracts, actual BSP variants, full traversal and runtime/platform behavior. No original process, allocator or native callee executes.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
## 2026-10-11 — Native BSP payload loading
|
| 186 |
|
| 187 |
Completed bounded RE-321–323 with `analyze_bsp_loading.py`: eight verified graphs, six header descriptor-routing slices, 72 supplied-allocation plane/node/leaf payload conversions and corresponding allocation-size slices. LOAD-001–003/F-100–102 distinguish disk/native records, +1 type priority from unit-normal validation, signed plane-pointer construction from valid indices, and projected leaf words/maxima from complete ownership. Eight independent tests cover field copies/type/sign/padding, signed pointers/children, leaf projection/maxima, allocation/routing arithmetic, exact byte writes, paired outputs and restricted byte/conditional/signed-branch evaluator semantics. The full suite passes 254 tests; current and preceding reports regenerate byte-identically, storage and documentation checks pass. RE-320 remains open; RE-324 tracks complete validation/reset/allocator/spare-record/reference/portal contracts, actual BSP variants, full traversal and runtime/platform behavior. No original process, allocator or native callee executes.
|
| 188 |
+
|
| 189 |
+
## 2026-10-11 — Leaf reference arrays and area portal state
|
| 190 |
+
|
| 191 |
+
Completed bounded RE-325–327 with `analyze_bsp_references.py`: sixteen verified native graphs, 48 reference-copy and 48 consumer-address slices, 450 connectivity and 800 portal probes. REF-001–003/F-103–105 distinguish standalone copying from inline map loading, native signed address calculations from valid indices, supplied label equality from flood generation, and directed reference counters from Boolean portal state. Portal frame omission and all stopped callee boundaries are explicit. Eight independent tests verify copies/allocation/canaries, signed addressing, connectivity guard priority, full sampled matrix/write/error/epoch/request outcomes, self-pairs, prior-loader slots, paired outputs and low-byte aliases. The full suite passes 262 tests; new and preceding reports regenerate byte-identically, storage and documentation checks pass. RE-324 remains open; RE-328 tracks inline/range/layout/geometry/flood/lifecycle/snapshot and runtime/platform coverage. No original process, allocator or native callee executes.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -387,3 +387,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 387 |
- [x] RE-322 Recover disk-to-native node reconstruction, signed stride-20 plane pointers, unchanged children and allocation sizes ([LOAD-002](research/BSP_LOADING.md)).
|
| 388 |
- [x] RE-323 Recover leaf word projection, running cluster/area maxima and bounded leaf/area/portal allocation requests ([LOAD-003](research/BSP_LOADING.md)).
|
| 389 |
- [ ] RE-324 Recover complete header/lump/error/endian/count validation, allocator/reset/spare-record initialization, brush/surface references and area/portal ownership; audit actual BSP variants and compose full loading/traversal/callback/runtime/all-platform fixtures.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
- [x] RE-322 Recover disk-to-native node reconstruction, signed stride-20 plane pointers, unchanged children and allocation sizes ([LOAD-002](research/BSP_LOADING.md)).
|
| 388 |
- [x] RE-323 Recover leaf word projection, running cluster/area maxima and bounded leaf/area/portal allocation requests ([LOAD-003](research/BSP_LOADING.md)).
|
| 389 |
- [ ] RE-324 Recover complete header/lump/error/endian/count validation, allocator/reset/spare-record initialization, brush/surface references and area/portal ownership; audit actual BSP variants and compose full loading/traversal/callback/runtime/all-platform fixtures.
|
| 390 |
+
|
| 391 |
+
- [x] RE-325 Recover bounded brush/surface reference copies, allocation differences and signed leaf-consumer address relationships ([REF-001](research/BSP_REFERENCES.md)).
|
| 392 |
+
- [x] RE-326 Recover area connectivity bypass/index-guard priority and eight-byte record label comparisons ([REF-002](research/BSP_REFERENCES.md)).
|
| 393 |
+
- [x] RE-327 Recover paired portal counters, self-pair writes, close-underflow/error boundaries and epoch/first-flood requests ([REF-003](research/BSP_REFERENCES.md)).
|
| 394 |
+
- [ ] RE-328 Recover inline reference-loader equivalence and actual map index ranges, complete brush/surface layouts/stamps/geometry, all unrolled/recursive flooding and epoch/revisit invariants, portal writers/reset/serialization, area-bit/snapshot consumers and original-runtime/all-platform conformance.
|
docs/research/BSP_LOADING.md
CHANGED
|
@@ -56,3 +56,5 @@ Twenty-four paired leaf payload probes cover odd/even peel and pair-loop paths.
|
|
| 56 |
Eight independent tests cover copy/type/sign/padding behavior, signed node pointer arithmetic and child bits, leaf word reordering/maxima, allocation arithmetic, header descriptor routing, exact per-record byte writes, paired outcomes and restricted evaluator byte/conditional/signed-branch semantics. No probe writes into a spare record. Larger instruction traces retain counts/hashes; small traces retain addresses.
|
| 57 |
|
| 58 |
RE-324: validate complete header/lump bounds, alignment/division/error paths, endian handling and count overflow; recover allocator/reset/zeroing and spare-record initialization; recover leaf brush/surface references, area/portal structures and lifetime; audit every actual BSP variant and compose loading with full recursive query/callback paths; obtain original-runtime and other-platform differential fixtures. Rendering geometry and Rust/Bevy/Solari conversion remain separate open work.
|
|
|
|
|
|
|
|
|
| 56 |
Eight independent tests cover copy/type/sign/padding behavior, signed node pointer arithmetic and child bits, leaf word reordering/maxima, allocation arithmetic, header descriptor routing, exact per-record byte writes, paired outcomes and restricted evaluator byte/conditional/signed-branch semantics. No probe writes into a spare record. Larger instruction traces retain counts/hashes; small traces retain addresses.
|
| 57 |
|
| 58 |
RE-324: validate complete header/lump bounds, alignment/division/error paths, endian handling and count overflow; recover allocator/reset/zeroing and spare-record initialization; recover leaf brush/surface references, area/portal structures and lifetime; audit every actual BSP variant and compose loading with full recursive query/callback paths; obtain original-runtime and other-platform differential fixtures. Rendering geometry and Rust/Bevy/Solari conversion remain separate open work.
|
| 59 |
+
|
| 60 |
+
Follow-up: [leaf references and area portal state](BSP_REFERENCES.md) connects projected reference words to consumers and bounds local connectivity/portal behavior. Complete actual loading/validation/ownership and recursive flooding remain open under RE-324/RE-328.
|
docs/research/BSP_REFERENCES.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Leaf references, connectivity and portal counters
|
| 2 |
+
|
| 3 |
+
Reproduce with `python3 scripts/analyze_bsp_references.py`; validate with `python3 scripts/test_bsp_references.py`. Evidence: [bsp-references.json](bsp-references.json), with eight hash-verified native graphs per Linux ELF64 engine. This extends [BSP loading](BSP_LOADING.md) and [leaf metadata](LEAF_METADATA.md).
|
| 4 |
+
|
| 5 |
+
These are bounded synthetic instructions with supplied allocation returns, arrays, pointers and area labels. Original processes, allocations, errors, geometry callees and flood callees never execute. Portal PUSH instructions are explicitly replaced with NOPs at the same addresses; execution stops before POP/return, error calls or the first flood call. Connectivity starts after frame setup and stops before stack restoration. The retained flooding graphs are static evidence, not executed compositions.
|
| 6 |
+
|
| 7 |
+
## REF-001 — Reference arrays and leaf consumers (RE-325)
|
| 8 |
+
|
| 9 |
+
Forty-eight paired reference-loader probes cover counts 0–9, 16 and 17, including every copy remainder and eight-word loop paths. CMod_LoadLeafBrushes requests `4 * (count + 1)` bytes; CMod_LoadLeafSurfaces requests `4 * count`. Both publish supplied allocation pointers and counts, preserve all original words in order, and omit payload copies for zero count. Repeated IDs and -1 words are copied unchanged; these deliberately supplied IDs do not establish legal map values. The spare brush word is untouched under the supplied sentinel initialization. Alignment/error paths are retained but not evaluated.
|
| 10 |
+
|
| 11 |
+
No direct calls to either standalone helper appear in the retained CM_LoadMap graph. Actual inline-loading equivalence and descriptor routing for these arrays remain open; standalone helper behavior is not evidence of complete original map loading.
|
| 12 |
+
|
| 13 |
+
The leaf consumer slices connect the prior disk/native projection to index arithmetic:
|
| 14 |
+
|
| 15 |
+
| Native leaf offsets | Observed consumer relationship |
|
| 16 |
+
| --- | --- |
|
| 17 |
+
| +8 first, +12 count | brush-reference array; signed referenced ID selects `brush_base + 56 * ID` |
|
| 18 |
+
| +16 first, +20 count | surface-reference array; signed referenced ID selects an eight-byte pointer-table entry |
|
| 19 |
+
|
| 20 |
+
Forty-eight paired address slices vary first index, ordinal and supplied IDs -1/0/7. Brush slices stop before the record stamp comparison. Surface slices stop before the selected pointer's NULL test. Both read `reference[first + ordinal]`; no local index bounds checks occur inside these selected arithmetic slices. Negative-ID fixtures use explicitly supplied memory and are not valid-map or safety demonstrations.
|
| 21 |
+
|
| 22 |
+
The full retained consumer graphs show brush iteration before curve/surface iteration, reference-count gates, stamp checks, content tests and geometry calls. Those bodies/order/state effects are not composed in this report. The 56-byte brush record stride is native ELF64 layout, not a brush disk-record size or Bevy asset layout.
|
| 23 |
+
|
| 24 |
+
## REF-002 — Area connectivity predicate (RE-326)
|
| 25 |
+
|
| 26 |
+
Four hundred fifty paired probes cover supplied area counts 0/1/3, both indices -1 through 3, and cm_noAreas raw words 0/1/7. The observed priority is:
|
| 27 |
+
|
| 28 |
+
1. Nonzero cm_noAreas value returns true before index validation.
|
| 29 |
+
2. Either negative area returns false, before either upper bound is checked.
|
| 30 |
+
3. A nonnegative area >= area count requests Com_Error with `area >= cm.numAreas`.
|
| 31 |
+
4. Valid areas compare the first word of their eight-byte native records for equality.
|
| 32 |
+
|
| 33 |
+
Supplied labels `[1,1,2]` establish connected versus disconnected outcomes. The record's second word is not used by this predicate. The base/count slots match the preceding leaf loader's area allocation/count state. This is label comparison, not a portal walk, line-of-sight test or independently verified connectivity generation. Label initialization/refresh must be established before applying this rule to real snapshots.
|
| 34 |
+
|
| 35 |
+
## REF-003 — Portal mutation and reflood boundary (RE-327)
|
| 36 |
+
|
| 37 |
+
Eight hundred paired probes use three areas, indices -1 through 3, open words 0/1/7/-1 and initial selected counter values -1/0/1/2. The matrix contains four-byte counters indexed `area1 * count + area2`; the reverse entry is addressed separately.
|
| 38 |
+
|
| 39 |
+
- Either negative index returns without mutation, even if the other index exceeds the upper bound.
|
| 40 |
+
- Nonnegative out-of-range indices request `CM_ChangeAreaPortalState: bad area number` before mutation.
|
| 41 |
+
- Every tested nonzero open word increments both directed entries; zero decrements both.
|
| 42 |
+
- Self-pairs write the same cell twice. Starting at one, open produces three and close produces -1.
|
| 43 |
+
- Closing checks the reversed entry after both decrements. Negative results request `CM_AdjustAreaPortalState: negative reference count`; both writes have already occurred at the stopped error boundary.
|
| 44 |
+
- Successful updates increment the flood epoch and begin a label rebuild. With epoch 40 and all area stamp words explicitly seeded to zero, the first request is CM_FloodArea_r(area 0, label 1) at epoch 41.
|
| 45 |
+
|
| 46 |
+
The tests compare the whole matrix, write offsets/order, guard priority, stopped error text, epoch and requested arguments. Deliberately negative initial counters characterize local paths and do not establish reachable runtime states. Error handling itself is not executed; persistence after Com_Error is not claimed.
|
| 47 |
+
|
| 48 |
+
The retained CM_FloodArea_r graph uses record +0 for the label and +4 for the epoch stamp; positive matrix entries select further visits. CM_FloodAreaConnections similarly increments the epoch and scans area indices in ascending order. Complete unrolled/recursive flooding, its revisit-label consistency errors, asymmetric matrices, stale/prestamped records, count/epoch wraparound and full update-to-query composition remain open.
|
| 49 |
+
|
| 50 |
+
## Validation and next work
|
| 51 |
+
|
| 52 |
+
Eight independent tests cover copy/allocation/canaries, signed consumer addressing, connectivity guard priority, complete sampled matrix mutations, self-pairs/nonzero open flags, prior-loader slot correspondence, paired outputs/trace lengths and low-byte register preservation. Small slices retain instruction addresses; larger collections retain trace lengths and hashes. The report does not copy any original asset into assets/.
|
| 53 |
+
|
| 54 |
+
RE-328: prove inline/reference-loading equivalence and valid reference ranges across all BSP variants; recover complete brush/surface layouts, stamps and geometry; compose full flood generation/revisit/epoch behavior and update-to-connectivity/area-bit/snapshot consumers; connect server/QVM portal writers and lifetime/reset/serialization; obtain original-runtime and other-platform fixtures. Rust/Bevy/Solari collision and rendering implementation remain open.
|
docs/research/README.md
CHANGED
|
@@ -117,3 +117,5 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 117 |
[Native BSP planes and bounded node traversal](BSP_PLANES.md) adds RE-317–319: axial/general equality rules, verified eight-case sign dispatch and one-node child order/addressing. Reproduce with `python3 scripts/analyze_bsp_planes.py`; check with `python3 scripts/test_bsp_planes.py`. Full traversal/loading/callback/float/runtime/platform contracts remain open.
|
| 118 |
|
| 119 |
[Native BSP plane, node and leaf loading](BSP_LOADING.md) adds RE-321–323: descriptor routing, bounded payload conversion, derived plane fields, native node pointers, leaf projection/maxima and allocation requests. Reproduce with `python3 scripts/analyze_bsp_loading.py`; check with `python3 scripts/test_bsp_loading.py`. Complete validation/loading/ownership/recursive-query/runtime/platform contracts remain open.
|
|
|
|
|
|
|
|
|
| 117 |
[Native BSP planes and bounded node traversal](BSP_PLANES.md) adds RE-317–319: axial/general equality rules, verified eight-case sign dispatch and one-node child order/addressing. Reproduce with `python3 scripts/analyze_bsp_planes.py`; check with `python3 scripts/test_bsp_planes.py`. Full traversal/loading/callback/float/runtime/platform contracts remain open.
|
| 118 |
|
| 119 |
[Native BSP plane, node and leaf loading](BSP_LOADING.md) adds RE-321–323: descriptor routing, bounded payload conversion, derived plane fields, native node pointers, leaf projection/maxima and allocation requests. Reproduce with `python3 scripts/analyze_bsp_loading.py`; check with `python3 scripts/test_bsp_loading.py`. Complete validation/loading/ownership/recursive-query/runtime/platform contracts remain open.
|
| 120 |
+
|
| 121 |
+
[Leaf references, connectivity and portal counters](BSP_REFERENCES.md) adds RE-325–327: bounded reference copying/consumer addressing, connectivity guard priority and portal mutation/epoch/flood-request boundaries. Reproduce with `python3 scripts/analyze_bsp_references.py`; check with `python3 scripts/test_bsp_references.py`. Full inline loading/flooding/lifecycle/geometry/runtime/platform contracts remain open.
|
docs/research/bsp-references.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_bsp_references.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Bounded reference copies/addressing, area connectivity and portal mutations."""
|
| 3 |
+
import gzip
|
| 4 |
+
import hashlib
|
| 5 |
+
import itertools
|
| 6 |
+
import json
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import re
|
| 9 |
+
from analyze_area_queries import site
|
| 10 |
+
from analyze_chat_paths import native_string
|
| 11 |
+
from analyze_collision_composition import evaluate,get_word,put_word
|
| 12 |
+
from analyze_engine_control import control_flow
|
| 13 |
+
from analyze_native import elf
|
| 14 |
+
from analyze_qvm import write_json
|
| 15 |
+
from analyze_vm_extensions import native_functions,verified
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def operand(x):return x['operands'].split('#')[0].strip()
|
| 19 |
+
def global_address(x):return int(re.search(r'#\s*0x([0-9a-f]+)',x['operands'])[1],16)
|
| 20 |
+
def trace_fields(t,full=False):return {'trace':t['trace'] if full else None,'trace_length':len(t['trace']),'trace_sha256':hashlib.sha256(json.dumps(t['trace'],separators=(',',':')).encode()).hexdigest()}
|
| 21 |
+
def run(g,start,stops,m,regs):return evaluate(g,start,stops,m,regs,limit=2048,capture_registers=True)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def errors(g,data,sections):
|
| 25 |
+
result=[];rows=g['instructions']
|
| 26 |
+
for i,x in enumerate(rows):
|
| 27 |
+
if '<Com_Error>' not in x['operands']:continue
|
| 28 |
+
fmt=next(y for y in reversed(rows[max(0,i-4):i]) if y['opcode']=='movl' and operand(y).endswith(', %esi'))
|
| 29 |
+
result.append({'call':x['address'],'text':native_string(data,sections,int(operand(fmt).split(',')[0][1:],0))})
|
| 30 |
+
return result
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def main():
|
| 34 |
+
out={'native':{},'limitations':'Synthetic supplied-allocation copies and stopped instruction slices. '
|
| 35 |
+
'Original processes, allocations, errors, collision callees and flooding never execute. '
|
| 36 |
+
'Reference array IDs, pointer validity, caller/inline loader equivalence, reset/zeroing, '
|
| 37 |
+
'full flood/portal lifecycle, snapshots and original-runtime/all-platform conformance remain open.'}
|
| 38 |
+
source,output,area_base,matrix,cvar=0x10000,0x30000,0x50000,0x60000,0x70000
|
| 39 |
+
names=['CMod_LoadLeafBrushes','CMod_LoadLeafSurfaces','CM_TraceThroughLeaf','CM_TestInLeaf','CM_AreasConnected','CM_AdjustAreaPortalState','CM_FloodAreaConnections','CM_FloodArea_r']
|
| 40 |
+
for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
|
| 41 |
+
r=json.loads(Path(f'docs/research/native/{label}.json').read_text());data=verified((Path('.cache/UrbanTerror43')/r['source']['path']).read_bytes(),r['source']['sha256'])
|
| 42 |
+
a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode());metadata,_=elf(data);graphs={n:control_flow(fs[n]) for n in names}
|
| 43 |
+
references={}
|
| 44 |
+
for name in names[:2]:
|
| 45 |
+
g=graphs[name];rows=g['instructions'];alloc=next(i for i,x in enumerate(rows) if '<Hunk_Alloc>' in x['operands']);start=rows[alloc+1]['address'];end=site(g,'addq','$0x8, %rsp')
|
| 46 |
+
allocation_start=site(g,'leal','0x1(%rbx), %edi') if name.endswith('Brushes') else site(g,'movslq','%ebx, %rdi')
|
| 47 |
+
count_slot=global_address(next(x for x in rows if x['opcode']=='movl' and '(%rip)' in x['operands'] and operand(x).startswith('%ebx,')))
|
| 48 |
+
base_slot=global_address(next(x for x in rows if x['opcode']=='movq' and '(%rip)' in x['operands'] and operand(x).startswith('%rax,')))
|
| 49 |
+
probes=[]
|
| 50 |
+
for count in list(range(10))+[16,17]:
|
| 51 |
+
m={};words=[[-1,0,7,7,2][i%5]&0xffffffff for i in range(count)]
|
| 52 |
+
for i,v in enumerate(words):put_word(m,source+4*i,v)
|
| 53 |
+
for i in range(4*(count+1)+2):m[output-1+i]=0xa5
|
| 54 |
+
allocation=run(g,allocation_start,[rows[alloc]['address']],m,{'%rbx':count})
|
| 55 |
+
t=run(g,start,[end],m,{'%rax':output,'%rbx':count,'%rbp':count,'%r13':0,'%r12':source})
|
| 56 |
+
probes.append({'count':count,'input_words':words,'output_words':[get_word(t['memory'],output+4*i) for i in range(count)],'allocation_bytes':allocation['registers']['rdi'],
|
| 57 |
+
'count_word':get_word(t['memory'],count_slot),'base_word':get_word(t['memory'],base_slot,8),'canaries':[t['memory'][output-1],t['memory'][output+4*count]],'writes':[w for w in t['writes'] if output<=w['address']<output+4*(count+1)],**trace_fields(t,count<=3)})
|
| 58 |
+
references[name]={'count_slot':count_slot,'base_slot':base_slot,'probes':probes,'errors':errors(g,data,metadata['sections']),
|
| 59 |
+
'direct_CM_LoadMap_calls':[x['address'] for x in control_flow(fs['CM_LoadMap'])['instructions'] if '<'+name+'>' in x['operands']]}
|
| 60 |
+
# Connect leaf fields to the first indexed brush and surface address calculations.
|
| 61 |
+
consumers=[];leaf,brush_base,patch_table=0x80000,0x90000,0xa0000
|
| 62 |
+
g=graphs['CM_TraceThroughLeaf'];rows=g['instructions'];start=next(x['address'] for x in rows if x['opcode']=='movl' and operand(x)=='%ebp, %eax')
|
| 63 |
+
# Two MOVL EBP,EAX sites exist (brush and surface); select the first explicitly.
|
| 64 |
+
brush_stop=next(x['address'] for x in rows if x['opcode']=='cmpl' and operand(x)=='%eax, 0x30(%rbx)')
|
| 65 |
+
brush_base_slot=global_address(next(x for x in rows if x['opcode']=='addq' and '(%rip)' in x['operands'] and operand(x).endswith(', %rbx')))
|
| 66 |
+
brush_refs_slot=references['CMod_LoadLeafBrushes']['base_slot']
|
| 67 |
+
stamp_load=next(x for x in rows if x['opcode']=='movl' and '(%rip)' in x['operands'] and operand(x).endswith(', %eax'))
|
| 68 |
+
for first,index,value in itertools.product([0,2],[0,1],[-1,0,7]):
|
| 69 |
+
m={};put_word(m,leaf+8,first);put_word(m,brush_refs_slot,source,8);put_word(m,source+4*(first+index),value);put_word(m,brush_base_slot,brush_base,8);put_word(m,global_address(stamp_load),41)
|
| 70 |
+
t=run(g,start,[brush_stop],m,{'%rbp':index,'%r12':leaf})
|
| 71 |
+
consumers.append({'kind':'brush','first':first,'ordinal':index,'supplied_id':value,'pointer':t['registers']['rbx'],**trace_fields(t,True)})
|
| 72 |
+
g=graphs['CM_TestInLeaf'];rows=g['instructions'];start=site(g,'movl','%ebp, %eax');end=site(g,'testq','%rbx, %rbx')
|
| 73 |
+
patch_slot=global_address(next(x for x in rows if x['opcode']=='movq' and '(%rip)' in x['operands'] and operand(x).endswith(', %r9')))
|
| 74 |
+
for first,index,value in itertools.product([0,2],[0,1],[-1,0,7]):
|
| 75 |
+
m={};put_word(m,leaf+16,first);put_word(m,references['CMod_LoadLeafSurfaces']['base_slot'],source,8);put_word(m,source+4*(first+index),value);put_word(m,patch_slot,patch_table,8);put_word(m,patch_table+8*value,0xb0000+16*(value+1),8)
|
| 76 |
+
t=run(g,start,[end],m,{'%rbp':index,'%r13':leaf})
|
| 77 |
+
consumers.append({'kind':'surface','first':first,'ordinal':index,'supplied_id':value,'pointer':t['registers']['rbx'],**trace_fields(t,True)})
|
| 78 |
+
# Area predicate: start after frame setup, stop before stack restore or error call.
|
| 79 |
+
g=graphs['CM_AreasConnected'];rows=g['instructions'];start=next(x['address'] for x in rows if x['opcode']=='movq' and '(%rip)' in x['operands']);end=site(g,'movq','0x8(%rsp), %rbx');error=errors(g,data,metadata['sections'])
|
| 80 |
+
noareas_slot=global_address(next(x for x in rows if x['opcode']=='movq' and '(%rip)' in x['operands']));count_slot=global_address(next(x for x in rows if x['opcode']=='movl' and '(%rip)' in x['operands']));area_slot=global_address(next(x for x in rows if x['opcode']=='movq' and '(%rip)' in x['operands'] and operand(x).endswith(', %rsi')))
|
| 81 |
+
connected=[]
|
| 82 |
+
for count,a1,a2,disabled in itertools.product([0,1,3],[-1,0,1,2,3],[-1,0,1,2,3],[0,1,7]):
|
| 83 |
+
m={};put_word(m,noareas_slot,cvar,8);put_word(m,cvar+48,disabled);put_word(m,count_slot,count);put_word(m,area_slot,area_base,8)
|
| 84 |
+
for i,v in enumerate([1,1,2]):put_word(m,area_base+8*i,v);put_word(m,area_base+8*i+4,999)
|
| 85 |
+
t=run(g,start,[end]+[e['call'] for e in error],m,{'%rdi':a1,'%rsi':a2})
|
| 86 |
+
connected.append({'area_count':count,'area1':a1,'area2':a2,'disabled_word':disabled,'error':t['stop']!=end,'result_word':t['registers']['rdx'] if t['stop']==end else None,**trace_fields(t,count==3 and disabled==0 and a1==0)})
|
| 87 |
+
# Portal updates: replace only PUSH frame traffic; stop before POP, errors or flooding.
|
| 88 |
+
g=graphs['CM_AdjustAreaPortalState'];rows=g['instructions'];model=dict(g);model['instructions']=[dict(x) for x in rows];omitted=[]
|
| 89 |
+
for x in model['instructions']:
|
| 90 |
+
if x['opcode']=='pushq':omitted.append(x['address']);x['opcode']='nop';x['operands']=''
|
| 91 |
+
if len(omitted)!=3:raise ValueError('portal frame changed')
|
| 92 |
+
end=next(x['address'] for x in rows if x['opcode']=='popq');flood=next(x['address'] for x in rows if '<CM_FloodArea_r>' in x['operands']);error2=errors(g,data,metadata['sections']);matrix_slot=global_address(next(x for x in rows if x['opcode']=='addq' and '(%rip)' in x['operands']));epoch_slot=global_address(next(x for x in rows if x['opcode']=='addl' and '(%rip)' in x['operands']))
|
| 93 |
+
portal=[]
|
| 94 |
+
for a1,a2,opened,initial in itertools.product([-1,0,1,2,3],[-1,0,1,2,3],[0,1,7,-1],[-1,0,1,2]):
|
| 95 |
+
count=3;m={};put_word(m,count_slot,count);put_word(m,matrix_slot,matrix,8);put_word(m,area_slot,area_base,8);put_word(m,epoch_slot,40)
|
| 96 |
+
for i in range(9):put_word(m,matrix+4*i,0)
|
| 97 |
+
for i in range(3):put_word(m,area_base+8*i,0);put_word(m,area_base+8*i+4,0)
|
| 98 |
+
if 0<=a1<count and 0<=a2<count:
|
| 99 |
+
for i in [a1*count+a2,a2*count+a1]:put_word(m,matrix+4*i,initial)
|
| 100 |
+
t=run(model,model['entry'],[end,flood]+[e['call'] for e in error2],m,{'%rdi':a1,'%rsi':a2,'%rdx':opened})
|
| 101 |
+
outcome='return' if t['stop']==end else 'flood_request' if t['stop']==flood else 'error'
|
| 102 |
+
portal.append({'area1':a1,'area2':a2,'open_word':opened,'initial_counter':initial,'outcome':outcome,'error_text':next((e['text'] for e in error2 if e['call']==t['stop']),None),
|
| 103 |
+
'matrix_words':[get_word(t['memory'],matrix+4*i) for i in range(9)],'matrix_writes':[w['address']-matrix for w in t['writes'] if matrix<=w['address']<matrix+36], 'epoch_word':get_word(t['memory'],epoch_slot),
|
| 104 |
+
'flood_arguments':[t['registers']['rdi'],t['registers']['rsi']] if outcome=='flood_request' else None,**trace_fields(t,a1==0 and a2==1 and opened in [0,1] and initial==1)})
|
| 105 |
+
out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],'graphs':graphs,'references':references,'consumer_probes':consumers,
|
| 106 |
+
'area_slots':{'count':count_slot,'base':area_slot,'noareas_cvar_pointer':noareas_slot,'matrix':matrix_slot,'epoch':epoch_slot},'connectivity_errors':error,'connectivity_probes':connected,
|
| 107 |
+
'portal_errors':error2,'portal_omitted_pushes':omitted,'portal_probes':portal}
|
| 108 |
+
write_json(Path('docs/research/bsp-references.json'),out)
|
| 109 |
+
print('Recovered 48 reference copies, 48 consumer-address, 450 connectivity and 800 portal probes')
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
if __name__=='__main__':main()
|
scripts/analyze_collision_composition.py
CHANGED
|
@@ -14,7 +14,7 @@ from analyze_vm_extensions import native_functions,verified
|
|
| 14 |
|
| 15 |
def register(r):
|
| 16 |
aliases={'eax':('rax',32),'edi':('rdi',32),'esi':('rsi',32),'edx':('rdx',32),
|
| 17 |
-
'ecx':('rcx',32),'ebp':('rbp',32),'ebx':('rbx',32),'al':('rax',8),'sil':('rsi',8),'dl':('rdx',8)}
|
| 18 |
r=r.lstrip('%')
|
| 19 |
if r in aliases:return aliases[r]
|
| 20 |
if re.fullmatch(r'r\d+d',r):return r[:-1],32
|
|
|
|
| 14 |
|
| 15 |
def register(r):
|
| 16 |
aliases={'eax':('rax',32),'edi':('rdi',32),'esi':('rsi',32),'edx':('rdx',32),
|
| 17 |
+
'ecx':('rcx',32),'ebp':('rbp',32),'ebx':('rbx',32),'al':('rax',8),'sil':('rsi',8),'dl':('rdx',8),'cl':('rcx',8),'dil':('rdi',8)}
|
| 18 |
r=r.lstrip('%')
|
| 19 |
if r in aliases:return aliases[r]
|
| 20 |
if re.fullmatch(r'r\d+d',r):return r[:-1],32
|
scripts/test_bsp_references.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Independent reference, connectivity and portal mutation oracles."""
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import unittest
|
| 6 |
+
from analyze_collision_composition import evaluate
|
| 7 |
+
from test_bsp_planes import graph
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class BspReferenceTests(unittest.TestCase):
|
| 11 |
+
@classmethod
|
| 12 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/bsp-references.json').read_text())
|
| 13 |
+
|
| 14 |
+
def test_reference_copy_allocation_and_canaries(self):
|
| 15 |
+
for n in self.r['native'].values():
|
| 16 |
+
for name,info in n['references'].items():
|
| 17 |
+
self.assertEqual(len(info['probes']),12)
|
| 18 |
+
self.assertEqual(info['direct_CM_LoadMap_calls'],[])
|
| 19 |
+
for p in info['probes']:
|
| 20 |
+
self.assertEqual(p['output_words'],p['input_words']);self.assertEqual(p['count_word'],p['count']);self.assertEqual(p['base_word'],0x30000)
|
| 21 |
+
self.assertEqual(p['allocation_bytes'],4*(p['count']+int(name.endswith('Brushes'))))
|
| 22 |
+
self.assertEqual(p['canaries'],[0xa5,0xa5]);self.assertEqual(p['writes'],[{'address':0x30000+4*i,'bytes':4} for i in range(p['count'])])
|
| 23 |
+
|
| 24 |
+
def test_consumer_signed_addressing(self):
|
| 25 |
+
for n in self.r['native'].values():
|
| 26 |
+
self.assertEqual(len(n['consumer_probes']),24)
|
| 27 |
+
for p in n['consumer_probes']:
|
| 28 |
+
expected=0x90000+56*p['supplied_id'] if p['kind']=='brush' else 0xb0000+16*(p['supplied_id']+1)
|
| 29 |
+
self.assertEqual(p['pointer'],expected)
|
| 30 |
+
|
| 31 |
+
def test_connectivity_bypass_negative_and_upper_bound_priority(self):
|
| 32 |
+
labels=[1,1,2]
|
| 33 |
+
for n in self.r['native'].values():
|
| 34 |
+
self.assertEqual(len(n['connectivity_probes']),225)
|
| 35 |
+
for p in n['connectivity_probes']:
|
| 36 |
+
a,b,c=p['area1'],p['area2'],p['area_count']
|
| 37 |
+
error=p['disabled_word']==0 and a>=0 and b>=0 and (a>=c or b>=c)
|
| 38 |
+
result=None if error else 1 if p['disabled_word'] else 0 if min(a,b)<0 else int(labels[a]==labels[b])
|
| 39 |
+
self.assertEqual(p['error'],error);self.assertEqual(p['result_word'],result)
|
| 40 |
+
|
| 41 |
+
def test_portal_pair_counters_errors_epoch_and_flood_request(self):
|
| 42 |
+
for n in self.r['native'].values():
|
| 43 |
+
self.assertEqual(len(n['portal_probes']),400);self.assertEqual(len(n['portal_omitted_pushes']),3)
|
| 44 |
+
for p in n['portal_probes']:
|
| 45 |
+
a,b=p['area1'],p['area2'];valid=0<=a<3 and 0<=b<3;m=[0]*9;writes=[];epoch=40;request=None;text=None
|
| 46 |
+
if valid:
|
| 47 |
+
for i in [3*a+b,3*b+a]:m[i]=p['initial_counter']&0xffffffff
|
| 48 |
+
if min(a,b)<0:outcome='return'
|
| 49 |
+
elif not valid:outcome='error';text='CM_ChangeAreaPortalState: bad area number'
|
| 50 |
+
else:
|
| 51 |
+
delta=1 if p['open_word'] else -1
|
| 52 |
+
for i in [3*a+b,3*b+a]:m[i]=(m[i]+delta)&0xffffffff;writes.append(4*i)
|
| 53 |
+
final=m[3*b+a];negative=final>=2**31
|
| 54 |
+
if not p['open_word'] and negative:outcome='error';text='CM_AdjustAreaPortalState: negative reference count'
|
| 55 |
+
else:outcome='flood_request';epoch=41;request=[0,1]
|
| 56 |
+
self.assertEqual(p['matrix_words'],m);self.assertEqual(p['matrix_writes'],writes);self.assertEqual(p['outcome'],outcome);self.assertEqual(p['error_text'],text);self.assertEqual(p['epoch_word'],epoch);self.assertEqual(p['flood_arguments'],request)
|
| 57 |
+
|
| 58 |
+
def test_self_pair_mutates_twice_and_nonzero_open_values_agree(self):
|
| 59 |
+
for n in self.r['native'].values():
|
| 60 |
+
rows=n['portal_probes']
|
| 61 |
+
selected=lambda opened:next(p for p in rows if p['area1']==p['area2']==0 and p['initial_counter']==1 and p['open_word']==opened)
|
| 62 |
+
self.assertEqual(selected(1)['matrix_words'][0],3)
|
| 63 |
+
self.assertEqual(selected(0)['matrix_words'][0],0xffffffff)
|
| 64 |
+
for v in [7,-1]:
|
| 65 |
+
self.assertEqual(selected(v)['matrix_words'],selected(1)['matrix_words']);self.assertEqual(selected(v)['outcome'],'flood_request')
|
| 66 |
+
|
| 67 |
+
def test_slots_connect_existing_leaf_loading_state(self):
|
| 68 |
+
prior=json.loads(Path('docs/research/bsp-loading.json').read_text())
|
| 69 |
+
for label,n in self.r['native'].items():
|
| 70 |
+
info=prior['native'][label]['loaders']['CMod_LoadLeafs'];self.assertEqual(n['area_slots']['count'],info['maxima_slots']['area'])
|
| 71 |
+
self.assertEqual(n['area_slots']['base']+8,n['area_slots']['matrix'])
|
| 72 |
+
self.assertEqual(n['references']['CMod_LoadLeafBrushes']['base_slot']+16,n['references']['CMod_LoadLeafSurfaces']['base_slot'])
|
| 73 |
+
|
| 74 |
+
def test_paired_outputs_and_trace_lengths(self):
|
| 75 |
+
a,b=self.r['native'].values()
|
| 76 |
+
def clean(rows):return [{k:v for k,v in p.items() if k not in ['trace','trace_sha256']} for p in rows]
|
| 77 |
+
for key in ['consumer_probes','connectivity_probes','portal_probes']:self.assertEqual(clean(a[key]),clean(b[key]))
|
| 78 |
+
for name in a['references']:self.assertEqual(clean(a['references'][name]['probes']),clean(b['references'][name]['probes']))
|
| 79 |
+
for n in self.r['native'].values():
|
| 80 |
+
for key in ['consumer_probes','connectivity_probes','portal_probes']:
|
| 81 |
+
for p in n[key]:
|
| 82 |
+
if p['trace'] is not None:self.assertEqual(len(p['trace']),p['trace_length'])
|
| 83 |
+
|
| 84 |
+
def test_byte_alias_preservation_and_unknown_flags(self):
|
| 85 |
+
g=graph([('movb','$0x7, %dil'),('cmpl','$0x1, %eax'),('sete','%cl'),('movzbl','%cl, %edx'),('nop','')])
|
| 86 |
+
for v in [0,1,0xffffffff]:
|
| 87 |
+
t=evaluate(g,0,[4],{}, {'%rdi':0x123400,'%rcx':0x567800,'%rax':v},capture_registers=True)
|
| 88 |
+
self.assertEqual(t['registers']['rdi'],0x123407);self.assertEqual(t['registers']['rcx'],0x567800+int(v==1));self.assertEqual(t['registers']['rdx'],int(v==1))
|
| 89 |
+
with self.assertRaises(ValueError):evaluate(graph([('sete','%cl')]),0,[1],{}, {'%rcx':0})
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
if __name__=='__main__':unittest.main()
|