Skip to content

Save emulation state with MCU in binary file crashes due to MMIO callbacks #1553

@antcpl

Description

@antcpl

Describe the bug
I work with Cortex M3 MCU (on the dev branch) and I tried to make a snapshot into a bin file with save() but it crashes due to MMIO callbacks.

Sample Code

def save_callback(ql, *args, **kw):  
    dic = ql.save(reg=True,mem=True,hw=True,snapshot="./snapshot.bin")
    ql.stop()

ql = Qiling(["./toto.elf"],
            archtype=QL_ARCH.CORTEX_M, ostype=QL_OS.MCU, env=stm32f103, verbose=QL_VERBOSE.DISABLED)

ql.hw.create('scb')
ql.hw.create('gpioa')
ql.hw.create('usart2').watch()
ql.hw.create('rcc')
ql.hw.create('afio')
ql.hw.create('exti')
ql.hw.create('gpioc')

ql.hook_address(save_callback,0x8008680)

ql.hw.usart2.send("totototototo".encode())

ql.run(count=1000000)

del ql

Expected behavior
Perform the snapshot without any problem.

Screenshots
Here is the traceback calls :

Traceback (most recent call last):
File "qiling/examples/mcu/fuzzing_test/version8/stm32_bo_hook_before_crash.py", line 47, in
ql.run(count=1000000)
File "qilingenv/lib/python3.12/site-packages/qiling/core.py", line 588, in run
self.os.run()
File "qilingenv/lib/python3.12/site-packages/qiling/os/mcu/mcu.py", line 80, in run
self.ql.emu_start(current_address, 0, count=1)
File "qilingenv/lib/python3.12/site-packages/qiling/core.py", line 775, in emu_start
raise self.internal_exception
File "qilingenv/lib/python3.12/site-packages/qiling/core_hooks.py", line 141, in wrapper
return callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "qilingenv/lib/python3.12/site-packages/qiling/core_hooks.py", line 286, in _hook_addr_cb
ret = hook.call(ql)
^^^^^^^^^^^^^
File "qilingenv/lib/python3.12/site-packages/qiling/core_hooks_types.py", line 25, in call
return self.callback(ql, *args)
^^^^^^^^^^^^^^^^^^^^^^^^
File "qiling/examples/mcu/fuzzing_test/version8/stm32_bo_hook_before_crash.py", line 24, in save_callback
dic = ql.save(reg=True,mem=True,hw=True,cpu_context=True,snapshot="./snapshot.bin")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "qilingenv/lib/python3.12/site-packages/qiling/core.py", line 655, in save
pickle.dump(saved_states, save_state)
AttributeError: Can't pickle local object 'QlHwManager.setup_mmio.<locals>.mmio_read_cb'

Additional context
I digged in the code to try to understand what happens here : the problem only happens when the mem=True parameter is set in the save call otherwise no problem.
I identified the following behaviour :

  • in core.py :
    save() function calls the save method in the QlMemoryManager class when the mem=True parameter is added
  • in memory.py in the save function has this line for mmios :
mem_dict['mmio'].append((lbound, ubound, perm, label, *self.mmio_cbs[(lbound, ubound)]))

self.mmio_cbs makes reference to the two mmio callbacks that are defined in the hw.py file in the setup_mmio function. Thus these two callbacks are part of the QlHwManager instance.

However these callbacks are defined in the setup_mmio function as local functions. Pickle doesn't serialize local functions. This is why the bug is happening.

Suggested correction
Honestly, quite hard to define a way to correct this. These are some of my thoughs :
Either using the dill module that allows serialization of local functions.
Either changing the scope definition of these functions.
Either not saving mmio callbacks and waiting for people to redefine them in the new restoring environment. (Not sure at all about this suggestion, I don't manage to see if this could work)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions