Repository navigation
fix: no host crash or heap overflow on garbage or hostile firmware - #71
Merged
Merged
Conversation
Embedders (iCircuit) must survive any firmware. The fixes ported from RP2350Sharp plus new fuzz tests (bare core, full two-core machine, raw MMIO, every 16-bit opcode) found: - Cortex-M0+: after an instruction-fetch fault from a region with no fetch window, the cached region id kept a null pointer; a handler in the same region read through null. The id is now poisoned (lockup if the HardFault handler is unfetchable). - Memory: halfword/word accesses at the last 1-3 bytes of SRAM, flash or boot ROM spilled past the native buffer; every store now has a 16-byte tail pad. - PUSH/POP/STMIA/LDMIA SRAM fast paths masked only the start address and could write past SRAM; bursts that do not fit take the per-word path. - Bootrom flash erase/program hooks bounds-checked offset + count in int, which wrapped: a host heap write of up to 2 GB. Now in long. - SSI erase starting past the end of flash wrapped end - start to ~4 GB for InitBlock. Such an erase is now ignored. - DMA CHAIN_TO 12-15 indexed past the 12 channels, and two enabled channels chained to each other recursed until a host stack overflow. Out-of-range targets are ignored and a chain stops after 12 hops. - USB: BUFFER_ADDRESS + LENGTH could run past the 4 KB DPRAM array; the IN copy and the OUT delivery are clamped.
Second hardening pass (line-by-line audit plus PIO and wireless fuzz): - PIO OUT EXEC / MOV EXEC could re-execute itself from inside itself (X = 0xA081, mov exec, x) until the host stack overflowed; forced execution is now nested at most 8 deep. - PIO WAIT cleared IRQ flag (index & 7) on every wait: missing braces around the IRQ-clear pair since 1.1.0. Only WAIT IRQ clears now. - DMA: a channel writing its own trigger register (AL1 TRANS_COUNT, MULTI_CHAN_TRIGGER) re-entered ExecuteChannel per beat until the host stack overflowed; chains and beat-triggered channels now share one depth limit of 12. - DMA transfers run in slices of at most 2^20 beats per call (the DMA is now ticked), so a garbage TRANS_COUNT no longer stalls one bus write for minutes. Real transfers fit in one slice. - VirtualNet: DHCP option 53 at the end of a frame and a TCP segment whose IP length exceeds the frame read past the buffer. - Bounded memory: SSI tx/rx buffers, USB pending IN completions, CYW43439 SDPCM queue, backplane storage, gSPI buffer, VirtualNet and VirtualSwitch tables and per-connection receive bytes.
Collaborator
Author
|
Second hardening pass added (see the latest commit): PIO EXEC recursion and WAIT IRQ-flag bug, DMA self-trigger re-entrancy, DMA transfers sliced at 2^20 beats, VirtualNet out-of-bounds reads, and bounded buffers across SSI/USB/CYW43439. Unit 578 (stable 5/5), integration 197 passed / 4 skipped; MicroPython CPU-bound loop unchanged (4940 → 4932 ms median). |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Embedders such as iCircuit must survive any firmware: corrupt, random, hostile, or a legitimate program with a stack or DMA bug. Several paths reachable from guest code crashed the host process or wrote past native buffers into the host heap.
Fixes
NullReferenceExceptionPUSH/POP/STMIA/LDMIASRAM fast paths with SP or base near the top of SRAMSramBurstFitsguard, falling back to the per-word pathintAccessViolation)longend - startwrapped to ~4 GBAccessViolationCHAIN_TO12–15, and two channels chained to each otherIndexOutOfRange; host stack overflowBUFFER_ADDRESS+LENGTHpast the 4 KB DPRAMArgumentOutOfRangeThe first three bugs mirror fixes in RP2350Sharp (PyMCU/RP2350Sharp#26). The last four were found by the new fuzz tests.
Tests
tests/RP2040Sharp.Tests/Cpu/GarbageFirmwareTests.cshas 30 tests. Every bug has a reproducer that failed before its fix. The fuzz tests cover:The class runs in about 0.25 s, with no wall-clock assertions. Beyond the committed seeds, up to 20000 extra seeds ran clean.
Performance
PUSH/POP/LDM/STM: +3 %.Known, not fixed
DMA runs a transfer to completion synchronously, so a huge
TRANS_COUNTstalls the caller. It does not crash or corrupt anything, and it is documented in the CHANGELOG.