aot: transfer_entries.exclude_writes keeps package-written data out of the transfer inventory

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-09-26 16:41:46 -07:00
parent ed07541f13
commit 4b4c4b8453
3 changed files with 29 additions and 1 deletions
+1 -1
View File
@@ -60,7 +60,7 @@ Profiles declare these reusable methods:
| `images[].method = fixed_address_files` | Whole original files placed at a verified `load_addr`. Optional duplicate-leaf checks require identical bytes. |
| `images[].method = fixed_address_extents` | Declared `{file, file_offset, base, address, size, sha256}` byte extents placed at a verified `load_addr`. Offset rules match `words` checks; size and SHA-256 are required. |
| `mod_packages[]` / `mod_package` | A named, verified selection of a trusted mod package. Images and checks carrying `mod_package` read the original disc with that selection applied; see below. |
| `images[].transfer_entries` | `{from: "mod_package_writes", count}`: J/JAL targets inside the image, taken only from words the selected package wrote. The count pins the inventory. |
| `images[].transfer_entries` | `{from: "mod_package_writes", count}`: J/JAL targets inside the image, taken only from words the selected package wrote. The count pins the inventory. Optional `exclude_writes: [{start, end, reason}]` names intervals of package-written DATA (for example a pack linked into the EXE that the package replaced) whose words are not transfers. |
| `images[].method = tagged_relocated_files` | Apply a trailing two-bit ABS32/HI16/LO16/J26 relocation stream to a verified `load_addr`, retaining only the original loader's image interval. Destinations are explicit evidence, never inferred from a historical RAM capture. |
| `images[].method = psx_exe` | Read the load address and image from a PS-X EXE header; the generic extractor may split resident and overlay floors. |
| `images[].method = packed_sector_members` | Decode u32 count/offset sector descriptors across ordered payload files. Verify every member classification and requested full-payload coverage; use loader-established addresses for executable members. |
+12
View File
@@ -133,13 +133,25 @@ def transfer_entries(source, view):
A detour farm is entered only through the package's patched transfers, so
those instructions establish its entry points statically. Unpatched text is
excluded: stock data words that happen to decode as a jump are not evidence.
Neither are package-written DATA words (for example a pack linked into the
EXE that the package replaced): `exclude_writes` names such intervals of
the loaded image, each with a reason, and their words are not transfers.
"""
spec = source['spec']['transfer_entries']
require(spec.get('from') == 'mod_package_writes' and hasattr(view, 'written_words'),
'transfer_entries requires a mod package view')
excluded = []
for item in spec.get('exclude_writes', []):
start, end = number(item['start']), number(item['end'])
require(start < end and start % 4 == end % 4 == 0,
'Invalid excluded write interval')
require(item.get('reason', '').strip(), 'Excluded write interval needs a reason')
excluded.append((start, end))
lo, hi = source['base'], source['base'] + len(source['body'])
found = set()
for address, word in view.written_words():
if any(start <= address < end for start, end in excluded):
continue
if word >> 26 in (2, 3):
target = ((address + 4) & 0xF0000000) | ((word & 0x3FFFFFF) << 2)
if lo <= target < hi:
+16
View File
@@ -245,6 +245,22 @@ class ModPackageImageTest(unittest.TestCase):
self.assertTrue(record['strict_producer_ranges'])
self.assertEqual(record['producer_ranges'], [dict(start='0x80780000', end='0x80780020')])
def test_excluded_package_writes_are_not_transfer_entries(self):
# The detour at 0x80010040 lies in an interval the profile declares as
# package-written data; only the call at 0x80010048 still counts.
exclude = [dict(start='0x80010040', end='0x80010048', reason='written data')]
inventory = self.prepare(self.profile(transfer_entries={
'from': 'mod_package_writes', 'count': 1, 'exclude_writes': exclude}))
self.assertEqual(inventory['jobs'][0]['required_entries'], [self.ENGINE])
for item, error in [(dict(start='0x80010040', end='0x80010048'), 'needs a reason'),
(dict(start='0x80010042', end='0x80010048', reason='x'),
'Invalid excluded write'),
(dict(start='0x80010048', end='0x80010040', reason='x'),
'Invalid excluded write')]:
with self.subTest(error=error), self.assertRaisesRegex(ValueError, error):
self.prepare(self.profile(transfer_entries={
'from': 'mod_package_writes', 'count': 1, 'exclude_writes': [item]}))
def test_extent_evidence_and_inventory_drift_fail_closed(self):
for changes, error in [(dict(sha256='0' * 64), 'Extent bytes changed'),
(dict(transfer_entries={'from': 'mod_package_writes', 'count': 3}),