XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
"""Pure offline resource-ownership and release negative controls; no native pass claimed."""
import copy
import unittest

from NativeDependentSlotEvidence import ClosedCycleOwnershipTracker, checked_address, parse_symbols, symbol_address
from TestEditorStability import (closed_cycle_release_observation, closed_cycle_physical_budget,
                                require_no_closed_cycle_handle_growth)


TLS_MT = 0xabc000


def owner(generic, index, base, target):
    return {'ClosedT': generic, 'ModuleIndex': 0, 'TypeIndex': index, 'TypeBase': base,
            'Target': target, 'RootKind': 'SchedulerThread', 'ThreadAddress': 0x1000,
            'ThreadId': 5, 'RegistryIndex': 3}


def add_tls(value, generic='INode', index=1, address=40, target=0x40000, base=0xb000):
    runtime = value['RuntimeTlsPools']; root = runtime['ThreadRootSnapshots'][0]
    row = root['TypeStorageRows'][0]
    while len(row['DataWords']) <= index: row['DataWords'].append(0)
    row['Length'] = len(row['DataWords']); row['DataWords'][index] = base
    field = owner(generic, index, base, target); root['PoolFields'].append(copy.deepcopy(field))
    slot = {'SlotAddress': address, 'Target': target, 'Dependent': 0, 'ClearedTombstone': False,
            'TargetObject': {'EEType': TLS_MT}}
    value['Slots'].append(slot)
    runtime['TlsArrayBodies'].append({'SlotAddress': address, 'TlsArray': target, 'Body': {'Length': 27},
        'ThreadOwners': [field], 'PoolRegistryOwners': [{'ClosedT': generic, 'Index': 1, 'HashCode': 7,
            'DependentHandle': address, 'SlotTarget': target, 'SlotDependent': 0}]})
    value['AllocatedTypedSlots'] = value['AliveCounterSum'] = len(value['Slots'])
    return value


def baseline():
    root = {'RootKind': 'SchedulerThread', 'ThreadAddress': 0x1000, 'ThreadId': 5, 'RegistryIndex': 3,
            'TlsRoot': 0x8000, 'ModuleStorage': {'Address': 0x8000, 'Length': 1, 'DataWords': [0x9000]},
            'TypeStorageRows': {0: {'Address': 0x9000, 'Length': 2, 'DataWords': [0, 0]}}, 'PoolFields': []}
    value = {'SupportedTlsArrayEEType': TLS_MT, 'AllocatedTypedSlots': 2, 'AliveCounterSum': 2,
        'Slots': [{'SlotAddress': 10, 'Target': 0x111, 'Dependent': 0x222, 'ClearedTombstone': False, 'TargetObject': {'EEType': 1}},
                  {'SlotAddress': 20, 'Target': 0, 'Dependent': 0, 'ClearedTombstone': True, 'TargetObject': {'Null': True}}],
        'RuntimeTlsPools': {'DescriptorCount': 2,
            'Descriptors': [{'ClosedT': 'Char', 'ModuleIndex': 0, 'TypeIndex': 0},
                            {'ClosedT': 'INode', 'ModuleIndex': 0, 'TypeIndex': 1}],
            'ThreadRootSnapshots': [root, {'RootKind': 'BootstrapFallback', 'ThreadId': None, 'TlsRoot': 0, 'PoolFields': []}],
            'TlsArrayBodies': []}}
    return add_tls(value, 'Char', 0, 30, 0x30000, 0xa000)


