def test_overlap_segment_head(self): # Existing segment: |_______| # New segment: |_______| prog = Program() segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xffff0040, 128, segment1) prog.add_memory_segment(0xffff0000, 128, segment2) prog.read(0xffff0000, 192) segment1.assert_called_once_with(0xffff0080, 64, 64, False) segment2.assert_called_once_with(0xffff0000, 128, 0, False)
def test_overlap_same_segment(self): # Existing segment: |_______| # New segment: |_______| prog = Program(MOCK_PLATFORM) segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xFFFF0000, 128, segment1) prog.add_memory_segment(0xFFFF0000, 128, segment2) prog.read(0xFFFF0000, 128) segment1.assert_not_called() segment2.assert_called_once_with(0xFFFF0000, 128, 0, False)
def test_overlap_same_address_larger_size(self): # Existing segment: |___| # New segment: |_______| prog = Program() segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xffff0000, 64, segment1) prog.add_memory_segment(0xffff0000, 128, segment2) prog.read(0xffff0000, 128) segment1.assert_not_called() segment2.assert_called_once_with(0xffff0000, 128, 0, False)
def test_overlap_subsume_at_and_after(self): # Existing segments: |_|_|_|_| # New segment: |_______| prog = Program() segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xffff0000, 32, segment1) prog.add_memory_segment(0xffff0020, 32, segment1) prog.add_memory_segment(0xffff0040, 32, segment1) prog.add_memory_segment(0xffff0060, 32, segment1) prog.add_memory_segment(0xffff0000, 128, segment2) prog.read(0xffff0000, 128) segment1.assert_not_called() segment2.assert_called_once_with(0xffff0000, 128, 0, False)
def test_overlap_within_segment(self): # Existing segment: |_______| # New segment: |___| prog = Program() segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xffff0000, 128, segment1) prog.add_memory_segment(0xffff0020, 64, segment2) prog.read(0xffff0000, 128) segment1.assert_has_calls([ unittest.mock.call(0xffff0000, 32, 00, False), unittest.mock.call(0xffff0060, 32, 96, False), ]) segment2.assert_called_once_with(0xffff0020, 64, 0, False)
def test_overlap_segment_head_and_tail(self): # Existing segment: |_______||_______| # New segment: |_______| prog = Program(MOCK_PLATFORM) segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) segment3 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xFFFF0000, 128, segment1) prog.add_memory_segment(0xFFFF0080, 128, segment2) prog.add_memory_segment(0xFFFF0040, 128, segment3) prog.read(0xFFFF0000, 256) segment1.assert_called_once_with(0xFFFF0000, 64, 0, False) segment2.assert_called_once_with(0xFFFF00C0, 64, 64, False) segment3.assert_called_once_with(0xFFFF0040, 128, 0, False)
def test_overlap_subsume_after(self): # Existing segments: |_|_|_|_| # New segment: |_______| prog = Program(MOCK_PLATFORM) segment1 = unittest.mock.Mock(side_effect=zero_memory_read) segment2 = unittest.mock.Mock(side_effect=zero_memory_read) segment3 = unittest.mock.Mock(side_effect=zero_memory_read) prog.add_memory_segment(0xFFFF0020, 32, segment1) prog.add_memory_segment(0xFFFF0040, 32, segment1) prog.add_memory_segment(0xFFFF0060, 32, segment1) prog.add_memory_segment(0xFFFF0080, 64, segment2) prog.add_memory_segment(0xFFFF0000, 128, segment3) prog.read(0xFFFF0000, 192) segment1.assert_not_called() segment2.assert_called_once_with(0xFFFF0080, 64, 0, False) segment3.assert_called_once_with(0xFFFF0000, 128, 0, False)
def test_physical(self): data = b'hello, world' prog = Program() with tempfile.NamedTemporaryFile() as f: f.write( create_elf_file(ET.CORE, [ ElfSection( p_type=PT.LOAD, vaddr=0xffff0000, paddr=0xa0, data=data, ), ])) f.flush() prog.set_core_dump(f.name) self.assertEqual(prog.read(0xffff0000, len(data)), data) self.assertEqual(prog.read(0xa0, len(data), physical=True), data)
def test_set_pid(self): # Debug the running Python interpreter itself. prog = Program() self.assertEqual(prog.arch, Architecture.AUTO) prog.set_pid(os.getpid()) self.assertEqual(prog.arch, Architecture.HOST) data = b'hello, world!' buf = ctypes.create_string_buffer(data) self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data) self.assertRaisesRegex(ValueError, 'program memory was already initialized', prog.set_pid, os.getpid())
def test_simple(self): data = b"hello, world" prog = Program() with tempfile.NamedTemporaryFile() as f: f.write( create_elf_file( ET.CORE, [ElfSection(p_type=PT.LOAD, vaddr=0xFFFF0000, data=data)])) f.flush() prog.set_core_dump(f.name) self.assertEqual(prog.read(0xFFFF0000, len(data)), data) self.assertRaises(FaultError, prog.read, 0x0, len(data), physical=True)
def _get_printk_records_structured(prog: Program) -> List[PrintkRecord]: printk_logp_type = prog.type("struct printk_log *") have_caller_id = printk_logp_type.type.has_member("caller_id") LOG_CONT = prog["LOG_CONT"].value_() result = [] log_buf = prog["log_buf"].read_() current_idx = prog["log_first_idx"].read_() next_idx = prog["log_next_idx"].read_() seq = prog["log_first_seq"].value_() while current_idx != next_idx: logp = cast(printk_logp_type, log_buf + current_idx) log = logp[0].read_() text_len = log.text_len.value_() dict_len = log.dict_len.value_() text_dict = prog.read(logp + 1, text_len + dict_len) if have_caller_id: caller_tid, caller_cpu = _caller_id(log.caller_id.value_()) else: caller_tid = caller_cpu = None context = {} if dict_len: for elmt in text_dict[text_len:].split(b"\0"): key, value = elmt.split(b"=", 1) context[key] = value result.append( PrintkRecord( text=text_dict[:text_len], facility=log.facility.value_(), level=log.level.value_(), seq=seq, timestamp=log.ts_nsec.value_(), caller_tid=caller_tid, caller_cpu=caller_cpu, continuation=bool(log.flags.value_() & LOG_CONT), context=context, )) log_len = log.len.read_() if log_len: current_idx += log_len else: # Zero means the buffer wrapped around. if current_idx < next_idx: # Avoid getting into an infinite loop if the buffer is # corrupted. break current_idx -= current_idx seq += 1 return result
def test_unsaved(self): data = b"hello, world" prog = Program() with tempfile.NamedTemporaryFile() as f: f.write( create_elf_file( ET.CORE, [ ElfSection( p_type=PT.LOAD, vaddr=0xFFFF0000, data=data, memsz=len(data) + 4, ), ], ) ) f.flush() prog.set_core_dump(f.name) with self.assertRaisesRegex(FaultError, "memory not saved in core dump") as cm: prog.read(0xFFFF0000, len(data) + 4) self.assertEqual(cm.exception.address, 0xFFFF000C)
def test_set_pid(self): # Debug the running Python interpreter itself. prog = Program() self.assertIsNone(prog.platform) self.assertFalse(prog.flags & ProgramFlags.IS_LIVE) prog.set_pid(os.getpid()) self.assertEqual(prog.platform, host_platform) self.assertTrue(prog.flags & ProgramFlags.IS_LIVE) data = b'hello, world!' buf = ctypes.create_string_buffer(data) self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data) self.assertRaisesRegex(ValueError, 'program memory was already initialized', prog.set_pid, os.getpid())
def get_kconfig(prog: Program) -> Mapping[str, str]: """ Get the kernel build configuration as a mapping from the option name to the value. >>> get_kconfig(prog)['CONFIG_SMP'] 'y' >>> get_kconfig(prog)['CONFIG_HZ'] '300' This is only supported if the kernel was compiled with ``CONFIG_IKCONFIG``. Note that most Linux distributions do not enable this option. """ try: return prog.cache["kconfig_map"] except KeyError: pass try: start = prog.symbol("kernel_config_data").address size = prog.symbol("kernel_config_data_end").address - start except LookupError: # Before Linux kernel commit 13610aa908dc ("kernel/configs: use .incbin # directive to embed config_data.gz") (in v5.1), the data is a variable # rather than two symbols. try: kernel_config_data = prog["kernel_config_data"] except KeyError: raise LookupError( "kernel configuration data not found; kernel must be compiled with CONFIG_IKCONFIG" ) # The data is delimited by the magic strings "IKCFG_ST" and "IKCFG_ED" # plus a NUL byte. start = kernel_config_data.address_ + 8 # type: ignore[operator] size = len(kernel_config_data) - 17 data = prog.read(start, size) kconfig = {} for line in gzip.decompress(data).decode().splitlines(): if not line or line.startswith("#"): continue name, _, value = line.partition("=") if value: kconfig[name] = value # Make result mapping 'immutable', so changes cannot propagate to the cache result = types.MappingProxyType(kconfig) prog.cache["kconfig_map"] = result return result
def test_zero_fill(self): data = b'hello, world' prog = Program() with tempfile.NamedTemporaryFile() as f: f.write( create_elf_file(ET.CORE, [ ElfSection( p_type=PT.LOAD, vaddr=0xffff0000, data=data, memsz=len(data) + 4, ), ])) f.flush() prog.set_core_dump(f.name) self.assertEqual(prog.read(0xffff0000, len(data) + 4), data + bytes(4))
def test_pid_memory(self): data = b"hello, world!" buf = ctypes.create_string_buffer(data) address = ctypes.addressof(buf) # QEMU user-mode emulation doesn't seem to emulate /proc/$pid/mem # correctly on a 64-bit host with a 32-bit guest; see # https://gitlab.com/qemu-project/qemu/-/issues/698. Packit uses mock # to cross-compile and test packages, which in turn uses QEMU user-mode # emulation. Skip this test if /proc/$pid/mem doesn't work so that # those builds succeed. try: with open("/proc/self/mem", "rb") as f: f.seek(address) functional_proc_pid_mem = f.read(len(data)) == data except OSError: functional_proc_pid_mem = False if not functional_proc_pid_mem: self.skipTest("/proc/$pid/mem is not functional") prog = Program() prog.set_pid(os.getpid()) self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data)