While writing my own assembler for ARM, I realized that an instruction fuzzer can spot some funky results on real CPUs. Undefined behaviour is a known topic for some ancient CPUs like z80, where many undefined instructions have been analyzed and exploited in order to achieve some performance tricks for the demoscene.
But undefined behaviour on modern SOCs and CPUs is not really a known topic for several reasons. First one is that there are several manufacturers and models for every single architecture, and in addition, the microcode is not documented and distributed in encrypted form, so understand what an instruction really does is hard.
This is also a problem for compilers and handwritten assembly optimizations which tend to require several fall-back implementations depending on the CPU model because the same instruction performs different on different chips.
As long as ARM is a fixed-length instruction length (except for Thumb2, which is quite easy to handle), it makes fuzzing those instructions easier than say x86. By understanding how instructions are composed you can quickly reduce the amount of instructions to fuzz.
And this is how I found 4 bytes that hang the RPI CPU; this undefined instruction doesnUt requires special privileges, so any user can execute it and brick the board. Requiring a hardware reset to bring it back to life.
A tiny ELF can be cooked with
$ rabin2 -a arm -b32 -C elf:fedeffe7 killrpi
Radare2 allows us to quickly check that instruction with different disassemblers.
$ for a in arm arm.gnu arm.winedbg ; do rasm2 -a $a -b 32 -d fedeffe7 ; done trap ; 0xe7ffdefe ldrbt sp,
This doesn’t seems to show us anything useful, so letUs go deeper with the documentation:
- http://infocenter.arm.com/help/topic/com.arm.doc.ddi0406c/index.html (requires registration)
So we can now decompose the bits of that instruction in order to understand what it is supposed to be doing:
BYTES: E7 FF DE FE E 7 F F D E F E 1110 011 1 1111 [imm12] 1111 [imm4]
So, according to this documentation, that instruction is decoded as
UDF, which is a variety of instructions that are undefined by definition and are used by debuggers to set breakpoints.