llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
27.92k stars 11.52k forks source link

ThinLTO + -fno-sanitize-cfi-canonical-jump-tables doesn't work with function aliases #42997

Open samitolvanen opened 4 years ago

samitolvanen commented 4 years ago
Bugzilla Link 43652
Version trunk
OS Linux
CC @jgalenson

Extended Description

If I compile my program with ThinLTO and -fno-sanitize-cfi-canonical-jump-tables, I cannot make an indirect call to a function alias. I can reproduce the issue as follows:

$ cat test.c

include

typedef int (*func_t)(int);

int a(int n) { return 1; }

int b(int n) attribute((alias("a")));

int main() { func_t f[] = { a, b, NULL };

for (int i = 0; f[i] != NULL; i++) {
    printf("calling %016lx\n", (unsigned long)f[i]);
    printf("\t-> %d\n", f[i](0));
}

return 0;

}

I would expect indirect calls to both a and b work fine with CFI, as seen here:

$ clang -flto=thin -fuse-ld=lld -fvisibility=default -fsanitize=cfi test.c $ ./a.out calling 00000000002017b0 -> 1 calling 00000000002017b0 -> 1

However, if I enable -fno-sanitize-cfi-canonical-jump-tables, the test program fails to compile:

$ clang -flto=thin -fuse-ld=lld -fvisibility=default -fsanitize=cfi -fno-sanitize-cfi-canonical-jump-tables test.c ld.lld: error: undefined symbol: a

referenced by ld-temp.o lto.tmp:(a.cfi_jt) clang-10: error: linker command failed with exit code 1 (use -v to see invocation)

Adding -fsanitize-cfi-cross-dso allows me to compile the program again, but it now trips CFI:

$ clang -flto=thin -fuse-ld=lld -fvisibility=default -fsanitize=cfi -fno-sanitize-cfi-canonical-jump-tables -fsanitize-cfi-cross-dso test.c $ ./a.out calling 00005648c2aab030 -> 1 calling 00005648c2aab040 Illegal instruction

Looking at a.out, we have:

1125: 0000000000029030 8 FUNC LOCAL HIDDEN 14 a.cfi_jt 1204: 0000000000029040 14 FUNC GLOBAL DEFAULT 14 a 1207: 0000000000029040 14 FUNC GLOBAL DEFAULT 14 b

Which means the alias points directly to a, instead of a.cfi_jt. Switching to -flto works though:

$ clang -flto -fuse-ld=lld -fvisibility=default -fsanitize=cfi -fno-sanitize-cfi-canonical-jump-tables -fsanitize-cfi-cross-dso test.c $ ./a.out calling 0000563f82e14030 -> 1 calling 0000563f82e14030 -> 1

This is currently reproducible with ToT LLVM:

$ clang --version | head -n1 clang version 10.0.0 (https://github.com/llvm/llvm-project.git b95d4c3a99794f57b3ac7accd86f5c061f6c69f9) $ ld.lld --version LLD 10.0.0 (https://github.com/llvm/llvm-project.git b95d4c3a99794f57b3ac7accd86f5c061f6c69f9) (compatible with GNU linkers)

samitolvanen commented 4 years ago

assigned to @pcc