Description
Target is an ATmega328P (Arduino Uno) connected to Atmel-ICE (also observed with AVR Dragon). Host is a MacBook Air M2 running macOS Ventura (also observed with an Intel MacBook Pro on an older OS).
Reading from flash via DebugWire can cause heap corruption on LP64 hosts, due to a confluence of errors. If the address would be a negative host int
(typically 0x80000000
or greater), chunksize
can get inappropriately reduced, leading to a very extended loop due to underflow/wraparound of numBytes
. This eventually causes host heap corruption as the loop attempts to write beyond the end of the flash cache.
This demo required a patched avarice instrumented to show the intermediate values involved in the erroneous calculations. I used netcat
to talk to avarice, because my previous reproducer was an older GDB plus an older version of avarice that didn't send memory maps (and thus allowed GDB to read from "negative" addresses).
It's possible that a future GDB bug or newer target memory maps might allow this to happen.
GDB: <mffffffe0,10>
GDB: Read 16 bytes from 0xFFFFFFE0
jtagRead large addr FFFFFFFFFF68FFE0
reducing chunksize from 10 to 20
command "read memory" [0x12, 0x21]
0E 00 09 00 12 21 00 B0 80 FF 68 FF 80 00 00 00
Received 0x81 0x11 0x00 0x87 0x0e 0x09
read: 0e 09 00 12 84 00 0a d0 8f e0 7a cf 81 35 11 f4 88 e0 18 d0 1d d0 80 e1 01 d0 65 cf 98 2f 80 91 c0 00 85 ff fc cf 90 93 c6 00 08 95 80 91 c0 00 87 ff fc cf 80 91 c0 00 84 fd 01 c0 a8 95 80 91 c6 00 08 95 e0 e6 f0 e0 98 e1 90 83 80 83 08 95 ed df 80 32 19 f0 88 e0 f5 df ff cf 84 e1 de cf 1f 93 18 2f e3 df 11 50 e9 f7 f2 df 1f 91 08 95 80 e0 e8 df ee 27 ff 27 09 94 ff ff ff ff ff ff ff ff ff ff 04 04 00
Got message seqno 9 (command_sequence == 9)
response: 12 84 00 0A D0 8F E0 7A CF 81 35 11 F4 88 E0 18 D0 1D D0 80 E1 01 D0 65 CF 98 2F 80 91 C0 00 85 FF FC CF 90 93 C6 00 08 95 80 91 C0 00 87 FF FC CF 80 91 C0 00 84 FD 01 C0 A8 95 80 91 C6 00 08 95 E0 E6 F0 E0 98 E1 90 83 80 83 08 95 ED DF 80 32 19 F0 88 E0 F5 DF FF CF 84 E1 DE CF 1F 93 18 2F E3 DF 11 50 E9 F7 F2 DF 1F 91 08 95 80 E0 E8 DF EE 27 FF 27 09 94 FF FF FF FF FF FF FF FF FF FF 04 04 00
It looped, continuing for over a hundred iterations. Eventually, this ended with:
command "read memory" [0x12, 0x21]
0E 00 8B 00 12 21 00 B0 80 40 69 FF 80 00 00 00
libc++abi: terminating due to uncaught exception of type jtag_exception: Querying for response: hid_write() failed
remote.cc
calls hexToInt
with a signed int addr
as the destination. It then calls jtagRead
, which takes an unsigned long
as the address. This causes sign extension while converting a negative number to unsigned long
. The chunksize
adjustment calculations in jtag3rw.cc
then increase chunksize
beyond numBytes
, leading to the integer underflow/wraparound and buffer overflow.
I have a patch in #90 that needs to be cleaned up and duplicated in jtag3rw.cc
. (I created it when I first ran across this bug with AVR Dragon.)
Full log: