1 #ifndef INTERNAL_SANITIZERS_H /*-*-C-*-vi:se ft=c:*/
2 #define INTERNAL_SANITIZERS_H
4 * @author Ruby developers <ruby-core@ruby-lang.org>
5 * @copyright This file is a part of the programming language Ruby.
6 * Permission is hereby granted, to either redistribute and/or
7 * modify this file, provided that the conditions mentioned in the
8 * file COPYING are met. Consult the file for details.
9 * @brief Internal header for ASAN / MSAN / etc.
11 #include "ruby/internal/config.h"
12 #include "internal/compilers.h" /* for __has_feature */
14 #ifdef HAVE_VALGRIND_MEMCHECK_H
15 # include <valgrind/memcheck.h>
18 #ifdef HAVE_SANITIZER_ASAN_INTERFACE_H
19 # if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
20 # define RUBY_ASAN_ENABLED
21 # include <sanitizer/asan_interface.h>
25 #ifdef HAVE_SANITIZER_MSAN_INTERFACE_H
26 # if __has_feature(memory_sanitizer)
27 # define RUBY_MSAN_ENABLED
28 # include <sanitizer/msan_interface.h>
32 #include "ruby/internal/stdbool.h" /* for bool */
33 #include "ruby/ruby.h" /* for VALUE */
36 #elif defined(RUBY_ASAN_ENABLED) && defined(RUBY_MSAN_ENABLED)
37 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
38 __attribute__((__no_sanitize__("memory, address"), __noinline__)) x
39 #elif defined(RUBY_ASAN_ENABLED)
40 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
41 __attribute__((__no_sanitize__("address"), __noinline__)) x
42 #elif defined(RUBY_MSAN_ENABLED)
43 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
44 __attribute__((__no_sanitize__("memory"), __noinline__)) x
45 #elif defined(NO_SANITIZE_ADDRESS)
46 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
47 NO_SANITIZE_ADDRESS(NOINLINE(x))
48 #elif defined(NO_ADDRESS_SAFETY_ANALYSIS)
49 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
50 NO_ADDRESS_SAFETY_ANALYSIS(NOINLINE(x))
52 # define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) x
55 #if defined(NO_SANITIZE) && RBIMPL_COMPILER_IS(GCC)
56 /* GCC warns about unknown sanitizer, which is annoying. */
57 # include "internal/warnings.h"
59 # define NO_SANITIZE(x, y) \
60 COMPILER_WARNING_PUSH \
61 COMPILER_WARNING_IGNORED(-Wattributes) \
62 __attribute__((__no_sanitize__(x))) y; \
63 COMPILER_WARNING_POP \
68 # define NO_SANITIZE(x, y) y
71 #ifndef RUBY_ASAN_ENABLED
72 # define __asan_poison_memory_region(x, y)
73 # define __asan_unpoison_memory_region(x, y)
74 # define __asan_region_is_poisoned(x, y) 0
75 # define __asan_get_current_fake_stack() NULL
76 # define __asan_addr_is_in_fake_stack(fake_stack, slot, start, end) NULL
79 #ifndef RUBY_MSAN_ENABLED
80 # define __msan_allocated_memory(x, y) ((void)(x), (void)(y))
81 # define __msan_poison(x, y) ((void)(x), (void)(y))
82 # define __msan_unpoison(x, y) ((void)(x), (void)(y))
83 # define __msan_unpoison_string(x) ((void)(x))
86 #ifdef VALGRIND_MAKE_READABLE
87 # define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE((p), (n))
90 #ifdef VALGRIND_MAKE_WRITABLE
91 # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE((p), (n))
94 #ifndef VALGRIND_MAKE_MEM_DEFINED
95 # define VALGRIND_MAKE_MEM_DEFINED(p, n) 0
98 #ifndef VALGRIND_MAKE_MEM_UNDEFINED
99 # define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
103 * This function asserts that a (continuous) memory region from ptr to size
104 * being "poisoned". Both read / write access to such memory region are
105 * prohibited until properly unpoisoned. The region must be previously
106 * allocated (do not pass a freed pointer here), but not necessarily be an
107 * entire object that the malloc returns. You can punch hole a part of a
108 * gigantic heap arena. This is handy when you do not free an allocated memory
109 * region to reuse later: poison when you keep it unused, and unpoison when you
112 * @param[in] ptr pointer to the beginning of the memory region to poison.
113 * @param[in] size the length of the memory region to poison.
116 asan_poison_memory_region(const volatile void *ptr
, size_t size
)
118 __msan_poison(ptr
, size
);
119 __asan_poison_memory_region(ptr
, size
);
122 #ifdef RUBY_ASAN_ENABLED
123 #define asan_poison_object_if(ptr, obj) do { \
124 if (ptr) rb_asan_poison_object(obj); \
127 #define asan_poison_object_if(ptr, obj) ((void)(ptr), (void)(obj))
130 RUBY_SYMBOL_EXPORT_BEGIN
132 * This is a variant of asan_poison_memory_region that takes a VALUE.
134 * @param[in] obj target object.
136 void rb_asan_poison_object(VALUE obj
);
139 * This function predicates if the given object is fully addressable or not.
141 * @param[in] obj target object.
142 * @retval 0 the given object is fully addressable.
143 * @retval otherwise pointer to first such byte who is poisoned.
145 void *rb_asan_poisoned_object_p(VALUE obj
);
148 * This is a variant of asan_unpoison_memory_region that takes a VALUE.
150 * @param[in] obj target object.
151 * @param[in] malloc_p if the memory region is like a malloc's return value or not.
153 void rb_asan_unpoison_object(VALUE obj
, bool newobj_p
);
155 RUBY_SYMBOL_EXPORT_END
158 * This function asserts that a (formally poisoned) memory region from ptr to
159 * size is now addressable. Write access to such memory region gets allowed.
160 * However read access might or might not be possible depending on situations,
161 * because the region can have contents of previous usages. That information
162 * should be passed by the malloc_p flag. If that is true, the contents of the
163 * region is _not_ fully defined (like the return value of malloc behaves).
164 * Reading from there is NG; write something first. If malloc_p is false on
165 * the other hand, that memory region is fully defined and can be read
168 * @param[in] ptr pointer to the beginning of the memory region to unpoison.
169 * @param[in] size the length of the memory region.
170 * @param[in] malloc_p if the memory region is like a malloc's return value or not.
173 asan_unpoison_memory_region(const volatile void *ptr
, size_t size
, bool malloc_p
)
175 __asan_unpoison_memory_region(ptr
, size
);
177 __msan_allocated_memory(ptr
, size
);
180 __msan_unpoison(ptr
, size
);
185 asan_unpoison_object_temporary(VALUE obj
)
187 void *ptr
= rb_asan_poisoned_object_p(obj
);
188 rb_asan_unpoison_object(obj
, false);
193 asan_poison_object_restore(VALUE obj
, void *ptr
)
196 rb_asan_poison_object(obj
);
201 #define asan_unpoisoning_object(obj) \
202 for (void *poisoned = asan_unpoison_object_temporary(obj), \
203 *unpoisoning = &poisoned; /* flag to loop just once */ \
205 unpoisoning = asan_poison_object_restore(obj, poisoned))
209 asan_unpoison_memory_region_temporary(void *ptr
, size_t len
)
211 void *poisoned_ptr
= __asan_region_is_poisoned(ptr
, len
);
212 asan_unpoison_memory_region(ptr
, len
, false);
217 asan_poison_memory_region_restore(void *ptr
, size_t len
, void *poisoned_ptr
)
220 asan_poison_memory_region(ptr
, len
);
225 #define asan_unpoisoning_memory_region(ptr, len) \
226 for (void *poisoned = asan_unpoison_memory_region_temporary(ptr, len), \
227 *unpoisoning = &poisoned; /* flag to loop just once */ \
229 unpoisoning = asan_poison_memory_region_restore(ptr, len, poisoned))
232 * Checks if the given pointer is on an ASAN fake stack. If so, it returns the
233 * address this variable has on the real frame; if not, it returns the origin
234 * address unmodified.
236 * n.b. - _dereferencing_ the returned address is meaningless and should not
237 * be done; even though ASAN reserves space for the variable in both the real and
238 * fake stacks, the _value_ of that variable is only in the fake stack.
240 * n.b. - this only works for addresses passed in from local variables on the same
241 * thread, because the ASAN fake stacks are threadlocal.
243 * @param[in] slot the address of some local variable
244 * @retval a pointer to something from that frame on the _real_ machine stack
247 asan_get_real_stack_addr(void* slot
)
250 addr
= __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), slot
, NULL
, NULL
);
251 return addr
? addr
: slot
;
255 * Gets the current thread's fake stack handle, which can be passed into get_fake_stack_extents
257 * @retval An opaque value which can be passed to asan_get_fake_stack_extents
260 asan_get_thread_fake_stack_handle(void)
262 return __asan_get_current_fake_stack();
266 * Checks if the given VALUE _actually_ represents a pointer to an ASAN fake stack.
268 * If the given slot _is_ actually a reference to an ASAN fake stack, and that fake stack
269 * contains the real values for the passed-in range of machine stack addresses, returns true
270 * and the range of the fake stack through the outparams.
272 * Otherwise, returns false, and sets the outparams to NULL.
274 * Note that this function expects "start" to be > "end" on downward-growing stack architectures;
276 * @param[in] thread_fake_stack_handle The asan fake stack reference for the thread we're scanning
277 * @param[in] slot The value on the machine stack we want to inspect
278 * @param[in] machine_stack_start The extents of the real machine stack on which slot lives
279 * @param[in] machine_stack_end The extents of the real machine stack on which slot lives
280 * @param[out] fake_stack_start_out The extents of the fake stack which contains real VALUEs
281 * @param[out] fake_stack_end_out The extents of the fake stack which contains real VALUEs
282 * @return Whether slot is a pointer to a fake stack for the given machine stack range
286 asan_get_fake_stack_extents(void *thread_fake_stack_handle
, VALUE slot
,
287 void *machine_stack_start
, void *machine_stack_end
,
288 void **fake_stack_start_out
, void **fake_stack_end_out
)
290 /* the ifdef is needed here to suppress a warning about fake_frame_{start/end} being
291 uninitialized if __asan_addr_is_in_fake_stack is an empty macro */
292 #ifdef RUBY_ASAN_ENABLED
293 void *fake_frame_start
;
294 void *fake_frame_end
;
295 void *real_stack_frame
= __asan_addr_is_in_fake_stack(
296 thread_fake_stack_handle
, (void *)slot
, &fake_frame_start
, &fake_frame_end
298 if (real_stack_frame
) {
300 #if STACK_GROW_DIRECTION < 0
301 in_range
= machine_stack_start
>= real_stack_frame
&& real_stack_frame
>= machine_stack_end
;
303 in_range
= machine_stack_start
<= real_stack_frame
&& real_stack_frame
<= machine_stack_end
;
306 *fake_stack_start_out
= fake_frame_start
;
307 *fake_stack_end_out
= fake_frame_end
;
312 *fake_stack_start_out
= 0;
313 *fake_stack_end_out
= 0;
317 extern const char ruby_asan_default_options
[];
319 #ifdef RUBY_ASAN_ENABLED
320 /* Compile in the ASAN options Ruby needs, rather than relying on environment variables, so
321 * that even tests which fork ruby with a clean environment will run ASAN with the right
323 # undef RUBY__ASAN_DEFAULT_OPTIONS
324 # define RUBY__ASAN_DEFAULT_OPTIONS \
325 RBIMPL_SYMBOL_EXPORT_BEGIN() \
326 const char * __asan_default_options(void) {return ruby_asan_default_options;} \
327 RBIMPL_SYMBOL_EXPORT_END()
330 #endif /* INTERNAL_SANITIZERS_H */