cclangfunction-pointersvolatilesanitizer

LLVM kCFI sanitizer with function of volatile arguments


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.


Solution

  • 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;