1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
#ifndef INTERNAL_SANITIZERS_H /*-*-C-*-vi:se ft=c:*/
#define INTERNAL_SANITIZERS_H
/**
* @author Ruby developers <ruby-core@ruby-lang.org>
* @copyright This file is a part of the programming language Ruby.
* Permission is hereby granted, to either redistribute and/or
* modify this file, provided that the conditions mentioned in the
* file COPYING are met. Consult the file for details.
* @brief Internal header for ASAN / MSAN / etc.
*/
#include "ruby/internal/config.h"
#include "internal/compilers.h" /* for __has_feature */
#ifdef HAVE_VALGRIND_MEMCHECK_H
# include <valgrind/memcheck.h>
#endif
#ifdef HAVE_SANITIZER_ASAN_INTERFACE_H
# include <sanitizer/asan_interface.h>
#endif
#ifdef HAVE_SANITIZER_MSAN_INTERFACE_H
# if __has_feature(memory_sanitizer)
# include <sanitizer/msan_interface.h>
# endif
#endif
#include "ruby/internal/stdbool.h" /* for bool */
#include "ruby/ruby.h" /* for VALUE */
#if 0
#elif __has_feature(memory_sanitizer) && __has_feature(address_sanitizer)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
__attribute__((__no_sanitize__("memory, address"), __noinline__)) x
#elif __has_feature(address_sanitizer)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
__attribute__((__no_sanitize__("address"), __noinline__)) x
#elif defined(NO_SANITIZE_ADDRESS)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
NO_SANITIZE_ADDRESS(NOINLINE(x))
#elif defined(NO_ADDRESS_SAFETY_ANALYSIS)
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) \
NO_ADDRESS_SAFETY_ANALYSIS(NOINLINE(x))
#else
# define ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS(x) x
#endif
#if defined(NO_SANITIZE) && RBIMPL_COMPILER_IS(GCC)
/* GCC warns about unknown sanitizer, which is annoying. */
# include "internal/warnings.h"
# undef NO_SANITIZE
# define NO_SANITIZE(x, y) \
COMPILER_WARNING_PUSH; \
COMPILER_WARNING_IGNORED(-Wattributes); \
__attribute__((__no_sanitize__(x))) y; \
COMPILER_WARNING_POP
#endif
#ifndef NO_SANITIZE
# define NO_SANITIZE(x, y) y
#endif
#if !__has_feature(address_sanitizer)
# define __asan_poison_memory_region(x, y)
# define __asan_unpoison_memory_region(x, y)
# define __asan_region_is_poisoned(x, y) 0
#endif
#if !__has_feature(memory_sanitizer)
# define __msan_allocated_memory(x, y) ((void)(x), (void)(y))
# define __msan_poison(x, y) ((void)(x), (void)(y))
# define __msan_unpoison(x, y) ((void)(x), (void)(y))
# define __msan_unpoison_string(x) ((void)(x))
#endif
#ifdef VALGRIND_MAKE_READABLE
# define VALGRIND_MAKE_MEM_DEFINED(p, n) VALGRIND_MAKE_READABLE((p), (n))
#endif
#ifdef VALGRIND_MAKE_WRITABLE
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) VALGRIND_MAKE_WRITABLE((p), (n))
#endif
#ifndef VALGRIND_MAKE_MEM_DEFINED
# define VALGRIND_MAKE_MEM_DEFINED(p, n) 0
#endif
#ifndef VALGRIND_MAKE_MEM_UNDEFINED
# define VALGRIND_MAKE_MEM_UNDEFINED(p, n) 0
#endif
#ifndef MJIT_HEADER
/*!
* This function asserts that a (continuous) memory region from ptr to size
* being "poisoned". Both read / write access to such memory region are
* prohibited until properly unpoisoned. The region must be previously
* allocated (do not pass a freed pointer here), but not necessarily be an
* entire object that the malloc returns. You can punch hole a part of a
* gigantic heap arena. This is handy when you do not free an allocated memory
* region to reuse later: poison when you keep it unused, and unpoison when you
* reuse.
*
* \param[in] ptr pointer to the beginning of the memory region to poison.
* \param[in] size the length of the memory region to poison.
*/
static inline void
asan_poison_memory_region(const volatile void *ptr, size_t size)
{
__msan_poison(ptr, size);
__asan_poison_memory_region(ptr, size);
}
/*!
* This is a variant of asan_poison_memory_region that takes a VALUE.
*
* \param[in] obj target object.
*/
static inline void
asan_poison_object(VALUE obj)
{
MAYBE_UNUSED(struct RVALUE *) ptr = (void *)obj;
asan_poison_memory_region(ptr, SIZEOF_VALUE);
}
#if !__has_feature(address_sanitizer)
#define asan_poison_object_if(ptr, obj) ((void)(ptr), (void)(obj))
#else
#define asan_poison_object_if(ptr, obj) do { \
if (ptr) asan_poison_object(obj); \
} while (0)
#endif
/*!
* This function predicates if the given object is fully addressable or not.
*
* \param[in] obj target object.
* \retval 0 the given object is fully addressable.
* \retval otherwise pointer to first such byte who is poisoned.
*/
static inline void *
asan_poisoned_object_p(VALUE obj)
{
MAYBE_UNUSED(struct RVALUE *) ptr = (void *)obj;
return __asan_region_is_poisoned(ptr, SIZEOF_VALUE);
}
/*!
* This function asserts that a (formally poisoned) memory region from ptr to
* size is now addressable. Write access to such memory region gets allowed.
* However read access might or might not be possible depending on situations,
* because the region can have contents of previous usages. That information
* should be passed by the malloc_p flag. If that is true, the contents of the
* region is _not_ fully defined (like the return value of malloc behaves).
* Reading from there is NG; write something first. If malloc_p is false on
* the other hand, that memory region is fully defined and can be read
* immediately.
*
* \param[in] ptr pointer to the beginning of the memory region to unpoison.
* \param[in] size the length of the memory region.
* \param[in] malloc_p if the memory region is like a malloc's return value or not.
*/
static inline void
asan_unpoison_memory_region(const volatile void *ptr, size_t size, bool malloc_p)
{
__asan_unpoison_memory_region(ptr, size);
if (malloc_p) {
__msan_allocated_memory(ptr, size);
}
else {
__msan_unpoison(ptr, size);
}
}
/*!
* This is a variant of asan_unpoison_memory_region that takes a VALUE.
*
* \param[in] obj target object.
* \param[in] malloc_p if the memory region is like a malloc's return value or not.
*/
static inline void
asan_unpoison_object(VALUE obj, bool newobj_p)
{
MAYBE_UNUSED(struct RVALUE *) ptr = (void *)obj;
asan_unpoison_memory_region(ptr, SIZEOF_VALUE, newobj_p);
}
#endif /* MJIT_HEADER */
#endif /* INTERNAL_SANITIZERS_H */
|