1

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();"
asked Jan 16, 2025 at 7:05
7
  • The posted output does not match the shell script or the C code, although the full output does. Commented Jan 16, 2025 at 11:02
  • @IanAbbott I forgot to update that, now fixed. Commented 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. Commented Jan 29, 2025 at 5:50
  • @NateEldredge stackoverflow.com/q/79407929 Commented 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. Commented Feb 3, 2025 at 15:54

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.