1 #pragma once
2
3 #include <petsc/private/petscimpl.h>
4
5 /* SUBMANSEC = Sys */
6
7 #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
8 #include <sanitizer/asan_interface.h> // ASAN_POISON/UNPOISON_MEMORY_REGION
9
10 #define PETSC_HAVE_ASAN 1
11 #endif
12
13 #if !defined(ASAN_POISON_MEMORY_REGION) // use poison as canary
14 #define ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
15 #define ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
16 #endif
17
18 #if !PetscDefined(HAVE_WINDOWS_COMPILERS) && !defined(__MINGW32__)
19 #include <petsc/private/valgrind/memcheck.h> // VALGRIND_MAKE_MEM_*
20
21 // defined in memcheck.h
22 #if defined(PLAT_amd64_linux) || defined(PLAT_x86_linux) || defined(PLAT_amd64_darwin)
23 #define PETSC_HAVE_VALGRIND_MEMPOISON 1
24 #endif
25 #endif
26
27 #if !defined(VALGRIND_MAKE_MEM_NOACCESS) // use noaccess as canary
28 #define VALGRIND_MAKE_MEM_NOACCESS(addr, size) ((void)(addr), (void)(size))
29 #define VALGRIND_MAKE_MEM_UNDEFINED(addr, size) ((void)(addr), (void)(size))
30 #define VALGRIND_MAKE_MEM_DEFINED(addr, size) ((void)(addr), (void)(size))
31 #endif
32
33 /*@C
34 PetscPoisonMemoryRegion - Poison a region in memory, making it undereferencable
35
36 Not Available From Fortran
37
38 Input Parameters:
39 + ptr - The pointer to the start of the region
40 - size - The size (in bytes) of the region to poison
41
42 Level: developer
43
44 Notes:
45 `ptr` must not be `NULL`. It is OK to poison the same memory region repeatedly (it is a
46 no-op).
47
48 Any attempt to dereference the region after this routine returns results in an error being
49 raised. The memory region may be un-poisoned using `PetscUnpoisonMemoryRegion()`, making it
50 safe to dereference again.
51
52 Example Usage:
53 .vb
54 PetscInt *array;
55
56 PetscMalloc1(15, &array);
57 // OK, memory is normal
58 array[0] = 10;
59 array[1] = 15;
60
61 PetscPoisonMemoryRegion(array, 15 * sizeof(*array));
62 // ERROR this region is poisoned!
63 array[0] = 10;
64 // ERROR reading is not allowed either!
65 PetscInt v = array[15];
66
67 // OK can re-poison the region
68 PetscPoisonMemoryRegion(array, 15 * sizeof(*array));
69 // OK can re-poison any subregion too
70 PetscPoisonMemoryRegion(array + 5, 1 * sizeof(*array));
71
72 PetscUnpoisonMemoryRegion(array, 1 * sizeof(*array));
73 // OK the first entry has been unpoisoned
74 array[0] = 10;
75 // ERROR the rest of the region is still poisoned!
76 array[1] = 12345;
77
78 PetscUnpoisonMemoryRegion(array + 10, sizeof(*array));
79 // OK this region is unpoisoned (even though surrounding memory is still poisoned!)
80 array[10] = 0;
81
82 PetscInt stack_array[10];
83
84 // OK can poison stack memory as well
85 PetscPoisonMemoryRegion(stack_array, 10 * sizeof(*stack_array));
86 // ERROR stack array is poisoned!
87 stack_array[0] = 10;
88 .ve
89
90 .seealso: `PetscUnpoisonMemoryRegion()`, `PetscIsRegionPoisoned()`
91 @*/
PetscPoisonMemoryRegion(const void * ptr,size_t size)92 static inline PetscErrorCode PetscPoisonMemoryRegion(const void *ptr, size_t size)
93 {
94 PetscFunctionBegin;
95 // cannot check ptr as it may be poisoned
96 // PetscAssertPointer(ptr, 1);
97 if (PetscDefined(HAVE_ASAN)) {
98 ASAN_POISON_MEMORY_REGION(ptr, size);
99 } else if (PetscDefined(HAVE_VALGRIND_MEMPOISON)) {
100 (void)VALGRIND_MAKE_MEM_NOACCESS(ptr, size);
101 (void)VALGRIND_MAKE_MEM_UNDEFINED(ptr, size);
102 } else {
103 (void)ptr;
104 (void)size;
105 }
106 PetscFunctionReturn(PETSC_SUCCESS);
107 }
108
109 /*@C
110 PetscUnpoisonMemoryRegion - Unpoison a previously poisoned memory region
111
112 Input Parameters:
113 + ptr - The pointer to the start of the region
114 - size - The size (in bytes) of the region to unpoison
115
116 Level: developer
117
118 Notes:
119 Removes poisoning from a previously poisoned region. `ptr` may not be `NULL`. It is OK to
120 unpoison an unpoisoned region.
121
122 See `PetscPoisonMemoryRegion()` for example usage and further discussion.
123
124 .seealso: `PetscPoisonMemoryRegion()`, `PetscIsRegionPoisoned()`
125 @*/
PetscUnpoisonMemoryRegion(const void * ptr,size_t size)126 static inline PetscErrorCode PetscUnpoisonMemoryRegion(const void *ptr, size_t size)
127 {
128 PetscFunctionBegin;
129 // cannot check pointer as it is poisoned, duh!
130 // PetscAssertPointer(ptr, 1);
131 if (PetscDefined(HAVE_ASAN)) {
132 ASAN_UNPOISON_MEMORY_REGION(ptr, size);
133 } else if (PetscDefined(HAVE_VALGRIND_MEMPOISON)) {
134 (void)VALGRIND_MAKE_MEM_DEFINED(ptr, size);
135 } else {
136 (void)ptr;
137 (void)size;
138 }
139 PetscFunctionReturn(PETSC_SUCCESS);
140 }
141
142 /*@C
143 PetscIsRegionPoisoned - Query whether a particular memory region is poisoned
144
145 Input Parameters:
146 + ptr - The pointer to the start of the region
147 - size - The size (in bytes) of the region to query
148
149 Output Parameter:
150 . poisoned - Whether the region is known to be poisoned
151
152 Level: developer
153
154 Notes:
155 Sets `poisoned` to `PETSC_BOOL3_TRUE` if at least 1 byte in the range [`ptr`, `ptr + size`) is
156 poisoned. Therefore a region must be entirely unpoisoned for `poisoned` to be `PETSC_BOOL3_FALSE`.
157
158 If `ptr` is `NULL` or `size` is `0` then `poisoned` is set to `PETSC_BOOL3_FALSE`.
159
160 If it is not possible to query the poisoned status of a region, then `poisoned` is set to
161 `PETSC_BOOL3_UNKNOWN`.
162
163 .seealso: `PetscPoisonMemoryRegion()`, `PetscUnpoisonMemoryRegion()`
164 @*/
PetscIsRegionPoisoned(const void * ptr,size_t size,PetscBool3 * poisoned)165 static inline PetscErrorCode PetscIsRegionPoisoned(const void *ptr, size_t size, PetscBool3 *poisoned)
166 {
167 PetscFunctionBegin;
168 // cannot check pointer as may be poisoned
169 // PetscAssertPointer(ptr, 1);
170 PetscAssertPointer(poisoned, 3);
171 *poisoned = PETSC_BOOL3_FALSE;
172 // if ptr is NULL, or if size = 0 then the "region" is not poisoned
173 if (ptr && size) {
174 #if PetscDefined(HAVE_ASAN)
175 if (__asan_region_is_poisoned((void *)ptr, size)) *poisoned = PETSC_BOOL3_TRUE;
176 #else
177 // valgrind does not appear to have a way of querying the status without raising an error
178 if (PetscDefined(HAVE_VALGRIND_MEMPOISON)) *poisoned = PETSC_BOOL3_UNKNOWN;
179 #endif
180 }
181 PetscFunctionReturn(PETSC_SUCCESS);
182 }
183