Issue
Background
I have an array of structs which store function pointers, which I loop over and call (my real code is more complex than the sample given). To ensure correct behaviour, I enabled CFI with -fsanitize=cfi -fvisiblity=hidden -flto -fcf-protection=full.
I then started to encounter false positives, where CFI is trapping upon calling these function pointers. These only seem to occur on compilers clang-11,12,13,14,15, and not on clang-18. This also only occurs when -fsanitize=cfi, -fcf-protection=full are used together.
Pattern
The issue does not appear to have any pattern that I can discern. I have identified multiple different changes that seem to affect both if and where the code fails:
- making the function
static - making the struct variable
static - making the struct variable
const - making the struct array
static - making the struct array
const - making the array length variable
static - making the array length variable
const - changing the optimisation level
- changing the compiler version
There are three different results I can get:
- all functions are called properly
- failure on the first function call
- failure on the second function call
Correctness
I believe that my code is correct, as none of these changes should be affecting the program's behaviour. Since I am using function pointers, I would expect it to be a casting issue, but this cannot be the case as there are no casts in the sample I have given. As far as I can tell, there is no issue with my code (no warnings even with -std=c89 -Wpedantic -Wall -Werror).
Am I correct in thinking this is a bug in Clang?
Sample
Code
#include <stdio.h>
typedef void (Func)(void);
typedef struct { char *name; Func *ptr; } Function;
STATIC_FUNC void func_a(void) { printf("()"); }
STATIC_FUNC void func_b(void) { printf("()"); }
STATIC_FUNC void func_c(void) { printf("()"); }
STATIC_FUNC void func_d(void) { printf("()"); }
STATIC_VAR CONST_STRUCT Function
function_a = { "a", &func_a },
function_b = { "b", &func_b },
function_c = { "c", &func_c },
function_d = { "d", &func_d };
STATIC_VAR CONST_STRUCT Function *const functions[] = {
&function_a,
&function_b,
&function_c,
&function_d,
};
STATIC_LEN CONST_LEN size_t functions_len = sizeof functions / sizeof *functions;
int main(void) {
size_t i;
setbuf(stdout, NULL);
for (i = 0; i < functions_len; i++) {
CONST_STRUCT Function *const func = functions[i];
printf("%s", func->name);
func->ptr();
printf(";");
}
return 0;
}
Shell Wrapper
#! /bin/bash
echo "Clang Version, Optimisation, Static Func, Static Variables, Const Struct, Static Len, Const Len, Result, Stdout"
for VER in 11 12 13 14 15 18; do
for OPT in 0 g s z 1 2 3 fast; do
for STATIC_FUNC in false true; do
for STATIC_VAR in false true; do
for CONST_STRUCT in false true; do
for STATIC_LEN in false true; do
for CONST_LEN in false true; do
rm ./main -f
clang-$VER main.c -o ./main -O$OPT \
-D STATIC_FUNC=$(if ( $STATIC_FUNC ); then echo 'static'; fi) \
-D STATIC_VAR=$(if ( $STATIC_VAR ); then echo 'static'; fi) \
-D CONST_STRUCT=$(if ( $CONST_STRUCT ); then echo 'const'; fi) \
-D STATIC_LEN=$(if ( $STATIC_LEN ); then echo 'static'; fi) \
-D CONST_LEN=$(if ( $CONST_LEN ); then echo 'const'; fi) \
-fsanitize=cfi -fvisibility=hidden -flto -fcf-protection=full \
-std=c89 -Wpedantic -Wall -Werror
STDOUT=`./main 2>/dev/null`
RESULT=$(if [ $? -eq 0 ]; then echo 'pass'; else echo 'fail'; fi)
echo "$VER, -O$OPT, $STATIC_FUNC, $STATIC_VAR, $CONST_STRUCT, $STATIC_LEN, $CONST_LEN, $RESULT, \"$STDOUT\""
done
done
done
done
done
done
done
Output
The full output is too long to post, but can be found here
Clang Version, Optimisation, Static Func, Static Variables, Const Struct, Static Len, Const Len, Result, Stdout
11, -O0, false, false, false, false, false, fail, "a();b"
11, -O0, false, false, false, false, true, fail, "a();b"
11, -O0, false, false, false, true, false, fail, "a();b"
11, -O0, false, false, false, true, true, fail, "a();b"
11, -O0, false, false, true, false, false, fail, "a();b"
11, -O0, false, false, true, false, true, fail, "a();b"
11, -O0, false, false, true, true, false, fail, "a();b"
11, -O0, false, false, true, true, true, fail, "a();b"
11, -O0, false, true, false, false, false, fail, "a();b"
11, -O0, false, true, false, false, true, fail, "a();b"
11, -O0, false, true, false, true, false, fail, "a();b"
11, -O0, false, true, false, true, true, fail, "a();b"
11, -O0, false, true, true, false, false, fail, "a();b"
11, -O0, false, true, true, false, true, fail, "a();b"
11, -O0, false, true, true, true, false, fail, "a();b"
11, -O0, false, true, true, true, true, fail, "a();b"
11, -O0, true, false, false, false, false, fail, "a"
11, -O0, true, false, false, false, true, fail, "a"
11, -O0, true, false, false, true, false, fail, "a"
11, -O0, true, false, false, true, true, fail, "a"
11, -O0, true, false, true, false, false, fail, "a"
11, -O0, true, false, true, false, true, fail, "a"
11, -O0, true, false, true, true, false, fail, "a"
11, -O0, true, false, true, true, true, fail, "a"
11, -O0, true, true, false, false, false, fail, "a"
11, -O0, true, true, false, false, true, fail, "a"
11, -O0, true, true, false, true, false, fail, "a"
11, -O0, true, true, false, true, true, fail, "a"
11, -O0, true, true, true, false, false, fail, "a"
11, -O0, true, true, true, false, true, fail, "a"
11, -O0, true, true, true, true, false, fail, "a"
11, -O0, true, true, true, true, true, fail, "a"
11, -Og, false, false, false, false, false, fail, "a();b"
11, -Og, false, false, false, false, true, fail, "a();b"
11, -Og, false, false, false, true, false, fail, "a();b"
11, -Og, false, false, false, true, true, fail, "a();b"
11, -Og, false, false, true, false, false, fail, "a();b"
11, -Og, false, false, true, false, true, fail, "a();b"
11, -Og, false, false, true, true, false, fail, "a();b"
11, -Og, false, false, true, true, true, fail, "a();b"
11, -Og, false, true, false, false, false, fail, "a();b"
11, -Og, false, true, false, false, true, fail, "a();b"
11, -Og, false, true, false, true, false, fail, "a();b"
11, -Og, false, true, false, true, true, fail, "a();b"
11, -Og, false, true, true, false, false, fail, "a();b"
11, -Og, false, true, true, false, true, fail, "a();b"
11, -Og, false, true, true, true, false, fail, "a();b"
11, -Og, false, true, true, true, true, fail, "a();b"
11, -Og, true, false, false, false, false, fail, "a"
11, -Og, true, false, false, false, true, fail, "a"
11, -Og, true, false, false, true, false, fail, "a"
11, -Og, true, false, false, true, true, fail, "a"
11, -Og, true, false, true, false, false, fail, "a"
11, -Og, true, false, true, false, true, fail, "a"
11, -Og, true, false, true, true, false, fail, "a"
11, -Og, true, false, true, true, true, fail, "a"
11, -Og, true, true, false, false, false, fail, "a"
11, -Og, true, true, false, false, true, fail, "a"
11, -Og, true, true, false, true, false, fail, "a"
11, -Og, true, true, false, true, true, fail, "a"
11, -Og, true, true, true, false, false, fail, "a"
11, -Og, true, true, true, false, true, fail, "a"
11, -Og, true, true, true, true, false, fail, "a"
11, -Og, true, true, true, true, true, fail, "a"
11, -Os, false, false, false, false, false, fail, "a();b"
11, -Os, false, false, false, false, true, fail, "a();b"
11, -Os, false, false, false, true, false, fail, "a();b"
11, -Os, false, false, false, true, true, fail, "a();b"
11, -Os, false, false, true, false, false, fail, "a();b"
11, -Os, false, false, true, false, true, fail, "a();b"
11, -Os, false, false, true, true, false, fail, "a();b"
11, -Os, false, false, true, true, true, fail, "a();b"
11, -Os, false, true, false, false, false, fail, "a();b"
11, -Os, false, true, false, false, true, fail, "a();b"
11, -Os, false, true, false, true, false, fail, "a();b"
11, -Os, false, true, false, true, true, fail, "a();b"
11, -Os, false, true, true, false, false, fail, "a();b"
11, -Os, false, true, true, false, true, fail, "a();b"
11, -Os, false, true, true, true, false, fail, "a();b"
11, -Os, false, true, true, true, true, fail, "a();b"
11, -Os, true, false, false, false, false, fail, "a"
11, -Os, true, false, false, false, true, fail, "a"
11, -Os, true, false, false, true, false, fail, "a"
11, -Os, true, false, false, true, true, fail, "a"
11, -Os, true, false, true, false, false, fail, "a"
11, -Os, true, false, true, false, true, fail, "a"
11, -Os, true, false, true, true, false, fail, "a"
11, -Os, true, false, true, true, true, fail, "a"
11, -Os, true, true, false, false, false, fail, "a"
11, -Os, true, true, false, false, true, fail, "a"
11, -Os, true, true, false, true, false, fail, "a"
11, -Os, true, true, false, true, true, fail, "a"
11, -Os, true, true, true, false, false, fail, "a"
11, -Os, true, true, true, false, true, fail, "a"
11, -Os, true, true, true, true, false, fail, "a"
11, -Os, true, true, true, true, true, fail, "a"
11, -Oz, false, false, false, false, false, fail, "a();b"
11, -Oz, false, false, false, false, true, fail, "a();b"
11, -Oz, false, false, false, true, false, fail, "a();b"
11, -Oz, false, false, false, true, true, fail, "a();b"
11, -Oz, false, false, true, false, false, fail, "a();b"
11, -Oz, false, false, true, false, true, fail, "a();b"
11, -Oz, false, false, true, true, false, fail, "a();b"
11, -Oz, false, false, true, true, true, fail, "a();b"
11, -Oz, false, true, false, false, false, fail, "a();b"
11, -Oz, false, true, false, false, true, fail, "a();b"
11, -Oz, false, true, false, true, false, fail, "a();b"
11, -Oz, false, true, false, true, true, fail, "a();b"
11, -Oz, false, true, true, false, false, fail, "a();b"
11, -Oz, false, true, true, false, true, fail, "a();b"
11, -Oz, false, true, true, true, false, fail, "a();b"
11, -Oz, false, true, true, true, true, fail, "a();b"
11, -Oz, true, false, false, false, false, fail, "a"
11, -Oz, true, false, false, false, true, fail, "a"
11, -Oz, true, false, false, true, false, fail, "a"
11, -Oz, true, false, false, true, true, fail, "a"
11, -Oz, true, false, true, false, false, fail, "a"
11, -Oz, true, false, true, false, true, fail, "a"
11, -Oz, true, false, true, true, false, fail, "a"
11, -Oz, true, false, true, true, true, fail, "a"
11, -Oz, true, true, false, false, false, fail, "a"
11, -Oz, true, true, false, false, true, fail, "a"
11, -Oz, true, true, false, true, false, fail, "a"
11, -Oz, true, true, false, true, true, fail, "a"
11, -Oz, true, true, true, false, false, fail, "a"
11, -Oz, true, true, true, false, true, fail, "a"
11, -Oz, true, true, true, true, false, fail, "a"
11, -Oz, true, true, true, true, true, fail, "a"
11, -O1, false, false, false, false, false, fail, "a();b"
11, -O1, false, false, false, false, true, fail, "a();b"
11, -O1, false, false, false, true, false, fail, "a();b"
11, -O1, false, false, false, true, true, fail, "a();b"
11, -O1, false, false, true, false, false, fail, "a();b"
11, -O1, false, false, true, false, true, fail, "a();b"
11, -O1, false, false, true, true, false, fail, "a();b"
11, -O1, false, false, true, true, true, fail, "a();b"
11, -O1, false, true, false, false, false, fail, "a();b"
11, -O1, false, true, false, false, true, fail, "a();b"
11, -O1, false, true, false, true, false, fail, "a();b"
11, -O1, false, true, false, true, true, fail, "a();b"
11, -O1, false, true, true, false, false, fail, "a();b"
11, -O1, false, true, true, false, true, fail, "a();b"
11, -O1, false, true, true, true, false, fail, "a();b"
11, -O1, false, true, true, true, true, fail, "a();b"
11, -O1, true, false, false, false, false, fail, "a"
11, -O1, true, false, false, false, true, fail, "a"
11, -O1, true, false, false, true, false, fail, "a"
11, -O1, true, false, false, true, true, fail, "a"
11, -O1, true, false, true, false, false, fail, "a"
11, -O1, true, false, true, false, true, fail, "a"
11, -O1, true, false, true, true, false, fail, "a"
11, -O1, true, false, true, true, true, fail, "a"
11, -O1, true, true, false, false, false, fail, "a"
11, -O1, true, true, false, false, true, fail, "a"
11, -O1, true, true, false, true, false, fail, "a"
11, -O1, true, true, false, true, true, fail, "a"
11, -O1, true, true, true, false, false, fail, "a"
11, -O1, true, true, true, false, true, fail, "a"
11, -O1, true, true, true, true, false, fail, "a"
11, -O1, true, true, true, true, true, fail, "a"
11, -O2, false, false, false, false, false, fail, "a();b"
11, -O2, false, false, false, false, true, pass, "a();b();c();d();"
11, -O2, false, false, false, true, false, pass, "a();b();c();d();"
11, -O2, false, false, false, true, true, pass, "a();b();c();d();"
11, -O2, false, false, true, false, false, pass, "a();b();c();d();"
11, -O2, false, false, true, false, true, pass, "a();b();c();d();"
11, -O2, false, false, true, true, false, pass, "a();b();c();d();"
11, -O2, false, false, true, true, true, pass, "a();b();c();d();"
11, -O2, false, true, false, false, false, fail, "a();b"
11, -O2, false, true, false, false, true, pass, "a();b();c();d();"
11, -O2, false, true, false, true, false, pass, "a();b();c();d();"
11, -O2, false, true, false, true, true, pass, "a();b();c();d();"
11, -O2, false, true, true, false, false, pass, "a();b();c();d();"
11, -O2, false, true, true, false, true, pass, "a();b();c();d();"
11, -O2, false, true, true, true, false, pass, "a();b();c();d();"
11, -O2, false, true, true, true, true, pass, "a();b();c();d();"
11, -O2, true, false, false, false, false, fail, "a"
11, -O2, true, false, false, false, true, pass, "a();b();c();d();"
11, -O2, true, false, false, true, false, pass, "a();b();c();d();"
11, -O2, true, false, false, true, true, pass, "a();b();c();d();"
11, -O2, true, false, true, false, false, pass, "a();b();c();d();"
11, -O2, true, false, true, false, true, pass, "a();b();c();d();"
11, -O2, true, false, true, true, false, pass, "a();b();c();d();"
11, -O2, true, false, true, true, true, pass, "a();b();c();d();"
11, -O2, true, true, false, false, false, fail, "a"
11, -O2, true, true, false, false, true, pass, "a();b();c();d();"
11, -O2, true, true, false, true, false, pass, "a();b();c();d();"
11, -O2, true, true, false, true, true, pass, "a();b();c();d();"
11, -O2, true, true, true, false, false, pass, "a();b();c();d();"
11, -O2, true, true, true, false, true, pass, "a();b();c();d();"
11, -O2, true, true, true, true, false, pass, "a();b();c();d();"
11, -O2, true, true, true, true, true, pass, "a();b();c();d();"
11, -O3, false, false, false, false, false, fail, "a();b"
11, -O3, false, false, false, false, true, pass, "a();b();c();d();"
11, -O3, false, false, false, true, false, pass, "a();b();c();d();"
11, -O3, false, false, false, true, true, pass, "a();b();c();d();"
11, -O3, false, false, true, false, false, pass, "a();b();c();d();"
11, -O3, false, false, true, false, true, pass, "a();b();c();d();"
11, -O3, false, false, true, true, false, pass, "a();b();c();d();"
11, -O3, false, false, true, true, true, pass, "a();b();c();d();"
11, -O3, false, true, false, false, false, fail, "a();b"
11, -O3, false, true, false, false, true, pass, "a();b();c();d();"
11, -O3, false, true, false, true, false, pass, "a();b();c();d();"
11, -O3, false, true, false, true, true, pass, "a();b();c();d();"
11, -O3, false, true, true, false, false, pass, "a();b();c();d();"
11, -O3, false, true, true, false, true, pass, "a();b();c();d();"
11, -O3, false, true, true, true, false, pass, "a();b();c();d();"
11, -O3, false, true, true, true, true, pass, "a();b();c();d();"
11, -O3, true, false, false, false, false, fail, "a"
11, -O3, true, false, false, false, true, pass, "a();b();c();d();"
11, -O3, true, false, false, true, false, pass, "a();b();c();d();"
11, -O3, true, false, false, true, true, pass, "a();b();c();d();"
11, -O3, true, false, true, false, false, pass, "a();b();c();d();"
11, -O3, true, false, true, false, true, pass, "a();b();c();d();"
11, -O3, true, false, true, true, false, pass, "a();b();c();d();"
11, -O3, true, false, true, true, true, pass, "a();b();c();d();"
11, -O3, true, true, false, false, false, fail, "a"
11, -O3, true, true, false, false, true, pass, "a();b();c();d();"
11, -O3, true, true, false, true, false, pass, "a();b();c();d();"
11, -O3, true, true, false, true, true, pass, "a();b();c();d();"
11, -O3, true, true, true, false, false, pass, "a();b();c();d();"
11, -O3, true, true, true, false, true, pass, "a();b();c();d();"
11, -O3, true, true, true, true, false, pass, "a();b();c();d();"
11, -O3, true, true, true, true, true, pass, "a();b();c();d();"
11, -Ofast, false, false, false, false, false, fail, "a();b"
11, -Ofast, false, false, false, false, true, pass, "a();b();c();d();"
11, -Ofast, false, false, false, true, false, pass, "a();b();c();d();"
11, -Ofast, false, false, false, true, true, pass, "a();b();c();d();"
11, -Ofast, false, false, true, false, false, pass, "a();b();c();d();"
11, -Ofast, false, false, true, false, true, pass, "a();b();c();d();"
11, -Ofast, false, false, true, true, false, pass, "a();b();c();d();"
11, -Ofast, false, false, true, true, true, pass, "a();b();c();d();"
11, -Ofast, false, true, false, false, false, fail, "a();b"
11, -Ofast, false, true, false, false, true, pass, "a();b();c();d();"
11, -Ofast, false, true, false, true, false, pass, "a();b();c();d();"
11, -Ofast, false, true, false, true, true, pass, "a();b();c();d();"
11, -Ofast, false, true, true, false, false, pass, "a();b();c();d();"
11, -Ofast, false, true, true, false, true, pass, "a();b();c();d();"
11, -Ofast, false, true, true, true, false, pass, "a();b();c();d();"
11, -Ofast, false, true, true, true, true, pass, "a();b();c();d();"
11, -Ofast, true, false, false, false, false, fail, "a"
11, -Ofast, true, false, false, false, true, pass, "a();b();c();d();"
11, -Ofast, true, false, false, true, false, pass, "a();b();c();d();"
11, -Ofast, true, false, false, true, true, pass, "a();b();c();d();"
11, -Ofast, true, false, true, false, false, pass, "a();b();c();d();"
11, -Ofast, true, false, true, false, true, pass, "a();b();c();d();"
11, -Ofast, true, false, true, true, false, pass, "a();b();c();d();"
11, -Ofast, true, false, true, true, true, pass, "a();b();c();d();"
11, -Ofast, true, true, false, false, false, fail, "a"
11, -Ofast, true, true, false, false, true, pass, "a();b();c();d();"
11, -Ofast, true, true, false, true, false, pass, "a();b();c();d();"
11, -Ofast, true, true, false, true, true, pass, "a();b();c();d();"
11, -Ofast, true, true, true, false, false, pass, "a();b();c();d();"
11, -Ofast, true, true, true, false, true, pass, "a();b();c();d();"
11, -Ofast, true, true, true, true, false, pass, "a();b();c();d();"
11, -Ofast, true, true, true, true, true, pass, "a();b();c();d();"
-
The posted output does not match the shell script or the C code, although the full output does.Ian Abbott– Ian Abbott2025年01月16日 11:02:41 +00:00Commented Jan 16, 2025 at 11:02
-
@IanAbbott I forgot to update that, now fixed.Anon– Anon2025年01月16日 11:46:49 +00:00Commented Jan 16, 2025 at 11:46
-
Could you focus on a single example that fails? The thing to do will be to look at the generated code and see if it is correct. When you have a version that fails, if you run it repeatedly, does it fail consistently every time? Also, tell us the target architecture and operating system.Nate Eldredge– Nate Eldredge2025年01月29日 05:50:54 +00:00Commented Jan 29, 2025 at 5:50
-
@NateEldredge stackoverflow.com/q/79407929Anon– Anon2025年02月03日 08:02:47 +00:00Commented Feb 3, 2025 at 8:02
-
As I noted on the other question, if it works on newer versions then it's reasonable to assume it is a bug that has been fixed. So if this question is just about whether an obsolete compiler version has a bug, then I don't see how it's of much interest to the community. If you have a more specific question (e.g. is the code free of undefined behavior? are such constructs documented as currently supported by Clang CFI?) then please ask it.Nate Eldredge– Nate Eldredge2025年02月03日 15:54:57 +00:00Commented Feb 3, 2025 at 15:54