Closed
Description
The Clang compiler generates code for a function that overwrites the lr
register without saving its data on stack beforehand.
This bug was detected on a large software component and the problem was minimized to a PoC (see example.c).
Clang versions info:
- llvmorg-17-init - bug wasn't detected
- llvmorg-17.0.2 - bug detected
- llvmorg-17.0.6 - bug detected
- llvmorg-18-init - bug detected
Host OS: Ubuntu 20.04.3 LTS
In disassembler the bug looks like this (generated example.c.o.objdump
):
00000010 <BUGGY_FUNCTION>:
; if (!ctx) {
10: cmp r0, #0
14: beq 0x48 <BUGGY_FUNCTION+0x38> @ imm = #0x2c <-- Goto 0x48 if "(!ctx)" is True.
; ctx->cmd_c = func_2;
18: ldr r12, [pc, #0x40] @ 0x60 <BUGGY_FUNCTION+0x50> <-- Continue if "(!ctx)" is False.
1c: mov r1, r0
20: mov r0, #0
; ctx->cmd_a = func_0;
24: add lr, r1, #8 <-- ERROR. THIS LOOKS CRAZY!!! "lr" WAS NOT STORED ON THE STACK!
; ctx->cmd_c = func_2;
28: ldr r12, [pc, r12]
... < SOME OTHER CODE > ...
44: bx lr <-- ERROR. BRANCH TO "lr" ADDRESS THAT WAS PREVIOUSLY CORRUPTED!!!
48: push {r11, lr}
... < SOME OTHER CODE > ...
Steps to reproduce.
- example.c
void __attribute__((optnone)) printer(int line) {}
#define INFO() printer(__LINE__);
extern int func_0(void);
extern int func_1(void);
extern int func_2(void);
typedef int (*f_ptr_t)(void);
typedef struct operation {
double double_0;
f_ptr_t cmd_a;
f_ptr_t cmd_b;
f_ptr_t cmd_c;
f_ptr_t cmd_d;
} operation_t;
int BUGGY_FUNCTION(operation_t *ctx)
{
// INFO(); // UNCOMMENT TO FIX ASSEMBLY CODE GENERATION!!!
if (!ctx) {
INFO()
return -1;
}
ctx->cmd_a = func_0;
ctx->cmd_b = func_1;
ctx->cmd_c = func_2;
ctx->cmd_d = (void *)0;
return 0;
}
- reproduce.sh
#!/bin/bash
SDK_BIN_PATH=<PUT YOUR BIN PATH LOCATION HERE>
CC="${SDK_BIN_PATH}/armv7-none-gnueabi-clang"
OBJDUMP="${SDK_BIN_PATH}/armv7-none-gnueabi-objdump"
INPUT_FILE=example.c
OBJ_FILE=example.c.o
OBJDUMP_FILE=example.c.o.objdump
echo "Compile and objdump..." \
&& "${CC}" -ggdb -Os -fPIE -c "${INPUT_FILE}" -o "${OBJ_FILE}" \
&& "${OBJDUMP}" -dS "${OBJ_FILE}" | tee "${OBJDUMP_FILE}"
Metadata
Metadata
Assignees
Type
Projects
Status
Done