mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-01-02 19:40:57 +00:00
feat(core): clear out memory space after every workflow
A small fixed list of modules is kept pre-loaded in the GC arena. These must not keep references to anything else, as all other modules are unloaded and the memory is cleared.
This commit is contained in:
parent
e629a72c3a
commit
7ca67cc4d9
@ -1,73 +1,25 @@
|
|||||||
# isort:skip_file
|
# isort:skip_file
|
||||||
|
# fmt: off
|
||||||
|
|
||||||
# unlock the device
|
# Import always-active modules
|
||||||
import boot # noqa: F401
|
import storage
|
||||||
|
import storage.device
|
||||||
|
from trezor import config, pin, utils # noqa: F401
|
||||||
|
|
||||||
# prepare the USB interfaces, but do not connect to the host yet
|
# Prepare the USB interfaces first. Do not connect to the host yet.
|
||||||
import usb
|
import usb
|
||||||
|
|
||||||
from trezor import loop, utils, wire, workflow
|
unimport_manager = utils.unimport()
|
||||||
|
|
||||||
|
# unlock the device, unload the boot module afterwards
|
||||||
|
with unimport_manager:
|
||||||
|
import boot
|
||||||
|
del boot
|
||||||
|
|
||||||
# start the USB
|
# start the USB
|
||||||
usb.bus.open()
|
usb.bus.open(storage.device.get_device_id())
|
||||||
|
|
||||||
|
while True:
|
||||||
def _boot_apps() -> None:
|
with unimport_manager:
|
||||||
# load applications
|
import session # noqa: F401
|
||||||
import apps.base
|
del session
|
||||||
import apps.management
|
|
||||||
import apps.bitcoin
|
|
||||||
import apps.misc
|
|
||||||
|
|
||||||
if not utils.BITCOIN_ONLY:
|
|
||||||
import apps.ethereum
|
|
||||||
import apps.lisk
|
|
||||||
import apps.monero
|
|
||||||
import apps.nem
|
|
||||||
import apps.stellar
|
|
||||||
import apps.ripple
|
|
||||||
import apps.cardano
|
|
||||||
import apps.tezos
|
|
||||||
import apps.eos
|
|
||||||
import apps.binance
|
|
||||||
import apps.webauthn
|
|
||||||
|
|
||||||
if __debug__:
|
|
||||||
import apps.debug
|
|
||||||
|
|
||||||
# boot applications
|
|
||||||
apps.base.boot()
|
|
||||||
apps.management.boot()
|
|
||||||
apps.bitcoin.boot()
|
|
||||||
apps.misc.boot()
|
|
||||||
if not utils.BITCOIN_ONLY:
|
|
||||||
apps.ethereum.boot()
|
|
||||||
apps.lisk.boot()
|
|
||||||
apps.monero.boot()
|
|
||||||
apps.nem.boot()
|
|
||||||
apps.stellar.boot()
|
|
||||||
apps.ripple.boot()
|
|
||||||
apps.cardano.boot()
|
|
||||||
apps.tezos.boot()
|
|
||||||
apps.eos.boot()
|
|
||||||
apps.binance.boot()
|
|
||||||
apps.webauthn.boot()
|
|
||||||
if __debug__:
|
|
||||||
apps.debug.boot()
|
|
||||||
|
|
||||||
# run main event loop and specify which screen is the default
|
|
||||||
apps.base.set_homescreen()
|
|
||||||
workflow.start_default()
|
|
||||||
|
|
||||||
|
|
||||||
_boot_apps()
|
|
||||||
|
|
||||||
# initialize the wire codec
|
|
||||||
wire.setup(usb.iface_wire)
|
|
||||||
if __debug__:
|
|
||||||
wire.setup(usb.iface_debug, is_debug_session=True)
|
|
||||||
|
|
||||||
loop.run()
|
|
||||||
|
|
||||||
# loop is empty. That should not happen
|
|
||||||
utils.halt("All tasks have died.")
|
|
||||||
|
28
core/src/session.py
Normal file
28
core/src/session.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from trezor import loop, utils, wire, workflow
|
||||||
|
|
||||||
|
# load applications
|
||||||
|
import apps.base
|
||||||
|
import usb
|
||||||
|
|
||||||
|
apps.base.boot()
|
||||||
|
|
||||||
|
if not utils.BITCOIN_ONLY and usb.ENABLE_IFACE_WEBAUTHN:
|
||||||
|
import apps.webauthn
|
||||||
|
|
||||||
|
apps.webauthn.boot()
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
import apps.debug
|
||||||
|
|
||||||
|
apps.debug.boot()
|
||||||
|
|
||||||
|
# run main event loop and specify which screen is the default
|
||||||
|
apps.base.set_homescreen()
|
||||||
|
workflow.start_default()
|
||||||
|
|
||||||
|
# initialize the wire codec
|
||||||
|
wire.setup(usb.iface_wire)
|
||||||
|
if __debug__:
|
||||||
|
wire.setup(usb.iface_debug, is_debug_session=True)
|
||||||
|
|
||||||
|
loop.run()
|
@ -27,20 +27,20 @@ if __debug__:
|
|||||||
if False:
|
if False:
|
||||||
from typing import (
|
from typing import (
|
||||||
Any,
|
Any,
|
||||||
Iterable,
|
|
||||||
Iterator,
|
Iterator,
|
||||||
Protocol,
|
Protocol,
|
||||||
Union,
|
Union,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Sequence,
|
Sequence,
|
||||||
|
Set,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def unimport_begin() -> Iterable[str]:
|
def unimport_begin() -> Set[str]:
|
||||||
return set(sys.modules)
|
return set(sys.modules)
|
||||||
|
|
||||||
|
|
||||||
def unimport_end(mods: Iterable[str]) -> None:
|
def unimport_end(mods: Set[str], collect: bool = True) -> None:
|
||||||
for mod in sys.modules:
|
for mod in sys.modules:
|
||||||
if mod not in mods:
|
if mod not in mods:
|
||||||
# remove reference from sys.modules
|
# remove reference from sys.modules
|
||||||
@ -58,7 +58,23 @@ def unimport_end(mods: Iterable[str]) -> None:
|
|||||||
# referenced from the parent package. both is fine.
|
# referenced from the parent package. both is fine.
|
||||||
pass
|
pass
|
||||||
# collect removed modules
|
# collect removed modules
|
||||||
gc.collect()
|
if collect:
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
|
class unimport:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.mods: Set[str] | None = None
|
||||||
|
|
||||||
|
def __enter__(self) -> None:
|
||||||
|
self.mods = unimport_begin()
|
||||||
|
|
||||||
|
def __exit__(self, _exc_type: Any, _exc_value: Any, _tb: Any) -> None:
|
||||||
|
assert self.mods is not None
|
||||||
|
unimport_end(self.mods, collect=False)
|
||||||
|
self.mods.clear()
|
||||||
|
self.mods = None
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
def ensure(cond: bool, msg: str | None = None) -> None:
|
def ensure(cond: bool, msg: str | None = None) -> None:
|
||||||
|
@ -284,7 +284,7 @@ class UnexpectedMessageError(Exception):
|
|||||||
|
|
||||||
|
|
||||||
async def handle_session(
|
async def handle_session(
|
||||||
iface: WireInterface, session_id: int, is_debug_session: bool = True
|
iface: WireInterface, session_id: int, is_debug_session: bool = False
|
||||||
) -> None:
|
) -> None:
|
||||||
if __debug__ and is_debug_session:
|
if __debug__ and is_debug_session:
|
||||||
ctx_buffer = WIRE_BUFFER_DEBUG
|
ctx_buffer = WIRE_BUFFER_DEBUG
|
||||||
@ -295,6 +295,8 @@ async def handle_session(
|
|||||||
res_msg: protobuf.MessageType | None = None
|
res_msg: protobuf.MessageType | None = None
|
||||||
req_type = None
|
req_type = None
|
||||||
req_msg = None
|
req_msg = None
|
||||||
|
|
||||||
|
modules = utils.unimport_begin()
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if next_msg is None:
|
if next_msg is None:
|
||||||
@ -330,7 +332,8 @@ async def handle_session(
|
|||||||
|
|
||||||
# Take a mark of modules that are imported at this point, so we can
|
# Take a mark of modules that are imported at this point, so we can
|
||||||
# roll back and un-import any others. Should not raise.
|
# roll back and un-import any others. Should not raise.
|
||||||
modules = utils.unimport_begin()
|
if is_debug_session:
|
||||||
|
modules = utils.unimport_begin()
|
||||||
|
|
||||||
# We need to find a handler for this message type. Should not
|
# We need to find a handler for this message type. Should not
|
||||||
# raise.
|
# raise.
|
||||||
@ -430,6 +433,10 @@ async def handle_session(
|
|||||||
# Unload modules imported by the workflow. Should not raise.
|
# Unload modules imported by the workflow. Should not raise.
|
||||||
utils.unimport_end(modules)
|
utils.unimport_end(modules)
|
||||||
|
|
||||||
|
if not is_debug_session and next_msg is None: # and msg_type != 0:
|
||||||
|
loop.clear()
|
||||||
|
return
|
||||||
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# The session handling should never exit, just log and continue.
|
# The session handling should never exit, just log and continue.
|
||||||
if __debug__:
|
if __debug__:
|
||||||
|
Loading…
Reference in New Issue
Block a user