Recover float transport and finite angle message codecs
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/MESSAGE_NUMERIC.md +48 -0
- docs/research/MESSAGE_STRINGS.md +2 -0
- docs/research/README.md +2 -0
- docs/research/message-numeric.json +0 -0
- scripts/analyze_collision_composition.py +26 -1
- scripts/analyze_message_numeric.py +91 -0
- scripts/test_message_numeric.py +78 -0
docs/ASSET_PIPELINE_TODO.md
CHANGED
|
@@ -232,3 +232,5 @@ Research progress: [message width codecs](research/MESSAGE_WIDTHS.md) adds bound
|
|
| 232 |
Research progress: [message wrappers](research/MESSAGE_WRAPPERS.md) adds scalar OOB and data overflow contracts (RE-373–375). Broader codec/packet conversion milestones remain open; RE-376 tracks compressed/read/string/delta/snapshot and runtime/platform validation.
|
| 233 |
|
| 234 |
Research progress: [message strings and data reads](research/MESSAGE_STRINGS.md) adds OOB reader/cursor/string contracts (RE-377–379). RE-380 retains compressed/library/caller/higher-level packet/runtime/platform work; no broad network conversion milestone is complete.
|
|
|
|
|
|
|
|
|
| 232 |
Research progress: [message wrappers](research/MESSAGE_WRAPPERS.md) adds scalar OOB and data overflow contracts (RE-373–375). Broader codec/packet conversion milestones remain open; RE-376 tracks compressed/read/string/delta/snapshot and runtime/platform validation.
|
| 233 |
|
| 234 |
Research progress: [message strings and data reads](research/MESSAGE_STRINGS.md) adds OOB reader/cursor/string contracts (RE-377–379). RE-380 retains compressed/library/caller/higher-level packet/runtime/platform work; no broad network conversion milestone is complete.
|
| 235 |
+
|
| 236 |
+
Research progress: [numeric message codecs](research/MESSAGE_NUMERIC.md) adds raw float and finite angle contracts (RE-381–383). RE-384 retains compressed/delta/caller/numeric-control/packet/runtime/platform validation; broad codec conversion milestones remain open.
|
docs/FINDINGS.md
CHANGED
|
@@ -426,3 +426,9 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
|
|
| 426 |
**F-143 — Observed string readers:** ordinary/line/big readers terminate on consumed NUL/EOF, with line also consuming newline; capacities store at most 1023/1023/8191 bytes. They return recovered static buffers and replace percent/high bytes with periods. Caller lifetime/compressed/malformed/platform coverage remains open. [TEXT-002](research/MESSAGE_STRINGS.md).
|
| 427 |
|
| 428 |
**F-144 — Observed string writers:** signed low-word length guards use limits 1024/8192; null/over-limit paths request an empty byte. Small supplied-copy fixtures sanitize high bytes, retain percent and request every byte plus NUL even after child space failure. Native library-copy/strlen, complete maximum emission and real compressed/caller/runtime behavior remain open. [TEXT-003](research/MESSAGE_STRINGS.md).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 426 |
**F-143 — Observed string readers:** ordinary/line/big readers terminate on consumed NUL/EOF, with line also consuming newline; capacities store at most 1023/1023/8191 bytes. They return recovered static buffers and replace percent/high bytes with periods. Caller lifetime/compressed/malformed/platform coverage remains open. [TEXT-002](research/MESSAGE_STRINGS.md).
|
| 427 |
|
| 428 |
**F-144 — Observed string writers:** signed low-word length guards use limits 1024/8192; null/over-limit paths request an empty byte. Small supplied-copy fixtures sanitize high bytes, retain percent and request every byte plus NUL even after child space failure. Native library-copy/strlen, complete maximum emission and real compressed/caller/runtime behavior remain open. [TEXT-003](research/MESSAGE_STRINGS.md).
|
| 429 |
+
|
| 430 |
+
**F-145 — Observed float transport:** raw low scalar bits forward as a 32-bit word; bounded OOB compositions retain even supplied nonfinite payload words without arithmetic. ReadFloat's signed post-read size failure substitutes 0xbf800000 (-1.0). Hardware nonfinite semantics/caller/compressed/runtime contracts remain open. [NUM-001](research/MESSAGE_NUMERIC.md).
|
| 431 |
+
|
| 432 |
+
**F-146 — Observed finite angle encoding:** file-backed binary32 scales, rounded multiplication, truncation and low 8/16-bit masks determine sampled OOB words. Nonfinite/conversion/control-state and caller/platform behavior remain open; absent parsed ReadAngle symbols do not prove absent decoding. [NUM-002](research/MESSAGE_NUMERIC.md).
|
| 433 |
+
|
| 434 |
+
**F-147 — Observed signed angle16 decoding:** accepted low words sign extend, scale through binary64 by 360/65536 and return binary32. Post-read EOF directly returns -360/65536, sharing a valid signed -1 angle word. Finite OOB compositions verify quantized results; full caller/compressed/runtime behavior remains open. [NUM-003](research/MESSAGE_NUMERIC.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -544,3 +544,5 @@ Research progress: [message width codecs](research/MESSAGE_WIDTHS.md) completes
|
|
| 544 |
Research progress: [message wrappers](research/MESSAGE_WRAPPERS.md) completes bounded RE-373–375. RE-376 leaves ReadData/compressed/string/delta/snapshot compositions, sentinel/lifecycle/alias and actual packet/platform validation open.
|
| 545 |
|
| 546 |
Research progress: [message strings and data reads](research/MESSAGE_STRINGS.md) completes bounded RE-377–379. RE-380 leaves compressed/library/caller-lifetime/alias/lifecycle and higher-level packet/platform contracts open.
|
|
|
|
|
|
|
|
|
| 544 |
Research progress: [message wrappers](research/MESSAGE_WRAPPERS.md) completes bounded RE-373–375. RE-376 leaves ReadData/compressed/string/delta/snapshot compositions, sentinel/lifecycle/alias and actual packet/platform validation open.
|
| 545 |
|
| 546 |
Research progress: [message strings and data reads](research/MESSAGE_STRINGS.md) completes bounded RE-377–379. RE-380 leaves compressed/library/caller-lifetime/alias/lifecycle and higher-level packet/platform contracts open.
|
| 547 |
+
|
| 548 |
+
Research progress: [numeric message codecs](research/MESSAGE_NUMERIC.md) completes bounded RE-381–383. RE-384 leaves delta/snapshot/angle callers and complete compressed/numeric-control/runtime/platform contracts open.
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -241,3 +241,7 @@ Completed bounded RE-373–375 with `analyze_message_wrappers.py`: 22 verified g
|
|
| 241 |
## 2026-10-11 — OOB data reads and message strings
|
| 242 |
|
| 243 |
Completed bounded RE-377–379 with `analyze_message_strings.py`: sixteen verified graphs across the Linux ELF64 engines, 106 actual OOB data-read compositions, ninety string-read compositions, 120 string-write compositions and 24 supplied writer length-guard probes. TEXT-001–003/F-142–144 distinguish continued EOF data requests and 0xff stores from read safety, consumed NUL/newline/EOF termination from capacity termination, static output addresses from caller lifetime, and reader percent/high-byte replacement from writer high-byte-only replacement. Complete OOB capacity fixtures recover 1023/1023/8191-byte ordinary/line/big outputs, terminators, unread following bytes and canaries. Writer strlen returns and Q_strncpyz copy/NUL effects are supplied explicitly; native sanitation and byte requests compose OOB child stores, including overflow continuation. File-backed empty-byte offsets are retained; over-limit and signed low-word guard probes do not claim legal huge strings or library conformance. Save/restore/PUSH/POP omissions, disjoint virtual frames and explicit caller-register retention are documented; no original process or native callee executes. Eight independent tests verify data requests/cursors/EOF prefixes and tails, string termination/sanitation/capacity/pointers/control bytes, writer copy/NUL/capacity effects, signed length/empty-byte boundaries and paired outcomes. All 366 tests pass; new and preceding reports regenerate byte-identically, with storage/link/whitespace checks passing. RE-376 remains open; RE-380 tracks compressed/library/caller lifetime/alias/nonterminated/overflow/reset/truncation/cursor contracts, float/angle/delta/entity/player/snapshot codecs, native seeded codewords and actual packet/runtime/all-platform/renderer validation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
## 2026-10-11 — OOB data reads and message strings
|
| 242 |
|
| 243 |
Completed bounded RE-377–379 with `analyze_message_strings.py`: sixteen verified graphs across the Linux ELF64 engines, 106 actual OOB data-read compositions, ninety string-read compositions, 120 string-write compositions and 24 supplied writer length-guard probes. TEXT-001–003/F-142–144 distinguish continued EOF data requests and 0xff stores from read safety, consumed NUL/newline/EOF termination from capacity termination, static output addresses from caller lifetime, and reader percent/high-byte replacement from writer high-byte-only replacement. Complete OOB capacity fixtures recover 1023/1023/8191-byte ordinary/line/big outputs, terminators, unread following bytes and canaries. Writer strlen returns and Q_strncpyz copy/NUL effects are supplied explicitly; native sanitation and byte requests compose OOB child stores, including overflow continuation. File-backed empty-byte offsets are retained; over-limit and signed low-word guard probes do not claim legal huge strings or library conformance. Save/restore/PUSH/POP omissions, disjoint virtual frames and explicit caller-register retention are documented; no original process or native callee executes. Eight independent tests verify data requests/cursors/EOF prefixes and tails, string termination/sanitation/capacity/pointers/control bytes, writer copy/NUL/capacity effects, signed length/empty-byte boundaries and paired outcomes. All 366 tests pass; new and preceding reports regenerate byte-identically, with storage/link/whitespace checks passing. RE-376 remains open; RE-380 tracks compressed/library/caller lifetime/alias/nonterminated/overflow/reset/truncation/cursor contracts, float/angle/delta/entity/player/snapshot codecs, native seeded codewords and actual packet/runtime/all-platform/renderer validation.
|
| 244 |
+
|
| 245 |
+
## 2026-10-11 — Float word transport and finite angle codecs
|
| 246 |
+
|
| 247 |
+
Completed bounded RE-381–383 with `analyze_message_numeric.py`: fourteen verified graphs across the Linux ELF64 engines, 48 raw float OOB compositions, 360 supplied float-reader tails, sixty finite angle-writer compositions and 210 supplied angle16-reader tails. Thirty angle16 compositions also decode exact writer bytes through OOB ReadBits and angle16 instructions. NUM-001–003/F-145–147 distinguish raw float bit transport (including supplied nonfinite payload words, without arithmetic) from numerical conversion, the float EOF word -1.0 from angle16's -360/65536 fallback, and finite scaled/truncated/masked angle output from unrestricted numeric/runtime behavior. Four file-backed constants per engine retain exact bytes/offsets; signed low-word angle decoding uses binary64 scaling before binary32 return, yielding a signed quantized domain. Scalar evaluator extensions are restricted to DX and finite/int32 conversion and low-double operations; exceptional/unsupported conversions fail closed. Frame-save/restore/PUSH/POP omissions, stopped helper/tail boundaries and explicit caller retention are documented; no original process or native callee executes. Eight independent tests verify raw payload/EOF transport, signed cursor guards, constants, struct-rounded finite encoding, signed decoding/fallback, exact angle16 OOB compositions, paired outcomes and restricted conversion/fail-closed behavior. All 374 tests pass; new and preceding reports regenerate byte-identically, and storage/link/whitespace checks pass. RE-380 remains open; RE-384 tracks delta/key/float/usercmd/entity/player/snapshot codecs, angle callers/inlined byte decoding, compressed/nonfinite/out-of-range/MXCSR/FTZ/DAZ/caller/lifecycle/truncation/cursor contracts, native seeded codewords and actual packet/runtime/all-platform/renderer conformance.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -457,3 +457,8 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 457 |
- [x] RE-378 Recover bounded ordinary/big/line string termination, percent/high-byte sanitation, static output and full capacity fixtures ([TEXT-002](research/MESSAGE_STRINGS.md)).
|
| 458 |
- [x] RE-379 Recover writer length guards/null/over-limit paths and compose small supplied-copy sanitation/NUL OOB emission ([TEXT-003](research/MESSAGE_STRINGS.md)).
|
| 459 |
- [ ] RE-380 Compose compressed scalar/data/string codecs and real library-copy/length behavior; recover string caller lifetime/limits/alias/nonterminated/overflow/reset/truncation/cursor invariants, float/angle/delta/entity/player/snapshot codecs, native seeded codewords and actual packet/runtime/all-platform/renderer contracts.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 457 |
- [x] RE-378 Recover bounded ordinary/big/line string termination, percent/high-byte sanitation, static output and full capacity fixtures ([TEXT-002](research/MESSAGE_STRINGS.md)).
|
| 458 |
- [x] RE-379 Recover writer length guards/null/over-limit paths and compose small supplied-copy sanitation/NUL OOB emission ([TEXT-003](research/MESSAGE_STRINGS.md)).
|
| 459 |
- [ ] RE-380 Compose compressed scalar/data/string codecs and real library-copy/length behavior; recover string caller lifetime/limits/alias/nonterminated/overflow/reset/truncation/cursor invariants, float/angle/delta/entity/player/snapshot codecs, native seeded codewords and actual packet/runtime/all-platform/renderer contracts.
|
| 460 |
+
|
| 461 |
+
- [x] RE-381 Recover raw float transport and bounded OOB/post-read EOF tails, including supplied nonfinite payload words ([NUM-001](research/MESSAGE_NUMERIC.md)).
|
| 462 |
+
- [x] RE-382 Recover file-backed angle scales and bounded finite rounding/truncation/masking with OOB output ([NUM-002](research/MESSAGE_NUMERIC.md)).
|
| 463 |
+
- [x] RE-383 Recover signed angle16 conversion/EOF fallback and compose finite OOB writer/reader paths ([NUM-003](research/MESSAGE_NUMERIC.md)).
|
| 464 |
+
- [ ] RE-384 Recover delta/key/float/usercmd/entity/player/snapshot codecs and angle callers/inlined byte-angle decoding; validate compressed numeric/nonfinite/out-of-range/MXCSR/FTZ/DAZ/caller-sentinel/lifecycle/truncation/cursor contracts, native seeded codewords and actual packet/runtime/all-platform/renderer conformance.
|
docs/research/MESSAGE_NUMERIC.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Raw float transport and finite angle codecs
|
| 2 |
+
|
| 3 |
+
Reproduce with `python3 scripts/analyze_message_numeric.py`; validate with `python3 scripts/test_message_numeric.py`. [message-numeric.json](message-numeric.json) retains fourteen source/disassembly-hash-verified graphs across the Linux ELF64 dedicated/client engines, 48 float OOB compositions, 360 supplied float-reader tails, sixty finite angle-writer compositions and 210 supplied angle16-reader tails. Thirty of the angle-writer compositions also decode their exact OOB output through ReadBits and ReadAngle16 instructions. Four file-backed numeric constants per engine retain addresses, offsets and exact bytes.
|
| 4 |
+
|
| 5 |
+
Original processes and native callees never execute. These are synthetic instruction compositions with explicit virtual memory/frames, stopped CALL/tail-jump boundaries and caller-register retention. Frame saves/restores and PUSH/POP are omitted/stopped. Supplied reader-tail return/count probes are separate from actual OOB writer/reader compositions. Float transport uses raw moves without numeric arithmetic. Angle arithmetic is restricted to finite in-range inputs and modeled ordinary scalar rounding; MXCSR, FTZ/DAZ, exceptions, nonfinite/out-of-range conversion and CPU/platform/runtime equivalence remain open. No compressed/full-packet conformance is established.
|
| 6 |
+
|
| 7 |
+
## NUM-001 — Float word transport and post-read EOF (RE-381)
|
| 8 |
+
|
| 9 |
+
MSG_WriteFloat moves the supplied low scalar word into a stack slot, reloads it as an integer and tail-forwards MSG_WriteBits(message, word, 32). It does not numerically convert the float to an integer. The recovered instruction slice therefore transports the raw 32-bit payload word.
|
| 10 |
+
|
| 11 |
+
The sampled words include positive/negative zero, ordinary finite values, the largest finite binary32 word, a subnormal word, infinity words and multiple NaN payload words. Only move/store/load instructions operate on these words. This is synthetic raw-bit transport evidence, not execution of nonfinite floating arithmetic or a complete hardware NaN contract.
|
| 12 |
+
|
| 13 |
+
Each engine has 24 OOB compositions: twelve raw words at capacities three/64. Ample capacity writes four little-endian bytes; the actual OOB reader consumes the same word and the Float reader tail returns its low scalar bits unchanged. Guarded capacity three sets writer overflow and emits nothing. The explicitly padded backing bytes remain 0xa5; the underlying OOB reader still advances readcount four and bit thirty-two, then ReadFloat substitutes the constant word 0xbf800000 (-1.0) because readcount exceeds cursize.
|
| 14 |
+
|
| 15 |
+
Another 180 tail probes per engine supply twelve raw words, size words zero/four/0xffffffff and post-read count words zero/four/five/0x80000000/0xffffffff. The native signed comparison returns the supplied float bits when post-read count <= size; otherwise it returns 0xbf800000. A valid -1.0 payload shares the EOF fallback bits. Caller interpretation, legal wrapping cursors, access safety and compressed reads remain open.
|
| 16 |
+
|
| 17 |
+
## NUM-002 — Finite angle writers (RE-382)
|
| 18 |
+
|
| 19 |
+
The recovered file-backed binary32 scales are:
|
| 20 |
+
|
| 21 |
+
| Writer | Scale bytes (little-endian) | Nominal expression |
|
| 22 |
+
| --- | --- | --- |
|
| 23 |
+
| MSG_WriteAngle | 610b363f | binary32(256 / 360) |
|
| 24 |
+
| MSG_WriteAngle16 | 610b3643 | binary32(65536 / 360) |
|
| 25 |
+
|
| 26 |
+
Each writer multiplies the supplied binary32 angle by its scale, converts the resulting scalar to a signed integer by truncation toward zero, masks that integer to eight/sixteen bits, and tail-forwards positive width eight/sixteen to WriteBits. The native scale is the rounded binary32 constant; the product is also modeled with binary32 rounding before truncation. Integer masking gives wrap behavior after conversion. It does not establish arbitrary input normalization or valid caller angle ranges.
|
| 27 |
+
|
| 28 |
+
Thirty finite writer fixtures per engine cover both widths, positive/negative zero, positive/negative one, ±45.125, ±90, 180, 270, 359.9, ±360, 720 and 65535 degrees. All scaled values are within the modeled signed conversion range. Independent struct-based float rounding and truncation oracles verify the intermediate scalar word, forwarded integer/width, little-endian OOB payload, size and bit cursor.
|
| 29 |
+
|
| 30 |
+
Examples: 90 maps to byte 64/short 16384; -90 maps to byte 192/short 49152; 180 maps to byte 128/short 32768; ±360 and 720 mask to zero in these fixtures. Nonfinite, conversion overflow, altered rounding modes, denormal handling and actual angle caller quantization remain unverified.
|
| 31 |
+
|
| 32 |
+
The parsed function sets contain MSG_WriteAngle but no MSG_ReadAngle symbol. This does not establish that eight-bit angle decoding is absent from inlined code, other binaries or platforms.
|
| 33 |
+
|
| 34 |
+
## NUM-003 — Signed angle16 reads and fallback (RE-383)
|
| 35 |
+
|
| 36 |
+
MSG_ReadAngle16 requests positive width sixteen, then performs a signed post-read count/size check. Accepted reads sign extend the low sixteen-bit word, convert that integer to scalar binary64, multiply by the file-backed binary64 constant 360/65536, and convert the result to binary32. The multiplier bytes are 000000000080763f. This maps the accepted local domain to [-180, 180), with the high half of unsigned sixteen-bit input represented as negative angles.
|
| 37 |
+
|
| 38 |
+
For example, 0x4000 returns +90; 0x8000 returns -180; 0xc000 returns -90; 0xffff returns -0.0054931640625. A failed post-read size check returns a file-backed binary32 constant directly: bytes 0000b4bb, also -0.0054931640625. This EOF fallback is therefore the same angle word as a valid signed short -1; it is not the float reader's -1.0 fallback. Caller sentinel handling remains open.
|
| 39 |
+
|
| 40 |
+
Each image has 105 supplied-tail probes across seven low/full input words, three size words and five count words, including signed-negative count/size boundaries. Every sampled finite WriteAngle16 OOB output is also read through actual ReadBits instructions and the angle16 tail, verifying the signed decoded domain and exact read count/bit updates. These are quantized results, not exact restoration of arbitrary input degrees.
|
| 41 |
+
|
| 42 |
+
The shared restricted evaluator adds DX, finite in-range CVTTSS2SI, signed-int32 CVTSI2SD, finite low-scalar MULSD and CVTSD2SS support. A dedicated independent fixture verifies register/memory conversion and truncation; NaN, infinity and out-of-range float-to-int inputs fail closed. Other double/vector lanes and machine floating control state remain unsupported/unmodeled.
|
| 43 |
+
|
| 44 |
+
## Validation and next work
|
| 45 |
+
|
| 46 |
+
Eight independent tests verify raw float words/OOB payloads, float signed EOF tails, exact file-backed constants, finite angle scale/round/truncate/mask, signed angle16 decoding/fallback, angle16 OOB compositions, paired engine outcomes and the restricted evaluator extension. The report retains compact composed instruction trace hashes.
|
| 47 |
+
|
| 48 |
+
RE-384: recover delta/key/float/usercmd/entity/player/snapshot codecs and angle callers/inlined byte-angle decoding; validate compressed numeric wrappers, nonfinite/out-of-range/MXCSR/FTZ/DAZ conversion behavior, caller sentinel/lifecycle/truncation/cursor contracts, native seeded codewords and actual packet/runtime/all-platform/renderer conformance.
|
docs/research/MESSAGE_STRINGS.md
CHANGED
|
@@ -41,3 +41,5 @@ The normal reader and writer differ: the writer retains percent, while the reade
|
|
| 41 |
Eight independent tests verify actual data-read requests/cursors and EOF prefixes; continuation/unchanged tails/canaries; string termination/sanitation; full capacity/return-pointer behavior; percent/high-byte/CR/newline cases; writer copied-byte/NUL/capacity effects; signed length guards/file-backed empty bytes; and paired engine outcomes. No evaluator extension was needed.
|
| 42 |
|
| 43 |
RE-380: compose compressed scalar/data/string codecs and real library-copy/length behavior; recover string caller buffer lifetime/limits, alias/nonterminated/overflow/allowoverflow/reset/truncation/cursor invariants, float/angle/delta/entity/player/snapshot codecs, native full-seed codewords and actual packet/runtime/all-platform/renderer contracts.
|
|
|
|
|
|
|
|
|
| 41 |
Eight independent tests verify actual data-read requests/cursors and EOF prefixes; continuation/unchanged tails/canaries; string termination/sanitation; full capacity/return-pointer behavior; percent/high-byte/CR/newline cases; writer copied-byte/NUL/capacity effects; signed length guards/file-backed empty bytes; and paired engine outcomes. No evaluator extension was needed.
|
| 42 |
|
| 43 |
RE-380: compose compressed scalar/data/string codecs and real library-copy/length behavior; recover string caller buffer lifetime/limits, alias/nonterminated/overflow/allowoverflow/reset/truncation/cursor invariants, float/angle/delta/entity/player/snapshot codecs, native full-seed codewords and actual packet/runtime/all-platform/renderer contracts.
|
| 44 |
+
|
| 45 |
+
Follow-up: [numeric message codecs](MESSAGE_NUMERIC.md) recovers bounded float and finite angle wrappers. Broader RE-380 remains open; RE-384 tracks delta/angle callers and complete numeric/packet/platform contracts.
|
docs/research/README.md
CHANGED
|
@@ -145,3 +145,5 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 145 |
[Scalar message wrappers and data overflow](MESSAGE_WRAPPERS.md) adds RE-373–375: fixed-width forwarding, scalar conversion/post-read EOF, and data-writer continuation after child space failure. Reproduce with `python3 scripts/analyze_message_wrappers.py`; check with `python3 scripts/test_message_wrappers.py`. ReadData, compressed/higher-level codecs and actual packet/runtime/platform coverage remain open.
|
| 146 |
|
| 147 |
[OOB data reads and message strings](MESSAGE_STRINGS.md) adds RE-377–379: actual ReadData cursor/EOF composition, ordinary/big/line string read limits/sanitation, and bounded supplied-copy string writing. Reproduce with `python3 scripts/analyze_message_strings.py`; check with `python3 scripts/test_message_strings.py`. Compressed/library/caller/higher-level and real packet/runtime/platform contracts remain open.
|
|
|
|
|
|
|
|
|
| 145 |
[Scalar message wrappers and data overflow](MESSAGE_WRAPPERS.md) adds RE-373–375: fixed-width forwarding, scalar conversion/post-read EOF, and data-writer continuation after child space failure. Reproduce with `python3 scripts/analyze_message_wrappers.py`; check with `python3 scripts/test_message_wrappers.py`. ReadData, compressed/higher-level codecs and actual packet/runtime/platform coverage remain open.
|
| 146 |
|
| 147 |
[OOB data reads and message strings](MESSAGE_STRINGS.md) adds RE-377–379: actual ReadData cursor/EOF composition, ordinary/big/line string read limits/sanitation, and bounded supplied-copy string writing. Reproduce with `python3 scripts/analyze_message_strings.py`; check with `python3 scripts/test_message_strings.py`. Compressed/library/caller/higher-level and real packet/runtime/platform contracts remain open.
|
| 148 |
+
|
| 149 |
+
[Raw float transport and finite angle codecs](MESSAGE_NUMERIC.md) adds RE-381–383: raw float OOB/EOF behavior, finite angle encoding and signed angle16 reads. Reproduce with `python3 scripts/analyze_message_numeric.py`; check with `python3 scripts/test_message_numeric.py`. Delta/caller/control-state/compressed and actual packet/runtime/platform contracts remain open.
|
docs/research/message-numeric.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_collision_composition.py
CHANGED
|
@@ -6,6 +6,7 @@ import json
|
|
| 6 |
import math
|
| 7 |
from pathlib import Path
|
| 8 |
import re
|
|
|
|
| 9 |
from analyze_chat_crosshair import bits,number
|
| 10 |
from analyze_engine_control import control_flow
|
| 11 |
from analyze_qvm import write_json
|
|
@@ -13,7 +14,7 @@ from analyze_vm_extensions import native_functions,verified
|
|
| 13 |
|
| 14 |
|
| 15 |
def register(r):
|
| 16 |
-
aliases={'eax':('rax',32),'ax':('rax',16),'edi':('rdi',32),'esi':('rsi',32),'edx':('rdx',32),
|
| 17 |
'ecx':('rcx',32),'ebp':('rbp',32),'ebx':('rbx',32),'bx':('rbx',16),'bl':('rbx',8),'al':('rax',8),'sil':('rsi',8),'dl':('rdx',8),'cl':('rcx',8),'dil':('rdi',8),'bpl':('rbp',8)}
|
| 18 |
r=r.lstrip('%')
|
| 19 |
if r in aliases:return aliases[r]
|
|
@@ -144,6 +145,30 @@ def evaluate(graph,start,stops,memory,registers,limit=128,capture_registers=Fals
|
|
| 144 |
elif op in ['addss','subss','mulss']:
|
| 145 |
a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4))
|
| 146 |
write(b,bits(w+v if op=='addss' else w-v if op=='subss' else w*v),4)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
elif op=='comiss':
|
| 148 |
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=signed_lt=negative=None
|
| 149 |
elif op in ['sete','setne','setb','setbe','setae']:
|
|
|
|
| 6 |
import math
|
| 7 |
from pathlib import Path
|
| 8 |
import re
|
| 9 |
+
import struct
|
| 10 |
from analyze_chat_crosshair import bits,number
|
| 11 |
from analyze_engine_control import control_flow
|
| 12 |
from analyze_qvm import write_json
|
|
|
|
| 14 |
|
| 15 |
|
| 16 |
def register(r):
|
| 17 |
+
aliases={'eax':('rax',32),'ax':('rax',16),'dx':('rdx',16),'edi':('rdi',32),'esi':('rsi',32),'edx':('rdx',32),
|
| 18 |
'ecx':('rcx',32),'ebp':('rbp',32),'ebx':('rbx',32),'bx':('rbx',16),'bl':('rbx',8),'al':('rax',8),'sil':('rsi',8),'dl':('rdx',8),'cl':('rcx',8),'dil':('rdi',8),'bpl':('rbp',8)}
|
| 19 |
r=r.lstrip('%')
|
| 20 |
if r in aliases:return aliases[r]
|
|
|
|
| 145 |
elif op in ['addss','subss','mulss']:
|
| 146 |
a,b=args.split(', ');v,w=number(read(a,4)),number(read(b,4))
|
| 147 |
write(b,bits(w+v if op=='addss' else w-v if op=='subss' else w*v),4)
|
| 148 |
+
elif op=='cvttss2si':
|
| 149 |
+
a,b=args.split(', ')
|
| 150 |
+
if register(b)[1]!=32:raise ValueError('unsupported non-int32 scalar conversion')
|
| 151 |
+
v=number(read(a,4));value=int(v)
|
| 152 |
+
if not -2**31<=value<2**31:raise ValueError('unsupported out-of-range scalar conversion')
|
| 153 |
+
write(b,value,4) # Finite truncation only; exception/MXCSR state unmodeled.
|
| 154 |
+
elif op=='cvtsi2sd':
|
| 155 |
+
a,b=args.split(', ')
|
| 156 |
+
if a.startswith('%') and register(a)[1]!=32:raise ValueError('unsupported non-int32 double conversion')
|
| 157 |
+
v=read(a,4)&0xffffffff;v=v if v<2**31 else v-2**32
|
| 158 |
+
regs[register(b)[0]]=struct.unpack('<Q',struct.pack('<d',float(v)))[0]
|
| 159 |
+
elif op in ['mulsd','cvtsd2ss']:
|
| 160 |
+
def scalar_double(arg):
|
| 161 |
+
if arg.startswith('%') and register(arg)[0] not in regs:raise ValueError('unknown scalar double register')
|
| 162 |
+
word=regs[register(arg)[0]] if arg.startswith('%') else read(arg,8)
|
| 163 |
+
v=struct.unpack('<d',struct.pack('<Q',word&((1<<64)-1)))[0]
|
| 164 |
+
if not math.isfinite(v):raise ValueError('unsupported nonfinite scalar double')
|
| 165 |
+
return v
|
| 166 |
+
a,b=args.split(', ');v=scalar_double(a)
|
| 167 |
+
if op=='mulsd':
|
| 168 |
+
result=scalar_double(b)*v
|
| 169 |
+
if not math.isfinite(result):raise ValueError('unsupported nonfinite scalar double result')
|
| 170 |
+
regs[register(b)[0]]=struct.unpack('<Q',struct.pack('<d',result))[0]
|
| 171 |
+
else:write(b,bits(v),4)
|
| 172 |
elif op=='comiss':
|
| 173 |
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=signed_lt=negative=None
|
| 174 |
elif op in ['sete','setne','setb','setbe','setae']:
|
scripts/analyze_message_numeric.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Raw float transport and bounded finite angle instruction compositions."""
|
| 3 |
+
import copy
|
| 4 |
+
import gzip
|
| 5 |
+
import itertools
|
| 6 |
+
import json
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import re
|
| 9 |
+
from analyze_bsp_loading import read_address
|
| 10 |
+
from analyze_bsp_references import global_address, trace_fields
|
| 11 |
+
from analyze_chat_crosshair import bits
|
| 12 |
+
from analyze_collision_composition import get_word, put_word
|
| 13 |
+
from analyze_engine_control import control_flow
|
| 14 |
+
from analyze_message_byte_codec import MSG
|
| 15 |
+
from analyze_message_widths import reader, run, writer
|
| 16 |
+
from analyze_message_wrappers import message_memory, WRAPPER_STACK
|
| 17 |
+
from analyze_native import elf
|
| 18 |
+
from analyze_qvm import write_json
|
| 19 |
+
from analyze_vm_extensions import native_functions, verified
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
WORDS=[0,0x80000000,0x3f800000,0xbf800000,0x3fc00000,0x7f7fffff,1,0x7f800000,0xff800000,0x7fc01234,0x7f801234,0xffffffff]
|
| 23 |
+
ANGLES=[0.0,-0.0,1.0,-1.0,45.125,-45.125,90.0,-90.0,180.0,270.0,359.9,360.0,-360.0,720.0,65535.0]
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def frame_graph(g):
|
| 27 |
+
g=copy.deepcopy(g);omitted=[]
|
| 28 |
+
for x in g['instructions']:
|
| 29 |
+
if x['opcode'] in ['pushq','popq'] or (x['opcode']=='movq' and '(%rsp)' in x['operands']):
|
| 30 |
+
omitted.append(x['address']);x['opcode']='nop';x['operands']=''
|
| 31 |
+
return g,omitted
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def read_float(g,m,value):
|
| 35 |
+
g,omitted=frame_graph(g);rows=g['instructions'];ci=next(i for i,x in enumerate(rows) if '<MSG_ReadBits>' in x['operands']);end=next(x['address'] for x in rows if x['opcode']=='retq')
|
| 36 |
+
# Start after the helper: supply its return and retained message/frame.
|
| 37 |
+
t=run(g,rows[ci+1]['address'],[end],m,{'%rax':value,'%rbx':MSG,'%rsp':WRAPPER_STACK})
|
| 38 |
+
return t['registers']['xmm0'],t['trace'],omitted
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def read_angle(g,m,value):
|
| 42 |
+
g,omitted=frame_graph(g);rows=g['instructions'];ci=next(i for i,x in enumerate(rows) if '<MSG_ReadBits>' in x['operands']);ends=[x['address'] for x in rows if x['opcode']=='retq']
|
| 43 |
+
t=run(g,rows[ci+1]['address'],ends,m,{'%rax':value,'%rbx':MSG})
|
| 44 |
+
return t['registers']['xmm0'],t['trace'],omitted
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def main():
|
| 48 |
+
out={'native':{},'limitations':'Synthetic Linux ELF64 instruction compositions only. Original processes and native callees never execute. Float transport evaluates raw moves, including nonfinite payload words, not floating arithmetic. Angle arithmetic is restricted to supplied finite in-range inputs, signed int32 conversion, scalar binary64/32 operations under modeled ordinary rounding; exception/MXCSR/FTZ/DAZ/NaN/overflow behavior unmodeled. OOB child writer/reader instructions composed with explicit caller-register retention; frame saves/restores/PUSH/POP omitted/stopped. Supplied tail return/count probes are distinct from actual OOB compositions. Compressed/full-packet/runtime/all-platform conformance remains open.'}
|
| 49 |
+
for label in ['linux--Quake3-UrT-Ded.x86_64','linux--Quake3-UrT.x86_64']:
|
| 50 |
+
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']);meta,_=elf(data);a=r['artifacts']['disassembly.txt'];fs=native_functions(verified(gzip.open(a['path'],'rb').read(),a['uncompressed_sha256']).decode());names=['MSG_WriteFloat','MSG_ReadFloat','MSG_WriteAngle','MSG_WriteAngle16','MSG_ReadAngle16','MSG_WriteBits','MSG_ReadBits'];graphs={n:control_flow(fs[n]) for n in names};constants=[];constant_memory={}
|
| 51 |
+
for name in ['MSG_WriteAngle','MSG_WriteAngle16','MSG_ReadAngle16']:
|
| 52 |
+
for x in graphs[name]['instructions']:
|
| 53 |
+
if '(%rip)' not in x['operands']:continue
|
| 54 |
+
size=8 if x['opcode']=='mulsd' else 4;address=global_address(x);raw,offset=read_address(data,meta['sections'],address,size)
|
| 55 |
+
for i,v in enumerate(raw):constant_memory[address+i]=v
|
| 56 |
+
constants.append({'function':name,'opcode':x['opcode'],'address':address,'file_offset':offset,'bytes_hex':raw.hex()})
|
| 57 |
+
floats=[];tails=[]
|
| 58 |
+
wg=graphs['MSG_WriteFloat'];jump=next(x['address'] for x in wg['instructions'] if x['opcode']=='jmp')
|
| 59 |
+
for word,capacity in itertools.product(WORDS,[3,64]):
|
| 60 |
+
t=run(wg,wg['instructions'][0]['address'],[jump],{}, {'%rdi':MSG,'%rsp':WRAPPER_STACK,'%xmm0':word});args=[t['registers'][k] for k in ['rdi','rsi','rdx']];m=message_memory(capacity)
|
| 61 |
+
for x in graphs['MSG_WriteBits']['instructions']:
|
| 62 |
+
if x['opcode']=='addl' and '(%rip)' in x['operands']:put_word(m,global_address(x),0)
|
| 63 |
+
m,requests,error,wt=writer(graphs,m,args[2],args[1]);size=get_word(m,MSG+28);bit=get_word(m,MSG+36);payload=bytes(m[0xa0000+i] for i in range(size)).hex();flag=get_word(m,MSG+4);put_word(m,MSG+32,0);put_word(m,MSG+36,7)
|
| 64 |
+
m,raw,read_error,rt=reader(graphs,m,32);result,tail,omitted=read_float(graphs['MSG_ReadFloat'],m,raw)
|
| 65 |
+
floats.append({'input_word':word,'capacity':capacity,'write_arguments':args,'payload_hex':payload,'write_size':size,'write_bit':bit,'read_count':get_word(m,MSG+32),'read_bit':get_word(m,MSG+36),'raw_read_word':raw,'result_word':result,'overflow_flag':flag,'omitted_frame_instructions':omitted,**trace_fields({'trace':t['trace']+wt+rt+tail},False)})
|
| 66 |
+
if requests or error or read_error:raise ValueError('unexpected float OOB helper')
|
| 67 |
+
for word,size,count in itertools.product(WORDS,[0,4,0xffffffff],[0,4,5,0x80000000,0xffffffff]):
|
| 68 |
+
m={};put_word(m,MSG+28,size);put_word(m,MSG+32,count);result,trace,omitted=read_float(graphs['MSG_ReadFloat'],m,word);tails.append({'supplied_word':word,'size_word':size,'post_count_word':count,'result_word':result,**trace_fields({'trace':trace},False)})
|
| 69 |
+
writes=[]
|
| 70 |
+
for name,angle in itertools.product(['MSG_WriteAngle','MSG_WriteAngle16'],ANGLES):
|
| 71 |
+
g=graphs[name];jump=next(x['address'] for x in g['instructions'] if x['opcode']=='jmp');word=bits(angle)&0xffffffff;t=run(g,g['instructions'][0]['address'],[jump],constant_memory,{'%xmm0':word,'%rdi':MSG});args=[t['registers'][k] for k in ['rdi','rsi','rdx']];m=message_memory(64)
|
| 72 |
+
for x in graphs['MSG_WriteBits']['instructions']:
|
| 73 |
+
if x['opcode']=='addl' and '(%rip)' in x['operands']:put_word(m,global_address(x),0)
|
| 74 |
+
m,requests,error,child=writer(graphs,m,args[2],args[1]);size=get_word(m,MSG+28);write_bit=get_word(m,MSG+36)
|
| 75 |
+
if requests or error:raise ValueError('angle OOB helper')
|
| 76 |
+
decoded=None;read_count=None;read_bit=None
|
| 77 |
+
if name=='MSG_WriteAngle16':
|
| 78 |
+
m.update(constant_memory);put_word(m,MSG+32,0);put_word(m,MSG+36,7)
|
| 79 |
+
m,raw,read_error,rt=reader(graphs,m,16);decoded,tail,_=read_angle(graphs['MSG_ReadAngle16'],m,raw);child+=rt+tail
|
| 80 |
+
if read_error:raise ValueError('angle16 OOB reader')
|
| 81 |
+
read_count=get_word(m,MSG+32);read_bit=get_word(m,MSG+36)
|
| 82 |
+
writes.append({'function':name,'input_word':word,'arguments':args,'scaled_word':t['registers']['xmm0'],'payload_hex':bytes(m[0xa0000+i] for i in range(size)).hex(),'size':size,'bit':write_bit,'angle16_read_word':decoded,'read_count':read_count,'read_bit':read_bit,**trace_fields({'trace':t['trace']+child},False)})
|
| 83 |
+
reads=[]
|
| 84 |
+
for word,size,count in itertools.product([0,1,32767,32768,65535,0x12348001,0xffffffff],[0,2,0xffffffff],[0,2,3,0x80000000,0xffffffff]):
|
| 85 |
+
m=dict(constant_memory);put_word(m,MSG+28,size);put_word(m,MSG+32,count);result,trace,omitted=read_angle(graphs['MSG_ReadAngle16'],m,word);reads.append({'supplied_word':word,'size_word':size,'post_count_word':count,'result_word':result,'omitted_frame_instructions':omitted,**trace_fields({'trace':trace},False)})
|
| 86 |
+
out['native'][label]={'source':r['source'],'disassembly_sha256':a['uncompressed_sha256'],'graphs':graphs,'file_backed_constants':constants,'float_oob':floats,'float_tails':tails,'angle_writes':writes,'angle16_tails':reads,'read_angle_symbol_present':'MSG_ReadAngle' in fs}
|
| 87 |
+
write_json(Path('docs/research/message-numeric.json'),out)
|
| 88 |
+
print('Recovered raw float transport and bounded finite angle conversions')
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
if __name__=='__main__':main()
|
scripts/test_message_numeric.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Independent raw-word transport, finite rounding and signed-angle oracles."""
|
| 3 |
+
import json
|
| 4 |
+
import math
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
import struct
|
| 7 |
+
import unittest
|
| 8 |
+
from analyze_collision_composition import evaluate, put_word
|
| 9 |
+
from test_bsp_planes import graph
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def f32(word):return struct.unpack('<f',struct.pack('<I',word&0xffffffff))[0]
|
| 13 |
+
def word32(value):return struct.unpack('<I',struct.pack('<f',value))[0]
|
| 14 |
+
def signed(word,bits=32):return ((word&((1<<bits)-1))^(1<<(bits-1)))-(1<<(bits-1))
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class MessageNumericTests(unittest.TestCase):
|
| 18 |
+
@classmethod
|
| 19 |
+
def setUpClass(cls):cls.r=json.loads(Path('docs/research/message-numeric.json').read_text())
|
| 20 |
+
|
| 21 |
+
def test_float_raw_words_and_oob_bytes_including_nonfinite_payloads(self):
|
| 22 |
+
for n in self.r['native'].values():
|
| 23 |
+
self.assertEqual(len(n['float_oob']),24)
|
| 24 |
+
for p in n['float_oob']:
|
| 25 |
+
accepted=p['capacity']>3;word=p['input_word'];self.assertEqual(p['write_arguments'],[0x90000,word,32]);self.assertEqual(p['payload_hex'],word.to_bytes(4,'little').hex() if accepted else '');self.assertEqual(p['write_size'],4 if accepted else 0);self.assertEqual(p['write_bit'],39 if accepted else 7);self.assertEqual(p['read_count'],4);self.assertEqual(p['read_bit'],39);self.assertEqual(p['raw_read_word'],word if accepted else 0xa5a5a5a5);self.assertEqual(p['result_word'],word if accepted else 0xbf800000);self.assertEqual(p['overflow_flag'],int(not accepted))
|
| 26 |
+
|
| 27 |
+
def test_float_post_read_signed_eof_word(self):
|
| 28 |
+
for n in self.r['native'].values():
|
| 29 |
+
self.assertEqual(len(n['float_tails']),180)
|
| 30 |
+
for p in n['float_tails']:
|
| 31 |
+
expected=p['supplied_word'] if signed(p['post_count_word'])<=signed(p['size_word']) else 0xbf800000
|
| 32 |
+
self.assertEqual(p['result_word'],expected)
|
| 33 |
+
|
| 34 |
+
def test_file_backed_angle_constants(self):
|
| 35 |
+
for n in self.r['native'].values():
|
| 36 |
+
expected={('MSG_WriteAngle','mulss'):struct.pack('<f',256/360).hex(),('MSG_WriteAngle16','mulss'):struct.pack('<f',65536/360).hex(),('MSG_ReadAngle16','movss'):struct.pack('<f',-360/65536).hex(),('MSG_ReadAngle16','mulsd'):struct.pack('<d',360/65536).hex()}
|
| 37 |
+
self.assertEqual({(p['function'],p['opcode']):p['bytes_hex'] for p in n['file_backed_constants']},expected)
|
| 38 |
+
for p in n['file_backed_constants']:self.assertGreaterEqual(p['file_offset'],0)
|
| 39 |
+
self.assertFalse(n['read_angle_symbol_present'])
|
| 40 |
+
|
| 41 |
+
def test_angle_finite_scale_truncate_mask_and_endian(self):
|
| 42 |
+
for n in self.r['native'].values():
|
| 43 |
+
self.assertEqual(len(n['angle_writes']),30)
|
| 44 |
+
for p in n['angle_writes']:
|
| 45 |
+
width=16 if p['function'].endswith('16') else 8;scale=f32(word32((1<<width)/360));scaled_word=word32(f32(p['input_word'])*scale);value=math.trunc(f32(scaled_word))&((1<<width)-1)
|
| 46 |
+
self.assertEqual(p['scaled_word'],scaled_word);self.assertEqual(p['arguments'],[0x90000,value,width]);self.assertEqual(p['payload_hex'],value.to_bytes(width//8,'little').hex());self.assertEqual(p['size'],width//8);self.assertEqual(p['bit'],7+width)
|
| 47 |
+
|
| 48 |
+
def test_angle16_signed_word_double_scale_and_eof_fallback(self):
|
| 49 |
+
for n in self.r['native'].values():
|
| 50 |
+
self.assertEqual(len(n['angle16_tails']),105)
|
| 51 |
+
for p in n['angle16_tails']:
|
| 52 |
+
accepted=signed(p['post_count_word'])<=signed(p['size_word']);value=signed(p['supplied_word'],16) if accepted else -1
|
| 53 |
+
self.assertEqual(p['result_word'],word32(value*(360/65536)))
|
| 54 |
+
|
| 55 |
+
def test_angle16_oob_roundtrip_signed_domain(self):
|
| 56 |
+
for n in self.r['native'].values():
|
| 57 |
+
for p in n['angle_writes']:
|
| 58 |
+
if not p['function'].endswith('16'):
|
| 59 |
+
self.assertIsNone(p['angle16_read_word']);continue
|
| 60 |
+
value=p['arguments'][1];self.assertEqual(p['angle16_read_word'],word32(signed(value,16)*(360/65536)));self.assertEqual(p['read_count'],2);self.assertEqual(p['read_bit'],23)
|
| 61 |
+
|
| 62 |
+
def test_paired_results(self):
|
| 63 |
+
a,b=self.r['native'].values()
|
| 64 |
+
for key in ['float_oob','float_tails','angle_writes','angle16_tails']:
|
| 65 |
+
def clean(rows):return [{k:v for k,v in p.items() if k not in ['trace','trace_sha256','omitted_frame_instructions']} for p in rows]
|
| 66 |
+
self.assertEqual(clean(a[key]),clean(b[key]))
|
| 67 |
+
|
| 68 |
+
def test_restricted_finite_scalar_conversion_and_fail_closed(self):
|
| 69 |
+
g=graph([('cvttss2si','%xmm0, %esi'),('cvtsi2sd','%esi, %xmm1'),('mulsd','(%rdi), %xmm1'),('cvtsd2ss','%xmm1, %xmm0'),('nop','')]);m={};put_word(m,100,struct.unpack('<Q',struct.pack('<d',0.25))[0],8)
|
| 70 |
+
t=evaluate(g,0,[4],m,{'%xmm0':word32(-7.75),'%rdi':100},capture_registers=True);self.assertEqual(t['registers']['rsi'],(-7)&0xffffffff);self.assertEqual(t['registers']['xmm0'],word32(-1.75))
|
| 71 |
+
for word in [word32(2**31),0x7fc01234,0x7f800000]:
|
| 72 |
+
with self.assertRaises(ValueError):evaluate(g,0,[4],m,{'%xmm0':word,'%rdi':100},capture_registers=True)
|
| 73 |
+
|
| 74 |
+
for bad,regs in [([('cvttss2si','%xmm0, %rsi'),('nop','')],{'%xmm0':word32(1)}),([('cvtsi2sd','%rsi, %xmm1'),('nop','')],{'%rsi':1})]:
|
| 75 |
+
with self.assertRaises(ValueError):evaluate(graph(bad),0,[1],{},regs,capture_registers=True)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
if __name__=='__main__':unittest.main()
|