• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-------- cfi.cc ------------------------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the runtime support for the cross-DSO CFI.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include <assert.h>
15 #include <elf.h>
16 #include <link.h>
17 #include <string.h>
18 #include <sys/mman.h>
19 
20 typedef ElfW(Phdr) Elf_Phdr;
21 typedef ElfW(Ehdr) Elf_Ehdr;
22 
23 #include "interception/interception.h"
24 #include "sanitizer_common/sanitizer_common.h"
25 #include "sanitizer_common/sanitizer_flag_parser.h"
26 #include "ubsan/ubsan_init.h"
27 #include "ubsan/ubsan_flags.h"
28 
29 #ifdef CFI_ENABLE_DIAG
30 #include "ubsan/ubsan_handlers.h"
31 #endif
32 
33 namespace __cfi {
34 
35 #define kCfiShadowLimitsStorageSize 4096 // 1 page
36 // Lets hope that the data segment is mapped with 4K pages.
37 // The pointer to the cfi shadow region is stored at the start of this page.
38 // The rest of the page is unused and re-mapped read-only.
39 static union {
40   char space[kCfiShadowLimitsStorageSize];
41   struct {
42     uptr start;
43     uptr size;
44   } limits;
45 } cfi_shadow_limits_storage
46     __attribute__((aligned(kCfiShadowLimitsStorageSize)));
47 static constexpr uptr kShadowGranularity = 12;
48 static constexpr uptr kShadowAlign = 1UL << kShadowGranularity; // 4096
49 
50 static constexpr uint16_t kInvalidShadow = 0;
51 static constexpr uint16_t kUncheckedShadow = 0xFFFFU;
52 
53 // Get the start address of the CFI shadow region.
GetShadow()54 uptr GetShadow() {
55   return cfi_shadow_limits_storage.limits.start;
56 }
57 
GetShadowSize()58 uptr GetShadowSize() {
59   return cfi_shadow_limits_storage.limits.size;
60 }
61 
62 // This will only work while the shadow is not allocated.
SetShadowSize(uptr size)63 void SetShadowSize(uptr size) {
64   cfi_shadow_limits_storage.limits.size = size;
65 }
66 
MemToShadowOffset(uptr x)67 uptr MemToShadowOffset(uptr x) {
68   return (x >> kShadowGranularity) << 1;
69 }
70 
MemToShadow(uptr x,uptr shadow_base)71 uint16_t *MemToShadow(uptr x, uptr shadow_base) {
72   return (uint16_t *)(shadow_base + MemToShadowOffset(x));
73 }
74 
75 typedef int (*CFICheckFn)(u64, void *, void *);
76 
77 // This class reads and decodes the shadow contents.
78 class ShadowValue {
79   uptr addr;
80   uint16_t v;
ShadowValue(uptr addr,uint16_t v)81   explicit ShadowValue(uptr addr, uint16_t v) : addr(addr), v(v) {}
82 
83 public:
is_invalid() const84   bool is_invalid() const { return v == kInvalidShadow; }
85 
is_unchecked() const86   bool is_unchecked() const { return v == kUncheckedShadow; }
87 
get_cfi_check() const88   CFICheckFn get_cfi_check() const {
89     assert(!is_invalid() && !is_unchecked());
90     uptr aligned_addr = addr & ~(kShadowAlign - 1);
91     uptr p = aligned_addr - (((uptr)v - 1) << kShadowGranularity);
92     return reinterpret_cast<CFICheckFn>(p);
93   }
94 
95   // Load a shadow value for the given application memory address.
load(uptr addr)96   static const ShadowValue load(uptr addr) {
97     uptr shadow_base = GetShadow();
98     uptr shadow_offset = MemToShadowOffset(addr);
99     if (shadow_offset > GetShadowSize())
100       return ShadowValue(addr, kInvalidShadow);
101     else
102       return ShadowValue(
103           addr, *reinterpret_cast<uint16_t *>(shadow_base + shadow_offset));
104   }
105 };
106 
107 class ShadowBuilder {
108   uptr shadow_;
109 
110 public:
111   // Allocate a new empty shadow (for the entire address space) on the side.
112   void Start();
113   // Mark the given address range as unchecked.
114   // This is used for uninstrumented libraries like libc.
115   // Any CFI check with a target in that range will pass.
116   void AddUnchecked(uptr begin, uptr end);
117   // Mark the given address range as belonging to a library with the given
118   // cfi_check function.
119   void Add(uptr begin, uptr end, uptr cfi_check);
120   // Finish shadow construction. Atomically switch the current active shadow
121   // region with the newly constructed one and deallocate the former.
122   void Install();
123 };
124 
Start()125 void ShadowBuilder::Start() {
126   shadow_ = (uptr)MmapNoReserveOrDie(GetShadowSize(), "CFI shadow");
127   VReport(1, "CFI: shadow at %zx .. %zx\n", shadow_, shadow_ + GetShadowSize());
128 }
129 
AddUnchecked(uptr begin,uptr end)130 void ShadowBuilder::AddUnchecked(uptr begin, uptr end) {
131   uint16_t *shadow_begin = MemToShadow(begin, shadow_);
132   uint16_t *shadow_end = MemToShadow(end - 1, shadow_) + 1;
133   memset(shadow_begin, kUncheckedShadow,
134          (shadow_end - shadow_begin) * sizeof(*shadow_begin));
135 }
136 
Add(uptr begin,uptr end,uptr cfi_check)137 void ShadowBuilder::Add(uptr begin, uptr end, uptr cfi_check) {
138   assert((cfi_check & (kShadowAlign - 1)) == 0);
139 
140   // Don't fill anything below cfi_check. We can not represent those addresses
141   // in the shadow, and must make sure at codegen to place all valid call
142   // targets above cfi_check.
143   begin = Max(begin, cfi_check);
144   uint16_t *s = MemToShadow(begin, shadow_);
145   uint16_t *s_end = MemToShadow(end - 1, shadow_) + 1;
146   uint16_t sv = ((begin - cfi_check) >> kShadowGranularity) + 1;
147   for (; s < s_end; s++, sv++)
148     *s = sv;
149 }
150 
151 #if SANITIZER_LINUX
Install()152 void ShadowBuilder::Install() {
153   MprotectReadOnly(shadow_, GetShadowSize());
154   uptr main_shadow = GetShadow();
155   if (main_shadow) {
156     // Update.
157     void *res = mremap((void *)shadow_, GetShadowSize(), GetShadowSize(),
158                        MREMAP_MAYMOVE | MREMAP_FIXED, (void *)main_shadow);
159     CHECK(res != MAP_FAILED);
160   } else {
161     // Initial setup.
162     CHECK_EQ(kCfiShadowLimitsStorageSize, GetPageSizeCached());
163     CHECK_EQ(0, GetShadow());
164     cfi_shadow_limits_storage.limits.start = shadow_;
165     MprotectReadOnly((uptr)&cfi_shadow_limits_storage,
166                      sizeof(cfi_shadow_limits_storage));
167     CHECK_EQ(shadow_, GetShadow());
168   }
169 }
170 #else
171 #error not implemented
172 #endif
173 
174 // This is a workaround for a glibc bug:
175 // https://sourceware.org/bugzilla/show_bug.cgi?id=15199
176 // Other platforms can, hopefully, just do
177 //    dlopen(RTLD_NOLOAD | RTLD_LAZY)
178 //    dlsym("__cfi_check").
find_cfi_check_in_dso(dl_phdr_info * info)179 uptr find_cfi_check_in_dso(dl_phdr_info *info) {
180   const ElfW(Dyn) *dynamic = nullptr;
181   for (int i = 0; i < info->dlpi_phnum; ++i) {
182     if (info->dlpi_phdr[i].p_type == PT_DYNAMIC) {
183       dynamic =
184           (const ElfW(Dyn) *)(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr);
185       break;
186     }
187   }
188   if (!dynamic) return 0;
189   uptr strtab = 0, symtab = 0;
190   for (const ElfW(Dyn) *p = dynamic; p->d_tag != PT_NULL; ++p) {
191     if (p->d_tag == DT_SYMTAB)
192       symtab = p->d_un.d_ptr;
193     else if (p->d_tag == DT_STRTAB)
194       strtab = p->d_un.d_ptr;
195   }
196 
197   if (symtab > strtab) {
198     VReport(1, "Can not handle: symtab > strtab (%p > %zx)\n", symtab, strtab);
199     return 0;
200   }
201 
202   // Verify that strtab and symtab are inside of the same LOAD segment.
203   // This excludes VDSO, which has (very high) bogus strtab and symtab pointers.
204   int phdr_idx;
205   for (phdr_idx = 0; phdr_idx < info->dlpi_phnum; phdr_idx++) {
206     const Elf_Phdr *phdr = &info->dlpi_phdr[phdr_idx];
207     if (phdr->p_type == PT_LOAD) {
208       uptr beg = info->dlpi_addr + phdr->p_vaddr;
209       uptr end = beg + phdr->p_memsz;
210       if (strtab >= beg && strtab < end && symtab >= beg && symtab < end)
211         break;
212     }
213   }
214   if (phdr_idx == info->dlpi_phnum) {
215     // Nope, either different segments or just bogus pointers.
216     // Can not handle this.
217     VReport(1, "Can not handle: symtab %p, strtab %zx\n", symtab, strtab);
218     return 0;
219   }
220 
221   for (const ElfW(Sym) *p = (const ElfW(Sym) *)symtab; (ElfW(Addr))p < strtab;
222        ++p) {
223     char *name = (char*)(strtab + p->st_name);
224     if (strcmp(name, "__cfi_check") == 0) {
225       assert(p->st_info == ELF32_ST_INFO(STB_GLOBAL, STT_FUNC));
226       uptr addr = info->dlpi_addr + p->st_value;
227       return addr;
228     }
229   }
230   return 0;
231 }
232 
dl_iterate_phdr_cb(dl_phdr_info * info,size_t size,void * data)233 int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *data) {
234   uptr cfi_check = find_cfi_check_in_dso(info);
235   if (cfi_check)
236     VReport(1, "Module '%s' __cfi_check %zx\n", info->dlpi_name, cfi_check);
237 
238   ShadowBuilder *b = reinterpret_cast<ShadowBuilder *>(data);
239 
240   for (int i = 0; i < info->dlpi_phnum; i++) {
241     const Elf_Phdr *phdr = &info->dlpi_phdr[i];
242     if (phdr->p_type == PT_LOAD) {
243       // Jump tables are in the executable segment.
244       // VTables are in the non-executable one.
245       // Need to fill shadow for both.
246       // FIXME: reject writable if vtables are in the r/o segment. Depend on
247       // PT_RELRO?
248       uptr cur_beg = info->dlpi_addr + phdr->p_vaddr;
249       uptr cur_end = cur_beg + phdr->p_memsz;
250       if (cfi_check) {
251         VReport(1, "   %zx .. %zx\n", cur_beg, cur_end);
252         b->Add(cur_beg, cur_end, cfi_check);
253       } else {
254         b->AddUnchecked(cur_beg, cur_end);
255       }
256     }
257   }
258   return 0;
259 }
260 
261 // Init or update shadow for the current set of loaded libraries.
UpdateShadow()262 void UpdateShadow() {
263   ShadowBuilder b;
264   b.Start();
265   dl_iterate_phdr(dl_iterate_phdr_cb, &b);
266   b.Install();
267 }
268 
InitShadow()269 void InitShadow() {
270   CHECK_EQ(0, GetShadow());
271   CHECK_EQ(0, GetShadowSize());
272 
273   uptr vma = GetMaxVirtualAddress();
274   // Shadow is 2 -> 2**kShadowGranularity.
275   SetShadowSize((vma >> (kShadowGranularity - 1)) + 1);
276   VReport(1, "CFI: VMA size %zx, shadow size %zx\n", vma, GetShadowSize());
277 
278   UpdateShadow();
279 }
280 
281 THREADLOCAL int in_loader;
282 BlockingMutex shadow_update_lock(LINKER_INITIALIZED);
283 
EnterLoader()284 void EnterLoader() {
285   if (in_loader == 0) {
286     shadow_update_lock.Lock();
287   }
288   ++in_loader;
289 }
290 
ExitLoader()291 void ExitLoader() {
292   CHECK(in_loader > 0);
293   --in_loader;
294   UpdateShadow();
295   if (in_loader == 0) {
296     shadow_update_lock.Unlock();
297   }
298 }
299 
CfiSlowPathCommon(u64 CallSiteTypeId,void * Ptr,void * DiagData)300 ALWAYS_INLINE void CfiSlowPathCommon(u64 CallSiteTypeId, void *Ptr,
301                                      void *DiagData) {
302   uptr Addr = (uptr)Ptr;
303   VReport(3, "__cfi_slowpath: %llx, %p\n", CallSiteTypeId, Ptr);
304   ShadowValue sv = ShadowValue::load(Addr);
305   if (sv.is_invalid()) {
306     VReport(1, "CFI: invalid memory region for a check target: %p\n", Ptr);
307 #ifdef CFI_ENABLE_DIAG
308     if (DiagData) {
309       __ubsan_handle_cfi_check_fail(
310           reinterpret_cast<__ubsan::CFICheckFailData *>(DiagData), Addr, false);
311       return;
312     }
313 #endif
314     Trap();
315   }
316   if (sv.is_unchecked()) {
317     VReport(2, "CFI: unchecked call (shadow=FFFF): %p\n", Ptr);
318     return;
319   }
320   CFICheckFn cfi_check = sv.get_cfi_check();
321   VReport(2, "__cfi_check at %p\n", cfi_check);
322   cfi_check(CallSiteTypeId, Ptr, DiagData);
323 }
324 
InitializeFlags()325 void InitializeFlags() {
326   SetCommonFlagsDefaults();
327 #ifdef CFI_ENABLE_DIAG
328   __ubsan::Flags *uf = __ubsan::flags();
329   uf->SetDefaults();
330 #endif
331 
332   FlagParser cfi_parser;
333   RegisterCommonFlags(&cfi_parser);
334   cfi_parser.ParseString(GetEnv("CFI_OPTIONS"));
335 
336 #ifdef CFI_ENABLE_DIAG
337   FlagParser ubsan_parser;
338   __ubsan::RegisterUbsanFlags(&ubsan_parser, uf);
339   RegisterCommonFlags(&ubsan_parser);
340 
341   const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions();
342   ubsan_parser.ParseString(ubsan_default_options);
343   ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS"));
344 #endif
345 
346   InitializeCommonFlags();
347 
348   if (Verbosity())
349     ReportUnrecognizedFlags();
350 
351   if (common_flags()->help) {
352     cfi_parser.PrintFlagDescriptions();
353   }
354 }
355 
356 } // namespace __cfi
357 
358 using namespace __cfi;
359 
360 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__cfi_slowpath(u64 CallSiteTypeId,void * Ptr)361 __cfi_slowpath(u64 CallSiteTypeId, void *Ptr) {
362   CfiSlowPathCommon(CallSiteTypeId, Ptr, nullptr);
363 }
364 
365 #ifdef CFI_ENABLE_DIAG
366 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
__cfi_slowpath_diag(u64 CallSiteTypeId,void * Ptr,void * DiagData)367 __cfi_slowpath_diag(u64 CallSiteTypeId, void *Ptr, void *DiagData) {
368   CfiSlowPathCommon(CallSiteTypeId, Ptr, DiagData);
369 }
370 #endif
371 
372 // Setup shadow for dlopen()ed libraries.
373 // The actual shadow setup happens after dlopen() returns, which means that
374 // a library can not be a target of any CFI checks while its constructors are
375 // running. It's unclear how to fix this without some extra help from libc.
376 // In glibc, mmap inside dlopen is not interceptable.
377 // Maybe a seccomp-bpf filter?
378 // We could insert a high-priority constructor into the library, but that would
379 // not help with the uninstrumented libraries.
INTERCEPTOR(void *,dlopen,const char * filename,int flag)380 INTERCEPTOR(void*, dlopen, const char *filename, int flag) {
381   EnterLoader();
382   void *handle = REAL(dlopen)(filename, flag);
383   ExitLoader();
384   return handle;
385 }
386 
INTERCEPTOR(int,dlclose,void * handle)387 INTERCEPTOR(int, dlclose, void *handle) {
388   EnterLoader();
389   int res = REAL(dlclose)(handle);
390   ExitLoader();
391   return res;
392 }
393 
394 extern "C" SANITIZER_INTERFACE_ATTRIBUTE
395 #if !SANITIZER_CAN_USE_PREINIT_ARRAY
396 // On ELF platforms, the constructor is invoked using .preinit_array (see below)
397 __attribute__((constructor(0)))
398 #endif
__cfi_init()399 void __cfi_init() {
400   SanitizerToolName = "CFI";
401   InitializeFlags();
402   InitShadow();
403 
404   INTERCEPT_FUNCTION(dlopen);
405   INTERCEPT_FUNCTION(dlclose);
406 
407 #ifdef CFI_ENABLE_DIAG
408   __ubsan::InitAsPlugin();
409 #endif
410 }
411 
412 #if SANITIZER_CAN_USE_PREINIT_ARRAY
413 // On ELF platforms, run cfi initialization before any other constructors.
414 // On other platforms we use the constructor attribute to arrange to run our
415 // initialization early.
416 extern "C" {
417 __attribute__((section(".preinit_array"),
418                used)) void (*__cfi_preinit)(void) = __cfi_init;
419 }
420 #endif
421