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: }