class NativeOwnershipGuards(unittest.TestCase):
    def qualify_change(self, changed):
        tracker = ClosedCycleOwnershipTracker(); tracker.qualify(baseline())
        return tracker.qualify(changed)

    def test_same_thread_same_t_first_nil_to_root_qualifies_but_raw_counter_stays_false(self):
        initial = baseline(); changed = add_tls(copy.deepcopy(initial))
        tracker = ClosedCycleOwnershipTracker(); tracker.qualify(initial)
        result = tracker.qualify(changed)
        self.assertTrue(result['Passed'])
        self.assertEqual(1, len(result['ProvenFirstUseRuntimeTlsInitializations']))
        self.assertEqual(2, result['FixedNonTlsSlots'])
        self.assertFalse(result['WarmBaselineRolled'])
        with self.assertRaises(AssertionError): require_no_closed_cycle_handle_growth(4, 3)

    def test_fake_tls_type_missing_root_or_wrong_cwt_never_qualifies(self):
        for corruption in ('type', 'root', 't', 'handle', 'target', 'free_entry'):
            value = add_tls(baseline()); body = value['RuntimeTlsPools']['TlsArrayBodies'][-1]
            if corruption == 'type': value['Slots'][-1]['TargetObject']['EEType'] = 0xBAD
            if corruption == 'root': body['ThreadOwners'] = []
            if corruption == 't': body['PoolRegistryOwners'][0]['ClosedT'] = 'Char'
            if corruption == 'handle': body['PoolRegistryOwners'][0]['DependentHandle'] = 999
            if corruption == 'target': body['PoolRegistryOwners'][0]['SlotTarget'] = 999
            if corruption == 'free_entry': body['PoolRegistryOwners'][0]['HashCode'] = -1
            with self.subTest(corruption=corruption), self.assertRaises(AssertionError): self.qualify_change(value)

    def test_dead_or_changed_thread_identity_is_rejected(self):
        for change in ('remove', 'id', 'address', 'new'):
            value = baseline(); roots = value['RuntimeTlsPools']['ThreadRootSnapshots']
            if change == 'remove': roots.pop(0)
            if change == 'id': roots[0]['ThreadId'] = 6
            if change == 'address': roots[0]['ThreadAddress'] = 0x9999
            if change == 'new': roots.append({'RootKind': 'SchedulerThread', 'ThreadAddress': 0x9999,
                'ThreadId': 9, 'RegistryIndex': 4, 'TlsRoot': 0, 'PoolFields': []})
            with self.subTest(change=change), self.assertRaises(AssertionError): self.qualify_change(value)

    def test_existing_type_base_target_or_native_slot_replacement_fails(self):
        for change in ('base', 'target', 'slot'):
            value = baseline(); root = value['RuntimeTlsPools']['ThreadRootSnapshots'][0]; body = value['RuntimeTlsPools']['TlsArrayBodies'][0]
            if change == 'base':
                root['TypeStorageRows'][0]['DataWords'][0] = 0xc000
                root['PoolFields'][0]['TypeBase'] = body['ThreadOwners'][0]['TypeBase'] = 0xc000
            if change == 'target':
                root['PoolFields'][0]['Target'] = body['ThreadOwners'][0]['Target'] = body['TlsArray'] = value['Slots'][-1]['Target'] = 0x99999
                body['PoolRegistryOwners'][0]['SlotTarget'] = 0x99999
            if change == 'slot':
                value['Slots'][-1]['SlotAddress'] = body['SlotAddress'] = body['PoolRegistryOwners'][0]['DependentHandle'] = 99
            with self.subTest(change=change), self.assertRaises(AssertionError): self.qualify_change(value)

    def test_new_tombstone_or_non_tls_slot_or_changed_non_tls_pair_fails(self):
        for change in ('tombstone', 'new', 'pair', 'delete'):
            value = baseline()
            if change == 'tombstone': value['Slots'].append({'SlotAddress': 99, 'Target': 0, 'Dependent': 0, 'ClearedTombstone': True})
            if change == 'new': value['Slots'].append({'SlotAddress': 99, 'Target': 0x999, 'Dependent': 0x888, 'ClearedTombstone': False})
            if change == 'pair': value['Slots'][0]['Dependent'] = 0x999
            if change == 'delete': value['Slots'].pop(0)
            value['AllocatedTypedSlots'] = value['AliveCounterSum'] = len(value['Slots'])
            with self.subTest(change=change), self.assertRaises(AssertionError): self.qualify_change(value)

    def test_new_tls_target_alias_or_duplicate_t_thread_fails(self):
        value = add_tls(baseline(), target=0x30000)
        with self.assertRaises(AssertionError): self.qualify_change(value)
        value = baseline(); body = copy.deepcopy(value['RuntimeTlsPools']['TlsArrayBodies'][0])
        value['RuntimeTlsPools']['TlsArrayBodies'].append(body)
        with self.assertRaises(AssertionError): self.qualify_change(value)

    def test_storage_array_resize_preserving_every_prior_field_is_allowed(self):
        initial = baseline(); initial['RuntimeTlsPools']['ThreadRootSnapshots'][0]['TypeStorageRows'][0]['Length'] = 1
        initial['RuntimeTlsPools']['ThreadRootSnapshots'][0]['TypeStorageRows'][0]['DataWords'] = [0xa000]
        changed = add_tls(copy.deepcopy(initial)); root = changed['RuntimeTlsPools']['ThreadRootSnapshots'][0]
        root['TlsRoot'] = root['ModuleStorage']['Address'] = 0x18000
        root['ModuleStorage'].update(Length=2, DataWords=[0x19000, 0])
        root['TypeStorageRows'][0]['Address'] = 0x19000
        tracker = ClosedCycleOwnershipTracker(); tracker.qualify(initial)
        self.assertTrue(tracker.qualify(changed)['Passed'])

    def test_unsafe_resize_or_disappearing_old_field_is_rejected(self):
        changed = baseline(); root = changed['RuntimeTlsPools']['ThreadRootSnapshots'][0]
        root['ModuleStorage']['DataWords'][0] = 0x19000
        root['TypeStorageRows'][0].update(Address=0x19000, Length=1, DataWords=[0])
        root['PoolFields'] = []
        with self.assertRaises(AssertionError): self.qualify_change(changed)

    def test_recorded_historical_plus442_cycle_growth_is_not_excluded_as_runtime_tls(self):
        # These totals were read from the historical real Files-only log before
        # its build directory was unexpectedly removed. This synthetic slot
        # negative control cannot claim the lost original serial was replayed.
        for total in (1630, 2071, 2513):
            value = baseline()
            for index in range(total - 1188):
                value['Slots'].append({'SlotAddress': 100 + index, 'Target': 10000 + index,
                    'Dependent': 0x999, 'ClearedTombstone': False})
            value['AllocatedTypedSlots'] = value['AliveCounterSum'] = len(value['Slots'])
            with self.subTest(recorded_total=total), self.assertRaises(AssertionError): self.qualify_change(value)

    def test_read_boundaries_and_dynamic_symbol_resolution_fail_closed(self):
        for address, count in ((0, 1), (0xffff800000000001, 1), (0xffff800000000000, 4097)):
            with self.assertRaises(AssertionError): checked_address(address, count)
        symbols = parse_symbols('ffffffff81234560 d PoolCell\nffffffff83456780 d OtherCell\n')
        self.assertEqual(0xffffffff81234560, symbol_address(symbols, 'PoolCell'))
        with self.assertRaises(AssertionError): symbol_address(symbols, 'Missing')


