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