1 of 28

fuckitllvm

An experiment in improving program reliability

LLVM

2 of 28

Motivation

Modern day C/C++ programming is hard.

3 of 28

Motivation

Javascript and Python developers have advanced tooling at their disposal

  • Fuckitjs
  • Fuckitpy

4 of 28

5 of 28

6 of 28

7 of 28

8 of 28

Motivation

We want to make compiled binaries that refuse to give up.

9 of 28

Motivation

We want to make compiled binaries that refuse to give up.

Or at least look like they might still be working.

10 of 28

Architectural Overview

Fuckitllvm is made up of a series of passes which improve reliability and resiliency.

11 of 28

Architectural Overview

Fuckitllvm is made up of a series of passes which improve reliability and resiliency.

These passes implement those values into target programs.

  • Error Redirection and Mitigation
  • Dynamic Program Correction
  • “Survival of the Fittest”
  • Control Flow Recovery

12 of 28

Error Redirection and Mitigation

Programs have two primary/standard ways of inconveniencing the user when they encounter potential problems.

  1. Printing to Standard Error (stderr)
  2. Returning a non-zero exit-code

13 of 28

Error Redirection and Mitigation

Programs have two primary/standard ways of inconveniencing the user when they encounter potential problems.

  • Printing to Standard Error (stderr)
  • Returning a non-zero exit-code

Our Solution: Mitigate the risk of unintended consequences by employing advanced redirection techniques.

14 of 28

Error Redirection and Mitigation

Programs have two primary/standard ways of inconveniencing the user when they encounter potential problems.

  • Printing to Standard Error (stderr)
    1. Close Stderr. Open /dev/null. Errors now go where they belong.
  • Returning a non-zero exit-code
    • Identify calls to exit. Make them return 0.
    • Identify return from main. Make it return 0.
    • No More Errors!!!

15 of 28

Dynamic Program Correction

Programs are not perfect. Sometimes, there are unavoidable mistakes.

int i = 0x41414141; *(int *)i = 0x10; printf("I'm still alive!\n");

16 of 28

Dynamic Program Correction

Programs are not perfect. Sometimes, there are unavoidable mistakes.

Advanced Program Analysis Techniques allow us to dynamically correct such subtle errors.

int i = 0x41414141; *(int *)i = 0x10; printf("I'm still alive!\n");

17 of 28

Dynamic Program Correction

Programs are not perfect. Sometimes, there are unavoidable mistakes.

  1. Inject Signal Handlers at program start
  2. If we get a SIGSEGV or SIGILL
    1. Increment instruction pointer

int i = 0x41414141; *(int *)i = 0x10; printf("I'm still alive!\n");

18 of 28

Survival of the Fittest

Unfortunately, our program has to share resources with all other userspace programs.

19 of 28

Survival of the Fittest

Unfortunately, our program has to share resources with all other userspace programs.

Normal programs are prone to being terminated by other programs, especially if another program thinks it is malfunctioning.

20 of 28

Survival of the Fittest

Unfortunately, our program has to share resources with all other userspace programs.

Our Solution: Create a genetically superior program.

if ( (siginfo->si_signo == SIGTERM) ) {

if ( (my_pid != siginfo->si_pid)) {

// wanna fite m8?

kill(siginfo->si_pid, SIGKILL);

return;

}

}

21 of 28

Control Flow Recovery

Sometimes, things go really bad.

Like, end up at a point where something is calling SIGABRT bad.

22 of 28

Control Flow Recovery

Just catch it and try to find a suitable place to resume execution!

General Strategy:

  • Walk up the stack until we see something that looks like it might be a suitable return address.
  • Return there and throw the stack back around that spot.

23 of 28

Control Flow Recovery

Just catch it and try to find a suitable place to resume execution!

Problems:

  • Lots of pointers to places we don’t want to return to
  • Library Functions handle stack frames differently.

24 of 28

Control Flow Recovery

Lots of pointers to places we don’t want to return to

  • Add “Marker” instructions after all calls that we think are an “okay” place to return to (.text is a good candidate)
  • Check for these markers in SIGABRT handler
  • Stick 0 into RAX before jumping back for good measure.

25 of 28

Control Flow Recovery

Return there and throw the stack back around that spot.

Accidentally discovered an interesting problem!

Stack Frames for library calls are often somewhat “non-standard”.

26 of 28

Control Flow Recovery

Return there and throw the stack back around that spot.

Accidentally discovered an interesting problem!

Stack Frames for library calls are often somewhat “non-standard”.

Makes it difficult to “put the stack back”, so it matches the frame we return to.

27 of 28

Future Work

Add hooks before calling functions to record frame boundaries in some dedicated memory region.

Check if potential base pointers fall within these boundaries

Add try-catch around every basic block

28 of 28

Questions/Outrage?