blob: f89126a05e89a704632392ef0c7690fea573e0e0 [file] [edit]
// Test that -fsanitize-cfi-icall-experimental-normalize-integers does not
// normalize C++ class vtable !type metadata and break WholeProgramDevirt:
// 1. Uniform ThinLTO with --lto-validate-all-vtables-have-type-infos still
// matches vtable !type metadata against RTTI _ZTI symbols and
// devirtualizes single-implementation virtual calls.
// 2. Mixed-TU ThinLTO (where only some TUs pass
// -fsanitize-cfi-icall-experimental-normalize-integers) still sees
// overriding subclasses across TUs and does not unsoundly devirtualize.
// REQUIRES: linux, lld-available, thinlto, cxxabi, (x86_64-target-arch || aarch64-target-arch)
// Static libunwind is built with -fno-rtti, so allow its internal UnwindCursor
// vtables under --lto-validate-all-vtables-have-type-infos.
// --- KCFI: uniform + --lto-validate-all-vtables-have-type-infos ---
// RUN: %clangxx -O2 -flto=thin -fno-split-lto-unit -fwhole-program-vtables \
// RUN: -fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_A -c %s -o %t.kcfi.a.o
// RUN: %clangxx -O2 -flto=thin -fno-split-lto-unit -fwhole-program-vtables \
// RUN: -fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_MAIN -c %s -o %t.kcfi.main.o
// RUN: %clangxx -O2 -flto=thin -fuse-ld=lld %t.kcfi.a.o %t.kcfi.main.o \
// RUN: -o %t.kcfi.uniform -Wl,--lto-whole-program-visibility \
// RUN: -Wl,--lto-validate-all-vtables-have-type-infos \
// RUN: -Wl,--lto-known-safe-vtables='_ZTVN9libunwind*' \
// RUN: -Wl,-mllvm,-pass-remarks=wholeprogramdevirt 2>&1 | \
// RUN: FileCheck %s --check-prefix=DEVIRT
// RUN: %run %t.kcfi.uniform
// --- KCFI: mixed-TU (TU_B without normalize-integers defines overrides) ---
// RUN: %clangxx -O2 -flto=thin -fno-split-lto-unit -fwhole-program-vtables \
// RUN: -DTU_B -c %s -o %t.plain.b.o
// RUN: %clangxx -O2 -flto=thin -fno-split-lto-unit -fwhole-program-vtables \
// RUN: -fsanitize=kcfi -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_MAIN -DWITH_ALT -c %s -o %t.kcfi.main_alt.o
// RUN: %clangxx -O2 -flto=thin -fuse-ld=lld %t.kcfi.a.o %t.plain.b.o \
// RUN: %t.kcfi.main_alt.o -o %t.kcfi.mixed -Wl,--lto-whole-program-visibility
// RUN: %run %t.kcfi.mixed
// --- cfi-icall: uniform + --lto-validate-all-vtables-have-type-infos ---
// RUN: %clangxx -O2 -flto=thin -fwhole-program-vtables \
// RUN: -fsanitize=cfi-icall -fsanitize-trap=cfi-icall \
// RUN: -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_A -c %s -o %t.cfi.a.o
// RUN: %clangxx -O2 -flto=thin -fwhole-program-vtables \
// RUN: -fsanitize=cfi-icall -fsanitize-trap=cfi-icall \
// RUN: -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_MAIN -c %s -o %t.cfi.main.o
// RUN: %clangxx -O2 -flto=thin -fuse-ld=lld -fsanitize=cfi-icall \
// RUN: -fsanitize-trap=cfi-icall %t.cfi.a.o %t.cfi.main.o \
// RUN: -o %t.cfi.uniform -Wl,--lto-whole-program-visibility \
// RUN: -Wl,--lto-validate-all-vtables-have-type-infos \
// RUN: -Wl,--lto-known-safe-vtables='_ZTVN9libunwind*' \
// RUN: -Wl,-mllvm,-pass-remarks=wholeprogramdevirt 2>&1 | \
// RUN: FileCheck %s --check-prefix=DEVIRT
// RUN: %run %t.cfi.uniform
// --- cfi-icall: mixed-TU (TU_B without normalize-integers defines overrides) ---
// RUN: %clangxx -O2 -flto=thin -fwhole-program-vtables \
// RUN: -DTU_B -c %s -o %t.plain_split.b.o
// RUN: %clangxx -O2 -flto=thin -fwhole-program-vtables \
// RUN: -fsanitize=cfi-icall -fsanitize-trap=cfi-icall \
// RUN: -fsanitize-cfi-icall-experimental-normalize-integers \
// RUN: -DTU_MAIN -DWITH_ALT -c %s -o %t.cfi.main_alt.o
// RUN: %clangxx -O2 -flto=thin -fuse-ld=lld -fsanitize=cfi-icall \
// RUN: -fsanitize-trap=cfi-icall %t.cfi.a.o %t.plain_split.b.o \
// RUN: %t.cfi.main_alt.o -o %t.cfi.mixed -Wl,--lto-whole-program-visibility
// RUN: %run %t.cfi.mixed
// DEVIRT-DAG: single-impl: devirtualized a call to _ZNK12_GLOBAL__N_17Primary3tagEv
// DEVIRT-DAG: single-impl: devirtualized a call to _ZNK12_GLOBAL__N_17Primary3valEi
// DEVIRT-DAG: single-impl: devirtualized a call to _ZNK3BoxIiE3getEi
struct Shape {
virtual int tag() const = 0;
virtual int val(int x) const = 0;
virtual ~Shape() = default;
};
template <typename T> struct Box {
virtual T get(T x) const { return x + 1; }
virtual ~Box() = default;
};
Shape *make_primary();
Box<int> *make_primary_box();
Shape *make_alt();
Box<int> *make_alt_box();
int add_ten(int x);
int apply_icall(int (*fn)(int), int x);
int eval_shape(const Shape *s, const Box<int> *b, int x);
#if defined(TU_A)
namespace {
struct Primary : Shape {
int tag() const override { return 10; }
int val(int x) const override { return x + 100; }
};
} // namespace
template struct Box<int>;
Shape *make_primary() {
static Primary p;
return &p;
}
Box<int> *make_primary_box() {
static Box<int> b;
return &b;
}
int add_ten(int x) { return x + 10; }
__attribute__((noinline)) int apply_icall(int (*fn)(int), int x) {
return fn(x);
}
__attribute__((noinline)) int eval_shape(const Shape *s, const Box<int> *b,
int x) {
return s->tag() + s->val(x) + b->get(x);
}
#elif defined(TU_B)
namespace {
struct Alt : Shape {
int tag() const override { return 20; }
int val(int x) const override { return x + 200; }
};
struct AltBox : Box<int> {
int get(int x) const override { return x + 50; }
};
} // namespace
Shape *make_alt() {
static Alt a;
return &a;
}
Box<int> *make_alt_box() {
static AltBox ab;
return &ab;
}
#elif defined(TU_MAIN)
int main() {
if (apply_icall(&add_ten, 5) != 15)
return 1;
if (eval_shape(make_primary(), make_primary_box(), 5) != 121)
return 2;
# if defined(WITH_ALT)
if (eval_shape(make_alt(), make_alt_box(), 5) != 280)
return 3;
# endif
return 0;
}
#endif