1 //===-- dfsan_interceptors.cpp --------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of DataFlowSanitizer.
10 //
11 // Interceptors for standard library functions.
12 //===----------------------------------------------------------------------===//
13 
14 #include <sys/syscall.h>
15 #include <unistd.h>
16 
17 #include "dfsan/dfsan.h"
18 #include "dfsan/dfsan_thread.h"
19 #include "interception/interception.h"
20 #include "sanitizer_common/sanitizer_allocator_interface.h"
21 #include "sanitizer_common/sanitizer_common.h"
22 #include "sanitizer_common/sanitizer_errno.h"
23 #include "sanitizer_common/sanitizer_platform_limits_posix.h"
24 #include "sanitizer_common/sanitizer_posix.h"
25 #include "sanitizer_common/sanitizer_tls_get_addr.h"
26 
27 using namespace __sanitizer;
28 
29 namespace {
30 
31 bool interceptors_initialized;
32 
33 }  // namespace
34 
35 INTERCEPTOR(void *, reallocarray, void *ptr, SIZE_T nmemb, SIZE_T size) {
36   return __dfsan::dfsan_reallocarray(ptr, nmemb, size);
37 }
38 
39 INTERCEPTOR(void *, __libc_memalign, SIZE_T alignment, SIZE_T size) {
40   void *ptr = __dfsan::dfsan_memalign(alignment, size);
41   if (ptr)
42     DTLS_on_libc_memalign(ptr, size);
43   return ptr;
44 }
45 
46 INTERCEPTOR(void *, aligned_alloc, SIZE_T alignment, SIZE_T size) {
47   return __dfsan::dfsan_aligned_alloc(alignment, size);
48 }
49 
50 static uptr allocated_for_dlsym;
51 static const uptr kDlsymAllocPoolSize = 1024;
52 static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize];
53 
54 static bool IsInDlsymAllocPool(const void *ptr) {
55   uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
56   return off < sizeof(alloc_memory_for_dlsym);
57 }
58 
59 static void *AllocateFromLocalPool(uptr size_in_bytes) {
60   uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize;
61   void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym];
62   allocated_for_dlsym += size_in_words;
63   CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize);
64   return mem;
65 }
66 
67 INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) {
68   if (UNLIKELY(!__dfsan::dfsan_inited))
69     // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym.
70     return AllocateFromLocalPool(nmemb * size);
71   return __dfsan::dfsan_calloc(nmemb, size);
72 }
73 
74 INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) {
75   if (UNLIKELY(IsInDlsymAllocPool(ptr))) {
76     uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym;
77     uptr copy_size = Min(size, kDlsymAllocPoolSize - offset);
78     void *new_ptr;
79     if (UNLIKELY(!__dfsan::dfsan_inited)) {
80       new_ptr = AllocateFromLocalPool(copy_size);
81     } else {
82       copy_size = size;
83       new_ptr = __dfsan::dfsan_malloc(copy_size);
84     }
85     internal_memcpy(new_ptr, ptr, copy_size);
86     return new_ptr;
87   }
88   return __dfsan::dfsan_realloc(ptr, size);
89 }
90 
91 INTERCEPTOR(void *, malloc, SIZE_T size) {
92   if (UNLIKELY(!__dfsan::dfsan_inited))
93     // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym.
94     return AllocateFromLocalPool(size);
95   return __dfsan::dfsan_malloc(size);
96 }
97 
98 INTERCEPTOR(void, free, void *ptr) {
99   if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
100     return;
101   return __dfsan::dfsan_deallocate(ptr);
102 }
103 
104 INTERCEPTOR(void, cfree, void *ptr) {
105   if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr)))
106     return;
107   return __dfsan::dfsan_deallocate(ptr);
108 }
109 
110 INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) {
111   CHECK_NE(memptr, 0);
112   int res = __dfsan::dfsan_posix_memalign(memptr, alignment, size);
113   if (!res)
114     dfsan_set_label(0, memptr, sizeof(*memptr));
115   return res;
116 }
117 
118 INTERCEPTOR(void *, memalign, SIZE_T alignment, SIZE_T size) {
119   return __dfsan::dfsan_memalign(alignment, size);
120 }
121 
122 INTERCEPTOR(void *, valloc, SIZE_T size) { return __dfsan::dfsan_valloc(size); }
123 
124 INTERCEPTOR(void *, pvalloc, SIZE_T size) {
125   return __dfsan::dfsan_pvalloc(size);
126 }
127 
128 INTERCEPTOR(void, mallinfo, __sanitizer_struct_mallinfo *sret) {
129   internal_memset(sret, 0, sizeof(*sret));
130   dfsan_set_label(0, sret, sizeof(*sret));
131 }
132 
133 INTERCEPTOR(int, mallopt, int cmd, int value) { return 0; }
134 
135 INTERCEPTOR(void, malloc_stats, void) {
136   // FIXME: implement, but don't call REAL(malloc_stats)!
137 }
138 
139 INTERCEPTOR(uptr, malloc_usable_size, void *ptr) {
140   return __sanitizer_get_allocated_size(ptr);
141 }
142 
143 #define ENSURE_DFSAN_INITED()               \
144   do {                                      \
145     CHECK(!__dfsan::dfsan_init_is_running); \
146     if (!__dfsan::dfsan_inited) {           \
147       __dfsan::dfsan_init();                \
148     }                                       \
149   } while (0)
150 
151 #define COMMON_INTERCEPTOR_ENTER(func, ...) \
152   if (__dfsan::dfsan_init_is_running)       \
153     return REAL(func)(__VA_ARGS__);         \
154   ENSURE_DFSAN_INITED();                    \
155   dfsan_set_label(0, __errno_location(), sizeof(int)); /* NOLINT */
156 
157 INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags,
158             int fd, OFF_T offset) {
159   if (common_flags()->detect_write_exec)
160     ReportMmapWriteExec(prot);
161   if (!__dfsan::dfsan_inited)
162     return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
163   COMMON_INTERCEPTOR_ENTER(mmap, addr, length, prot, flags, fd, offset);
164   void *res = REAL(mmap)(addr, length, prot, flags, fd, offset);
165   if (res != (void *)-1) {
166     dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached()));
167   }
168   return res;
169 }
170 
171 INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags,
172             int fd, OFF64_T offset) {
173   if (common_flags()->detect_write_exec)
174     ReportMmapWriteExec(prot);
175   if (!__dfsan::dfsan_inited)
176     return (void *)internal_mmap(addr, length, prot, flags, fd, offset);
177   COMMON_INTERCEPTOR_ENTER(mmap64, addr, length, prot, flags, fd, offset);
178   void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset);
179   if (res != (void *)-1) {
180     dfsan_set_label(0, res, RoundUpTo(length, GetPageSizeCached()));
181   }
182   return res;
183 }
184 
185 INTERCEPTOR(int, munmap, void *addr, SIZE_T length) {
186   if (!__dfsan::dfsan_inited)
187     return internal_munmap(addr, length);
188   COMMON_INTERCEPTOR_ENTER(munmap, addr, length);
189   int res = REAL(munmap)(addr, length);
190   if (res != -1)
191     dfsan_set_label(0, addr, RoundUpTo(length, GetPageSizeCached()));
192   return res;
193 }
194 
195 #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end)           \
196   if (__dfsan::DFsanThread *t = __dfsan::GetCurrentThread()) { \
197     *begin = t->tls_begin();                                   \
198     *end = t->tls_end();                                       \
199   } else {                                                     \
200     *begin = *end = 0;                                         \
201   }
202 #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \
203   dfsan_set_label(0, ptr, size)
204 
205 INTERCEPTOR(void *, __tls_get_addr, void *arg) {
206   COMMON_INTERCEPTOR_ENTER(__tls_get_addr, arg);
207   void *res = REAL(__tls_get_addr)(arg);
208   uptr tls_begin, tls_end;
209   COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end);
210   DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end);
211   if (dtv) {
212     // New DTLS block has been allocated.
213     COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size);
214   }
215   return res;
216 }
217 
218 namespace __dfsan {
219 void initialize_interceptors() {
220   CHECK(!interceptors_initialized);
221 
222   INTERCEPT_FUNCTION(aligned_alloc);
223   INTERCEPT_FUNCTION(calloc);
224   INTERCEPT_FUNCTION(cfree);
225   INTERCEPT_FUNCTION(free);
226   INTERCEPT_FUNCTION(mallinfo);
227   INTERCEPT_FUNCTION(malloc);
228   INTERCEPT_FUNCTION(malloc_stats);
229   INTERCEPT_FUNCTION(malloc_usable_size);
230   INTERCEPT_FUNCTION(mallopt);
231   INTERCEPT_FUNCTION(memalign);
232   INTERCEPT_FUNCTION(mmap);
233   INTERCEPT_FUNCTION(mmap64);
234   INTERCEPT_FUNCTION(munmap);
235   INTERCEPT_FUNCTION(posix_memalign);
236   INTERCEPT_FUNCTION(pvalloc);
237   INTERCEPT_FUNCTION(realloc);
238   INTERCEPT_FUNCTION(reallocarray);
239   INTERCEPT_FUNCTION(valloc);
240   INTERCEPT_FUNCTION(__tls_get_addr);
241   INTERCEPT_FUNCTION(__libc_memalign);
242 
243   interceptors_initialized = true;
244 }
245 }  // namespace __dfsan
246