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