def release_packet(page, reason='Release'):
    return ('XFE_BROWSER_RELEASE|HandlesBefore|100|HandlesAfter|0\n'
            'XFE_BROWSER_WEAK_RELEASE|TablesBefore|8|EntriesBefore|20|TablesAfter|0|EntriesAfter|0'
            '|Created|30|Disposed|30|OwnerHandlesCreated|8|OwnerHandlesDisposed|8'
            f'|Page|{page}|Reason|{reason}\n')


def release_fixture():
    log = ''; entries = []; retired = []
    for index, name in enumerate(('Hello.cs', 'Hello.js', 'Readme.md')):
        preview_start = len(log); log += release_packet(100 + index * 2)
        preview_end = len(log); log += 'EDITOR_ACK\n'
        entry = {'SerialStart': 0, 'Path': name, 'EditorId': 3 + index * 2,
                 'Preview': {'BrowserId': 4 + index * 2, 'CloseSerialStart': preview_start,
                             'ClosedToEditor': {'SerialOffset': preview_end}}}
        if index < 2:
            entry['CloseSerialStart'] = len(log); log += release_packet(101 + index * 2)
            retired.append({'WindowId': entry['EditorId'], 'SerialOffset': len(log)})
            log += f'XFE_EDITOR_BACKGROUND|Retired|Window|{entry["EditorId"]}|Generation|1|Thread|5\n'
        entries.append(entry)
    return log, entries, retired


class ReleaseAndPhysicalGuards(unittest.TestCase):
    def test_all_five_corresponding_owner_packets_must_release_dom_and_weak_resources(self):
        log, entries, retired = release_fixture()
        proof = closed_cycle_release_observation(log, entries, retired, len(log), set())
        self.assertEqual(5, len(proof['ExpectedOwnerReleases']))
        self.assertTrue(proof['Passed'])

    def test_missing_partial_malformed_or_unreleased_weak_packet_is_rejected(self):
        log, entries, retired = release_fixture()
        for bad in (log.replace('|Disposed|30', '|Disposed|29', 1),
                    log.replace('|OwnerHandlesDisposed|8', '|OwnerHandlesDisposed|7', 1),
                    log.replace('|TablesAfter|0', '|TablesAfter|1', 1),
                    log.replace('XFE_BROWSER_WEAK_RELEASE', 'BROKEN', 1),
                    log.replace('|Reason|Release\n', '|Reason|Release', 1)):
            with self.subTest(bad=bad[-100:]), self.assertRaises(AssertionError):
                closed_cycle_release_observation(bad, entries, retired, len(bad), set())

    def test_historical_duplicate_or_wrong_close_interval_packet_is_rejected(self):
        log, entries, retired = release_fixture()
        with self.assertRaises(AssertionError): closed_cycle_release_observation(log, entries, retired, len(log), {100})
        with self.assertRaises(AssertionError): closed_cycle_release_observation(log.replace('|Page|102|', '|Page|100|'), entries, retired, len(log), set())
        entries[0]['Preview']['ClosedToEditor']['SerialOffset'] = entries[0]['Preview']['CloseSerialStart']
        with self.assertRaises(AssertionError): closed_cycle_release_observation(log, entries, retired, len(log), set())

    def test_extra_replacement_packet_must_release_but_does_not_replace_expected_close(self):
        log, entries, retired = release_fixture()
        suffix = release_packet(100, 'Replace')
        proof = closed_cycle_release_observation(log + suffix, entries, retired, len(log + suffix), set())
        self.assertEqual(6, len(proof['AllReleasePackets']))
        bad = log + suffix.replace('|EntriesAfter|0', '|EntriesAfter|1')
        with self.assertRaises(AssertionError): closed_cycle_release_observation(bad, entries, retired, len(bad), set())

    def test_closed_physical_budget_keeps_original64mib_fixed_baseline(self):
        def observation(free): return {'ConsecutiveNativeSamples': [{'RawFreePages': free}, {'RawFreePages': free}]}
        self.assertTrue(closed_cycle_physical_budget([observation(50000), observation(33616)])['Passed'])
        self.assertFalse(closed_cycle_physical_budget([observation(50000), observation(33615)])['Passed'])
        self.assertFalse(closed_cycle_physical_budget([observation(50000), observation(50000), observation(30000)])['Passed'])


if __name__ == '__main__': unittest.main()