Ruby 3.5.0dev (2025-01-10 revision 5fab31b15e32622c4b71d1d347a41937e9f9c212)
sanitizers.h
1#ifndef INTERNAL_SANITIZERS_H /*-*-C-*-vi:se ft=c:*/
2#define INTERNAL_SANITIZERS_H
11#include "ruby/internal/config.h"
12#include "internal/compilers.h" /* for __has_feature */
13
14#ifdef HAVE_VALGRIND_MEMCHECK_H
15# include <valgrind/memcheck.h>
16#endif
17
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>
22# endif
23#endif
24
25#ifdef HAVE_SANITIZER_MSAN_INTERFACE_H
26# if __has_feature(memory_sanitizer)
27# define RUBY_MSAN_ENABLED
28# include <sanitizer/msan_interface.h>
29# endif
30#endif
31
32#include "ruby/internal/stdbool.h" /* for bool */
33#include "ruby/ruby.h" /* for VALUE */
34
35#if 0
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))
51#else
52# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) x
53#endif
54
55#if defined(NO_SANITIZE) && RBIMPL_COMPILER_IS(GCC)
56/* GCC warns about unknown sanitizer, which is annoying. */
57# include "internal/warnings.h"
58# undef NO_SANITIZE
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 \
64 y
65#endif
66
67#ifndef NO_SANITIZE
68# define NO_SANITIZE(x, y) y
69#endif
70
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
77#endif
78
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))
84#endif
85
86#ifdef VALGRIND_MAKE_READABLE
87# define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE((p), (n))
88#endif
89
90#ifdef VALGRIND_MAKE_WRITABLE
91# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE((p), (n))
92#endif
93
94#ifndef VALGRIND_MAKE_MEM_DEFINED
95# define VALGRIND_MAKE_MEM_DEFINED(p, n) 0
96#endif
97
98#ifndef VALGRIND_MAKE_MEM_UNDEFINED
99# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
100#endif
101
115static inline void
116asan_poison_memory_region(const volatile void *ptr, size_t size)
117{
118 __msan_poison(ptr, size);
119 __asan_poison_memory_region(ptr, size);
120}
121
122#ifdef RUBY_ASAN_ENABLED
123#define asan_poison_object_if(ptr, obj) do { \
124 if (ptr) rb_asan_poison_object(obj); \
125 } while (0)
126#else
127#define asan_poison_object_if(ptr, obj) ((void)(ptr), (void)(obj))
128#endif
129
130RUBY_SYMBOL_EXPORT_BEGIN
136void rb_asan_poison_object(VALUE obj);
137
145void *rb_asan_poisoned_object_p(VALUE obj);
146
153void rb_asan_unpoison_object(VALUE obj, bool newobj_p);
154
155RUBY_SYMBOL_EXPORT_END
156
172static inline void
173asan_unpoison_memory_region(const volatile void *ptr, size_t size, bool malloc_p)
174{
175 __asan_unpoison_memory_region(ptr, size);
176 if (malloc_p) {
177 __msan_allocated_memory(ptr, size);
178 }
179 else {
180 __msan_unpoison(ptr, size);
181 }
182}
183
184static inline void *
185asan_unpoison_object_temporary(VALUE obj)
186{
187 void *ptr = rb_asan_poisoned_object_p(obj);
188 rb_asan_unpoison_object(obj, false);
189 return ptr;
190}
191
192static inline void *
193asan_poison_object_restore(VALUE obj, void *ptr)
194{
195 if (ptr) {
196 rb_asan_poison_object(obj);
197 }
198 return NULL;
199}
200
201#define asan_unpoisoning_object(obj) \
202 for (void *poisoned = asan_unpoison_object_temporary(obj), \
203 *unpoisoning = &poisoned; /* flag to loop just once */ \
204 unpoisoning; \
205 unpoisoning = asan_poison_object_restore(obj, poisoned))
206
207
208static inline void *
209asan_unpoison_memory_region_temporary(void *ptr, size_t len)
210{
211 void *poisoned_ptr = __asan_region_is_poisoned(ptr, len);
212 asan_unpoison_memory_region(ptr, len, false);
213 return poisoned_ptr;
214}
215
216static inline void *
217asan_poison_memory_region_restore(void *ptr, size_t len, void *poisoned_ptr)
218{
219 if (poisoned_ptr) {
220 asan_poison_memory_region(ptr, len);
221 }
222 return NULL;
223}
224
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 */ \
228 unpoisoning; \
229 unpoisoning = asan_poison_memory_region_restore(ptr, len, poisoned))
230
246static inline void *
247asan_get_real_stack_addr(void* slot)
248{
249 VALUE *addr;
250 addr = __asan_addr_is_in_fake_stack(__asan_get_current_fake_stack(), slot, NULL, NULL);
251 return addr ? addr : slot;
252}
253
259static inline void *
260asan_get_thread_fake_stack_handle(void)
261{
262 return __asan_get_current_fake_stack();
263}
264
285static inline bool
286asan_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)
289{
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
297 );
298 if (real_stack_frame) {
299 bool in_range;
300#if STACK_GROW_DIRECTION < 0
301 in_range = machine_stack_start >= real_stack_frame && real_stack_frame >= machine_stack_end;
302#else
303 in_range = machine_stack_start <= real_stack_frame && real_stack_frame <= machine_stack_end;
304#endif
305 if (in_range) {
306 *fake_stack_start_out = fake_frame_start;
307 *fake_stack_end_out = fake_frame_end;
308 return true;
309 }
310 }
311#endif
312 *fake_stack_start_out = 0;
313 *fake_stack_end_out = 0;
314 return false;
315}
316
317
318#endif /* INTERNAL_SANITIZERS_H */
int len
Length of the buffer.
Definition io.h:8
C99 shim for <stdbool.h>
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40