Here is the minimal reproducible example:
// my_func.h
typedef volatile struct {
int a;
} my_vdata_t;
typedef struct {
int a;
} my_data_t;
extern void (*vfunc)(my_vdata_t* data);
extern void (*func)(my_data_t* data);
void init_func();
// my_func.c
#include "stdio.h"
#include "my_func.h"
void my_vfunc( my_vdata_t* data ) {
printf("volatile a = %d\n", data->a);
}
void my_func( my_data_t* data ) {
printf("a = %d\n", data->a);
}
void (*vfunc)(my_vdata_t*) = NULL;
void (*func)(my_data_t*) = NULL;
void init_func() {
vfunc = my_vfunc;
func = my_func;
}
// main.c
#include "stdio.h"
#include "my_func.h"
int main() {
init_func();
my_vdata_t my_vdata;
my_vdata.a = 100;
my_data_t my_data;
my_data.a = 200;
func(&my_data);
vfunc(&my_vdata);
}
The original issue is from kernel code built with clang-16
and -fsanitize=kcfi
but
my test environment is with clang-6
and -fsanitize=cfi-icall -flto
instead.
clang -m32 -g -O3 -fsanitize=cfi-icall -flto -c my_func.c my_func.o
clang -m32 -g -O3 -fsanitize=cfi-icall -flto -c main.c main.o
clang -m32 -fsanitize=cfi-icall -flto -o test main.o my_func.o
the error output:
a = 200
Illegal instruction (core dumped)
As you can see, program exited with signal SIGILL, Illegal instruction.
If I remove -fsanitize=cfi-icall -flto
during compilation, the output is within expectation:
a = 200
volatile a = 100
without illegal instruction error.
This MRE is a bit different from my original case, but still confuses my a lot.
Looks like volatile
keyword conflicts with -fsanitize=cfi-icall
sanitizer.
This is issue 106593: https://github.com/llvm/llvm-project/issues/106593
You can work round it by adding a tag to the struct type definiton:
typedef volatile struct my_vdata {
int a;
} my_vdata_t;