xref: /petsc/include/petsc/private/mempoison.h (revision 9d47de495d3c23378050c1b4a410c12a375cb6c6)
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