1 //===-------- cfi.cpp -----------------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file implements the runtime support for the cross-DSO CFI.
11 //===----------------------------------------------------------------------===//
16 #include "sanitizer_common/sanitizer_common.h"
18 #include <sys/link_elf.h>
26 typedef ElfW(Phdr
) Elf_Phdr
;
27 typedef ElfW(Ehdr
) Elf_Ehdr
;
28 typedef ElfW(Addr
) Elf_Addr
;
29 typedef ElfW(Sym
) Elf_Sym
;
30 typedef ElfW(Dyn
) Elf_Dyn
;
31 #elif SANITIZER_FREEBSD
32 #if SANITIZER_WORDSIZE == 64
33 #define ElfW64_Dyn Elf_Dyn
34 #define ElfW64_Sym Elf_Sym
36 #define ElfW32_Dyn Elf_Dyn
37 #define ElfW32_Sym Elf_Sym
41 #include "interception/interception.h"
42 #include "sanitizer_common/sanitizer_flag_parser.h"
43 #include "ubsan/ubsan_init.h"
44 #include "ubsan/ubsan_flags.h"
46 #ifdef CFI_ENABLE_DIAG
47 #include "ubsan/ubsan_handlers.h"
50 using namespace __sanitizer
;
54 #if SANITIZER_LOONGARCH64
55 #define kCfiShadowLimitsStorageSize 16384 // 16KiB on loongarch64 per page
57 #define kCfiShadowLimitsStorageSize 4096 // 1 page
59 // Lets hope that the data segment is mapped with 4K pages.
60 // The pointer to the cfi shadow region is stored at the start of this page.
61 // The rest of the page is unused and re-mapped read-only.
63 char space
[kCfiShadowLimitsStorageSize
];
68 } cfi_shadow_limits_storage
69 __attribute__((aligned(kCfiShadowLimitsStorageSize
)));
70 static constexpr uptr kShadowGranularity
= 12;
71 static constexpr uptr kShadowAlign
= 1UL << kShadowGranularity
; // 4096
73 static constexpr uint16_t kInvalidShadow
= 0;
74 static constexpr uint16_t kUncheckedShadow
= 0xFFFFU
;
76 // Get the start address of the CFI shadow region.
78 return cfi_shadow_limits_storage
.limits
.start
;
81 uptr
GetShadowSize() {
82 return cfi_shadow_limits_storage
.limits
.size
;
85 // This will only work while the shadow is not allocated.
86 void SetShadowSize(uptr size
) {
87 cfi_shadow_limits_storage
.limits
.size
= size
;
90 uptr
MemToShadowOffset(uptr x
) {
91 return (x
>> kShadowGranularity
) << 1;
94 uint16_t *MemToShadow(uptr x
, uptr shadow_base
) {
95 return (uint16_t *)(shadow_base
+ MemToShadowOffset(x
));
98 typedef int (*CFICheckFn
)(u64
, void *, void *);
100 // This class reads and decodes the shadow contents.
104 explicit ShadowValue(uptr addr
, uint16_t v
) : addr(addr
), v(v
) {}
107 bool is_invalid() const { return v
== kInvalidShadow
; }
109 bool is_unchecked() const { return v
== kUncheckedShadow
; }
111 CFICheckFn
get_cfi_check() const {
112 assert(!is_invalid() && !is_unchecked());
113 uptr aligned_addr
= addr
& ~(kShadowAlign
- 1);
114 uptr p
= aligned_addr
- (((uptr
)v
- 1) << kShadowGranularity
);
115 return reinterpret_cast<CFICheckFn
>(p
);
118 // Load a shadow value for the given application memory address.
119 static const ShadowValue
load(uptr addr
) {
120 uptr shadow_base
= GetShadow();
121 uptr shadow_offset
= MemToShadowOffset(addr
);
122 if (shadow_offset
> GetShadowSize())
123 return ShadowValue(addr
, kInvalidShadow
);
126 addr
, *reinterpret_cast<uint16_t *>(shadow_base
+ shadow_offset
));
130 class ShadowBuilder
{
134 // Allocate a new empty shadow (for the entire address space) on the side.
136 // Mark the given address range as unchecked.
137 // This is used for uninstrumented libraries like libc.
138 // Any CFI check with a target in that range will pass.
139 void AddUnchecked(uptr begin
, uptr end
);
140 // Mark the given address range as belonging to a library with the given
141 // cfi_check function.
142 void Add(uptr begin
, uptr end
, uptr cfi_check
);
143 // Finish shadow construction. Atomically switch the current active shadow
144 // region with the newly constructed one and deallocate the former.
148 void ShadowBuilder::Start() {
149 shadow_
= (uptr
)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow");
150 VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_
, shadow_
+ GetShadowSize());
153 void ShadowBuilder::AddUnchecked(uptr begin
, uptr end
) {
154 uint16_t *shadow_begin
= MemToShadow(begin
, shadow_
);
155 uint16_t *shadow_end
= MemToShadow(end
- 1, shadow_
) + 1;
156 // memset takes a byte, so our unchecked shadow value requires both bytes to
157 // be the same. Make sure we're ok during compilation.
158 static_assert((kUncheckedShadow
& 0xff) == ((kUncheckedShadow
>> 8) & 0xff),
159 "Both bytes of the 16-bit value must be the same!");
160 memset(shadow_begin
, kUncheckedShadow
& 0xff,
161 (shadow_end
- shadow_begin
) * sizeof(*shadow_begin
));
164 void ShadowBuilder::Add(uptr begin
, uptr end
, uptr cfi_check
) {
165 assert((cfi_check
& (kShadowAlign
- 1)) == 0);
167 // Don't fill anything below cfi_check. We can not represent those addresses
168 // in the shadow, and must make sure at codegen to place all valid call
169 // targets above cfi_check.
170 begin
= Max(begin
, cfi_check
);
171 uint16_t *s
= MemToShadow(begin
, shadow_
);
172 uint16_t *s_end
= MemToShadow(end
- 1, shadow_
) + 1;
173 uint16_t sv
= ((begin
- cfi_check
) >> kShadowGranularity
) + 1;
174 for (; s
< s_end
; s
++, sv
++)
178 #if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD
179 void ShadowBuilder::Install() {
180 MprotectReadOnly(shadow_
, GetShadowSize());
181 uptr main_shadow
= GetShadow();
185 void *res
= mremap((void *)shadow_
, GetShadowSize(), GetShadowSize(),
186 MREMAP_MAYMOVE
| MREMAP_FIXED
, (void *)main_shadow
);
187 CHECK(res
!= MAP_FAILED
);
188 #elif SANITIZER_NETBSD
189 void *res
= mremap((void *)shadow_
, GetShadowSize(), (void *)main_shadow
,
190 GetShadowSize(), MAP_FIXED
);
191 CHECK(res
!= MAP_FAILED
);
193 void *res
= MmapFixedOrDie(shadow_
, GetShadowSize(), "cfi shadow");
194 CHECK(res
!= MAP_FAILED
);
195 ::memcpy(&shadow_
, &main_shadow
, GetShadowSize());
199 CHECK_EQ(kCfiShadowLimitsStorageSize
, GetPageSizeCached());
200 CHECK_EQ(0, GetShadow());
201 cfi_shadow_limits_storage
.limits
.start
= shadow_
;
202 MprotectReadOnly((uptr
)&cfi_shadow_limits_storage
,
203 sizeof(cfi_shadow_limits_storage
));
204 CHECK_EQ(shadow_
, GetShadow());
208 #error not implemented
211 // This is a workaround for a glibc bug:
212 // https://sourceware.org/bugzilla/show_bug.cgi?id=15199
213 // Other platforms can, hopefully, just do
214 // dlopen(RTLD_NOLOAD | RTLD_LAZY)
215 // dlsym("__cfi_check").
216 uptr
find_cfi_check_in_dso(dl_phdr_info
*info
) {
217 const Elf_Dyn
*dynamic
= nullptr;
218 for (int i
= 0; i
< info
->dlpi_phnum
; ++i
) {
219 if (info
->dlpi_phdr
[i
].p_type
== PT_DYNAMIC
) {
221 (const Elf_Dyn
*)(info
->dlpi_addr
+ info
->dlpi_phdr
[i
].p_vaddr
);
225 if (!dynamic
) return 0;
226 uptr strtab
= 0, symtab
= 0, strsz
= 0;
227 for (const Elf_Dyn
*p
= dynamic
; p
->d_tag
!= PT_NULL
; ++p
) {
228 if (p
->d_tag
== DT_SYMTAB
)
229 symtab
= p
->d_un
.d_ptr
;
230 else if (p
->d_tag
== DT_STRTAB
)
231 strtab
= p
->d_un
.d_ptr
;
232 else if (p
->d_tag
== DT_STRSZ
)
233 strsz
= p
->d_un
.d_ptr
;
236 if (symtab
> strtab
) {
237 VReport(1, "Can not handle: symtab > strtab (%zx > %zx)\n", symtab
, strtab
);
241 // Verify that strtab and symtab are inside of the same LOAD segment.
242 // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
244 for (phdr_idx
= 0; phdr_idx
< info
->dlpi_phnum
; phdr_idx
++) {
245 const Elf_Phdr
*phdr
= &info
->dlpi_phdr
[phdr_idx
];
246 if (phdr
->p_type
== PT_LOAD
) {
247 uptr beg
= info
->dlpi_addr
+ phdr
->p_vaddr
;
248 uptr end
= beg
+ phdr
->p_memsz
;
249 if (strtab
>= beg
&& strtab
+ strsz
< end
&& symtab
>= beg
&&
254 if (phdr_idx
== info
->dlpi_phnum
) {
255 // Nope, either different segments or just bogus pointers.
256 // Can not handle this.
257 VReport(1, "Can not handle: symtab %zx, strtab %zx\n", symtab
, strtab
);
261 for (const Elf_Sym
*p
= (const Elf_Sym
*)symtab
; (Elf_Addr
)p
< strtab
;
263 // There is no reliable way to find the end of the symbol table. In
264 // lld-produces files, there are other sections between symtab and strtab.
265 // Stop looking when the symbol name is not inside strtab.
266 if (p
->st_name
>= strsz
) break;
267 char *name
= (char*)(strtab
+ p
->st_name
);
268 if (strcmp(name
, "__cfi_check") == 0) {
269 assert(p
->st_info
== ELF32_ST_INFO(STB_GLOBAL
, STT_FUNC
) ||
270 p
->st_info
== ELF32_ST_INFO(STB_WEAK
, STT_FUNC
));
271 uptr addr
= info
->dlpi_addr
+ p
->st_value
;
278 int dl_iterate_phdr_cb(dl_phdr_info
*info
, size_t size
, void *data
) {
279 uptr cfi_check
= find_cfi_check_in_dso(info
);
281 VReport(1, "Module '%s' __cfi_check %zx\n", info
->dlpi_name
, cfi_check
);
283 ShadowBuilder
*b
= reinterpret_cast<ShadowBuilder
*>(data
);
285 for (int i
= 0; i
< info
->dlpi_phnum
; i
++) {
286 const Elf_Phdr
*phdr
= &info
->dlpi_phdr
[i
];
287 if (phdr
->p_type
== PT_LOAD
) {
288 // Jump tables are in the executable segment.
289 // VTables are in the non-executable one.
290 // Need to fill shadow for both.
291 // FIXME: reject writable if vtables are in the r/o segment. Depend on
293 uptr cur_beg
= info
->dlpi_addr
+ phdr
->p_vaddr
;
294 uptr cur_end
= cur_beg
+ phdr
->p_memsz
;
296 VReport(1, " %zx .. %zx\n", cur_beg
, cur_end
);
297 b
->Add(cur_beg
, cur_end
, cfi_check
);
299 b
->AddUnchecked(cur_beg
, cur_end
);
306 // Init or update shadow for the current set of loaded libraries.
307 void UpdateShadow() {
310 dl_iterate_phdr(dl_iterate_phdr_cb
, &b
);
315 CHECK_EQ(0, GetShadow());
316 CHECK_EQ(0, GetShadowSize());
318 uptr vma
= GetMaxUserVirtualAddress();
319 // Shadow is 2 -> 2**kShadowGranularity.
320 SetShadowSize((vma
>> (kShadowGranularity
- 1)) + 1);
321 VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma
, GetShadowSize());
326 THREADLOCAL
int in_loader
;
327 Mutex shadow_update_lock
;
329 void EnterLoader() SANITIZER_NO_THREAD_SAFETY_ANALYSIS
{
330 if (in_loader
== 0) {
331 shadow_update_lock
.Lock();
336 void ExitLoader() SANITIZER_NO_THREAD_SAFETY_ANALYSIS
{
337 CHECK(in_loader
> 0);
340 if (in_loader
== 0) {
341 shadow_update_lock
.Unlock();
345 ALWAYS_INLINE
void CfiSlowPathCommon(u64 CallSiteTypeId
, void *Ptr
,
347 uptr Addr
= (uptr
)Ptr
;
348 VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId
, Ptr
);
349 ShadowValue sv
= ShadowValue::load(Addr
);
350 if (sv
.is_invalid()) {
351 VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr
);
352 #ifdef CFI_ENABLE_DIAG
354 __ubsan_handle_cfi_check_fail(
355 reinterpret_cast<__ubsan::CFICheckFailData
*>(DiagData
), Addr
, false);
361 if (sv
.is_unchecked()) {
362 VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr
);
365 CFICheckFn cfi_check
= sv
.get_cfi_check();
366 VReport(2, "__cfi_check at %p\n", (void *)cfi_check
);
367 cfi_check(CallSiteTypeId
, Ptr
, DiagData
);
370 void InitializeFlags() {
371 SetCommonFlagsDefaults();
372 #ifdef CFI_ENABLE_DIAG
373 __ubsan::Flags
*uf
= __ubsan::flags();
377 FlagParser cfi_parser
;
378 RegisterCommonFlags(&cfi_parser
);
379 cfi_parser
.ParseStringFromEnv("CFI_OPTIONS");
381 #ifdef CFI_ENABLE_DIAG
382 FlagParser ubsan_parser
;
383 __ubsan::RegisterUbsanFlags(&ubsan_parser
, uf
);
384 RegisterCommonFlags(&ubsan_parser
);
386 const char *ubsan_default_options
= __ubsan_default_options();
387 ubsan_parser
.ParseString(ubsan_default_options
);
388 ubsan_parser
.ParseStringFromEnv("UBSAN_OPTIONS");
391 InitializeCommonFlags();
394 ReportUnrecognizedFlags();
396 if (common_flags()->help
) {
397 cfi_parser
.PrintFlagDescriptions();
403 using namespace __cfi
;
405 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void
406 __cfi_slowpath(u64 CallSiteTypeId
, void *Ptr
) {
407 CfiSlowPathCommon(CallSiteTypeId
, Ptr
, nullptr);
410 #ifdef CFI_ENABLE_DIAG
411 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void
412 __cfi_slowpath_diag(u64 CallSiteTypeId
, void *Ptr
, void *DiagData
) {
413 CfiSlowPathCommon(CallSiteTypeId
, Ptr
, DiagData
);
417 static void EnsureInterceptorsInitialized();
419 // Setup shadow for dlopen()ed libraries.
420 // The actual shadow setup happens after dlopen() returns, which means that
421 // a library can not be a target of any CFI checks while its constructors are
422 // running. It's unclear how to fix this without some extra help from libc.
423 // In glibc, mmap inside dlopen is not interceptable.
424 // Maybe a seccomp-bpf filter?
425 // We could insert a high-priority constructor into the library, but that would
426 // not help with the uninstrumented libraries.
427 INTERCEPTOR(void*, dlopen
, const char *filename
, int flag
) {
428 EnsureInterceptorsInitialized();
430 void *handle
= REAL(dlopen
)(filename
, flag
);
435 INTERCEPTOR(int, dlclose
, void *handle
) {
436 EnsureInterceptorsInitialized();
438 int res
= REAL(dlclose
)(handle
);
443 static Mutex interceptor_init_lock
;
444 static bool interceptors_inited
= false;
446 static void EnsureInterceptorsInitialized() {
447 Lock
lock(&interceptor_init_lock
);
448 if (interceptors_inited
)
451 INTERCEPT_FUNCTION(dlopen
);
452 INTERCEPT_FUNCTION(dlclose
);
454 interceptors_inited
= true;
457 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
458 #if !SANITIZER_CAN_USE_PREINIT_ARRAY
459 // On ELF platforms, the constructor is invoked using .preinit_array (see below)
460 __attribute__((constructor(0)))
463 SanitizerToolName
= "CFI";
467 #ifdef CFI_ENABLE_DIAG
468 __ubsan::InitAsPlugin();
472 #if SANITIZER_CAN_USE_PREINIT_ARRAY
473 // On ELF platforms, run cfi initialization before any other constructors.
474 // On other platforms we use the constructor attribute to arrange to run our
475 // initialization early.
477 __attribute__((section(".preinit_array"),
478 used
)) void (*__cfi_preinit
)(void) = __cfi_init
;