Skip to content

Instantly share code, notes, and snippets.

@moyix
Created September 7, 2022 22:33
Show Gist options
  • Save moyix/46b7d7fdb9e100dad866821793b08058 to your computer and use it in GitHub Desktop.
Save moyix/46b7d7fdb9e100dad866821793b08058 to your computer and use it in GitHub Desktop.

The ffast and the Furious

This is a small and admittedly contrived demo showing how some weird but safe code could become vulnerable if run in an environment where some shared library has changed the FPU's FTZ/DAZ bits to force denormals to zero.

To run it:

# Create an empty file
$ touch gofast.c      

# Build it with -ffast-math, which links in crtfastmath.o, installing a
# constructor that enables FTZ+DAZ on load.
$ gcc -ffast-math -fpic -shared gofast.c -o gofast.so

# Build our main program, WITHOUT -ffast-math. Enable ASAN so we catch
# any overflows, no matter how small.
$ gcc -fsanitize=address ffast_and_furious.c -o ffast_and_furious -ldl -lm

# Run without the "fast" option and see that it's safe
$ ./ffast_and_furious 
DEBUG: Will write to pixbuf[127]

# Now run it with "fast", which loads gofast.so, enabling FTZ/DAZ
$ ./ffast_and_furious fast
Loaded ./gofast.so
Fast math mode enabled! Gotta go fast!
DEBUG: Will write to pixbuf[128]
=================================================================
==922888==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c0000000c0 at pc 0x55f753dd4422 bp 0x7ffdf8c54bf0 sp 0x7ffdf8c54be0
WRITE of size 1 at 0x60c0000000c0 thread T0
    #0 0x55f753dd4421 in put_pixel (/home/moyix/ffast_and_furious+0x1421)
    #1 0x55f753dd45ba in main (/home/moyix/ffast_and_furious+0x15ba)
    #2 0x7f4dccb40082 in __libc_start_main ../csu/libc-start.c:308
    #3 0x55f753dd426d in _start (/home/moyix/ffast_and_furious+0x126d)

0x60c0000000c0 is located 0 bytes to the right of 128-byte region [0x60c000000040,0x60c0000000c0)
allocated by thread T0 here:
    #0 0x7f4dccf70808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144
    #1 0x55f753dd4586 in main (/home/moyix/ffast_and_furious+0x1586)
    #2 0x7f4dccb40082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/moyix/ffast_and_furious+0x1421) in put_pixel
Shadow bytes around the buggy address:
  0x0c187fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c187fff8000: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c187fff8010: 00 00 00 00 00 00 00 00[fa]fa fa fa fa fa fa fa
  0x0c187fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c187fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==922888==ABORTING
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <dlfcn.h>
// Demo of a buffer overflow that occurs only when FTZ/DAZ are enabled.
// In this imaginary app, we have a graphics library that uses a floating point
// coordinate space, but then renders to a pixel buffer whose coordinates are
// integers and in the opposite direction.
#define PIXBUF_WIDTH 128
void put_pixel(unsigned char *pixbuf, int width, float x, unsigned char color) {
int i_x;
// Safety check: coordinate space goes from right to left, so take the
// ceiling of the float to get the pixel coordinate. Check if it's too
// close to 0 to avoid a buffer overflow.
if (x >= nextafterf(0.0, INFINITY)) {
i_x = (int)ceilf(x);
}
else {
i_x = 1;
}
// Check the other end.
if (x > width) {
i_x = (int)width;
}
fprintf(stderr, "DEBUG: Will write to pixbuf[%d]\n", PIXBUF_WIDTH - i_x);
pixbuf[PIXBUF_WIDTH - i_x] = color;
}
int main(int argc, char **argv) {
if (argc > 1 && strcmp(argv[1], "fast") == 0) {
// Load the fast math library (in reality just an empty file built
// with -ffast-math).
void *handle = dlopen("./gofast.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Failed to load ./gofast.so: %s\n", dlerror());
}
else {
fprintf(stderr, "Loaded ./gofast.so\n");
fprintf(stderr, "Fast math mode enabled! Gotta go fast!\n");
}
}
unsigned char *pixbuf = malloc(PIXBUF_WIDTH);
memset(pixbuf, 0, PIXBUF_WIDTH);
put_pixel(pixbuf, PIXBUF_WIDTH, 0.0, 0xff);
free(pixbuf);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment