/*
* Copyright (c) 2017 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file parc_SafeMemory.h
* @ingroup developer
*
* @brief Facade to memory functions to make calls safer.
*
* This is a facade to interpose between an application and the standard C library functions posix_memalign(3),
* malloc(3), calloc(3), realloc(3) and free(3) that establishes detectable boundaries around an allocated memory segment,
* records a stack backtrace for each allocation,
* detects buffer overruns and underruns by checking the boundaries when the memory is deallocated,
* and tries to prevent a stray pointer to reference the memory again once it's been deallocated.
*
* The allocated memory consists of three contiguous segments: the prefix, the memory usable by the caller, and the suffix.
* The memory usable by the caller is aligned as specified by the caller.
* The alignment must be a power of 2 greater than or equal to the size of a {@code void *}.
*
* +--base +-prefix +-- aligned memory +-- suffix aligned on (void *)
* v v v v
* |________|PPPPPPPPPPPP|mmmmmmmmm...mmmm|___|SSSSSSSSS
* ^
* +-- variable padding
*
* Where '-' indicates padding, 'P' indicates the prefix data structure, 'm'
* indicates contiguous memory for use by the caller, and 'S" indicates the suffix data structure.
*
* To enable this facade, you must include the following line in your execution before any allocations are performed.
*
* @code
* {
* parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
* }
* @endcode
*
*/
#ifndef libparc_parc_SafeMemory_h
#define libparc_parc_SafeMemory_h
#include
#include
#include
#include
typedef enum parc_safety_memory_state {
PARCSafeMemoryState_OK = 0,
PARCSafeMemoryState_MISMATCHED = 1,
PARCSafeMemoryState_UNDERRUN = 2,
PARCSafeMemoryState_OVERRUN = 3,
PARCSafeMemoryState_NOTHINGALLOCATED = 4,
PARCSafeMemoryState_ALREADYFREE = 5
} PARCSafeMemoryState;
/**
* Generate a readable, null-terminated C string representation
* for the specified `PARCSafeMemoryState` type.
*
* @param [in] status A `PARCSafeMemoryState` value.
*
* @return A null-terminated C string that must be freed when done.
*
* Example:
* @code
* {
* size_t size = 100;
* uint32_t alignment = sizeof(void *);
* void *memory;
*
* memory = parcSafeMemory_Allocate(size);
* PARCSafeMemoryState state = parcSafeMemory_Destroy(&memory);
*
* printf("SafeMemoryState = %s\n", parcSafeMemoryState_ToString(state));
* }
* @endcode
*/
const char *parcSafeMemoryState_ToString(PARCSafeMemoryState status);
/**
* Memory operations defined by {@link PARCMemoryInterface}
* and implemented by the Safe Memory functions.
*/
extern PARCMemoryInterface PARCSafeMemoryAsPARCMemory;
/**
* Allocate Safe Memory.
*
* Allocate memory through the configured memory allocator, setting the environment to track this memory.
*
* @param [in] size The number of bytes necessary.
*
* @return non-NULL A pointer to allocated memory.
* @return NULL Memory could not be allocated.
*
* Example:
* @code
* {
* size_t size = 100;
* void *memory = parcSafeMemory_Allocate(size);
* }
* @endcode
*/
void *parcSafeMemory_Allocate(size_t size);
/**
* Allocate Safe Memory.
*
* Allocate memory through the configured memory allocator, setting the environment to track this memory.
*
* @param [in] requestedSize The number of bytes necessary.
*
* @return non-NULL A pointer to allocated memory.
* @return NULL Memory could not be allocated.
*
* Example:
* @code
* {
* size_t size = 100;
* void *memory = parcSafeMemory_Allocate(size);
* }
* @endcode
*/
void *parcSafeMemory_AllocateAndClear(size_t requestedSize);
/**
* Allocate aligned memory.
*
* Allocates @p size bytes of memory such that the allocation's
* base address is an exact multiple of alignment,
* and returns the allocation in the value pointed to by @p pointer.
*
* The requested alignment must be a power of 2 greater than or equal to `sizeof(void *)`.
*
* Memory that is allocated can be used as an argument in subsequent call `Reallocate`, however
* `Reallocate` is not guaranteed to preserve the original alignment.
*
* @param [out] pointer A pointer to a `void *` pointer that will be set to the address of the allocated memory.
* @param [in] alignment A power of 2 greater than or equal to `sizeof(void *)`
* @param [in] size The number of bytes to allocate.
*
* @return 0 Successful
* @return EINVAL The alignment parameter is not a power of 2 at least as large as sizeof(void *)
* @return ENOMEM Memory allocation error.
*
* Example:
* @code
* {
* void *allocatedMemory;
*
* int failure = parcSafeMemory_MemAlign(&allocatedMemory, sizeof(void *), 100);
* if (failure == 0) {
* parcSafeMemory_Deallocate(&allocatedMemory);
* // allocatedMemory is now equal to zero.
* }
* }
* @endcode
* @see `posix_memalign`
*/
int parcSafeMemory_MemAlign(void **pointer, size_t alignment, size_t size);
/**
* Deallocate memory previously allocated with {@link parcSafeMemory_Allocate}
*
* The value pointed to by @p pointer will be set to NULL.
*
* @param [in,out] pointer A pointer to a pointer to the allocated memory.
*
* Example:
* @code
* {
* size_t size = 100;
* void *memory = parcSafeMemory_Allocate(size);
*
* parcSafeMemory_Deallocate(&memory);
* }
* @endcode
*/
void parcSafeMemory_Deallocate(void **pointer);
/**
* A (mostly) suitable replacement for realloc(3).
* The primary difference is that it is an error if newSize is zero.
* If the newSize is equal to the old size, then NULL is returned.
*
* @param [in] original Pointer to the original memory
* @param [in] newSize The size of the newly re-allocated memory.
*
* @return Non-NULL A pointer to the newly allocated memory
* @return NULL An error occurred (newSize == oldSize, or newSize == 0)
*
* Example:
* @code
* {
* void *memory = parcSafeMemory_Allocate(100);
*
* size_t newLength = 0;
* unsigned char *newMemory = parcSafeMemory_Reallocate(memory, newLength);
*
* assertTrue(newMemory == NULL, "Expected NULL, actual %p", newMemory);
* }
* @endcode
*/
void *parcSafeMemory_Reallocate(void *original, size_t newSize);
/**
* Duplicate the given null-terminated C string.
*
* @param [in] string A pointer to a null-terminated C string.
* @param [in] length The length of the string, not including the terminating null character.
*
* @return non-NULL Allocated Safe Memory containing the duplicate string. This must be freed via `parcSafeMemory_Deallocate`.
* @return NULL Memory could not be allocated.
*
* Example:
* @code
* {
* char *string = "hello world";
* char *actual = parcSafeMemory_StringDuplicate(string, strlen(string));
* ...
* }
* @endcode
*
* @see parcSafeMemory_Deallocate
*/
char *parcSafeMemory_StringDuplicate(const char *string, size_t length);
/**
* Return the number of outstanding allocations.
*
* In practice, every allocation should be matched with a corresponding deallocation.
* This return the number of allocations that have not been deallocated.
*
* @return The number of outstanding allocations.
*
*/
uint32_t parcSafeMemory_Outstanding(void);
/**
* Display information about outstanding memory allocations.
*
* To enable this function, you must include the following line in your execution before any allocations are performed.
*
* @code
* {
* parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
* }
* @endcode
*
* @param [in] outputFd Output file descriptor.
*
* @return The number of currenly outstanding allocations.
*
* Example:
* @code
* {
* parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
*
* ...
*
* FILE *fd = fopen ("log.txt", "w");
* size_t outstandingAllocations = parcSafeMemory_ReportAllocation(fd);
* }
* @endcode
*/
uint32_t parcSafeMemory_ReportAllocation(int outputFd);
/**
* Determine if a pointer to Safe Memory is valid.
*
* Invalid indicates the memory is overrun or underrun.
*
* To enable this function, you must include the following line in your execution before any allocations are performed.
*
* @code
* {
* parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
* }
* @endcode
*
* @param [in] memory A pointer to previously allocated Safe Memory.
*
* @return true The memory is valid;
* @return false The memory is invalid;
*
* Example:
* @code
* {
* void *memory = parcSafeMemory_Allocate(100);
* if (parcSafeMemory_IsValid(memory) == false) {
* printf("Memory is invalid\n");
* }
* @endcode
*/
bool parcSafeMemory_IsValid(const void *memory);
/**
* Print a human readable representation of the given PARC Safe Memory array.
*
* To enable this function, you must include the following line in your execution before any allocations are performed.
*
* @code
* {
* parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
* }
* @endcode
*
* @param [in] indentation The level of indentation to use to pretty-print the output.
* @param [in] memory A pointer to the memory to display.
*
* Example:
* @code
* {
* PARCBuffer *instance = parcBuffer_Create();
*
* parcBuffer_Display(instance, 0);
*
* parcBuffer_Release(&instance);
* }
* @endcode
*/
void parcSafeMemory_Display(const void *memory, int indentation);
#endif // libparc_parc_SafeMemory_h