github.com/anchore/syft@v1.4.2-0.20240516191711-1bec1fc5d397/syft/file/cataloger/executable/test-fixtures/elf/project/main.c (about) 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdint.h> 4 5 // source: https://github.com/trailofbits/clang-cfi-showcase/blob/master/cfi_icall.c 6 7 typedef int (*int_arg_fn)(int); 8 typedef int (*float_arg_fn)(float); 9 10 static int int_arg(int arg) { 11 printf("In %s: (%d)\n", __FUNCTION__, arg); 12 return 0; 13 } 14 15 static int float_arg(float arg) { 16 printf("CFI should protect transfer to here\n"); 17 printf("In %s: (%f)\n", __FUNCTION__, (double)arg); 18 return 0; 19 } 20 21 static int bad_int_arg(int arg) { 22 printf("CFI will not protect transfer to here\n"); 23 printf("In %s: (%d)\n", __FUNCTION__, arg); 24 return 0; 25 } 26 27 static int not_entry_point(int arg) { 28 // nop sled for x86 / x86-64 29 // these instructions act as a buffer 30 // for an indirect control flow transfer to skip 31 // a valid function entry point, but continue 32 // to execute normal code 33 __asm__ volatile ( 34 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 35 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 36 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 37 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 38 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 39 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 40 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 41 "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" "nop\n" 42 ); 43 printf("CFI ensures control flow only transfers to potentially valid destinations\n"); 44 printf("In %s: (%d)\n", __FUNCTION__, arg); 45 // need to exit or the program will segfault anyway, 46 // since the indirect call skipped the function preamble 47 exit(arg); 48 } 49 50 struct foo { 51 int_arg_fn int_funcs[1]; 52 int_arg_fn bad_int_funcs[1]; 53 float_arg_fn float_funcs[1]; 54 int_arg_fn not_entries[1]; 55 }; 56 57 // the struct aligns the function pointer arrays 58 // so indexing past the end will reliably 59 // call working function pointers 60 static struct foo f = { 61 .int_funcs = {int_arg}, 62 .bad_int_funcs = {bad_int_arg}, 63 .float_funcs = {float_arg}, 64 .not_entries = {(int_arg_fn)((uintptr_t)(not_entry_point)+0x20)} 65 }; 66 67 void simple1() { 68 char buf[16]; 69 fgets(buf, sizeof(buf), stdin); 70 printf(buf); 71 } 72 73 void simple2() { 74 char buf[16]; 75 scanf("%s", buf); 76 } 77 78 79 int main(int argc, char **argv) { 80 if(argc != 2) { 81 printf("Usage: %s <option>\n", argv[0]); 82 printf("Option values:\n"); 83 printf("\t0\tCall correct function\n"); 84 printf("\t1\tCall the wrong function but with the same signature\n"); 85 printf("\t2\tCall a float function with an int function signature\n"); 86 printf("\t3\tCall into the middle of a function\n"); 87 printf("\n"); 88 printf("\tAll other options are undefined, but should be caught by CFI :)\n"); 89 printf("\n\n"); 90 printf("Here are some pointers so clang doesn't optimize away members of `struct foo f`:\n"); 91 printf("\tint_funcs: %p\n", (void*)f.int_funcs); 92 printf("\tbad_int_funcs: %p\n", (void*)f.bad_int_funcs); 93 printf("\tfloat_funcs: %p\n", (void*)f.float_funcs); 94 printf("\tnot_entries: %p\n", (void*)f.not_entries); 95 return 1; 96 } 97 98 simple1(); 99 simple2(); 100 101 printf("Calling a function:\n"); 102 103 int idx = argv[1][0] - '0'; 104 105 return f.int_funcs[idx](idx); 106 }