avrdudes / avarice

AVaRICE is a program for interfacing the Atmel JTAG ICE to GDB to allow users to debug their embedded AVR target.
GNU General Public License v2.0
35 stars 11 forks source link

DebugWire flash read causes heap corruption on LP64 hosts #107

Open tlyu opened 8 months ago

tlyu commented 8 months ago

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:

overflow.txt

tlyu commented 8 months ago

Updated #90.

tlyu commented 8 months ago

Crash reporter excerpt:

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_CRASH (SIGABRT)
Exception Codes:       0x0000000000000000, 0x0000000000000000

Termination Reason:    Namespace SIGNAL, Code 6 Abort trap: 6
Terminating Process:   avarice [92774]

Application Specific Information:
abort() called

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib                 0x19af78744 __pthread_kill + 8
1   libsystem_pthread.dylib                0x19afafc28 pthread_kill + 288
2   libsystem_c.dylib                      0x19aebdae8 abort + 180
3   libsystem_malloc.dylib                 0x19addee28 malloc_vreport + 908
4   libsystem_malloc.dylib                 0x19adf55d4 malloc_zone_error + 104
5   libsystem_malloc.dylib                 0x19aded148 nanov2_guard_corruption_detected + 44
6   libsystem_malloc.dylib                 0x19adec344 nanov2_allocate_outlined + 404
7   libc++abi.dylib                        0x19af6a924 operator new(unsigned long) + 32
8   avarice                                0x10009282c jtag3::sendFrame(unsigned char*, int) + 72
9   avarice                                0x100093064 jtag3::sendJtagCommand(unsigned char*, int, char const*, unsigned char*&, int&) + 116
10  avarice                                0x1000931d0 jtag3::doJtagCommand(unsigned char*, int, char const*, unsigned char*&, int&) + 64
11  avarice                                0x1000964ac jtag3::jtagRead(unsigned long, unsigned int) + 892
12  avarice                                0x1000a08c8 talkToGdb() + 1268
13  avarice                                0x10009e5c8 main + 4088
14  dyld                                   0x19ac57f28 start + 2236
tlyu commented 8 months ago

Diagnostics printed to terminal, which I wasn't able to capture by redirection:

avarice(92815,0x1f66fa100) malloc: Heap corruption detected, free list is damaged at 0x600000f78010
*** Incorrect guard value: 18446744071898212351
avarice(92815,0x1f66fa100) malloc: *** set a breakpoint in malloc_error_break to debug
[1]    92815 abort      ~/src/avarice/src/avarice -d -w -C -4 localhost:6000