Actual source code: mempoison.h

  1: #pragma once

  3: #include <petsc/private/petscimpl.h>

  5: /* SUBMANSEC = Sys */

  7: #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
  8:   #include <sanitizer/asan_interface.h> // ASAN_POISON/UNPOISON_MEMORY_REGION

 10:   #define PETSC_HAVE_ASAN 1
 11: #endif

 13: #ifndef 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

 18: #if !PetscDefined(HAVE_WINDOWS_COMPILERS) && !defined(__MINGW32__)
 19: #include <petsc/private/valgrind/memcheck.h>

 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

 27: #ifndef 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

 33: /*@C
 34:   PetscPoisonMemoryRegion - Poison a region in memory, making it undereferencable

 36:   Not Available From Fortran

 38:   Input Parameters:
 39: + ptr  - The pointer to the start of the region
 40: - size - The size (in bytes) of the region to poison

 42:   Level: developer

 44:   Notes:
 45:   `ptr` must not be `NULL`. It is OK to poison the same memory region repeatedly (it is a
 46:   no-op).

 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.

 52:   Example Usage:
 53: .vb
 54:   PetscInt *array;

 56:   PetscMalloc1(15, &array);
 57:   // OK, memory is normal
 58:   array[0] = 10;
 59:   array[1] = 15;

 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];

 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));

 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;

 78:   PetscUnpoisonMemoryRegion(array + 10, sizeof(*array));
 79:   // OK this region is unpoisoned (even though surrounding memory is still poisoned!)
 80:   array[10] = 0;

 82:   PetscInt stack_array[10];

 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

 90: .seealso: `PetscUnpoisonMemoryRegion()`, `PetscIsRegionPoisoned()`
 91: @*/
 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: }

109: /*@C
110:   PetscUnpoisonMemoryRegion - Unpoison a previously poisoned memory region

112:   Input Parameters:
113: + ptr  - The pointer to the start of the region
114: - size - The size (in bytes) of the region to unpoison

116:   Level: developer

118:   Notes:
119:   Removes poisoning from a previously poisoned region. `ptr` may not be `NULL`. It is OK to
120:   unpoison an unpoisoned region.

122:   See `PetscPoisonMemoryRegion()` for example usage and further discussion.

124: .seealso: `PetscPoisonMemoryRegion()`, `PetscIsRegionPoisoned()`
125: @*/
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: }

142: /*@C
143:   PetscIsRegionPoisoned - Query whether a particular memory region is poisoned

145:   Input Parameters:
146: + ptr  - The pointer to the start of the region
147: - size - The size (in bytes) of the region to query

149:   Output Parameter:
150: . poisoned - Whether the region is known to be poisoned

152:   Level: developer

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`.

158:   If `ptr` is `NULL` or `size` is `0` then `poisoned` is set to `PETSC_BOOL3_FALSE`.

160:   If it is not possible to query the poisoned status of a region, then `poisoned` is set to
161:   `PETSC_BOOL3_UNKNOWN`.

163: .seealso: `PetscPoisonMemoryRegion()`, `PetscUnpoisonMemoryRegion()`
164: @*/
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: }