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