Skip to content

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

@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}"

Activity

added
clangClang issues not falling into any other category
on Feb 1, 2024
jroelofs

jroelofs commented on Feb 1, 2024

@jroelofs
Contributor

Smells like shrink-wrapping.

llvmbot

llvmbot commented on Feb 1, 2024

@llvmbot
Member

@llvm/issue-subscribers-backend-arm

Author: Victor Signaevskyi (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 &lt;BUGGY_FUNCTION&gt;:
;     if (!ctx) {
      10:     	cmp	r0, #<!-- -->0
      14:     	beq	0x48 &lt;BUGGY_FUNCTION+0x38&gt; @ imm = #<!-- -->0x2c                &lt;-- Goto 0x48 if "(!ctx)" is True.
;     ctx-&gt;cmd_c = func_2;
      18:     	ldr	r12, [pc, #<!-- -->0x40]        @ 0x60 &lt;BUGGY_FUNCTION+0x50&gt;    &lt;-- Continue if "(!ctx)" is False.
      1c:     	mov	r1, r0
      20:     	mov	r0, #<!-- -->0
;     ctx-&gt;cmd_a = func_0;
      24:     	add	lr, r1, #<!-- -->8              &lt;-- ERROR. THIS LOOKS CRAZY!!! "lr" WAS NOT STORED ON THE STACK!
;     ctx-&gt;cmd_c = func_2;
      28:     	ldr	r12, [pc, r12]
... &lt; SOME OTHER CODE &gt; ...
      44:     	bx	lr                      &lt;-- ERROR. BRANCH TO "lr" ADDRESS THAT WAS PREVIOUSLY CORRUPTED!!!
      48:     	push	{r11, lr}
... &lt; SOME OTHER CODE &gt; ...

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-&gt;cmd_a = func_0;
    ctx-&gt;cmd_b = func_1;
    ctx-&gt;cmd_c = func_2;
    ctx-&gt;cmd_d = (void *)0;
    return 0;
}
  • reproduce.sh
#!/bin/bash

SDK_BIN_PATH=&lt;PUT YOUR BIN PATH LOCATION HERE&gt;

CC="${SDK_BIN_PATH}/arm-secureos-gnueabi-clang"
OBJDUMP="${SDK_BIN_PATH}/arm-secureos-gnueabi-objdump"

INPUT_FILE=example.c
OBJ_FILE=example.c.o
OBJDUMP_FILE=example.c.o.objdump

echo "Compile and objdump..." \
&amp;&amp; "${CC}" -ggdb -Os -fPIE -c "${INPUT_FILE}" -o "${OBJ_FILE}" \
&amp;&amp; "${OBJDUMP}" -dS "${OBJ_FILE}" | tee "${OBJDUMP_FILE}"
fhahn

fhahn commented on Feb 1, 2024

@fhahn
Contributor

I suspect this was fixed by swiftlang@b1a5ee1

Could you check with a build from current main or the upcoming 18.x release candidates?

davemgreen

davemgreen commented on Feb 1, 2024

@davemgreen
Collaborator

I still see the same in https://godbolt.org/z/7PKxqj7vx I'm afraid.

It looks like what @jroelofs says - shrink wrapping leads to frame lowering in one of the two branches, followed by a bit of tail folding ends up with the load/store optimizing using lr that it thinks is available.

removed
clangClang issues not falling into any other category
on Feb 1, 2024
P1119r1m

P1119r1m commented on Feb 1, 2024

@P1119r1m
Author

BTW, there is no bug if to build it for -target armv4t-none-eabi instead of -target thumbv7a-none-eabi:

P1119r1m

P1119r1m commented on Feb 5, 2024

@P1119r1m
Author

Dear contributors/collaborators,

are there any chance to fix this compiler bug?

There is a feeling that this bug can manifest itself in many other places in the code.
Maybe not even only in 32-bit architectures.

Also, I think this bug could be used in some hacker attacks.

P1119r1m

P1119r1m commented on Feb 21, 2024

@P1119r1m
Author

@jroelofs @davemgreen @fhahn
I think we should report a CVE for this Clang issue to warn at least all ARM32 users about this Clang compiler vulnerability.

smithp35

smithp35 commented on Feb 21, 2024

@smithp35
Collaborator

If you feel like this is a security issue please report using the process in https://llvm.org/docs/Security.html . My instinct is that this would not be a worthy of a CVE. CVEs are used to report security vulnerabilities in programs, this looks like a code-generation bug that if it affects your program it is likely to crash. It doesn't look like something that would be systemicly reproduced across many programs.

P1119r1m

P1119r1m commented on Feb 21, 2024

@P1119r1m
Author

@smithp35 Thanks for the link.
I think the bug deserves a CVE because "security experts" can intentionally look for this incorrect binary pattern in binaries and exploit it.
Theoretically, thanks to this compiler bug, one can branch to any address.

smithp35

smithp35 commented on Feb 21, 2024

@smithp35
Collaborator

Feel free to report it to the LLVM security group if you disagree with me.

P1119r1m

P1119r1m commented on Feb 21, 2024

@P1119r1m
Author

@smithp35 Thank you. Reported locally to LLVM security group as:

jroelofs

jroelofs commented on Feb 21, 2024

@jroelofs
Contributor

workaround: -mllvm --enable-shrink-wrap=false

https://godbolt.org/z/3Koq6YEWW

197 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

Status

Done

Relationships

None yet

    Development

    Participants

    @tstellar@fhahn@jroelofs@ostannard@EugeneZelenko

    Issue actions

      [Bug][Clang][ARM] Wrong assembly code generation, "LR" corrupted. · Issue #80287 · llvm/llvm-project