diff --git a/src/plugins/e-acsl/headers/header_spec.txt b/src/plugins/e-acsl/headers/header_spec.txt index afc89c322e8e373f3408e907dc33065c8f1748bd..33989f466fab8af824d5db02ea2bdb8620290b19 100644 --- a/src/plugins/e-acsl/headers/header_spec.txt +++ b/src/plugins/e-acsl/headers/header_spec.txt @@ -69,6 +69,8 @@ share/e-acsl/observation_model/segment_model/e_acsl_segment_omodel_debug.c: CEA_ share/e-acsl/observation_model/segment_model/e_acsl_segment_timestamp_retrieval.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.h: CEA_LGPL_OR_PROPRIETARY.E_ACSL +share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.h: CEA_LGPL_OR_PROPRIETARY.E_ACSL +share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.h: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/observation_model/e_acsl_heap.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/bittree_model/e_acsl_bittree_observation_model.c b/src/plugins/e-acsl/share/e-acsl/observation_model/bittree_model/e_acsl_bittree_observation_model.c index 3e48a5e1c20a2c871d095d3f5df7e011bba02089..4a68035f8c2dcabe21588acda40da18af398b203 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/bittree_model/e_acsl_bittree_observation_model.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/bittree_model/e_acsl_bittree_observation_model.c @@ -634,3 +634,20 @@ void eacsl_block_info(char *p) { } #endif /* }}} */ + +/************************************************************************/ +/*** E-ACSL concurrency primitives {{{ ***/ +/************************************************************************/ + +extern int pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict attr, + pthread_routine_t start_routine, void *restrict arg); + +int eacsl_pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict original_attr, + pthread_routine_t start_routine, void *restrict arg) { + private_abort("Concurrency is not supported for bittree model\n"); + return EPERM; +} + +/* }}} */ diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.c b/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.c index c7a9fd1d2b26121ca08f1fe76ffd2b98307214c8..88c7501c2c04597650779e3fae9500678f9d4db4 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.c @@ -103,6 +103,7 @@ int eacsl_separated(size_t count, ...) { # include "segment_model/e_acsl_segment_omodel_debug.c" # include "segment_model/e_acsl_segment_timestamp_retrieval.c" # include "segment_model/e_acsl_segment_tracking.c" +# include "segment_model/e_acsl_shadow_concurrency.c" # include "segment_model/e_acsl_shadow_layout.c" #elif defined E_ACSL_BITTREE_MMODEL # include "bittree_model/e_acsl_bittree.c" diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.h b/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.h index 6bc4c9f7654a700397fd339f9a5f923154c57b04..c465c3b09d98cc8c45ac5a0f1e5fb301e17af6c0 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.h +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/e_acsl_observation_model.h @@ -31,6 +31,8 @@ #ifndef E_ACSL_OBSERVATION_MODEL_H #define E_ACSL_OBSERVATION_MODEL_H +#include <errno.h> +#include <pthread.h> #include <stddef.h> #include <stdint.h> @@ -82,6 +84,9 @@ #define eacsl_initialize export_alias(initialize) #define eacsl_full_init export_alias(full_init) +/* Concurrency */ +#define eacsl_pthread_create export_alias(pthread_create) + /* }}} */ /************************************************************************/ @@ -395,4 +400,34 @@ int eacsl_separated(size_t count, ...) __attribute__((FC_BUILTIN)); /* }}} */ +/************************************************************************/ +/*** E-ACSL concurrency primitives {{{ ***/ +/************************************************************************/ + +/*! \brief Function pointer to a function given to \p pthread_create. */ +typedef void *(*pthread_routine_t)(void *); + +/*! \brief Drop-in replacement for \p pthread_create with memory tracking + * enabled. + * + * For further information, see \p pthread_create(3). + */ +// Specification extracted from share/libc/pthread.h in Frama-C +/*@ requires valid_thread: \valid(thread); + requires valid_null_attr: attr == \null || \valid_read(attr); + requires valid_routine: \valid_function(start_routine); + requires valid_null_arg: arg == \null || \valid((char*)arg); + assigns *thread \from *attr; + assigns \result \from indirect:*attr; + ensures success_or_error: + \result == 0 || + \result == EAGAIN || \result == EINVAL || \result == EPERM; + */ +int eacsl_pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict attr, + pthread_routine_t start_routine, void *restrict arg) + __attribute__((FC_BUILTIN)); + +/* }}} */ + #endif // E_ACSL_OBSERVATION_MODEL_H diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/internals/e_acsl_patricia_trie.c b/src/plugins/e-acsl/share/e-acsl/observation_model/internals/e_acsl_patricia_trie.c index 973e1ab2d09deadcfcddf3d9759ff148a6c289ce..816b086f85923f2584bebc11a8cf791f4fa3f9a0 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/internals/e_acsl_patricia_trie.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/internals/e_acsl_patricia_trie.c @@ -27,6 +27,7 @@ #include <stdint.h> +#include "../../internals/e_acsl_concurrency.h" #include "../../internals/e_acsl_config.h" #include "../../internals/e_acsl_malloc.h" #include "../../internals/e_acsl_private_assert.h" @@ -133,6 +134,8 @@ typedef struct pt_struct { clean_leaf_fct_t clean_leaf_fct; /*! \brief Function to print the content of a leaf. */ print_leaf_fct_t print_leaf_fct; + /*! \brief Read-write lock to concurrently access the trie. */ + E_ACSL_RWLOCK_DECL(lock); } pt_struct_t; /* common prefix of two addresses */ @@ -187,12 +190,15 @@ pt_struct_t *pt_create(get_ptr_fct_t get_ptr_fct, pt->contains_ptr_fct = contains_ptr_fct; pt->clean_leaf_fct = clean_leaf_fct; pt->print_leaf_fct = print_leaf_fct; + + E_ACSL_RWINIT(pt->lock, NULL); return pt; } void pt_destroy(pt_struct_t *pt) { DASSERT(pt != NULL); pt_clean(pt); + E_ACSL_RWDESTROY(pt->lock); private_free(pt); } @@ -234,6 +240,8 @@ void pt_insert(pt_struct_t *pt, pt_leaf_t leaf) { DASSERT(leaf != NULL); pt_node_t *new_leaf_node; + E_ACSL_WLOCK(pt->lock); + uintptr_t ptr = (uintptr_t)pt->get_ptr_fct(leaf); new_leaf_node = private_malloc(sizeof(pt_node_t)); @@ -294,6 +302,8 @@ void pt_insert(pt_struct_t *pt, pt_leaf_t leaf) { DASSERT((parent->left == sibling && parent->right == new_leaf_node) || (parent->left == new_leaf_node && parent->right == sibling)); } + + E_ACSL_RWUNLOCK(pt->lock); } /* called from pt_remove */ @@ -380,10 +390,15 @@ static void pt_remove_leaf_node(pt_struct_t *pt, void pt_remove(pt_struct_t *pt, pt_leaf_t leaf) { DASSERT(pt != NULL); + + E_ACSL_WLOCK(pt->lock); + pt_node_t *leaf_node_to_delete = pt_get_leaf_node_from_leaf(pt, leaf); DASSERT(leaf_node_to_delete->leaf == leaf); pt_remove_leaf_node(pt, leaf_node_to_delete); + + E_ACSL_RWUNLOCK(pt->lock); } /** Starting at `node`, remove all leaves that satisfy the given predicate from @@ -414,13 +429,19 @@ int pt_remove_node_if(pt_struct_t *pt, pt_node_t *node, void pt_remove_if(pt_struct_t *pt, pt_predicate_t predicate) { DASSERT(pt != NULL); + E_ACSL_WLOCK(pt->lock); pt_remove_node_if(pt, pt->root, predicate); + E_ACSL_RWUNLOCK(pt->lock); } pt_leaf_t pt_lookup(const pt_struct_t *pt, void *ptr) { DASSERT(pt != NULL); - DASSERT(pt->root != NULL); DASSERT(ptr != NULL); + + E_ACSL_RLOCK(pt->lock); + DASSERT(pt->root != NULL); + + pt_leaf_t res; pt_node_t *tmp = pt->root; /*@ loop assigns tmp; @@ -428,7 +449,8 @@ pt_leaf_t pt_lookup(const pt_struct_t *pt, void *ptr) { while (!tmp->is_leaf) { // if the ptr we are looking for does not share the prefix of tmp if ((tmp->addr & tmp->mask) != ((uintptr_t)ptr & tmp->mask)) { - return NULL; + res = NULL; + goto end; } // two children @@ -441,21 +463,28 @@ pt_leaf_t pt_lookup(const pt_struct_t *pt, void *ptr) { == ((uintptr_t)ptr & tmp->left->mask)) { tmp = tmp->left; } else { - return NULL; + res = NULL; + goto end; } } if (pt->get_ptr_fct(tmp->leaf) != ptr) { - return NULL; + res = NULL; } - return tmp->leaf; + +end: + E_ACSL_RWUNLOCK(pt->lock); + return res; } pt_leaf_t pt_find(const pt_struct_t *pt, void *ptr) { DASSERT(pt != NULL); + E_ACSL_RLOCK(pt->lock); + pt_leaf_t res; if (pt->root == NULL || ptr == NULL) { - return NULL; + res = NULL; + goto end; } pt_node_t *tmp = pt->root; @@ -465,16 +494,19 @@ pt_leaf_t pt_find(const pt_struct_t *pt, void *ptr) { if (tmp->is_leaf) { /* tmp cannot contain ptr because its begin addr is higher */ if (tmp->addr > (uintptr_t)ptr) { - return NULL; + res = NULL; + goto end; } /* tmp->addr <= ptr, tmp may contain ptr */ else if (pt->contains_ptr_fct(tmp->leaf, ptr)) { - return tmp->leaf; + res = tmp->leaf; + goto end; } /* tmp->addr <= ptr, but tmp does not contain ptr */ else { - return NULL; + res = NULL; + goto end; } } @@ -490,13 +522,18 @@ pt_leaf_t pt_find(const pt_struct_t *pt, void *ptr) { tmp = tmp->left; } else { if (other_choice == NULL) { - return NULL; + res = NULL; + goto end; } else { tmp = other_choice; other_choice = NULL; } } } + +end: + E_ACSL_RWUNLOCK(pt->lock); + return res; } static pt_leaf_t pt_find_if_rec(pt_node_t *node, pt_predicate_t predicate) { @@ -514,12 +551,17 @@ static pt_leaf_t pt_find_if_rec(pt_node_t *node, pt_predicate_t predicate) { pt_leaf_t pt_find_if(const pt_struct_t *pt, pt_predicate_t predicate) { DASSERT(pt != NULL); + E_ACSL_RLOCK(pt->lock); + pt_leaf_t res; if (pt->root == NULL) { - return NULL; + res = NULL; } else { - return pt_find_if_rec(pt->root, predicate); + res = pt_find_if_rec(pt->root, predicate); } + + E_ACSL_RWUNLOCK(pt->lock); + return res; } static void pt_clean_rec(const pt_struct_t *pt, pt_node_t *ptr) { @@ -538,8 +580,10 @@ static void pt_clean_rec(const pt_struct_t *pt, pt_node_t *ptr) { void pt_clean(pt_struct_t *pt) { DASSERT(pt != NULL); + E_ACSL_WLOCK(pt->lock); pt_clean_rec(pt, pt->root); pt->root = NULL; + E_ACSL_RWUNLOCK(pt->lock); } #ifdef E_ACSL_DEBUG @@ -564,7 +608,9 @@ static void pt_print_node(const pt_struct_t *pt, pt_node_t *node, int depth) { void pt_print(const pt_struct_t *pt) { DLOG("------------DEBUG\n"); if (pt != NULL) { + E_ACSL_RLOCK(pt->lock); pt_print_node(pt, pt->root, 0); + E_ACSL_RWUNLOCK(pt->lock); } else { DLOG("Patricia trie is NULL\n"); } diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_observation_model.c b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_observation_model.c index 90c2522e41f7247eecb27f2f6a02bc0183e5dea7..8ac970e200e4e7b11fadc9781136559efbaab2cf 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_observation_model.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_observation_model.c @@ -30,6 +30,7 @@ #include <stdint.h> #include "../../instrumentation_model/e_acsl_temporal.h" +#include "../../internals/e_acsl_concurrency.h" #include "../../internals/e_acsl_debug.h" #include "../../internals/e_acsl_malloc.h" #include "../../internals/e_acsl_private_assert.h" @@ -41,95 +42,182 @@ #include "../e_acsl_observation_model.h" #include "../internals/e_acsl_timestamp_retrieval.h" +#ifdef E_ACSL_CONCURRENCY_PTHREAD +/*! \brief Global read-write lock for the memory model. */ +static pthread_rwlock_t __e_acsl_segment_model_global_lock = + PTHREAD_RWLOCK_INITIALIZER; +#endif + +void *malloc(size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + void *result = unsafe_malloc(size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + +void *calloc(size_t nbr_elt, size_t size_elt) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + void *result = unsafe_calloc(nbr_elt, size_elt); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + +void *realloc(void *ptr, size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + void *result = unsafe_realloc(ptr, size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + +void free(void *ptr) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + unsafe_free(ptr); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); +} + +void *aligned_alloc(size_t alignment, size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + void *result = unsafe_aligned_alloc(alignment, size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + +int posix_memalign(void **memptr, size_t alignment, size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + int result = unsafe_posix_memalign(memptr, alignment, size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + void *eacsl_store_block(void *ptr, size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); /* Only stack-global memory blocks are recorded explicitly via this function. Heap blocks should be tracked internally using memory allocation functions such as malloc or calloc. */ shadow_alloca(ptr, size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); return ptr; } void eacsl_delete_block(void *ptr) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); /* Block deletion should be performed on stack/global addresses only, * heap blocks should be deallocated manually via free/cfree/realloc. */ shadow_freea(ptr); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); } void *eacsl_store_block_duplicate(void *ptr, size_t size) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); if (allocated((uintptr_t)ptr, size, (uintptr_t)ptr)) - eacsl_delete_block(ptr); + shadow_freea(ptr); shadow_alloca(ptr, size); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); return ptr; } /*! \brief Initialize a chunk of memory given by its start address (`addr`) * and byte length (`n`). */ void eacsl_initialize(void *ptr, size_t n) { - TRY_SEGMENT((uintptr_t)ptr, initialize_heap_region((uintptr_t)ptr, n), - initialize_static_region((uintptr_t)ptr, n)) + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + unsafe_initialize(ptr, n); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); } void eacsl_full_init(void *ptr) { - eacsl_initialize(ptr, _block_length(ptr)); + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); + unsafe_initialize(ptr, _block_length(ptr)); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); } void eacsl_mark_readonly(void *ptr) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); mark_readonly_region((uintptr_t)ptr, _block_length(ptr)); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); } /* ********************** */ /* E-ACSL annotations {{{ */ /* ********************** */ +int eacsl_freeable(void *ptr) { + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + int result = unsafe_freeable(ptr); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; +} + int eacsl_valid(void *ptr, size_t size, void *ptr_base, void *addrof_base) { - return size == 0 - || (allocated((uintptr_t)ptr, size, (uintptr_t)ptr_base) + int result = size == 0; + if (!result) { + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + result = allocated((uintptr_t)ptr, size, (uintptr_t)ptr_base) && !readonly(ptr) #ifdef E_ACSL_TEMPORAL && temporal_valid(ptr_base, addrof_base) #endif - ); + ; + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + } + return result; } int eacsl_valid_read(void *ptr, size_t size, void *ptr_base, void *addrof_base) { - return size == 0 - || (allocated((uintptr_t)ptr, size, (uintptr_t)ptr_base) + int result = size == 0; + if (!result) { + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + result = allocated((uintptr_t)ptr, size, (uintptr_t)ptr_base) #ifdef E_ACSL_TEMPORAL && temporal_valid(ptr_base, addrof_base) #endif - ); + ; + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + } + return result; } /*! NB: The implementation for this function can also be specified via \p _base_addr macro that will eventually call ::TRY_SEGMENT. The following implementation is preferred for performance reasons. */ void *eacsl_base_addr(void *ptr) { - TRY_SEGMENT(ptr, return (void *)heap_info((uintptr_t)ptr, 'B'), - return (void *)static_info((uintptr_t)ptr, 'B')); - return NULL; + void *result = NULL; + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + TRY_SEGMENT(ptr, result = (void *)heap_info((uintptr_t)ptr, 'B'), + result = (void *)static_info((uintptr_t)ptr, 'B')); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; } /*! NB: Implementation of the following function can also be specified via \p _block_length macro. A more direct approach via ::TRY_SEGMENT is preferred for performance reasons. */ size_t eacsl_block_length(void *ptr) { - TRY_SEGMENT(ptr, return heap_info((uintptr_t)ptr, 'L'), - return static_info((uintptr_t)ptr, 'L')) - return 0; + size_t result = 0; + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + TRY_SEGMENT(ptr, result = heap_info((uintptr_t)ptr, 'L'), + result = static_info((uintptr_t)ptr, 'L')) + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; } size_t eacsl_offset(void *ptr) { - TRY_SEGMENT(ptr, return heap_info((uintptr_t)ptr, 'O'), - return static_info((uintptr_t)ptr, 'O')); - return 0; + size_t result = 0; + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + TRY_SEGMENT(ptr, result = heap_info((uintptr_t)ptr, 'O'), + result = static_info((uintptr_t)ptr, 'O')); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; } int eacsl_initialized(void *ptr, size_t size) { + int result = 0; uintptr_t addr = (uintptr_t)ptr; - TRY_SEGMENT_WEAK(addr, return heap_initialized(addr, size), - return static_initialized(addr, size)); - return 0; + E_ACSL_RLOCK(__e_acsl_segment_model_global_lock); + TRY_SEGMENT_WEAK(addr, result = heap_initialized(addr, size), + result = static_initialized(addr, size)); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); + return result; } /* }}} */ @@ -196,67 +284,69 @@ static void argv_alloca(int *argc_ref, char ***argv_ref) { /* Program initialization {{{ */ extern int main(void); +static void do_mspaces_init() { + describe_run(); + eacsl_make_memory_spaces(64 * MB, get_heap_size()); + /* Allocate and log shadow memory layout of the execution. + Case of the segments available before main. */ + init_shadow_layout_pre_main(); +} + void mspaces_init() { - /* [already_run] avoids reentrancy issue (see Gitlab issue #83), + /* [E_ACSL_RUN_ONCE] avoids reentrancy issue (see Gitlab issue #83), e.g. in presence of a GCC's constructors that invokes malloc possibly several times before calling main. */ - static char already_run = 0; - if (!already_run) { - describe_run(); - eacsl_make_memory_spaces(64 * MB, get_heap_size()); - /* Allocate and log shadow memory layout of the execution. - Case of the segments available before main. */ - init_shadow_layout_pre_main(); - already_run = 1; - } + E_ACSL_RUN_ONCE(do_mspaces_init); } -void eacsl_memory_init(int *argc_ref, char ***argv_ref, size_t ptr_size) { - /* [already_run] avoids reentrancy issue (see Gitlab issue #83), - e.g. in presence of a recursive call to 'main' */ - static char already_run = 0; - if (!already_run) { - mspaces_init(); - /* Verify that the given size of a pointer matches the one in the present +void do_eacsl_memory_init(int *argc_ref, char ***argv_ref, size_t ptr_size) { + mspaces_init(); + /* Verify that the given size of a pointer matches the one in the present architecture. This is a guard against Frama-C instrumentations using architectures different to the given one. */ - arch_assert(ptr_size); - /* Initialize report file with debug logs (only in debug mode). */ - initialize_report_file(argc_ref, argv_ref); - /* Lift stack limit to account for extra stack memory overhead. */ - increase_stack_limit(E_ACSL_STACK_SIZE * MB); - /* Allocate and log shadow memory layout of the execution. Case of the + arch_assert(ptr_size); + /* Initialize report file with debug logs (only in debug mode). */ + initialize_report_file(argc_ref, argv_ref); + /* Lift stack limit to account for extra stack memory overhead. */ + increase_stack_limit(E_ACSL_STACK_SIZE * MB); + /* Allocate and log shadow memory layout of the execution. Case of the segments available after main. */ - init_shadow_layout_main(argc_ref, argv_ref); - //DEBUG_PRINT_LAYOUT; - /* Make sure the layout holds */ - DVALIDATE_SHADOW_LAYOUT; - /* Track program arguments. */ - if (argc_ref && argv_ref) - argv_alloca(argc_ref, argv_ref); - /* Track main function */ - shadow_alloca(&main, sizeof(&main)); - initialize_static_region((uintptr_t)&main, sizeof(&main)); - /* Tracking safe locations */ - collect_safe_locations(); - int i; - for (i = 0; i < get_safe_locations_count(); i++) { - memory_location *loc = get_safe_location(i); - if (loc->is_on_static) { - void *addr = (void *)loc->address; - uintptr_t len = loc->length; - shadow_alloca(addr, len); - if (loc->is_initialized) - eacsl_initialize(addr, len); - } + init_shadow_layout_main(argc_ref, argv_ref); + //DEBUG_PRINT_LAYOUT; + /* Make sure the layout holds */ + DVALIDATE_SHADOW_LAYOUT; + /* Track program arguments. */ + if (argc_ref && argv_ref) + argv_alloca(argc_ref, argv_ref); + /* Track main function */ + shadow_alloca(&main, sizeof(&main)); + initialize_static_region((uintptr_t)&main, sizeof(&main)); + /* Tracking safe locations */ + collect_safe_locations(); + int i; + for (i = 0; i < get_safe_locations_count(); i++) { + memory_location *loc = get_safe_location(i); + if (loc->is_on_static) { + void *addr = (void *)loc->address; + uintptr_t len = loc->length; + shadow_alloca(addr, len); + if (loc->is_initialized) + unsafe_initialize(addr, len); } - init_infinity_values(); - already_run = 1; } + init_infinity_values(); +} + +void eacsl_memory_init(int *argc_ref, char ***argv_ref, size_t ptr_size) { + /* [E_ACSL_RUN_ONCE_WITH_ARGS] avoids reentrancy issue (see Gitlab issue #83), + e.g. in presence of a recursive call to 'main'. */ + E_ACSL_RUN_ONCE_WITH_ARGS(do_eacsl_memory_init, argc_ref, argv_ref, ptr_size); } void eacsl_memory_clean(void) { + E_ACSL_WLOCK(__e_acsl_segment_model_global_lock); clean_shadow_layout(); report_heap_leaks(); + E_ACSL_RWUNLOCK(__e_acsl_segment_model_global_lock); } /* }}} */ diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c index 56f7535cec778d4d5b9f3d9eaa8840dd7fa1c750..add2d3db74d22f5f1f4df3727e732ddd0fcffdc5 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.c @@ -584,8 +584,7 @@ static void set_heap_segment(void *ptr, size_t size, size_t alloc_size, } } -void *malloc(size_t size) { - +void *unsafe_malloc(size_t size) { size_t alloc_size = ALLOC_SIZE(size); /* Return NULL if the size is too large to be aligned */ @@ -604,7 +603,7 @@ void *malloc(size_t size) { return res; } -void *calloc(size_t nmemb, size_t size) { +void *unsafe_calloc(size_t nmemb, size_t size) { /* Since both `nmemb` and `size` are both of size `size_t` the multiplication * of the arguments (which gives the actual allocation size) might lead to an * integer overflow. The below code checks for an overflow and sets the @@ -631,7 +630,7 @@ void *calloc(size_t nmemb, size_t size) { } void *shadow_copy(const void *ptr, size_t size, int init) { - char *ret = (init) ? calloc(1, size) : malloc(size); + char *ret = (init) ? unsafe_calloc(1, size) : unsafe_malloc(size); private_assert(ret != NULL, "Shadow copy failed\n", NULL); /* Shadow copy is internal, therefore heap status should not be updated. Since it is set via `set_heap_segment`, it needs to be reverted back. */ @@ -678,7 +677,7 @@ static void unset_heap_segment(void *ptr, int init, const char *function) { } } -void free(void *ptr) { +void unsafe_free(void *ptr) { if (ptr == NULL) { /* Fail if instructed to treat NULL input to free as invalid. */ #ifdef E_ACSL_FREE_VALID_ADDRESS @@ -688,7 +687,7 @@ void free(void *ptr) { } if (ptr != NULL) { /* NULL is a valid behaviour */ - if (eacsl_freeable(ptr)) { + if (unsafe_freeable(ptr)) { unset_heap_segment(ptr, 1, "free"); public_free(ptr); } else { @@ -699,17 +698,17 @@ void free(void *ptr) { /* }}} */ /* Heap reallocation (realloc) {{{ */ -void *realloc(void *ptr, size_t size) { +void *unsafe_realloc(void *ptr, size_t size) { char *res = NULL; /* Resulting pointer */ /* If the pointer is NULL then realloc is equivalent to malloc(size) */ if (ptr == NULL) - return malloc(size); + return unsafe_malloc(size); /* If the pointer is not NULL and the size is zero then realloc is * equivalent to free(ptr) */ else if (ptr != NULL && size == 0) { - free(ptr); + unsafe_free(ptr); } else { - if (eacsl_freeable(ptr)) { /* ... and can be used as an input to `free` */ + if (unsafe_freeable(ptr)) { /* ... and can be used as an input to `free` */ size_t alloc_size = ALLOC_SIZE(size); res = public_realloc(ptr, alloc_size); DVALIDATE_ALIGNMENT(res); @@ -822,7 +821,7 @@ void *realloc(void *ptr, size_t size) { /* }}} */ /* Heap aligned allocation (aligned_alloc) {{{ */ -void *aligned_alloc(size_t alignment, size_t size) { +void *unsafe_aligned_alloc(size_t alignment, size_t size) { /* Check if: * - size and alignment are greater than zero * - alignment is a power of 2 @@ -841,7 +840,7 @@ void *aligned_alloc(size_t alignment, size_t size) { /* }}} */ /* Heap aligned allocation (posix_memalign) {{{ */ -int posix_memalign(void **memptr, size_t alignment, size_t size) { +int unsafe_posix_memalign(void **memptr, size_t alignment, size_t size) { /* Check if: * - size and alignment are greater than zero * - alignment is a power of 2 and a multiple of sizeof(void*) */ @@ -892,7 +891,7 @@ int heap_allocated(uintptr_t addr, size_t size, uintptr_t base_ptr) { return 0; } -int eacsl_freeable(void *ptr) { /* + */ +int unsafe_freeable(void *ptr) { /* + */ uintptr_t addr = (uintptr_t)ptr; /* Address is not on the program's heap, so cannot be freed */ if (!IS_ON_HEAP(addr)) @@ -1019,6 +1018,13 @@ void initialize_heap_region(uintptr_t addr, long len) { } /* }}} */ +/* Heap or static initialization {{{ */ +void unsafe_initialize(void *ptr, size_t n) { + TRY_SEGMENT((uintptr_t)ptr, initialize_heap_region((uintptr_t)ptr, n), + initialize_static_region((uintptr_t)ptr, n)) +} +/* }}} */ + /* Internal state print (debug mode) {{{ */ #ifdef E_ACSL_DEBUG void printbyte(unsigned char c, char buf[], size_t bufsize) { diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.h b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.h index 6f2c8cc80a9412f7086617177e742958d48dc96d..de93d8999bf64a8b9f78535b2f9819e13c9f10d7 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.h +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_segment_tracking.h @@ -401,11 +401,19 @@ void static_store_temporal_referent(uintptr_t addr, uint32_t ref); #endif /*}}} E_ACSL_TEMPORAL*/ /* }}} */ -/* Static initialization {{{ */ -/*! \brief The following function marks n bytes starting from the address - * given by addr as initialized. `size` equating to zero indicates that the - * whole block should be marked as initialized. */ +/* Initialization {{{ */ + +/*! \brief Unsafe version of `eacsl_initialize()` that does not lock the memory + model. */ +void unsafe_initialize(void *ptr, size_t n); + +/*! \brief The following function marks n bytes on a static region starting from + * the address given by addr as initialized. `size` equating to zero indicates + * that the whole block should be marked as initialized. */ void initialize_static_region(uintptr_t addr, long size); + +/*! \brief Mark n bytes on the heap starting from address addr as initialized */ +void initialize_heap_region(uintptr_t addr, long len); /* }}} */ /* Read-only {{{ */ @@ -418,6 +426,27 @@ void mark_readonly_region(uintptr_t addr, long size); /* }}} */ /* Heap allocation {{{ (malloc/calloc) */ + +/*! \brief Unsafe version of `malloc()` that does not lock the memory model. */ +void *unsafe_malloc(size_t size); + +/*! \brief Unsafe version of `calloc()` that does not lock the memory model. */ +void *unsafe_calloc(size_t nbr_elt, size_t size_elt); + +/*! \brief Unsafe version of `realloc()` that does not lock the memory model. */ +void *unsafe_realloc(void *ptr, size_t size); + +/*! \brief Unsafe version of `free()` that does not lock the memory model. */ +void unsafe_free(void *ptr); + +/*! \brief Unsafe version of `aligned_alloc()` that does not lock the memory + model. */ +void *unsafe_aligned_alloc(size_t alignment, size_t size); + +/*! \brief Unsafe version of `posix_memalign()` that does not lock the memory + model. */ +int unsafe_posix_memalign(void **memptr, size_t alignment, size_t size); + /** \brief Return shadowed copy of a memory chunk on a program's heap using. * If `init` parameter is set to a non-zero value the memory occupied by the * resulting block is set to be initialized and uninitialized otherwise. */ @@ -443,13 +472,16 @@ void *shadow_copy(const void *ptr, size_t size, int init); * only legal if both `p` and `p+i` point to the same block. */ int heap_allocated(uintptr_t addr, size_t size, uintptr_t base_ptr); -/*! \brief Return a non-zero value if a given address is a base address of a +/*! \brief Unsafe version of `eacsl_freeable()` that does not lock the memory + * model. + * + * Return a non-zero value if a given address is a base address of a * heap-allocated memory block that `addr` belongs to. * * As some of the other functions, \b \\freeable can be expressed using * ::IS_ON_HEAP, ::heap_allocated and ::_base_addr. Here direct * implementation is preferred for performance reasons. */ -int eacsl_freeable(void *ptr); +int unsafe_freeable(void *ptr); /*! \brief Querying information about a specific heap memory address. * This function is similar to ::static_info except it returns data @@ -474,11 +506,6 @@ uint32_t heap_temporal_info(uintptr_t addr, int origin); void heap_store_temporal_referent(uintptr_t addr, uint32_t ref); #endif /*}}} E_ACSL_TEMPORAL*/ -/* Heap initialization {{{ */ -/*! \brief Mark n bytes on the heap starting from address addr as initialized */ -void initialize_heap_region(uintptr_t addr, long len); -/* }}} */ - /* Internal state print (debug mode) {{{ */ #ifdef E_ACSL_DEBUG /* ! \brief Print human-readable representation of a byte in a primary diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.c b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.c new file mode 100644 index 0000000000000000000000000000000000000000..0365a26d2d6cc92abf2f06cac3421a22ed290ab7 --- /dev/null +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.c @@ -0,0 +1,425 @@ +/**************************************************************************/ +/* */ +/* This file is part of the Frama-C's E-ACSL plug-in. */ +/* */ +/* Copyright (C) 2012-2021 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/**************************************************************************/ + +/*! *********************************************************************** + * \file + * \brief Implementation of E-ACSL concurrency support for the shadow memory + * model. +***************************************************************************/ + +#include "e_acsl_shadow_concurrency.h" + +#ifdef E_ACSL_CONCURRENCY_PTHREAD + +# include <pthread.h> +# include <stdlib.h> +# include <string.h> + +# include "../../internals/e_acsl_concurrency.h" +# include "../../internals/e_acsl_rtl_error.h" +# include "../internals/e_acsl_patricia_trie.h" +# include "e_acsl_shadow_layout.h" + +/************************************************************************/ +/*** Patricia trie for the thread memory partitions {{{ ***/ +/* Each thread has its own stack and TLS segment. To be able to monitor the + * threaded code, we need to track those memory segments and quickly determine + * on which segment is a given pointer. + * The patricia trie structure offers great lookup performance and adequate + * insertion and removal performance for this use-case. + */ +/************************************************************************/ + +/*! Leaf of the patricia trie containing a memory segment belonging to a + thread. */ +typedef struct thread_partition { + /*! Id of the thread */ + pthread_t id; + /*! Memory segment belonging to a thread */ + memory_partition p; +} thread_partition_t; + +/*! \param leaf Leaf of the patricia trie. + \return The start address of the memory segment on the given leaf. */ +static void *tp_get_ptr(pt_leaf_t leaf) { + DASSERT(leaf != NULL); + return (void *)((thread_partition_t *)leaf)->p.application.start; +} + +/*! \param leaf Leaf of the patricia trie. + \param ptr Pointer address that we are looking for. + \return true (!= 0) if the pointer is contained in the memory segment + represented by the leaf, false (== 0) otherwise. */ +static int tp_contains_ptr(pt_leaf_t leaf, void *ptr) { + DASSERT(leaf != NULL); + thread_partition_t *tp = (thread_partition_t *)leaf; + return IS_ON(ptr, tp->p.application); +} + +/*! \brief Deallocate the leaf, i.e. deallocate the shadow regions used by the + runtime analysis for the given leaf, and the leaf itself. */ +static void tp_clean(pt_leaf_t leaf) { + DASSERT(leaf != NULL); + thread_partition_t *tp = (thread_partition_t *)leaf; + if (tp->p.primary.mspace) { + eacsl_destroy_mspace(tp->p.primary.mspace); + } + if (tp->p.secondary.mspace) { + eacsl_destroy_mspace(tp->p.secondary.mspace); + } +# ifdef E_ACSL_TEMPORAL + if (tp->p.temporal_primary.mspace) { + eacsl_destroy_mspace(tp->p.temporal_primary.mspace); + } + if (tp->p.temporal_secondary.mspace) { + eacsl_destroy_mspace(tp->p.temporal_secondary.mspace); + } +# endif + private_free(tp); +} + +/*! \brief Print the content of the given leaf */ +static void tp_print(pt_leaf_t leaf) { +# if defined(E_ACSL_DEBUG) + DASSERT(leaf != NULL); + thread_partition_t *tp = (thread_partition_t *)leaf; + DLOG("thread id: %lu\n", tp->id); + print_memory_partition(&tp->p); +# endif +} + +/*! \return true (!= 0) if the leaf corresponds to the current thread, false + (== 0) otherwise. */ +static int tp_is_same_thread(pt_leaf_t leaf) { + DASSERT(leaf != NULL); + pthread_t id = pthread_self(); + return pthread_equal(id, ((thread_partition_t *)leaf)->id); +} + +/*! Instance of patricia trie to store the memory segments of the threads. */ +pt_struct_t *thread_partitions = NULL; + +/*! \brief Creates the thread partitions trie. This function should be used with + E_ACSL_RUN_ONCE. */ +static void create_thread_partitions_trie() { + DVASSERT(thread_partitions == NULL, + "Thread partitions trie already created\n"); + thread_partitions = + pt_create(tp_get_ptr, tp_contains_ptr, tp_clean, tp_print); +} + +/* }}} */ + +/************************************************************************/ +/*** Thread shadow layout {{{ ***/ +/************************************************************************/ + +/*! \brief Fill the given `memory_partition` with the addresses of the stack + segment for the current thread. */ +static void fill_thread_layout_stack(memory_partition *pstack, + size_t stack_size) { + // Scan /proc/self/maps to retrieve the memory segment corresponding to the + // current thread stack + FILE *maps = fopen("/proc/self/maps", "r"); + DVASSERT(maps != NULL, "Unable to open /proc/self/maps: %s\n", + rtl_strerror(errno)); + + int result; + // Use the address of the local variable `maps`, stored on the stack, to find + // the segment corresponding to the current stack + uintptr_t stack_addr = (uintptr_t)&maps; + uintptr_t start, end; + char buffer[255]; + while (fgets(buffer, sizeof(buffer), maps) != NULL) { + result = sscanf(buffer, "%lx-%lx", &start, &end); + DVASSERT(result == 2, "Reading /proc/self/maps failed: %s\n", + rtl_strerror(errno)); + + if (start <= stack_addr && stack_addr <= end) { + break; + } + } + + result = fclose(maps); + DVASSERT(result == 0, "Unable to close /proc/self/maps: %s\n", + rtl_strerror(errno)); + + // Allocate shadow memory spaces and compute offsets + set_application_segment(&pstack->application, start, stack_size, + "thread_stack", NULL); + set_shadow_segment(&pstack->primary, &pstack->application, 1, + "thread_stack_primary"); + set_shadow_segment(&pstack->secondary, &pstack->application, 1, + "thread_stack_secondary"); +# ifdef E_ACSL_TEMPORAL + set_shadow_segment(&pstack->temporal_primary, &pstack->application, 1, + "temporal_thread_stack_primary"); + set_shadow_segment(&pstack->temporal_secondary, &pstack->application, 1, + "temporal_thread_stack_secondary"); +# endif +} + +/*! \brief Fill the given `memory_partition` with the addresses of the TLS + segment for the current thread. */ +static void fill_thread_layout_tls(memory_partition *ptls) { + // Since the TLS is by design specific to each thread, we can reuse the + // method used to identify the TLS segment in the main thread + set_application_segment(&ptls->application, get_tls_start(), get_tls_size(), + "thread_tls", NULL); + set_shadow_segment(&ptls->primary, &ptls->application, 1, + "thread_tls_primary"); + set_shadow_segment(&ptls->secondary, &ptls->application, 1, + "thread_tls_secondary"); +# ifdef E_ACSL_TEMPORAL + set_shadow_segment(&ptls->temporal_primary, &ptls->application, 1, + "thread_temporal_tls_primary"); + set_shadow_segment(&ptls->temporal_secondary, &ptls->application, 1, + "thread_temporal_tls_secondary"); +# endif +} + +void init_thread_shadow_layout(size_t stack_size) { + DASSERT(thread_partitions != NULL); + + pthread_t id = pthread_self(); + + thread_partition_t *stack = private_malloc(sizeof(thread_partition_t)); + stack->id = id; + fill_thread_layout_stack(&stack->p, stack_size); + pt_insert(thread_partitions, (pt_leaf_t)stack); + + thread_partition_t *tls = private_malloc(sizeof(thread_partition_t)); + tls->id = id; + fill_thread_layout_tls(&tls->p); + pt_insert(thread_partitions, (pt_leaf_t)tls); + +# if defined(E_ACSL_DEBUG) && defined(E_ACSL_DEBUG_VERBOSE) + DLOG(">>> Thread stack -------------\n"); + print_memory_partition(&stack->p); + DLOG(">>> Thread TLS ---------------\n"); + print_memory_partition(&tls->p); +# endif +} + +void clean_thread_shadow_layout() { + DASSERT(thread_partitions != NULL); + pt_remove_if(thread_partitions, tp_is_same_thread); +} + +/*! \return The memory segment corresponding to the given address or NULL if no + memory segment can be found. */ +static memory_partition *find_thread_partition(uintptr_t addr) { + memory_partition *result = NULL; + if (thread_partitions != NULL) { + thread_partition_t *tp = + (thread_partition_t *)pt_find(thread_partitions, (void *)addr); + if (tp != NULL) { + result = &tp->p; + } + } + return result; +} + +int is_on_thread(uintptr_t addr) { + memory_partition *thread_partition = find_thread_partition(addr); + return thread_partition != NULL; +} + +intptr_t primary_thread_shadow(uintptr_t addr) { + memory_partition *thread_partition = find_thread_partition(addr); + return SHADOW_ACCESS(addr, thread_partition->primary.shadow_offset); +} + +intptr_t secondary_thread_shadow(uintptr_t addr) { + memory_partition *thread_partition = find_thread_partition(addr); + return SHADOW_ACCESS(addr, thread_partition->secondary.shadow_offset); +} + +# ifdef E_ACSL_TEMPORAL +intptr_t temporal_primary_thread_shadow(uintptr_t addr) { + memory_partition *thread_partition = find_thread_partition(addr); + return SHADOW_ACCESS(addr, thread_partition->temporal_primary.shadow_offset); +} + +intptr_t temporal_secondary_thread_shadow(uintptr_t addr) { + memory_partition *thread_partition = find_thread_partition(addr); + return SHADOW_ACCESS(addr, + thread_partition->temporal_secondary.shadow_offset); +} +# endif // E_ACSL_TEMPORAL + +/* }}} */ + +/************************************************************************/ +/*** Pthread integration {{{ ***/ +/************************************************************************/ + +/*! Wrapper around the original argument used in `pthread_create`. */ +typedef struct eacsl_pthread_wrapper { + /*! The original function pointer given to `pthread_create`. */ + pthread_routine_t start_routine; + /*! The original argument given to `pthread_create`. */ + void *restrict arg; + /*! The stack size of the created thread. */ + size_t stack_size; +} eacsl_pthread_wrapper_t; + +/*! \brief Cleanup function called before destroying the thread. + + The shadow layout for the thread is destroyed and the memory allocated for + the wrapper is freed. */ +static void eacsl_pthread_cleanup(void *arg) { + clean_thread_shadow_layout(); + private_free((eacsl_pthread_wrapper_t *)arg); +} + +/*! \brief Wrapper function given to `pthread_create` instead of the original + function. + + Initialize the shadow layout and register the cleanup function. */ +static void *eacsl_pthread_wrapper(void *arg) { + void *res = NULL; + eacsl_pthread_wrapper_t *wrapped = (eacsl_pthread_wrapper_t *)arg; + init_thread_shadow_layout(wrapped->stack_size); + + // Register cleanup function + pthread_cleanup_push(eacsl_pthread_cleanup, arg); + // Execute original thread function + res = wrapped->start_routine(wrapped->arg); + // Pop and execute (argument is 1) the cleanup function + pthread_cleanup_pop(1); + return res; +} + +int eacsl_pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict original_attr, + pthread_routine_t start_routine, void *restrict arg) { + // Initialize the thread partitions trie on the first execution of this + // function + E_ACSL_RUN_ONCE(create_thread_partitions_trie); + + // Create a wrapper for the original routine and arguments + eacsl_pthread_wrapper_t *wrapped = + private_malloc(sizeof(eacsl_pthread_wrapper_t)); + wrapped->start_routine = start_routine; + wrapped->arg = arg; + + // We need pthread creation attributes to customize the stack size for the + // thread. If the user already provided pthread creation attributes, we can + // update those, otherwise we need to use our own structure + pthread_attr_t attr; + pthread_attr_t *pattr; + if (original_attr == NULL) { + // There are no original attributes, initialize a new structure + pthread_attr_init(&attr); + pattr = &attr; + } else { + // There are original attributes, use them + pattr = (pthread_attr_t *)original_attr; + } + + // Update the stack size for the new thread, either to the default value set + // in E-ACSL, or twice the original stack size if the original stack size is + // already greater than the default value of E-ACSL + size_t original_stack_size; + pthread_attr_getstacksize(pattr, &original_stack_size); + if (original_stack_size >= E_ACSL_THREAD_STACK_SIZE * MB) { + wrapped->stack_size = original_stack_size * 2; + } else { + wrapped->stack_size = E_ACSL_THREAD_STACK_SIZE * MB; + } + pthread_attr_setstacksize(pattr, wrapped->stack_size); + + // Create the thread using our custom wrapper routine + int result = pthread_create(thread, pattr, eacsl_pthread_wrapper, wrapped); + + // Destroy the attributes if we created them earlier, or restore the stack + // size to its original value otherwise + if (original_attr == NULL) { + pthread_attr_destroy(pattr); + } else { + pthread_attr_setstacksize(pattr, original_stack_size); + } + + return result; +} + +/* }}} */ + +#else // E_ACSL_CONCURRENCY_PTHREAD + +# include <errno.h> + +# include "../../internals/e_acsl_private_assert.h" + +# define CONCURRENCY_ABORT_MESSAGE \ + "Compile with option '--concurrency' of e-acsl-gcc.sh to support " \ + "concurrency.\n" \ + "When compiling without e-acsl-gcc.sh, E_ACSL_CONCURRENCY_PTHREAD must\n " \ + "be defined and the pthread library linked.\n" + +void init_thread_shadow_layout(size_t stack_size) { + private_abort(CONCURRENCY_ABORT_MESSAGE); +} + +void clean_thread_shadow_layout() { + private_abort(CONCURRENCY_ABORT_MESSAGE); +} + +int is_on_thread(uintptr_t addr) { + return 0; +} + +intptr_t primary_thread_shadow(uintptr_t addr) { + private_abort(CONCURRENCY_ABORT_MESSAGE); + return 0; +} + +intptr_t secondary_thread_shadow(uintptr_t addr) { + private_abort(CONCURRENCY_ABORT_MESSAGE); + return 0; +} + +# ifdef E_ACSL_TEMPORAL +intptr_t temporal_primary_thread_shadow(uintptr_t addr) { + private_abort(CONCURRENCY_ABORT_MESSAGE); + return 0; +} + +intptr_t temporal_secondary_thread_shadow(uintptr_t addr) { + private_abort(CONCURRENCY_ABORT_MESSAGE); + return 0; +} +# endif // E_ACSL_TEMPORAL + +extern int pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict attr, + pthread_routine_t start_routine, void *restrict arg); + +int eacsl_pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict original_attr, + pthread_routine_t start_routine, void *restrict arg) { + private_abort(CONCURRENCY_ABORT_MESSAGE); + return EPERM; +} + +#endif // E_ACSL_CONCURRENCY_PTHREAD diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.h b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.h new file mode 100644 index 0000000000000000000000000000000000000000..73debfe97a012dfd45e06091ea398b5e5b8b42f6 --- /dev/null +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_concurrency.h @@ -0,0 +1,66 @@ +/**************************************************************************/ +/* */ +/* This file is part of the Frama-C's E-ACSL plug-in. */ +/* */ +/* Copyright (C) 2012-2021 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/**************************************************************************/ + +/*! *********************************************************************** + * \file + * \brief E-ACSL concurrency support for the shadow memory model. +***************************************************************************/ + +#ifndef E_ACSL_SHADOW_CONCURRENCY_H +#define E_ACSL_SHADOW_CONCURRENCY_H + +/* Default size of a thread stack tracked via shadow memory */ +#ifndef E_ACSL_THREAD_STACK_SIZE +# define E_ACSL_THREAD_STACK_SIZE 4 +#endif + +/*! \brief Initialize memory layout for the current thread, i.e. determine + bounds of program segments, allocate shadow memory spaces and compute + offsets. + + \param stack_size The stack size of the current thread. */ +void init_thread_shadow_layout(size_t stack_size); + +/*! \brief Deallocate shadow regions used by the runtime analysis for the + current thread. */ +void clean_thread_shadow_layout(); + +/*! \brief Evaluate to true if `addr` is a thread address. */ +int is_on_thread(uintptr_t addr); + +/*! \brief Convert a thread address into its primary shadow counterpart. */ +intptr_t primary_thread_shadow(uintptr_t addr); + +/*! \brief Convert a thread address into its secondary shadow counterpart. */ +intptr_t secondary_thread_shadow(uintptr_t addr); + +#ifdef E_ACSL_TEMPORAL +/*! \brief Convert a thread address into its primary temporal shadow + counterpart. */ +intptr_t temporal_primary_thread_shadow(uintptr_t addr); + +/*! \brief Convert a thread address into its secondary temporal shadow + counterpart. */ +intptr_t temporal_secondary_thread_shadow(uintptr_t addr); +#endif + +#endif // E_ACSL_SHADOW_CONCURRENCY_H diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.c b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.c index 0b52b6595c18fbceb288f85df8a6e757d950356a..8086b280e3a560876fa2a2430598b53b59e446ac 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.c +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.c @@ -163,7 +163,7 @@ static size_t get_global_size() { */ /*! \brief Return byte-size of the TLS segment */ -inline static size_t get_tls_size() { +inline size_t get_tls_size() { return PGM_TLS_SIZE; } @@ -219,7 +219,7 @@ static void grow_bounds_for_size(uintptr_t *min_bound, uintptr_t *max_bound, } /*! \brief Return start address of a program's TLS */ -static uintptr_t get_tls_start() { +uintptr_t get_tls_start() { size_t tls_size = get_tls_size(); uintptr_t data = (uintptr_t)&id_tdata, bss = (uintptr_t)&id_tbss; uintptr_t min_addr = data < bss ? data : bss; diff --git a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.h b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.h index 3ed69e3d42a564beee5d663e5b2140c70494d4c1..dfc069364e436f22c07b71f5efc0a040308e0fa1 100644 --- a/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.h +++ b/src/plugins/e-acsl/share/e-acsl/observation_model/segment_model/e_acsl_shadow_layout.h @@ -33,6 +33,7 @@ #include "../../internals/e_acsl_config.h" #include "../../internals/e_acsl_malloc.h" +#include "e_acsl_shadow_concurrency.h" /* Default size of a program's heap tracked via shadow memory */ #ifndef E_ACSL_HEAP_SIZE @@ -109,6 +110,14 @@ size_t get_stack_size(); size_t get_heap_size(); /** }}} */ +/** Thread-local storage information {{{ */ +/*! \brief Return byte-size of the TLS segment */ +size_t get_tls_size(); + +/*! \brief Return start address of a program's TLS */ +uintptr_t get_tls_start(); +/** }}} */ + /** Shadow Layout {{{ */ /***************************************************************************** * Memory Layout ************************************************************* @@ -363,6 +372,13 @@ void clean_shadow_layout(); # define SECONDARY_VDSO_SHADOW(_addr) \ SHADOW_ACCESS(_addr, vdso_secondary_offset) +/*! \brief Convert a thread address into its primary shadow counterpart */ +# define PRIMARY_THREAD_SHADOW(_addr) primary_thread_shadow((uintptr_t)_addr) + +/*! \brief Convert a thread address into its secondary shadow counterpart */ +# define SECONDARY_THREAD_SHADOW(_addr) \ + secondary_thread_shadow((uintptr_t)_addr) + #elif E_ACSL_OS_IS_WINDOWS /*! \brief Convert a text address into its primary shadow counterpart */ # define PRIMARY_TEXT_SHADOW(_addr) SHADOW_ACCESS(_addr, text_primary_offset) @@ -431,6 +447,7 @@ void clean_shadow_layout(); : IS_ON_GLOBAL(_addr) ? _region##_GLOBAL_SHADOW(_addr) \ : IS_ON_TLS(_addr) ? _region##_TLS_SHADOW(_addr) \ : IS_ON_VDSO(_addr) ? _region##_VDSO_SHADOW(_addr) \ + : IS_ON_THREAD(_addr) ? _region##_THREAD_SHADOW(_addr) \ : (intptr_t)0) // clang-format on #elif E_ACSL_OS_IS_WINDOWS @@ -478,11 +495,14 @@ void clean_shadow_layout(); /*! \brief Evaluate to true if _addr is a VDSO address */ # define IS_ON_VDSO(_addr) IS_ON(_addr, mem_layout.vdso.application) +/*! \brief Evaluate to true if _addr is a thread address */ +# define IS_ON_THREAD(_addr) is_on_thread((uintptr_t)_addr) + /*! \brief Shortcut for evaluating an address via ::IS_ON_STACK, - * ::IS_ON_GLOBAL or ::IS_ON_TLS */ + * ::IS_ON_GLOBAL, ::IS_ON_TLS or ::IS_ON_THREAD */ # define IS_ON_STATIC(_addr) \ (IS_ON_STACK(_addr) || IS_ON_GLOBAL(_addr) || IS_ON_TLS(_addr) \ - || IS_ON_VDSO(_addr)) + || IS_ON_VDSO(_addr) || IS_ON_THREAD(_addr)) #elif E_ACSL_OS_IS_WINDOWS /*! \brief Evaluate to true if `_addr` is a text address */ # define IS_ON_TEXT(_addr) IS_ON(_addr, mem_layout.text.application) @@ -555,6 +575,14 @@ void clean_shadow_layout(); # define TEMPORAL_SECONDARY_VDSO_SHADOW(_addr) \ SHADOW_ACCESS(_addr, mem_layout.vdso.temporal_secondary.shadow_offset) +/*! \brief Convert a thread address into its primary temporal shadow counterpart */ +# define TEMPORAL_PRIMARY_THREAD_SHADOW(_addr) \ + temporal_primary_thread_shadow((uintptr_t)_addr) + +/*! \brief Convert a thread address into its secondary temporal shadow counterpart */ +# define TEMPORAL_SECONDARY_THREAD_SHADOW(_addr) \ + temporal_secondary_thread_shadow((uintptr_t)_addr) + # elif E_ACSL_OS_IS_WINDOWS /*! \brief Convert a text address into its primary temporal shadow counterpart */ # define TEMPORAL_PRIMARY_TEXT_SHADOW(_addr) \