Skip to content

[Bug][Clang][ARM] Wrong assembly code generation, "LR" corrupted. #80287

Closed
@P1119r1m

Description

@P1119r1m

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

No type

Projects

Status

Done

Relationships

None yet

Development

No branches or pull requests

Issue actions