[cfi] CFI and KCFI reject indirect calls that are valid according to the C standard
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Consider the following C program ([Godbolt](https://godbolt.org/z/bsKfK9bfa)):
```c
// lib.c
#include
typedef struct {
char a;
} foo;
void fun(foo s) {
printf("Field a has value %d\n", s.a);
return;
}
void* fnptr = (void*)fun;
```
```c
// main.c (linked together with lib.c)
extern void* fnptr;
typedef struct {
char a;
} bar;
int main() {
bar s = {42};
void (*casted)(bar) = (void (*)(bar))fnptr;
casted(s);
return 0;
}
```
According to the C standard, the behavior should be [well-defined](https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf#subsubsection.6.5.3.3). `foo` and `bar` are [compatible types](https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf#subsection.6.2.7), as they are both completed `structs` with identical members in identical order declared without tags in separate translation units. However, with CFI or KCFI indirect call checking enabled, the program crashes.
The underlying issue is that CFI identifies the `typedef`ed no-tag structs based on the hash of the `typedef` name. This is incorrect; the CFI hash for these tag-less types should instead be based on the names and types of their fields.
Contributor guide
Research direction
Reproduce the failure with the lib.c and main.c examples under CFI and KCFI, starting from the indirect-call type-hash generation path. Trace how the tag-less foo and bar structs are identified; done means compatible structs derive matching hashes from their fields and the call is no longer rejected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100