minix/minix/llvm/static/magic/magic_st.c
David van Moolenbroek 7c48de6cc4 Resolve more warnings
Change-Id: Ibc1b7f7cd45ad7295285e59c6ce55888266fece8
2015-09-23 12:04:58 +00:00

4583 lines
172 KiB
C

#include <magic.h>
#include <magic_mem.h>
#include <magic_analysis.h>
#include <magic_asr.h>
#include <stdarg.h>
#include <st/state_transfer.h>
#include <st/metadata_transfer.h>
#include <st/typedefs.h>
#include <st/private.h>
#define printf _magic_printf
#ifdef __MINIX
EXTERN endpoint_t sef_self_endpoint;
#else
#define DO_SKIP_ENVIRON_HACK 1
#define TODO_DSENTRY_PARENT_NAME_BUG 1
#define DO_SKIP_UNPAIRED_PTR_TARGETS 1
#endif
#define DO_SKIP_INVARIANTS_VIOLATIONS 0
PRIVATE st_alloc_pages *st_alloc_pages_current = NULL;
PRIVATE int st_alloc_buff_available = 0;
PRIVATE char *st_alloc_buff_pt = NULL;
PRIVATE char *st_pre_allocated_page_pt = NULL;
PRIVATE struct _magic_dsentry *st_dsentry_buff = NULL;
PRIVATE void *st_data_buff = NULL;
PRIVATE unsigned st_num_type_transformations = 0;
/* Magic variables and counterparts. */
struct _magic_vars_t st_remote_magic_vars, st_cached_magic_vars;
struct _magic_vars_t *st_local_magic_vars_ptr = &_magic_vars_buff;
st_counterparts_t st_counterparts;
/* Private variables. */
PRIVATE int st_init_done = FALSE;
PRIVATE int st_policies = ST_POLICIES_DEFAULT;
PRIVATE double st_unpaired_types_ratio = ST_UNPAIRED_TYPES_RATIO_DEFAULT;
PRIVATE double st_unpaired_struct_types_ratio = ST_UNPAIRED_STRUCT_TYPES_RATIO_DEFAULT;
/* Forward declarations. */
PRIVATE INLINE int default_transfer_selement_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info);
/* State transfer callbacks. */
PRIVATE struct st_cbs_t st_cbs = {
ST_CB_PAGES_ALLOCATE_DEFAULT,
ST_CB_PAGES_FREE_DEFAULT,
ST_CB_STATE_CLEANUP_DEFAULT,
ST_CB_STATE_CHECKING_DEFAULT,
ST_CB_SELEMENT_MAP_DEFAULT,
ST_CB_SELEMENT_TRANSFER_EMPTY
};
/* OS dependent callbacks. */
PRIVATE struct st_cbs_os_t st_cbs_os = {
ST_CB_OS_PANIC_EMPTY,
ST_CB_OS_OLD_STATE_TABLE_LOOKUP_EMPTY,
ST_CB_OS_COPY_STATE_REGION_EMPTY,
ST_CB_OS_ALLOC_CONTIG_EMPTY,
ST_CB_OS_FREE_CONTIG_EMPTY,
ST_CB_OS_DEBUG_HEADER_EMPTY
};
/* State transfer prototypes for st_receive(). */
PUBLIC void do_st_before_receive(void);
/* Callback setters */
PUBLIC void st_setcb_pages_allocate (st_cb_pages_allocate_t cb)
{
assert(cb != NULL);
st_cbs.st_cb_pages_allocate = cb;
}
PUBLIC void st_setcb_pages_free (st_cb_pages_free_t cb)
{
assert(cb != NULL);
st_cbs.st_cb_pages_free = cb;
}
PUBLIC void st_setcb_state_cleanup (st_cb_state_cleanup_t cb)
{
assert(cb != NULL);
st_cbs.st_cb_state_cleanup = cb;
magic_setcb_sentries_analyze_pre(cb);
}
PUBLIC void st_setcb_state_checking (st_cb_state_checking_t cb)
{
assert(cb != NULL);
st_cbs.st_cb_state_checking = cb;
}
PUBLIC void st_setcb_selement_map(st_cb_selement_map_t cb)
{
assert(cb != NULL);
st_cbs.st_cb_selement_map = cb;
}
PUBLIC void st_setcb_selement_transfer(st_cb_selement_transfer_t cb, int flags)
{
int i, j;
for (i = 0 ; i < NUM_CB_ARRAYS ; i++) {
if (i & flags) {
int is_registered = FALSE;
for (j = 0; j < MAX_NUM_CBS ; j++) {
if (st_cbs.st_cb_selement_transfer[i][j] == NULL) {
st_cbs.st_cb_selement_transfer[i][j] = cb;
is_registered = TRUE;
break;
}
}
assert(is_registered && "Number of registered callbacks exceeds MAX_NUM_CBS");
}
}
}
/* OS Callback setters. */
PUBLIC void st_setcb_os_panic(st_cb_os_panic_t cb)
{
assert(cb != NULL && "No callback defined for panic().");
st_cbs_os.panic = cb;
}
PUBLIC void st_setcb_os_old_state_table_lookup(st_cb_os_old_state_table_lookup_t cb)
{
assert(cb != NULL && "No callback defined for old_state_table_lookup().");
st_cbs_os.old_state_table_lookup = cb;
}
PUBLIC void st_setcb_os_copy_state_region(st_cb_os_copy_state_region_t cb)
{
assert(cb != NULL && "No callback defined for copy_state_region().");
st_cbs_os.copy_state_region = cb;
}
PUBLIC void st_setcb_os_alloc_contig(st_cb_os_alloc_contig_t cb)
{
assert(cb != NULL && "No callback defined for alloc_contig().");
st_cbs_os.alloc_contig = cb;
}
PUBLIC void st_setcb_os_free_contig(st_cb_os_free_contig_t cb)
{
assert(cb != NULL && "No callback defined for free_contig().");
st_cbs_os.free_contig = cb;
}
PUBLIC void st_setcb_os_debug_header(st_cb_os_debug_header_t cb)
{
assert(cb != NULL && "No callback defined for debug_header().");
st_cbs_os.debug_header = cb;
}
PUBLIC void st_setcb_os_all(struct st_cbs_os_t *cbs)
{
st_setcb_os_panic(cbs->panic);
st_setcb_os_old_state_table_lookup(cbs->old_state_table_lookup);
st_setcb_os_copy_state_region(cbs->copy_state_region);
st_setcb_os_alloc_contig(cbs->alloc_contig);
st_setcb_os_free_contig(cbs->free_contig);
st_setcb_os_debug_header(cbs->debug_header);
}
/* Status variables to be transfered at state transfer time. */
PUBLIC int __st_before_receive_enabled = 0;
PRIVATE int __st_before_receive_sc_max_cycles;
PRIVATE int __st_before_receive_sc_max_violations;
/* Typedef registration and lookup */
int st_strcmp_wildcard(char *with_wildcard, char *without_wildcard)
{
/* Note: this implementation only supports basic regexes with a '*'
* at the beginning or the end of the string.
*/
char *star = strchr(with_wildcard, '*');
if (star) {
if (star == with_wildcard) {
size_t len = strlen(with_wildcard+1);
size_t len_without_wildcard = strlen(without_wildcard);
char *match_without_wildcard = without_wildcard+
len_without_wildcard-len;
if (match_without_wildcard < without_wildcard) {
return -1;
}
return strncmp(with_wildcard+1, match_without_wildcard, len);
}
return strncmp(with_wildcard, without_wildcard, star - with_wildcard);
}
return strcmp(with_wildcard, without_wildcard);
}
char *st_typename_noxfers[] = { ST_TYPENAME_NO_TRANSFER_NAMES, NULL };
char *st_typename_ixfers[] = { ST_TYPENAME_IDENTITY_TRANSFER_NAMES, NULL };
char *st_typename_cixfers[] = { ST_TYPENAME_CIDENTITY_TRANSFER_NAMES, NULL };
char *st_typename_pxfers[] = { ST_TYPENAME_PTR_TRANSFER_NAMES, NULL };
char *st_typename_sxfers[] = { ST_TYPENAME_STRUCT_TRANSFER_NAMES, NULL };
char *st_sentryname_ixfers[] = { ST_SENTRYNAME_IDENTITY_TRANSFER_NAMES, NULL };
char *st_sentryname_cixfers[] = { ST_SENTRYNAME_CIDENTITY_TRANSFER_NAMES, NULL};
char *st_sentryname_pxfers[] = { ST_SENTRYNAME_PTR_TRANSFER_NAMES, NULL };
/* Exclude stack references in addition to the default sentry names from state transfer. */
char *st_sentryname_noxfers[] = {
ST_SENTRYNAME_NO_TRANSFER_NAMES,
#define __X(R) #R /* Stringify the symbol names. */
ST_STACK_REFS_INT_LIST,
#if ST_STACK_REFS_CUSTOM_NUM > 0
ST_STACK_REFS_CUSTOM_LIST,
#endif
#undef __X
NULL };
char *st_sentryname_noxfers_mem[] = { ST_SENTRYNAME_NO_TRANSFER_MEM_NAMES, NULL };
/* Exclude the data segments of certain libs from state transfer. */
char *st_dsentry_lib_noxfer[] = {
#ifdef ST_DSENTRYLIB_NO_TRANSFER_NAMES
ST_DSENTRYLIB_NO_TRANSFER_NAMES,
#endif
NULL };
char *st_typename_key_registrations[MAX_NUM_TYPENAMES];
int is_typename(char *search_key, struct _magic_type *type)
{
int i;
/* We can't use a cached lookup result */
if (!st_strcmp_wildcard(search_key, type->name)) {
/* The name matches */
return TRUE;
}
for (i = 0 ; i < type->num_names ; i++) {
if(!st_strcmp_wildcard(search_key, type->names[i])) {
/* One of the typename names matches */
return TRUE;
}
}
/* No match is found */
return FALSE;
}
PUBLIC void st_register_typename_key(char *key)
{
int i, is_registered = FALSE;
for(i = 0 ; i < MAX_NUM_TYPENAMES ; i++) {
if (st_typename_key_registrations[i] == NULL) {
st_typename_key_registrations[i] = key;
is_registered = TRUE;
break;
}
}
assert(is_registered && "Error, number of typename registrations > MAX_NUM_TYPENAMES.\n");
}
PUBLIC void st_register_typename_keys(char **keys)
{
int i = 0;
while (keys[i] != NULL) {
st_register_typename_key(keys[i]);
i++;
}
}
PRIVATE void set_typename_key(struct _magic_type *type)
{
char **registration = st_typename_key_registrations;
while (*registration != NULL) {
if (is_typename(*registration, type)) {
type->ext = *registration;
break;
}
registration++;
}
}
PUBLIC void register_typenames()
{
int i;
/* Register typenames */
st_register_typename_keys(st_typename_noxfers);
st_register_typename_keys(st_typename_ixfers);
st_register_typename_keys(st_typename_cixfers);
st_register_typename_keys(st_typename_pxfers);
st_register_typename_keys(st_typename_sxfers);
for(i = 0 ; i < _magic_types_num ; i++) {
set_typename_key(&_magic_types[i]);
}
}
PRIVATE INLINE void register_typenames_and_callbacks()
{
static int st_is_registered = FALSE;
if(st_is_registered) {
return;
}
register_typenames();
st_setcb_selement_transfer(st_cb_transfer_sentry_default, ST_CB_TYPE_SENTRY);
st_setcb_selement_transfer(st_cb_transfer_typename_default, ST_CB_TYPE_TYPENAME);
st_is_registered = TRUE;
}
PUBLIC int st_type_name_match_any(char **registered_type_name_keys,
char *key)
{
int i = 0;
while (registered_type_name_keys[i] != NULL) {
if (ST_TYPE_NAME_MATCH(registered_type_name_keys[i], key)) {
return TRUE;
}
i++;
}
return FALSE;
}
PUBLIC int st_sentry_name_match_any(char **sentry_wildcard_names,
char *name)
{
int i = 0;
while (sentry_wildcard_names[i] != NULL) {
if (ST_SENTRY_NAME_MATCH(sentry_wildcard_names[i], name)) {
return TRUE;
}
i++;
}
return FALSE;
}
PUBLIC int st_dsentry_parent_name_match_any(char **wildcard_names,
char *name)
{
int i = 0;
while (wildcard_names[i] != NULL) {
if (ST_DSENTRY_PARENT_NAME_MATCH(wildcard_names[i], name)) {
return TRUE;
}
i++;
}
return FALSE;
}
/* Utilities. */
PUBLIC void st_cb_print(int level, char *msg, _magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
if (ST_CB_PRINT_LEVEL(level)) {
_magic_printf("[%s] %s. Current state element:\n",
ST_CB_LEVEL_TO_STR(level), msg);
MAGIC_SELEMENT_PRINT(selement, MAGIC_EXPAND_TYPE_STR);
_magic_printf("\n");
MAGIC_SEL_ANALYZED_PRINT(sel_analyzed, MAGIC_EXPAND_TYPE_STR);
_magic_printf("\n");
MAGIC_SEL_STATS_PRINT(sel_stats);
_magic_printf("\n\n");
}
}
PUBLIC void st_cb_selement_type_cast(const struct _magic_type* new_selement_type, const struct _magic_type* new_local_selement_type, _magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
magic_selement_type_cast(selement, ST_SEL_ANALYZE_FLAGS,
new_selement_type, sel_analyzed, sel_stats);
if (!ST_CB_FLAG(ST_CB_CHECK_ONLY)) {
cb_info->local_selement->type = new_local_selement_type;
}
}
/* Stack management. */
PUBLIC void st_stack_refs_save_restore(char* stack_buff, int is_save)
{
struct _magic_dsentry *prev_dsentry, *dsentry;
struct _magic_sentry* sentry;
struct st_stack_refs_buff *buff_ptr;
int i;
#define __X(P) P
extern int ST_STACK_REFS_INT_LIST;
#undef __X
#define __X(P) ((int *)&(P))
int* int_ptrs[] = { ST_STACK_REFS_INT_LIST, ST_STACK_REFS_CUSTOM_LIST };
#undef __X
assert((ST_STACK_REFS_NUM) == sizeof(int_ptrs)/sizeof(int_ptrs[0]));
assert(sizeof(int) == sizeof(void*));
buff_ptr = (struct st_stack_refs_buff*) stack_buff;
/* Save. */
if (is_save) {
buff_ptr->first_stack_dsentry = _magic_first_stack_dsentry;
buff_ptr->last_stack_dsentry = _magic_last_stack_dsentry;
if (_magic_first_stack_dsentry) {
buff_ptr->first_stack_obdsentry_buff = *MAGIC_OBDSENTRY_FROM_DSENTRY(_magic_first_stack_dsentry);
}
memcpy(buff_ptr->stack_range, magic_stack_range, 2*sizeof(void*));
for (i = 0 ; i < ST_STACK_REFS_NUM ; i++) {
memcpy(&buff_ptr->stack_int_refs[i], int_ptrs[i], sizeof(int));
}
return;
}
/* Restore. */
if (_magic_first_dsentry == _magic_last_stack_dsentry) {
_magic_first_dsentry = buff_ptr->last_stack_dsentry;
}
else {
MAGIC_DSENTRY_ITER(_magic_first_dsentry, prev_dsentry, dsentry, sentry,
if (MAGIC_DSENTRY_HAS_NEXT(dsentry)
&& MAGIC_DSENTRY_NEXT(dsentry) == _magic_last_stack_dsentry) {
MAGIC_DSENTRY_NEXT(dsentry) = buff_ptr->last_stack_dsentry;
break;
}
);
}
_magic_first_stack_dsentry = buff_ptr->first_stack_dsentry;
_magic_last_stack_dsentry = buff_ptr->last_stack_dsentry;
if (_magic_first_stack_dsentry) {
*MAGIC_OBDSENTRY_FROM_DSENTRY(_magic_first_stack_dsentry) = buff_ptr->first_stack_obdsentry_buff;
}
memcpy(magic_stack_range, buff_ptr->stack_range, 2*sizeof(void*));
for (i = 0 ; i < ST_STACK_REFS_NUM ; i++) {
memcpy(int_ptrs[i], &buff_ptr->stack_int_refs[i], sizeof(int));
}
}
/* Metadata management. */
PUBLIC int st_add_special_mmapped_region(void *address, size_t size,
const char* name)
{
struct _magic_obdsentry* obdsentry;
char addr_name[24];
if (!_magic_enabled) return OK;
if (!name) {
sprintf(addr_name, "%%MMAP_0x%08x", (unsigned int) address);
name = addr_name;
}
obdsentry = magic_create_obdsentry(address, MAGIC_VOID_TYPE,
size, MAGIC_STATE_MAP, name, NULL);
return obdsentry ? OK : EINVAL;
}
PUBLIC int st_del_special_mmapped_region_by_addr(void *address)
{
int ret;
if (!_magic_enabled) return OK;
ret = magic_destroy_obdsentry_by_addr(address);
if (ret < 0) {
return EINVAL;
}
return OK;
}
/* Selement transfer callbacks. */
PRIVATE INLINE int transfer_walkable_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
/* Do nothing for complex type. process only its members, not the complex type itself */
return MAGIC_SENTRY_ANALYZE_CONTINUE;
}
PRIVATE INLINE int transfer_try_raw_copy_sel_cb(_magic_selement_t *selement, struct st_cb_info *cb_info)
{
/* Only do raw copying if there are no type transformations. */
if ((selement->type->num_child_types == 0 && selement->type->size == cb_info->local_selement->type->size) || selement->type == cb_info->local_selement->type || ST_TYPE_IS_CACHED_COUNTERPART(selement->type, cb_info->local_selement->type)) {
memcpy(cb_info->local_selement->address, selement->address, cb_info->local_selement->type->size);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
return MAGIC_SENTRY_ANALYZE_CONTINUE;
}
PRIVATE INLINE int transfer_identity_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
if (!ST_CB_FLAG(ST_CB_CHECK_ONLY)) {
/* First try to do raw copying, assuming there are no type transformations. */
if (transfer_try_raw_copy_sel_cb(selement, cb_info) == MAGIC_SENTRY_ANALYZE_SKIP_PATH)
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
#if CHECK_ASR && !FORCE_SOME_UNPAIRED_TYPES
if (cb_info->init_info->flags & ST_LU_ASR) {
st_cbs_os.panic("ASR should never get here!");
}
#endif
if (selement->type->type_id == MAGIC_TYPE_UNION) {
ST_CB_PRINT(ST_CB_ERR, "uncaught ixfer union with type changes", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
cb_info->st_cb_flags |= ST_CB_FORCE_IXFER;
return MAGIC_SENTRY_ANALYZE_CONTINUE;
}
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
PRIVATE INLINE int transfer_cond_identity_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
int r;
int saved_flags = cb_info->st_cb_flags;
cb_info->st_cb_flags &= ~ST_CB_PRINT_ERR;
r = default_transfer_selement_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
cb_info->st_cb_flags = saved_flags;
if (r < 0) {
ST_CB_PRINT(ST_CB_DBG, "conditional ixfer resorting to ixfer", selement, sel_analyzed, sel_stats, cb_info);
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
return r;
}
PRIVATE INLINE int transfer_nonptr_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
if (sel_analyzed->flags & MAGIC_SEL_FOUND_VIOLATIONS) {
ST_CB_PRINT(ST_CB_ERR, "uncaught non-ptr with violations", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PRIVATE int transfer_ptr_sel_with_trg_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
int trg_flags, trg_extf_flags, trg_transferred, trg_paired;
_magic_selement_t cached_trg_selement, local_trg_selement;
void **local_selement_address = cb_info->local_selement->address;
int r;
r = lookup_trg_info(selement, sel_analyzed, sel_stats, cb_info, &cached_trg_selement, &local_trg_selement);
if (r != OK) {
return r;
}
trg_flags = sel_analyzed->u.ptr.trg_flags;
trg_extf_flags = MAGIC_STATE_FLAGS_TO_EXTF(trg_flags);
trg_transferred = (trg_extf_flags & (ST_NEEDS_TRANSFER | ST_TRANSFER_DONE));
trg_paired = (local_trg_selement.type != NULL);
if (!trg_transferred && trg_paired && (trg_extf_flags & ST_ON_PTRXFER_CASCADE)) {
/* Propagate transfer on the target. */
if (cached_trg_selement.sentry && !(trg_extf_flags & ST_NEEDS_TRANSFER)) {
ST_CB_PRINT(ST_CB_DBG, "ptr lookup results in cascade transfer for the target", selement, sel_analyzed, sel_stats, cb_info);
st_set_status_by_sentry_id(ST_NEEDS_TRANSFER, ST_OP_ADD, MAGIC_SENTRY_ID(cached_trg_selement.sentry));
}
/* Force code below to transfer the pointer normally. */
trg_transferred = TRUE;
}
if (trg_transferred && trg_paired) {
*local_selement_address = local_trg_selement.address;
}
else if (trg_extf_flags & ST_ON_PTRXFER_SET_NULL) {
ST_CB_PRINT(ST_CB_DBG, "ptr lookup results in forcefully setting ptr to NULL", selement, sel_analyzed, sel_stats, cb_info);
*local_selement_address = NULL;
}
else if(trg_extf_flags & ST_ON_PTRXFER_SET_DEFAULT) {
ST_CB_PRINT(ST_CB_DBG, "ptr lookup results in forcefully setting ptr to default value", selement, sel_analyzed, sel_stats, cb_info);
if (trg_flags & MAGIC_STATE_STRING) {
*((char**)local_selement_address) = "";
}
else {
*local_selement_address = NULL;
}
}
else if (trg_extf_flags & ST_ON_PTRXFER_SKIP) {
ST_CB_PRINT(ST_CB_DBG, "ptr lookup results in skipping ptr transfer", selement, sel_analyzed, sel_stats, cb_info);
}
else {
if (trg_paired) {
ST_CB_PRINT(ST_CB_ERR, "uncaught ptr lookup for non-transferred target", selement, sel_analyzed, sel_stats, cb_info);
}
else {
ST_CB_PRINT(ST_CB_ERR, "uncaught ptr lookup for unpaired target", selement, sel_analyzed, sel_stats, cb_info);
}
#if DO_SKIP_UNPAIRED_PTR_TARGETS
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
#else
return ENOENT;
#endif
}
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
PRIVATE INLINE int transfer_ptr_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
const struct _magic_type *first_trg_type;
if (selement->type->type_id != MAGIC_TYPE_POINTER) {
if (selement->type->size != sizeof(void*)) {
ST_CB_PRINT(ST_CB_ERR, "wrong pointer size", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
ST_CB_PRINT(ST_CB_DBG, "casting non-ptr to ptr element", selement, sel_analyzed, sel_stats, cb_info);
st_cb_selement_type_cast(MAGIC_VOID_PTR_INT_CAST_TYPE, MAGIC_VOID_PTR_INT_CAST_TYPE, selement, sel_analyzed, sel_stats, cb_info);
}
first_trg_type = MAGIC_SEL_ANALYZED_PTR_FIRST_TRG_TYPE(sel_analyzed);
if (first_trg_type == MAGIC_TYPE_NULL_ENTRY
|| first_trg_type == MAGIC_TYPE_VALUE_FOUND) {
/* NULL pointer or special value. Don't adjust value */
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
} else if (!(sel_analyzed->flags & MAGIC_SEL_FOUND_VIOLATIONS)) {
/* Valid pointer found */
if (!ST_CB_FLAG(ST_CB_CHECK_ONLY)) {
return transfer_ptr_sel_with_trg_cb(selement, sel_analyzed, sel_stats, cb_info);
}
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
} else if(MAGIC_STATE_FLAG(selement->sentry, MAGIC_STATE_STACK)) {
struct _magic_sentry *trg_sentry = magic_sentry_lookup_by_range(sel_analyzed->u.ptr.value, NULL);
if (trg_sentry && !strcmp(trg_sentry->name, MAGIC_ALLOC_INITIAL_STACK_NAME)) {
/* Stack pointer to initial stack area. This is common (e.g., argv).
* We can safely assume the pointer will be already correctly
* initialized in the new version and simply skip transfer.
*/
ST_CB_PRINT(ST_CB_DBG, "skipping stack ptr element pointing to initial stack area", selement, sel_analyzed, sel_stats, cb_info);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
}
#ifdef __MINIX
#define IS_KERNEL_PTR(p) (((intptr_t)(p) & 0xf0000000) == 0xf0000000) /* TODO: make this more dynamic */
else if (IS_KERNEL_PTR(sel_analyzed->u.ptr.value))
return MAGIC_SENTRY_ANALYZE_SKIP_PATH; /* Kernel-mapped pointer */
#endif
/* Pointer with violations found */
ST_CB_PRINT(ST_CB_ERR, "uncaught ptr with violations", selement, sel_analyzed, sel_stats, cb_info);
#if DO_SKIP_INVARIANTS_VIOLATIONS
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
#else
return EFAULT;
#endif
}
PRIVATE INLINE int transfer_struct_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
static int st_counter = 0;
unsigned parent_offset, offset;
int walk_flags, ret;
if (selement->type->type_id != MAGIC_TYPE_UNION && selement->type->type_id != MAGIC_TYPE_STRUCT) {
ST_CB_PRINT(ST_CB_ERR, "struct transfer is only for structs and unions!", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
if (selement->type->type_id == MAGIC_TYPE_STRUCT || st_counter > 0) {
return MAGIC_SENTRY_ANALYZE_CONTINUE;
}
/* Walk the union as a struct. */
walk_flags = cb_info->walk_flags;
cb_info->walk_flags = (MAGIC_TYPE_WALK_DEFAULT_FLAGS & (~MAGIC_TYPE_WALK_UNIONS_AS_VOID));
st_counter++;
parent_offset = (unsigned)selement->parent_address - (unsigned)selement->sentry->address;
offset = (unsigned)selement->address - (unsigned)selement->sentry->address;
ret = magic_type_walk_flags(selement->parent_type, parent_offset,
selement->child_num, selement->type, offset,
0, ULONG_MAX, magic_type_analyzer_cb, selement->cb_args, cb_info->walk_flags);
st_counter--;
cb_info->walk_flags = walk_flags;
if (ret != 0) {
return ret == MAGIC_TYPE_WALK_STOP ? MAGIC_SENTRY_ANALYZE_STOP : ret;
}
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
PRIVATE INLINE int default_transfer_selement_sel_cb(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
/* Default handler for walkable, ptr and nonptr types. */
#if ST_TRANSFER_IDENTITY_FOR_NO_INNER_PTRS
if (MAGIC_TYPE_FLAG(selement->type, MAGIC_TYPE_NO_INNER_PTRS)) {
/* If the type has no inner pointers, try to do raw copying. */
if (transfer_try_raw_copy_sel_cb(selement, cb_info) == MAGIC_SENTRY_ANALYZE_SKIP_PATH)
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
#endif
if (selement->type->type_id == MAGIC_TYPE_UNION) {
if (!(st_policies & ST_IXFER_UNCAUGHT_UNIONS) && !MAGIC_TYPE_FLAG(selement->type, MAGIC_TYPE_NO_INNER_PTRS)) {
ST_CB_PRINT(ST_CB_ERR, "uncaught union", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
else {
int level = (st_policies & ST_REPORT_UNCAUGHT_UNIONS) ? ST_CB_ERR : ST_CB_DBG;
ST_CB_PRINT(level, "uncaught union", selement, sel_analyzed, sel_stats, cb_info);
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
} else if (MAGIC_TYPE_IS_WALKABLE(selement->type)) {
return transfer_walkable_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
} else if (selement->type->type_id == MAGIC_TYPE_POINTER) {
return transfer_ptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
} else {
return transfer_nonptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
/* Not reachable. */
ST_CB_PRINT(ST_CB_ERR, "Bug!", selement, sel_analyzed, sel_stats, cb_info);
return EINTR;
}
PUBLIC int st_cb_transfer_sentry_default(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
char *sentry_name = selement->sentry->name;
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
if (MAGIC_STATE_FLAGS(selement->sentry, MAGIC_STATE_DYNAMIC)) {
if (MAGIC_STATE_FLAG(selement->sentry, MAGIC_STATE_LIB) || MAGIC_SENTRY_IS_EXT_ALLOC(selement->sentry))
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
#endif
if (ST_SENTRY_NAME_MATCH_ANY(st_sentryname_ixfers, sentry_name)) {
ST_CB_PRINT(ST_CB_DBG, "sentry name matches ixfer", selement, sel_analyzed, sel_stats, cb_info);
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (ST_SENTRY_NAME_MATCH_ANY(st_sentryname_cixfers, sentry_name)) {
ST_CB_PRINT(ST_CB_DBG, "sentry name matches cixfer", selement, sel_analyzed, sel_stats, cb_info);
return transfer_cond_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (ST_SENTRY_NAME_MATCH_ANY(st_sentryname_noxfers, sentry_name)) {
ST_CB_PRINT(ST_CB_DBG, "sentry name matches noxfer", selement, sel_analyzed, sel_stats, cb_info);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
/* Skip memory management related sentries only when memory functions have
* been instrumented (which is *not* the case for the MINIX3 VM service).
*/
if (_magic_no_mem_inst == 0 && ST_SENTRY_NAME_MATCH_ANY(st_sentryname_noxfers_mem, sentry_name)) {
ST_CB_PRINT(ST_CB_DBG, "sentry name matches noxfer", selement, sel_analyzed, sel_stats, cb_info);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
if (ST_SENTRY_NAME_MATCH_ANY(st_sentryname_pxfers, sentry_name)) {
ST_CB_PRINT(ST_CB_DBG, "sentry name matches pxfer", selement, sel_analyzed, sel_stats, cb_info);
return transfer_ptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (MAGIC_STATE_FLAGS(selement->sentry, MAGIC_STATE_DYNAMIC | MAGIC_STATE_MAP | MAGIC_STATE_LIB)) {
struct _magic_dsentry *dsentry = MAGIC_DSENTRY_FROM_SENTRY(selement->sentry);
if (ST_DSENTRY_PARENT_NAME_MATCH_ANY(st_dsentry_lib_noxfer, dsentry->parent_name)) {
ST_CB_PRINT(ST_CB_DBG, "dsentry is a lib map and parent_name matches dsentry_lib_noxfer", selement, sel_analyzed, sel_stats, cb_info);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
}
return ST_CB_NOT_PROCESSED;
}
PUBLIC int st_cb_transfer_typename_default(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
char *typename_key = ST_TYPE_NAME_KEY(selement->type);
if (ST_TYPE_NAME_MATCH_ANY(st_typename_ixfers, typename_key)) {
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (ST_TYPE_NAME_MATCH_ANY(st_typename_cixfers, typename_key)) {
return transfer_cond_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (ST_TYPE_NAME_MATCH_ANY(st_typename_noxfers, typename_key)) {
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
if (ST_TYPE_NAME_MATCH_ANY(st_typename_pxfers, typename_key)) {
return transfer_ptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
if (ST_TYPE_NAME_MATCH_ANY(st_typename_sxfers, typename_key)) {
return transfer_struct_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
return ST_CB_NOT_PROCESSED;
}
PUBLIC int st_cb_transfer_walkable(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_walkable_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_ptr(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_ptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_identity(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_cond_identity(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_cond_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_nonptr(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_nonptr_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_struct(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_struct_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_selement_base(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return default_transfer_selement_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
}
PUBLIC int st_cb_transfer_selement_generic(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info)
{
return transfer_data_selement(selement, sel_analyzed, sel_stats, cb_info);
}
/* Selement mapping functions and callbacks. */
PRIVATE int st_map_selement_from_sentry_cb(const struct _magic_type* parent_type,
const unsigned parent_offset, int child_num,
const struct _magic_type* type, const unsigned offset, int depth, void* cb_args)
{
void **args_array = (void**) cb_args;
_magic_selement_t cached_selement;
_magic_selement_t *local_selement = (_magic_selement_t*) args_array[1];
_magic_selement_t *selement = (_magic_selement_t*) args_array[0];
struct _magic_sentry *sentry = selement->sentry;
void *address = (char*)sentry->address + offset;
void *selement_address = selement->address;
int is_trg_mapping;
struct st_cb_info *cb_info;
if ((char*) selement_address >= ((char*) address + type->size)) {
return MAGIC_TYPE_WALK_SKIP_PATH;
}
cb_info = (struct st_cb_info*) args_array[2];
is_trg_mapping = (int) args_array[3];
cached_selement.sentry = sentry;
cached_selement.parent_type = parent_type;
cached_selement.parent_address = (char*)sentry->address + parent_offset;
cached_selement.child_num = child_num;
cached_selement.type = type;
cached_selement.address = address;
cached_selement.depth = depth;
st_map_selement(&cached_selement, local_selement, cb_info, is_trg_mapping);
if (local_selement->type == NULL) {
return ENOENT;
}
if (address == selement_address && type == selement->type) {
return MAGIC_TYPE_WALK_STOP;
}
if (type->num_child_types == 0) {
return EINVAL;
}
local_selement->parent_type = local_selement->type;
local_selement->parent_address = local_selement->address;
return MAGIC_TYPE_WALK_CONTINUE;
}
PRIVATE INLINE void st_map_selement_from_sentry(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct _magic_sentry *local_sentry, struct st_cb_info *cb_info, int is_trg_mapping)
{
unsigned max_offset;
int r;
void *args_array[4];
max_offset = (unsigned) ( (char *)cached_selement->address - (char *)cached_selement->sentry->address);
args_array[0] = cached_selement;
args_array[1] = magic_selement_from_sentry(local_sentry, local_selement);
args_array[2] = cb_info;
args_array[3] = (void*) is_trg_mapping;
r = magic_type_walk_root(cached_selement->sentry->type, 0, max_offset, st_map_selement_from_sentry_cb, (void*) args_array);
if (r < 0) {
assert(r == ENOENT);
local_selement->type = NULL;
}
}
PRIVATE INLINE void st_map_sel_analyzed_from_target(_magic_sel_analyzed_t *cached_sel_analyzed, _magic_sel_analyzed_t *local_sel_analyzed, struct _magic_sentry *local_trg_sentry, struct _magic_function *local_trg_function, struct st_cb_info *cb_info)
{
_magic_selement_t *csel, *lsel;
assert(local_trg_sentry || local_trg_function);
assert(cached_sel_analyzed->type_id == MAGIC_TYPE_POINTER);
assert(cached_sel_analyzed->u.ptr.first_legal_trg_type>=0);
local_sel_analyzed->type_id = cached_sel_analyzed->type_id;
local_sel_analyzed->num = cached_sel_analyzed->num;
local_sel_analyzed->flags = cached_sel_analyzed->flags;
local_sel_analyzed->u.ptr.trg_flags = local_trg_sentry ? local_trg_sentry->flags : local_trg_function->flags;
local_sel_analyzed->u.ptr.first_legal_trg_type = -1;
local_sel_analyzed->u.ptr.num_legal_trg_types = 0;
if (local_trg_function) {
assert(cached_sel_analyzed->u.ptr.num_legal_trg_types == 1);
lsel = &local_sel_analyzed->u.ptr.trg_selements[0];
memset(lsel, 0, sizeof(_magic_selement_t));
lsel->sentry = NULL;
lsel->type = local_trg_function->type;
lsel->address = local_trg_function->address;
local_sel_analyzed->u.ptr.num_trg_types = 1;
}
else {
int i;
void *address = NULL;
local_sel_analyzed->u.ptr.num_trg_types = 0;
for (i = cached_sel_analyzed->u.ptr.first_legal_trg_type ; i < cached_sel_analyzed->u.ptr.num_trg_types ; i++) {
_magic_trg_stats_t trg_stats = cached_sel_analyzed->u.ptr.trg_stats[i];
if (MAGIC_SEL_ANALYZED_TRG_STATS_HAS_VIOLATIONS(trg_stats)) {
continue;
}
csel = &cached_sel_analyzed->u.ptr.trg_selements[i];
lsel = &local_sel_analyzed->u.ptr.trg_selements[local_sel_analyzed->u.ptr.num_trg_types++];
st_map_selement_from_sentry(csel, lsel, local_trg_sentry, cb_info, TRUE);
if (lsel->type == NULL || (address && lsel->address != address)) {
/* Unpaired selement or ambiguous local address. */
local_sel_analyzed->u.ptr.num_trg_types = 0;
return;
}
address = lsel->address;
}
assert(local_sel_analyzed->u.ptr.num_trg_types > 0);
}
}
PUBLIC void st_map_local_selement_from_child_num(_magic_selement_t *local_selement, struct st_cb_info *cb_info, int child_num)
{
local_selement->child_num = child_num;
magic_selement_fill_from_parent_info(local_selement, cb_info->walk_flags);
}
PUBLIC void st_cb_map_from_parent_array_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
int cached_num_elements, local_num_elements, is_trg_at_array_end, is_trg_at_str_end;
/* Match arrays/vectors with arrays/vectors. */
assert(cached_selement->parent_type->type_id == MAGIC_TYPE_ARRAY || cached_selement->parent_type->type_id == MAGIC_TYPE_VECTOR);
if (local_selement->parent_type->type_id != MAGIC_TYPE_ARRAY && local_selement->parent_type->type_id != MAGIC_TYPE_VECTOR) {
local_selement->type = NULL;
return;
}
cached_num_elements = cached_selement->parent_type->num_child_types;
local_num_elements = local_selement->parent_type->num_child_types;
/* Same size or first child? We are done. */
if (cached_num_elements == local_num_elements || local_selement->child_num == 0) {
st_map_local_selement_from_child_num(local_selement, cb_info, cached_selement->child_num);
return;
}
assert(local_num_elements > 0);
is_trg_at_str_end = FALSE;
is_trg_at_array_end = FALSE;
if (is_trg_mapping && cached_selement->child_num == cached_num_elements-1) {
is_trg_at_str_end = MAGIC_SENTRY_IS_STRING(cached_selement->sentry) || MAGIC_SENTRY_IS_STRING(local_selement->sentry);
is_trg_at_array_end = !is_trg_at_str_end;
}
if (is_trg_at_array_end && (st_policies & ST_DEFAULT_MAP_GUARD_PTRS_TO_ARRAY_END)) {
/* This should be interpreted as a target of a guard pointer pointing to the last element of an array and needs to be remapped as such. */
st_map_local_selement_from_child_num(local_selement, cb_info, local_num_elements-1);
}
else if (is_trg_at_str_end && (st_policies & ST_DEFAULT_MAP_GUARD_PTRS_TO_STR_END)) {
/* This should be interpreted as a target of a guard pointer pointing to the last element of a string and needs to be remapped as such. */
st_map_local_selement_from_child_num(local_selement, cb_info, local_num_elements-1);
}
else if (cached_selement->child_num >= local_num_elements) {
/* New array got truncated and this element is gone. */
local_selement->type = NULL;
}
else {
/* New array is bigger, just keep the original position in the array. */
st_map_local_selement_from_child_num(local_selement, cb_info, cached_selement->child_num);
}
}
PUBLIC void st_cb_map_child_array_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
size_t cached_size = cached_selement->type->num_child_types, local_size = local_selement->type->num_child_types;
/* Match arrays/vectors with arrays/vectors. */
assert(cached_selement->type->type_id == MAGIC_TYPE_ARRAY || cached_selement->type->type_id == MAGIC_TYPE_VECTOR);
if (local_selement->type->type_id != MAGIC_TYPE_ARRAY && local_selement->type->type_id != MAGIC_TYPE_VECTOR) {
local_selement->type = NULL;
return;
}
/* Varsized arrays have to be consistent across versions. */
if (MAGIC_TYPE_FLAG(cached_selement->type, MAGIC_TYPE_VARSIZE) != MAGIC_TYPE_FLAG(local_selement->type, MAGIC_TYPE_VARSIZE)) {
local_selement->type = NULL;
return;
}
/* Check size. */
if (cached_size != local_size) {
int report;
int is_string = MAGIC_SENTRY_IS_STRING(cached_selement->sentry) || MAGIC_SENTRY_IS_STRING(local_selement->sentry);
if (local_size < cached_size) {
report = is_string ? (st_policies & ST_REPORT_SMALLER_STRINGS) : (st_policies & ST_REPORT_SMALLER_ARRAYS);
}
else {
report = is_string ? (st_policies & ST_REPORT_LARGER_STRINGS) : (st_policies & ST_REPORT_LARGER_ARRAYS);
}
if (report) {
printf("st_cb_map_child_array_selement_generic: %s size found while mapping array selements:\n", local_size < cached_size ? "Smaller" : "Larger");
MAGIC_SELEMENT_PRINT(cached_selement, MAGIC_EXPAND_TYPE_STR); printf("\n");
MAGIC_SELEMENT_PRINT(local_selement, MAGIC_EXPAND_TYPE_STR); printf("\n");
}
}
}
PUBLIC void st_cb_map_from_parent_union_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
/* This should only be called in case of unions transferred as structs. */
st_cb_map_from_parent_struct_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
}
PUBLIC void st_cb_map_child_union_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
/* Match unions just like structs. */
st_cb_map_child_struct_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
}
PUBLIC void st_cb_map_from_parent_struct_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
int i;
char *cached_member_name;
/* Match struct/unions with struct/unions. */
assert(cached_selement->parent_type->type_id == MAGIC_TYPE_STRUCT || cached_selement->parent_type->type_id == MAGIC_TYPE_UNION);
if (local_selement->parent_type->type_id != MAGIC_TYPE_STRUCT && local_selement->parent_type->type_id != MAGIC_TYPE_UNION) {
local_selement->type = NULL;
return;
}
/* Match struct/unions members by name. */
cached_member_name = cached_selement->parent_type->member_names[cached_selement->child_num];
for (i = 0 ; i < local_selement->parent_type->num_child_types ; i++) {
if (!strcmp(local_selement->parent_type->member_names[i], cached_member_name)) {
st_map_local_selement_from_child_num(local_selement, cb_info, i);
return;
}
}
local_selement->type = NULL;
}
PUBLIC void st_cb_map_child_struct_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
int i, j;
const struct _magic_type *cached_type = cached_selement->type;
const struct _magic_type *local_type = local_selement->type;
assert(cached_type->type_id == MAGIC_TYPE_STRUCT || cached_type->type_id == MAGIC_TYPE_UNION);
if (local_type->type_id != MAGIC_TYPE_STRUCT && local_type->type_id != MAGIC_TYPE_UNION) {
local_selement->type = NULL;
return;
}
/* Match struct/unions by name(s). */
if (!strcmp(cached_type->name, local_type->name)) {
return;
}
if (cached_type->num_names > 1 || local_type->num_names > 1 ) {
for (i = 0 ; i < cached_type->num_names ; i++) {
for (j = 0 ; j < local_type->num_names ; j++) {
if (!strcmp(cached_type->names[i], local_type->names[j])) {
return;
}
}
}
}
local_selement->type = NULL;
}
PUBLIC void st_cb_map_child_nonaggr_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
int r;
static char magic_value_buffer[32];
r = magic_selement_value_cast(cached_selement, local_selement, magic_value_buffer);
if (r == 0) {
return;
}
if (r < 0 && r != MAGIC_ERANGE && r != MAGIC_ESIGN) {
local_selement->type = NULL;
return;
}
if ((r == MAGIC_ERANGE && (st_policies & ST_REPORT_PRECISION_LOSS))
|| (r == MAGIC_ESIGN && (st_policies & ST_REPORT_SIGN_CHANGE))) {
_magic_selement_t converted_selement = *cached_selement;
converted_selement.address = magic_value_buffer;
converted_selement.type = local_selement->type;
printf("st_cb_map_child_nonaggr_selement_generic: %s while mapping non-aggregate selements:\n", r == MAGIC_ERANGE ? "Precision loss" : "Sign change");
MAGIC_SELEMENT_PRINT(cached_selement, MAGIC_EXPAND_TYPE_STR); printf("\n");
MAGIC_SELEMENT_PRINT(local_selement, MAGIC_EXPAND_TYPE_STR); printf("\n");
printf(" - ORIGINAL VALUE: "); magic_selement_print_value(cached_selement); printf("\n");
printf(" - MAPPED VALUE: "); magic_selement_print_value(&converted_selement); printf("\n");
}
cached_selement->address = magic_value_buffer;
}
PUBLIC void st_cb_map_from_parent_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
assert(cached_selement->parent_type && local_selement->parent_type);
switch(cached_selement->parent_type->type_id) {
case MAGIC_TYPE_ARRAY:
case MAGIC_TYPE_VECTOR:
st_cb_map_from_parent_array_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
case MAGIC_TYPE_UNION:
st_cb_map_from_parent_union_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
case MAGIC_TYPE_STRUCT:
st_cb_map_from_parent_struct_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
default:
st_cbs_os.panic("Invalid parent type!");
break;
}
}
PUBLIC void st_cb_map_child_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
assert(cached_selement->type);
if (local_selement->type == NULL) {
return;
}
if (cached_selement->type->num_child_types == 0 || cached_selement->type->type_id == MAGIC_TYPE_POINTER) {
st_cb_map_child_nonaggr_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
return;
}
switch (cached_selement->type->type_id) {
case MAGIC_TYPE_ARRAY:
case MAGIC_TYPE_VECTOR:
st_cb_map_child_array_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
case MAGIC_TYPE_UNION:
st_cb_map_child_union_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
case MAGIC_TYPE_STRUCT:
st_cb_map_child_struct_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
break;
default:
st_cbs_os.panic("Invalid parent type!");
break;
}
}
PUBLIC void st_cb_map_selement_generic(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
int i;
assert(cached_selement->type->type_id != MAGIC_TYPE_FUNCTION);
for (i = 0 ; i < MAGIC_ST_TYPE_TRANS_ITERATIONS ; i++) {
if (cached_selement->parent_type) {
st_cb_map_from_parent_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
}
st_cb_map_child_selement_generic(cached_selement, local_selement, cb_info, is_trg_mapping);
}
}
PRIVATE INLINE void st_map_selement(_magic_selement_t *cached_selement, _magic_selement_t *local_selement, struct st_cb_info *cb_info, int is_trg_mapping)
{
const struct _magic_type *cached_parent_type = cached_selement->parent_type;
const struct _magic_type *local_parent_type = local_selement->parent_type;
const struct _magic_type *cached_type = cached_selement->type;
if (cached_parent_type) {
if (cached_parent_type == local_parent_type) {
/* Quickly propagate perfect type pairs from parents. */
local_selement->address = (char *)local_selement->parent_address + ((char *)cached_selement->address - (char *)cached_selement->parent_address);
local_selement->type = cached_type;
return;
}
else if (ST_TYPE_IS_CACHED_COUNTERPART(cached_parent_type, local_parent_type)) {
/* Quickly propagate type pairs from parents. */
st_map_local_selement_from_child_num(local_selement, cb_info, cached_selement->child_num);
return;
}
else {
local_selement->type = NULL;
}
}
else {
/* In case of target mapping, we don't care about compatible types. When paired types are found, add a perfect type pair to speed up subsequent lookups. */
if (ST_TYPE_IS_CACHED_COUNTERPART(cached_type, local_selement->type)) {
if (is_trg_mapping) local_selement->type = cached_type;
return;
}
}
#if CHECK_ASR && !FORCE_SOME_UNPAIRED_TYPES
if (cb_info->init_info->flags & ST_LU_ASR) {
st_cbs_os.panic("ASR should never get here!");
}
#endif
st_num_type_transformations++;
st_cbs.st_cb_selement_map(cached_selement, local_selement, cb_info, is_trg_mapping);
/* Check again for paired types and add a perfect type pair to speed up subsequent lookups in case of target mapping. */
if (is_trg_mapping && local_selement->type != NULL && local_selement->type != cached_selement->type) {
if (ST_TYPE_IS_CACHED_COUNTERPART(cached_selement->type, local_selement->type)) {
local_selement->type = cached_selement->type;
}
}
}
/* main functions */
PUBLIC int st_state_transfer(st_init_info_t *info)
{
int r;
/*
* Set all OS dependent callbacks first.
*/
st_setcb_os_all(&info->st_cbs_os);
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
_magic_vars->fake_malloc = 1;
#endif
r = st_init(info);
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
_magic_vars->fake_malloc = 0;
#endif
if (r != OK) {
return r;
}
r = st_data_transfer(info);
if (r != OK) {
return r;
}
#if ST_DEBUG_LEVEL > 0
printf("st_state_transfer: state transfer is done, num type transformations: %u.\n", st_num_type_transformations);
#endif
st_cleanup(info);
return OK;
}
PUBLIC void st_set_policies(int policies)
{
st_policies = policies;
}
#if MAGIC_LOOKUP_SENTRY_ALLOW_RANGE_INDEX
PRIVATE void st_init_rl_index(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
size_t buff_size;
void *buff;
EXEC_WITH_MAGIC_VARS(
buff_size = magic_sentry_rl_estimate_index_buff_size(0);
, magic_vars
);
buff = st_buff_allocate(info, buff_size);
EXEC_WITH_MAGIC_VARS(
magic_sentry_rl_build_index(buff, buff_size);
, magic_vars
);
}
PRIVATE void st_cleanup_rl_index(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
EXEC_WITH_MAGIC_VARS(
magic_sentry_rl_destroy_index();
, magic_vars
);
}
#endif
#if MAGIC_LOOKUP_SENTRY_ALLOW_NAME_HASH
PRIVATE void st_init_sentry_hash(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
size_t buff_size;
void *buff;
EXEC_WITH_MAGIC_VARS(
buff_size = magic_sentry_hash_estimate_buff_size(0);
, magic_vars
);
buff = st_buff_allocate(info, buff_size);
EXEC_WITH_MAGIC_VARS(
magic_sentry_hash_build(buff, buff_size);
, magic_vars
);
}
PRIVATE void st_cleanup_sentry_hash(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
EXEC_WITH_MAGIC_VARS(
magic_sentry_hash_destroy();
, magic_vars
);
}
#endif
#if MAGIC_LOOKUP_FUNCTION_ALLOW_ADDR_HASH
PRIVATE void st_init_function_hash(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
size_t buff_size;
void *buff;
EXEC_WITH_MAGIC_VARS(
buff_size = magic_function_hash_estimate_buff_size(0);
, magic_vars
);
buff = st_buff_allocate(info, buff_size);
EXEC_WITH_MAGIC_VARS(
magic_function_hash_build(buff, buff_size);
, magic_vars
);
}
PRIVATE void st_cleanup_function_hash(st_init_info_t *info,
struct _magic_vars_t *magic_vars)
{
EXEC_WITH_MAGIC_VARS(
magic_function_hash_destroy();
, magic_vars
);
}
#endif
PRIVATE void st_vars_clear_ptrs(struct _magic_vars_t *magic_vars)
{
#undef __X
#define __X(x) offsetof(struct _magic_vars_t, x)
size_t offset_list[] = { ST_MAGIC_VARS_PTR_CLEAR_LIST };
#undef __X
int i;
for (i = 0 ; i < sizeof(offset_list) / sizeof(size_t) ; i++)
*((void **)(((char *)magic_vars) + offset_list[i])) = NULL;
}
#ifdef __MINIX
PRIVATE void st_unmap_mem(struct _magic_vars_t *magic_vars)
{
int i, r;
for (i = 0; i < MAGIC_UNMAP_MEM_ENTRIES; i++) {
if (magic_vars->unmap_mem[i].length != 0) {
#if ST_DEBUG_LEVEL > 0
printf("st_unmap_mem: unmapping (%p, %zu)\n",
magic_vars->unmap_mem[i].start,
magic_vars->unmap_mem[i].length);
#endif
r = munmap(magic_vars->unmap_mem[i].start,
magic_vars->unmap_mem[i].length);
assert(r == 0);
}
}
}
#endif
PUBLIC int st_init(st_init_info_t *info)
{
int r, max_buff_sz = 0, dsentries_num;
int allow_unpaired_types = TRUE;
if (st_init_done) {
return OK;
}
if (!_magic_enabled) return ENOSYS;
st_init_done = TRUE;
/* Ignore nested mempool dsentries for now. */
magic_lookup_nested_dsentries = 0;
/* Override default state transfer policies for ASR. */
if ((info->flags & ST_LU_ASR) && st_policies == ST_POLICIES_DEFAULT) {
st_policies = ST_POLICIES_DEFAULT_TRANSFER_ASR;
}
/* Fixup state transfer policies based on current configuration. */
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
st_policies &= (~ST_DEFAULT_ALLOC_CASCADE_XFER);
#elif !defined(__MINIX)
st_policies |= ST_TRANSFER_DIRTY_ONLY;
#endif
assert((!info->init_buff_start || (info->flags & ST_LU_NOMMAP)) && "st_init: no mmapping allowed, and no buffer is available");
register_typenames_and_callbacks();
/* Transfer _magic_vars, which contain addresses of the magic variables */
r = st_cbs_os.old_state_table_lookup(info->info_opaque, &st_remote_magic_vars);
assert( r == OK && "ERROR occurred during transfer of _magic_vars.");
/*
* Clear all pointers not explictly transferred, as they are not valid in
* the new address space.
*/
st_vars_clear_ptrs(&st_remote_magic_vars);
/*
* Some magic_vars members do not need transfer or adjustment
* (e.g. the memory ranges). They are copied to st_cached_magic_vars
* this way.
*/
st_cached_magic_vars = st_remote_magic_vars;
/* Transfer and adjust metadata */
r = st_transfer_metadata_types(info, &st_cached_magic_vars, &st_remote_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during transfer of type metadata.");
r = transfer_metadata_functions(info, &st_cached_magic_vars, &st_remote_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during transfer of function metadata.");
r = transfer_metadata_dfunctions(info, &st_cached_magic_vars, &st_remote_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during transfer of dfunction metadata.");
r = transfer_metadata_sentries(info, &st_cached_magic_vars, &st_remote_magic_vars, &st_counterparts, &max_buff_sz);
assert( r == OK && "ERROR occurred during transfer of sentry metadata.");
r = st_transfer_metadata_dsentries(info, &st_cached_magic_vars, &st_remote_magic_vars, &st_counterparts, &max_buff_sz, &dsentries_num);
assert( r == OK && "ERROR occurred during transfer of dsentry metadata.");
/* Allocate buffer for data transfer */
st_dsentry_buff = st_buff_allocate(info, max_buff_sz + sizeof(struct _magic_dsentry));
if (!st_dsentry_buff) {
printf("st_dsentry_buff could not be allocated.\n");
return EGENERIC;
}
st_data_buff = &st_dsentry_buff[1];
/* Allocate and initialize counterparts buffers. */
st_counterparts.functions_size = st_cached_magic_vars.functions_num;
st_counterparts.functions = st_buff_allocate(info, st_counterparts.functions_size * sizeof(st_ptr_mapping));
assert(st_counterparts.functions && "st_counterparts.functions could not be allocated.");
st_counterparts.types_size = st_cached_magic_vars.types_num;
st_counterparts.types = st_buff_allocate(info, st_counterparts.types_size * sizeof(st_ptr_mapping));
assert(st_counterparts.types && "st_counterparts.types could not be allocated.");
st_counterparts.ptr_types = st_buff_allocate(info, st_counterparts.types_size * sizeof(st_ptr_mapping));
assert(st_counterparts.ptr_types && "st_counterparts.ptr_types could not be allocated.");
st_counterparts.sentries_size = st_cached_magic_vars.sentries_num + dsentries_num;
st_counterparts.sentries = st_buff_allocate(info, st_counterparts.sentries_size * sizeof(st_ptr_mapping));
assert(st_counterparts.sentries && "st_counterparts.sentries could not be allocated.");
st_counterparts.sentries_data = st_buff_allocate(info, st_counterparts.sentries_size * sizeof(st_ptr_mapping));
assert(st_counterparts.sentries_data && "st_counterparts.sentries_data could not be allocated.");
#if MAGIC_LOOKUP_SENTRY_ALLOW_RANGE_INDEX
st_init_rl_index(info, &st_cached_magic_vars);
#endif
#if MAGIC_LOOKUP_SENTRY_ALLOW_NAME_HASH
st_init_sentry_hash(info, &st_cached_magic_vars);
st_init_sentry_hash(info, _magic_vars);
#endif
#if MAGIC_LOOKUP_FUNCTION_ALLOW_ADDR_HASH
st_init_function_hash(info, &st_cached_magic_vars);
st_init_function_hash(info, _magic_vars);
#endif
#ifdef __MINIX
/* Unmap any memory ranges that are not needed in the new process. */
st_unmap_mem(&st_cached_magic_vars);
#endif
/* Pair metadata entities */
r = pair_metadata_types(info, &st_cached_magic_vars, &st_counterparts, allow_unpaired_types);
assert( r == OK && "ERROR occurred during call to pair_metadata_types().");
r = pair_metadata_functions(info, &st_cached_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during call to pair_metadata_functions().");
r = pair_metadata_sentries(info, &st_cached_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during call to pair_metadata_sentries().");
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
r = allocate_pair_metadata_dsentries_from_raw_copy(info, &st_cached_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during call to allocate_pair_metadata_dsentries().");
#else
r = allocate_pair_metadata_dsentries(info, &st_cached_magic_vars, &st_counterparts);
assert( r == OK && "ERROR occurred during call to allocate_pair_metadata_dsentries().");
#endif
/* Set state transfer status defaults from the predefined policies. */
st_set_status_defaults(info);
#if MAGIC_LOOKUP_SENTRY_ALLOW_RANGE_INDEX
st_init_rl_index(info, _magic_vars);
#endif
return OK;
}
PRIVATE INLINE char* st_lookup_str_local_data(struct _magic_sentry *cached_sentry)
{
void *local_data_addr;
assert(cached_sentry && MAGIC_SENTRY_IS_STRING(cached_sentry));
ST_GET_CACHED_COUNTERPART(cached_sentry, sentries, sentries_data, local_data_addr);
assert(local_data_addr && "String data not in cache!");
return (char*) local_data_addr;
}
#if ST_DEBUG_DATA_TRANSFER
PRIVATE void st_print_sentities(struct _magic_vars_t *magic_vars)
{
struct _magic_dsentry *dsentry = magic_vars->first_dsentry;
int i;
for (i = 0 ; i < magic_vars->sentries_num ; i++) {
struct _magic_sentry *sentry = &magic_vars->sentries[i];
ST_SENTRY_PRINT(sentry, 0);
printf("\n");
}
while (dsentry != NULL) {
ST_DSENTRY_PRINT(dsentry, 0);
printf("\n");
dsentry = dsentry->next;
}
for (i = 0 ; i < magic_vars->functions_num ; i++) {
struct _magic_function *function = &magic_vars->functions[i];
ST_FUNCTION_PRINT(function, 0);
printf("\n");
}
}
#endif
PUBLIC void st_map_str_sentries(struct _magic_sentry **cached_sentry_ptr, struct _magic_sentry **local_sentry_ptr)
{
struct _magic_sentry *cached_sentry = *cached_sentry_ptr;
struct _magic_sentry *local_sentry = *local_sentry_ptr;
struct _magic_sentry *sentry = cached_sentry ? cached_sentry : local_sentry;
int string_flags, match_by_name, match_by_content;
ST_CHECK_INIT();
assert((cached_sentry == NULL) ^ (local_sentry == NULL));
string_flags = sentry->flags & (MAGIC_STATE_STRING|MAGIC_STATE_NAMED_STRING);
assert(string_flags & MAGIC_STATE_STRING);
match_by_name = (string_flags & MAGIC_STATE_NAMED_STRING) && (st_policies & ST_MAP_NAMED_STRINGS_BY_NAME);
match_by_content = ((string_flags & MAGIC_STATE_NAMED_STRING) && (st_policies & ST_MAP_NAMED_STRINGS_BY_CONTENT))
|| (!(string_flags & MAGIC_STATE_NAMED_STRING) && (st_policies & ST_MAP_STRINGS_BY_CONTENT));
if (match_by_name) {
/* Pretend it's a regular sentry and match by name */
sentry->flags &= ~string_flags;
st_map_sentries(cached_sentry_ptr, local_sentry_ptr);
sentry->flags |= string_flags;
if (*cached_sentry_ptr && *local_sentry_ptr) {
/* Found by name. */
return;
}
}
if (!match_by_content) {
/* No match. */
return;
}
if (cached_sentry) {
EXEC_WITH_MAGIC_VARS(
local_sentry = magic_sentry_lookup_by_string(st_lookup_str_local_data(cached_sentry));
, st_local_magic_vars_ptr
);
}
else {
int i;
for(i = 0 ; i < st_cached_magic_vars.sentries_num ; i++) {
struct _magic_sentry *sentry = &st_cached_magic_vars.sentries[i];
if (MAGIC_SENTRY_IS_STRING(sentry) && !strcmp(st_lookup_str_local_data(sentry), (char*)local_sentry->address)) {
cached_sentry = sentry;
break;
}
}
}
*cached_sentry_ptr = cached_sentry;
*local_sentry_ptr = local_sentry;
}
PUBLIC void st_map_sentries(struct _magic_sentry **cached_sentry_ptr, struct _magic_sentry **local_sentry_ptr)
{
struct _magic_sentry *cached_sentry = *cached_sentry_ptr;
struct _magic_sentry *local_sentry = *local_sentry_ptr;
struct _magic_dsentry *cached_dsentry = cached_sentry ? (MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC) ? MAGIC_DSENTRY_FROM_SENTRY(cached_sentry) : NULL) : NULL;
struct _magic_dsentry *local_dsentry = local_sentry ? (MAGIC_STATE_FLAG(local_sentry, MAGIC_STATE_DYNAMIC) ? MAGIC_DSENTRY_FROM_SENTRY(local_sentry) : NULL) : NULL;
ST_CHECK_INIT();
assert((cached_sentry == NULL) ^ (local_sentry == NULL));
if ((cached_sentry && MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_STRING))
|| (local_sentry && MAGIC_STATE_FLAG(local_sentry, MAGIC_STATE_STRING))) {
st_map_str_sentries(cached_sentry_ptr, local_sentry_ptr);
return;
}
else if (cached_sentry) {
EXEC_WITH_MAGIC_VARS(
local_sentry = magic_sentry_lookup_by_name(cached_dsentry ? cached_dsentry->parent_name : "", cached_sentry->name, cached_dsentry ? cached_dsentry->site_id : 0, NULL);
, st_local_magic_vars_ptr
);
assert(!local_sentry || MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC) == MAGIC_STATE_FLAG(local_sentry, MAGIC_STATE_DYNAMIC));
}
else {
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_name(local_dsentry ? local_dsentry->parent_name : "", local_sentry->name, local_dsentry ? local_dsentry->site_id : 0, NULL);
, &st_cached_magic_vars
);
assert(!cached_sentry || MAGIC_STATE_FLAG(local_sentry, MAGIC_STATE_DYNAMIC) == MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC));
}
*cached_sentry_ptr = cached_sentry;
*local_sentry_ptr = local_sentry;
}
PRIVATE struct _magic_sentry *st_lookup_cached_sentry(struct _magic_sentry *local_sentry)
{
int i;
struct _magic_dsentry *cached_dsentry;
assert(local_sentry);
for (i = 0 ; i < st_counterparts.sentries_size ; i++) {
if (st_counterparts.sentries[i].counterpart == local_sentry) {
break;
}
}
if (i >= st_counterparts.sentries_size) {
return NULL;
}
if (i < st_cached_magic_vars.sentries_num) {
return &st_cached_magic_vars.sentries[i];
}
i -= st_cached_magic_vars.sentries_num;
cached_dsentry = st_cached_magic_vars.first_dsentry;
assert(i >= 0);
assert(cached_dsentry);
while (i > 0) {
cached_dsentry = cached_dsentry->next;
assert(cached_dsentry);
i--;
}
return MAGIC_DSENTRY_TO_SENTRY(cached_dsentry);
}
PUBLIC void st_lookup_sentry_pair(struct _magic_sentry **cached_sentry_ptr, struct _magic_sentry **local_sentry_ptr)
{
struct _magic_sentry *cached_sentry = *cached_sentry_ptr;
struct _magic_sentry *local_sentry = *local_sentry_ptr;
ST_CHECK_INIT();
assert((cached_sentry == NULL) ^ (local_sentry == NULL));
if (cached_sentry) {
ST_GET_CACHED_COUNTERPART(cached_sentry, sentries, sentries, local_sentry);
}
else if (MAGIC_SENTRY_IS_STRING(local_sentry)) {
/* strings are special, they may have multiple local duplicates */
struct _magic_sentry *csentry = NULL, *lsentry = NULL;
st_map_str_sentries(&csentry, &local_sentry);
if (csentry) {
st_lookup_sentry_pair(&csentry, &lsentry);
if (lsentry) {
cached_sentry = csentry;
}
}
}
else {
cached_sentry = st_lookup_cached_sentry(local_sentry);
}
*cached_sentry_ptr = cached_sentry;
*local_sentry_ptr = local_sentry;
}
PRIVATE INLINE void st_unpair_local_alloc_sentry(struct _magic_sentry *local_sentry)
{
if (st_policies & ST_ON_ALLOC_UNPAIR_ERROR) {
st_cbs_os.panic("st_unpair_local_alloc_sentry: Error: attempting to unpair a local alloc sentry!");
}
else if (st_policies & ST_ON_ALLOC_UNPAIR_DEALLOCATE) {
deallocate_local_dsentry(MAGIC_DSENTRY_FROM_SENTRY(local_sentry));
}
}
PUBLIC void st_add_sentry_pair(struct _magic_sentry *cached_sentry, struct _magic_sentry *local_sentry)
{
ST_CHECK_INIT();
assert(cached_sentry || local_sentry);
if (local_sentry) {
struct _magic_sentry *csentry = NULL;
st_lookup_sentry_pair(&csentry, &local_sentry);
if (csentry) {
ST_SET_CACHED_COUNTERPART(csentry, sentries, sentries, NULL);
}
if (!cached_sentry && MAGIC_SENTRY_IS_ALLOC(local_sentry)) {
st_unpair_local_alloc_sentry(local_sentry);
}
}
if (cached_sentry) {
struct _magic_sentry *lsentry = NULL;
st_lookup_sentry_pair(&cached_sentry, &lsentry);
if (lsentry && MAGIC_SENTRY_IS_ALLOC(lsentry)) {
st_unpair_local_alloc_sentry(lsentry);
}
ST_SET_CACHED_COUNTERPART(cached_sentry, sentries, sentries, local_sentry);
}
}
PUBLIC int st_add_sentry_pair_alloc_by_dsindex(st_init_info_t *info, struct _magic_sentry *cached_sentry, struct _magic_dsindex *local_dsindex, int num_elements, const union __alloc_flags *p_alloc_flags)
{
int r;
struct _magic_dsentry *local_dsentry;
ST_CHECK_INIT();
assert(cached_sentry);
if (!local_dsindex) {
st_add_sentry_pair(cached_sentry, NULL);
return OK;
}
r = allocate_local_dsentry(info, local_dsindex, num_elements, MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_TYPE_SIZE_MISMATCH), p_alloc_flags, &local_dsentry, NULL, MAGIC_PTR_TO_DSENTRY(cached_sentry->address));
if (r != OK) {
return r;
}
st_add_sentry_pair(cached_sentry, MAGIC_DSENTRY_TO_SENTRY(local_dsentry));
return OK;
}
PUBLIC void st_map_functions(struct _magic_function **cached_function_ptr, struct _magic_function **local_function_ptr)
{
struct _magic_function *cached_function = *cached_function_ptr;
struct _magic_function *local_function = *local_function_ptr;
ST_CHECK_INIT();
assert((cached_function == NULL) ^ (local_function == NULL));
if (cached_function) {
EXEC_WITH_MAGIC_VARS(
local_function = magic_function_lookup_by_name(NULL, cached_function->name);
, st_local_magic_vars_ptr
);
}
else {
EXEC_WITH_MAGIC_VARS(
cached_function = magic_function_lookup_by_name(NULL, local_function->name);
, &st_cached_magic_vars
);
}
*cached_function_ptr = cached_function;
*local_function_ptr = local_function;
}
PUBLIC void st_lookup_function_pair(struct _magic_function **cached_function_ptr, struct _magic_function **local_function_ptr)
{
struct _magic_function *cached_function = *cached_function_ptr;
struct _magic_function *local_function = *local_function_ptr;
int i;
ST_CHECK_INIT();
assert((cached_function == NULL) ^ (local_function == NULL));
if (cached_function) {
if (cached_function->id - 1 >= st_counterparts.functions_size) {
/*
* Try to check if this is a function
* from an external shared object.
* XXX: The number of dfunctions can be quite large,
* so this needs to be done more efficiently.
*/
struct _magic_dfunction *dfunc;
struct _magic_function *func;
MAGIC_DFUNCTION_FUNC_ITER(_magic_vars->first_dfunction, dfunc, func,
if (func->address == cached_function->address) {
local_function = func;
break;
}
);
assert(local_function != NULL && "No counterpart found for function.");
} else {
ST_GET_CACHED_COUNTERPART(cached_function, functions, functions, local_function);
}
}
else {
assert(st_counterparts.functions_size == st_cached_magic_vars.functions_num);
for(i = 0 ; i < st_counterparts.functions_size ; i++) {
if(st_counterparts.functions[i].counterpart == local_function) {
cached_function = &st_cached_magic_vars.functions[i];
break;
}
}
}
*cached_function_ptr = cached_function;
*local_function_ptr = local_function;
}
PUBLIC void st_add_function_pair(struct _magic_function *cached_function, struct _magic_function *local_function)
{
ST_CHECK_INIT();
assert(cached_function || local_function);
if (local_function) {
struct _magic_function *cfunction = NULL;
st_lookup_function_pair(&cfunction, &local_function);
if (cfunction) {
ST_SET_CACHED_COUNTERPART(cfunction, functions, functions, NULL);
}
}
if (cached_function) {
ST_SET_CACHED_COUNTERPART(cached_function, functions, functions, local_function);
}
}
PUBLIC int st_sentry_equals(struct _magic_sentry *cached_sentry, struct _magic_sentry *local_sentry)
{
char *cached_parent_name = "", *local_parent_name = "";
int cached_flags = MAGIC_STATE_FLAGS_TO_NONEXTF(cached_sentry->flags) & (~MAGIC_STATE_ADDR_NOT_TAKEN);
int local_flags = MAGIC_STATE_FLAGS_TO_NONEXTF(local_sentry->flags) & (~MAGIC_STATE_ADDR_NOT_TAKEN);
if (cached_flags != local_flags) {
return FALSE;
}
if (MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_STRING)) {
return !strcmp(st_lookup_str_local_data(cached_sentry), (char*)local_sentry->address);
}
if (MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC)) {
cached_parent_name = MAGIC_DSENTRY_FROM_SENTRY(cached_sentry)->parent_name;
local_parent_name = MAGIC_DSENTRY_FROM_SENTRY(local_sentry)->parent_name;
}
if (strcmp(cached_sentry->name, local_sentry->name) || strcmp(cached_parent_name, local_parent_name)) {
return FALSE;
}
return magic_type_compatible(cached_sentry->type, local_sentry->type, MAGIC_TYPE_COMPARE_ALL);
}
PUBLIC int st_function_equals(struct _magic_function *cached_function, struct _magic_function *local_function)
{
if (MAGIC_STATE_FLAGS_TO_NONEXTF(local_function->flags) != MAGIC_STATE_FLAGS_TO_NONEXTF(cached_function->flags)) {
return FALSE;
}
return !strcmp(cached_function->name, local_function->name);
}
PUBLIC void st_print_sentry_diff(st_init_info_t *info, struct _magic_sentry *cached_sentry, struct _magic_sentry *local_sentry, int raw_diff, int print_changed)
{
int is_paired_sentry;
ST_CHECK_INIT();
if (!cached_sentry || !local_sentry) {
if (raw_diff) {
st_map_sentries(&cached_sentry, &local_sentry);
}
else {
st_lookup_sentry_pair(&cached_sentry, &local_sentry);
}
}
is_paired_sentry = (cached_sentry != NULL && local_sentry != NULL);
if (is_paired_sentry && st_sentry_equals(cached_sentry, local_sentry)) {
return;
}
if (is_paired_sentry && !print_changed) {
return;
}
if (cached_sentry) {
printf("-"); ST_SENTRY_PRINT(cached_sentry, MAGIC_EXPAND_TYPE_STR); printf("\n");
}
if (local_sentry) {
printf("+"); ST_SENTRY_PRINT(local_sentry, MAGIC_EXPAND_TYPE_STR); printf("\n");
}
printf("\n");
}
PUBLIC void st_print_function_diff(st_init_info_t *info, struct _magic_function *cached_function, struct _magic_function *local_function, int raw_diff, int print_changed)
{
int is_paired_function;
ST_CHECK_INIT();
if (!cached_function || !local_function) {
if (raw_diff) {
st_map_functions(&cached_function, &local_function);
}
else {
st_lookup_function_pair(&cached_function, &local_function);
}
}
is_paired_function = (cached_function != NULL && local_function != NULL);
if (is_paired_function && st_function_equals(cached_function, local_function)) {
return;
}
if (is_paired_function && !print_changed) {
return;
}
if (cached_function) {
printf("-"); ST_FUNCTION_PRINT(cached_function, MAGIC_EXPAND_TYPE_STR); printf("\n");
}
if (local_function) {
printf("+"); ST_FUNCTION_PRINT(local_function, MAGIC_EXPAND_TYPE_STR); printf("\n");
}
printf("\n");
}
PUBLIC void st_print_sentries_diff(st_init_info_t *info, int raw_diff, int print_changed)
{
int i;
ST_CHECK_INIT();
for (i = 0 ; i < st_cached_magic_vars.sentries_num ; i++) {
struct _magic_sentry *cached_sentry = &st_cached_magic_vars.sentries[i];
st_print_sentry_diff(info, cached_sentry, NULL, raw_diff, print_changed);
}
print_changed = FALSE;
for (i = 0 ; i < st_local_magic_vars_ptr->sentries_num ; i++) {
struct _magic_sentry *local_sentry = &st_local_magic_vars_ptr->sentries[i];
st_print_sentry_diff(info, NULL, local_sentry, raw_diff, print_changed);
}
}
PUBLIC void st_print_dsentries_diff(st_init_info_t *info, int raw_diff, int print_changed)
{
struct _magic_dsentry *dsentry;
ST_CHECK_INIT();
dsentry = st_cached_magic_vars.first_dsentry;
while (dsentry != NULL) {
struct _magic_sentry *cached_sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
st_print_sentry_diff(info, cached_sentry, NULL, raw_diff, print_changed);
dsentry = dsentry->next;
}
dsentry = st_local_magic_vars_ptr->first_dsentry;
print_changed = FALSE;
while (dsentry != NULL) {
struct _magic_sentry *local_sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
st_print_sentry_diff(info, NULL, local_sentry, raw_diff, print_changed);
dsentry = dsentry->next;
}
}
PUBLIC void st_print_functions_diff(st_init_info_t *info, int raw_diff, int print_changed)
{
int i;
ST_CHECK_INIT();
for(i = 0 ; i < st_cached_magic_vars.functions_num ; i++) {
struct _magic_function *cached_function = &st_cached_magic_vars.functions[i];
st_print_function_diff(info, cached_function, NULL, raw_diff, print_changed);
}
print_changed = FALSE;
for (i = 0 ; i < st_local_magic_vars_ptr->functions_num ; i++) {
struct _magic_function *local_function = &st_local_magic_vars_ptr->functions[i];
st_print_function_diff(info, NULL, local_function, raw_diff, print_changed);
}
}
PUBLIC void st_print_state_diff(st_init_info_t *info, int raw_diff, int print_changed)
{
ST_CHECK_INIT();
printf("Index: sentries\n");
printf("===================================================================\n");
st_print_sentries_diff(info, raw_diff, print_changed);
printf("\nIndex: dsentries\n");
printf("===================================================================\n");
st_print_dsentries_diff(info, raw_diff, print_changed);
printf("\nIndex: functions\n");
printf("===================================================================\n");
st_print_functions_diff(info, raw_diff, print_changed);
printf("\n");
}
PUBLIC int st_data_transfer(st_init_info_t *info)
{
struct _magic_dsentry *dsentry;
int i, r;
int sentry_transferred;
#if ST_DEBUG_DATA_TRANSFER
int counter = 1;
#endif
ST_CHECK_INIT();
/* Check unpaired sentries. */
for (i = 0 ; i < st_cached_magic_vars.sentries_num ; i++) {
struct _magic_sentry *sentry = &st_cached_magic_vars.sentries[i];
int is_paired_sentry = ST_HAS_CACHED_COUNTERPART(sentry, sentries);
if (!is_paired_sentry) {
r = check_unpaired_sentry(info, sentry);
if (r != OK) {
return r;
}
}
}
/* Check unpaired dsentries. */
dsentry = st_cached_magic_vars.first_dsentry;
while (dsentry != NULL) {
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
int is_paired_sentry = ST_HAS_CACHED_COUNTERPART(sentry, sentries);
if (!is_paired_sentry) {
r = check_unpaired_sentry(info, sentry);
if (r != OK) {
return r;
}
}
dsentry = dsentry->next;
}
/* Data transfer. */
do {
sentry_transferred = 0;
#if ST_DEBUG_DATA_TRANSFER
printf("st_data_transfer: Round %d\n", counter++);
st_print_sentities(&st_cached_magic_vars);
#endif
/* process sentries */
#if ST_DEBUG_LEVEL > 0
printf("st_data_transfer: processing sentries\n");
#endif
for(i = 0 ; i < st_cached_magic_vars.sentries_num ; i++) {
struct _magic_sentry *sentry = &st_cached_magic_vars.sentries[i];
int is_paired_sentry = ST_HAS_CACHED_COUNTERPART(sentry, sentries);
int sentry_needs_transfer = MAGIC_STATE_EXTF_GET(sentry, ST_NEEDS_TRANSFER | ST_TRANSFER_DONE) == ST_NEEDS_TRANSFER;
if (sentry_needs_transfer && is_paired_sentry) {
r = transfer_data_sentry(info, sentry);
if (r != OK) {
return r;
}
sentry_transferred = 1;
}
}
/* process dsentries */
#if ST_DEBUG_LEVEL > 0
printf("st_data_transfer: processing dsentries\n");
#endif
dsentry = st_cached_magic_vars.first_dsentry;
while (dsentry != NULL) {
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
int is_paired_sentry = ST_HAS_CACHED_COUNTERPART(sentry, sentries);
int sentry_needs_transfer = MAGIC_STATE_EXTF_GET(sentry, ST_NEEDS_TRANSFER | ST_TRANSFER_DONE) == ST_NEEDS_TRANSFER;
if (sentry_needs_transfer && is_paired_sentry) {
r = transfer_data_sentry(info, sentry);
if (r != OK) {
return r;
}
sentry_transferred = 1;
}
dsentry = dsentry->next;
}
} while(sentry_transferred);
return OK;
}
PRIVATE INLINE void st_set_transfer_status(int status_flags, int status_op,
struct _magic_sentry *cached_sentry, struct _magic_function *cached_function)
{
#define __st_set_transfer_status(X) \
switch(status_op) { \
case ST_OP_NONE: \
return; \
break; \
case ST_OP_ADD: \
MAGIC_STATE_EXTF_ADD(X, status_flags); \
break; \
case ST_OP_DEL: \
MAGIC_STATE_EXTF_DEL(X, status_flags); \
break; \
case ST_OP_SET: \
MAGIC_STATE_EXTF_SET(X, status_flags); \
break; \
case ST_OP_CLEAR: \
MAGIC_STATE_EXTF_CLEAR(X); \
break; \
default: \
st_cbs_os.panic("Invalid operation!"); \
break; \
} \
if (cached_sentry) {
__st_set_transfer_status(cached_sentry);
}
else {
assert(cached_function);
__st_set_transfer_status(cached_function);
}
}
PUBLIC void st_set_unpaired_types_ratios(float unpaired_types_ratio,
float unpaired_struct_types_ratio)
{
st_unpaired_types_ratio = unpaired_types_ratio;
st_unpaired_struct_types_ratio = unpaired_struct_types_ratio;
}
PUBLIC void st_set_status_defaults(st_init_info_t *info)
{
int match_all = ~0, skip_none = 0;
int skip_state_flags = (st_policies & ST_DEFAULT_SKIP_STACK) ? MAGIC_STATE_STACK : 0;
if (!(st_policies & ST_DEFAULT_TRANSFER_NONE)) {
/*
* Transfer all the (d)sentries by default. Skip stack dsentries when
* requested. In that case, stack dsentries won't be transferred and an
* error will be raised on stack pointer transfer.
*/
st_set_status_by_state_flags(ST_NEEDS_TRANSFER, ST_OP_SET,
match_all, skip_state_flags);
if (st_policies & ST_DEFAULT_ALLOC_CASCADE_XFER) {
/*
* If requested, mark non-stack dsentries for cascade transfer
* instead of regular transfer.
*/
st_set_status_by_state_flags(ST_ON_PTRXFER_CASCADE, ST_OP_SET,
MAGIC_STATE_HEAP | MAGIC_STATE_MAP, skip_none);
}
}
else {
/*
* Don't transfer any (d)sentries by default. Mark all the (d)sentries
* for cascade transfer (except for stack dsentries when requested).
*/
st_set_status_by_state_flags(ST_ON_PTRXFER_CASCADE, ST_OP_SET,
match_all, skip_state_flags);
}
/*
* Always transfer all immutable objects.
*/
st_set_status_by_state_flags(ST_NEEDS_TRANSFER, ST_OP_SET,
MAGIC_STATE_IMMUTABLE, skip_none);
/*
* If requested, mark library state dsentries as already transferred too.
*/
if (st_policies & ST_DEFAULT_SKIP_LIB_STATE) {
st_set_status_by_state_flags(ST_NEEDS_TRANSFER | ST_TRANSFER_DONE,
ST_OP_ADD, MAGIC_STATE_LIB, skip_none);
}
/*
* In addition, mark functions, out-of-band/string sentries
* and shared dsentries as already transferred.
*/
st_set_status_by_state_flags(ST_NEEDS_TRANSFER | ST_TRANSFER_DONE,
ST_OP_ADD, MAGIC_STATE_TEXT | MAGIC_STATE_OUT_OF_BAND |
MAGIC_STATE_STRING | MAGIC_STATE_CONSTANT | MAGIC_STATE_SHM, skip_none);
/*
* Finally, if we only want to transfer dirty sentries, mark all the other ones
* as already transferred.
*/
if (st_policies & ST_TRANSFER_DIRTY_ONLY) {
st_set_status_by_state_flags(ST_TRANSFER_DONE, ST_OP_ADD, match_all, MAGIC_STATE_DIRTY_PAGE);
}
#if DO_SKIP_ENVIRON_HACK
st_set_status_by_name(ST_NEEDS_TRANSFER | ST_TRANSFER_DONE,
ST_OP_ADD, NULL, "__environ", MAGIC_DSENTRY_SITE_ID_NULL);
st_set_status_by_name(ST_NEEDS_TRANSFER | ST_TRANSFER_DONE,
ST_OP_ADD, NULL, "stderr", MAGIC_DSENTRY_SITE_ID_NULL);
#endif
}
PUBLIC void st_set_status_by_state_flags(int status_flags, int status_op,
int match_state_flags, int skip_state_flags)
{
struct _magic_dsentry *dsentry = st_cached_magic_vars.first_dsentry;
int i;
int candidate_sentry_flags = MAGIC_STATE_DATA | MAGIC_STATE_STRING | MAGIC_STATE_CONSTANT | MAGIC_STATE_ADDR_NOT_TAKEN;
int candidate_function_flags = MAGIC_STATE_TEXT;
int candidate_dsentry_flags = ~(candidate_sentry_flags | candidate_function_flags);
ST_CHECK_INIT();
/* process sentries */
if (match_state_flags & candidate_sentry_flags) {
for (i = 0 ; i < st_cached_magic_vars.sentries_num ; i++) {
int state_flags = st_cached_magic_vars.sentries[i].flags;
if ((state_flags & match_state_flags) && !(state_flags & skip_state_flags)) {
st_set_transfer_status(status_flags, status_op, &st_cached_magic_vars.sentries[i], NULL);
}
}
}
/* process dsentries */
if (match_state_flags & candidate_dsentry_flags) {
while (dsentry != NULL) {
int state_flags = dsentry->sentry.flags;
if ((state_flags & match_state_flags) && !(state_flags & skip_state_flags)) {
st_set_transfer_status(status_flags, status_op, MAGIC_DSENTRY_TO_SENTRY(dsentry), NULL);
}
dsentry = dsentry->next;
}
}
/* process functions */
if (match_state_flags & candidate_function_flags) {
for (i = 0 ; i < st_cached_magic_vars.functions_num ; i++) {
int state_flags = st_cached_magic_vars.functions[i].flags;
if ((state_flags & match_state_flags) && !(state_flags & skip_state_flags)) {
st_set_transfer_status(status_flags, status_op, NULL, &st_cached_magic_vars.functions[i]);
}
}
}
}
PUBLIC int st_set_status_by_function_ids(int status_flags, int status_op, _magic_id_t *ids)
{
int r, i = 0;
while (ids[i] != 0) {
r = st_set_status_by_function_id(status_flags, status_op, ids[i]);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC int st_set_status_by_sentry_ids(int status_flags, int status_op, _magic_id_t *ids)
{
int r, i=0;
while (ids[i] != 0) {
r = st_set_status_by_sentry_id(status_flags, status_op, ids[i]);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC int st_set_status_by_names(int status_flags, int status_op,
char **parent_names, char **names, _magic_id_t *dsentry_site_ids)
{
int r, i = 0;
while (names[i] != NULL) {
r = st_set_status_by_name(status_flags, status_op,
parent_names ? parent_names[i] : NULL, names[i],
dsentry_site_ids ? dsentry_site_ids[i] :
MAGIC_DSENTRY_SITE_ID_NULL);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC int st_set_status_by_local_addrs(int status_flags, int status_op,
void **addrs)
{
int r, i=0;
while (addrs[i] != NULL) {
r = st_set_status_by_local_addr(status_flags, status_op, addrs[i]);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC void st_set_status_by_sentry(int status_flags, int status_op,
void *cached_sentry)
{
ST_CHECK_INIT();
st_set_transfer_status(status_flags, status_op,
(struct _magic_sentry*) cached_sentry, NULL);
}
PUBLIC void st_set_status_by_function(int status_flags, int status_op,
void *cached_function)
{
ST_CHECK_INIT();
st_set_transfer_status(status_flags, status_op,
NULL, (struct _magic_function*) cached_function);
}
PUBLIC int st_set_status_by_name(int status_flags, int status_op,
char *parent_name, char *name, _magic_id_t dsentry_site_id)
{
struct _magic_sentry *cached_sentry = NULL;
struct _magic_function *cached_function = NULL;
ST_CHECK_INIT();
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_name(parent_name ? parent_name : "", name, dsentry_site_id, NULL);
if (!cached_sentry) {
cached_function = magic_function_lookup_by_name(parent_name, name);
}
, &st_cached_magic_vars
);
if (!cached_sentry && !cached_function) {
return ENOENT;
}
st_set_transfer_status(status_flags, status_op, cached_sentry, cached_function);
if (cached_sentry && MAGIC_SENTRY_IS_ALLOC(cached_sentry)) {
struct _magic_dsentry *prev_dsentry, *dsentry, *next_dsentry = MAGIC_DSENTRY_NEXT(MAGIC_DSENTRY_FROM_SENTRY(cached_sentry));
struct _magic_sentry* sentry;
/*
* Alloc sentries may have multiple instances with the same name.
* Use the site_id to distinguish between them.
*/
assert(parent_name && name);
MAGIC_DSENTRY_ALIVE_NAME_ID_ITER(next_dsentry, prev_dsentry, dsentry, sentry,
parent_name, name, dsentry_site_id,
st_set_transfer_status(status_flags, status_op, sentry, NULL);
);
}
return OK;
}
PUBLIC int st_set_status_by_function_id(int status_flags, int status_op,
_magic_id_t id)
{
struct _magic_function *cached_function = NULL;
ST_CHECK_INIT();
EXEC_WITH_MAGIC_VARS(
cached_function = magic_function_lookup_by_id(id, NULL);
, &st_cached_magic_vars
);
if (!cached_function) {
return ENOENT;
}
st_set_transfer_status(status_flags, status_op, NULL, cached_function);
return OK;
}
PUBLIC int st_set_status_by_sentry_id(int status_flags, int status_op,
_magic_id_t id)
{
struct _magic_sentry *cached_sentry = NULL;
ST_CHECK_INIT();
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_id(id, NULL);
, &st_cached_magic_vars
);
if (!cached_sentry) {
return ENOENT;
}
st_set_transfer_status(status_flags, status_op, cached_sentry, NULL);
return OK;
}
PUBLIC int st_set_status_by_local_addr(int status_flags, int status_op,
void *addr)
{
char *parent_name, *name;
_magic_id_t dsentry_site_id = MAGIC_DSENTRY_SITE_ID_NULL;
struct _magic_sentry *sentry = NULL;
struct _magic_function *function = NULL;
ST_CHECK_INIT();
sentry = magic_sentry_lookup_by_addr(addr, NULL);
if (!sentry) {
function = magic_function_lookup_by_addr(addr, NULL);
}
if (sentry && !MAGIC_STATE_FLAG(sentry, MAGIC_STATE_DYNAMIC)) {
name = sentry->name;
parent_name = MAGIC_SENTRY_PARENT(sentry);
dsentry_site_id = MAGIC_SENTRY_SITE_ID(sentry);
}
else if (function && !MAGIC_STATE_FLAG(function, MAGIC_STATE_DYNAMIC)) {
name = function->name;
parent_name = MAGIC_FUNCTION_PARENT(function);
}
else {
return ENOENT;
}
st_set_status_by_name(status_flags, status_op, parent_name, name, dsentry_site_id);
return OK;
}
PUBLIC int st_pair_by_function_ids(unsigned long *cached_ids, unsigned long *local_ids, int status_flags, int status_op)
{
int r, i=0;
ST_CHECK_INIT();
while (cached_ids[i] != 0) {
assert(local_ids[i] != 0);
r = st_pair_by_function_id(cached_ids[i], local_ids[i], status_flags, status_op);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC int st_pair_by_sentry_ids(unsigned long *cached_ids, unsigned long *local_ids, int status_flags, int status_op)
{
int r, i=0;
ST_CHECK_INIT();
while (cached_ids[i] != 0) {
assert(local_ids[i] != 0);
r = st_pair_by_sentry_id(cached_ids[i], local_ids[i], status_flags, status_op);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC int st_pair_by_names(char **cached_parent_names, char **cached_names,
char **local_parent_names, char **local_names, _magic_id_t *dsentry_site_ids,
int status_flags, int status_op)
{
int r, i=0;
while (cached_names[i] != NULL) {
assert(local_names[i]);
r = st_pair_by_name(cached_parent_names ? cached_parent_names[i] : NULL, cached_names[i],
local_parent_names ? local_parent_names[i] : NULL, local_names[i],
dsentry_site_ids ? dsentry_site_ids[i] : MAGIC_DSENTRY_SITE_ID_NULL,
status_flags, status_op);
if (r != OK) {
return r;
}
i++;
}
return OK;
}
PUBLIC void st_pair_by_sentry(void *cached_sentry, void *local_sentry, int status_flags, int status_op)
{
ST_CHECK_INIT();
st_add_sentry_pair(cached_sentry, local_sentry);
if (cached_sentry) {
st_set_status_by_sentry(status_flags, status_op, cached_sentry);
}
}
PUBLIC void st_pair_by_function(void *cached_function, void* local_function, int status_flags, int status_op)
{
ST_CHECK_INIT();
st_add_function_pair(cached_function, local_function);
if (cached_function) {
st_set_status_by_function(status_flags, status_op, cached_function);
}
}
PUBLIC int st_pair_alloc_by_dsindex(st_init_info_t *info, void *cached_sentry, void *local_dsindex, int num_elements, const union __alloc_flags *p_alloc_flags, int status_flags, int status_op)
{
int r;
ST_CHECK_INIT();
r = st_add_sentry_pair_alloc_by_dsindex(info, cached_sentry, local_dsindex, num_elements, p_alloc_flags);
if (r != OK) {
return r;
}
if (cached_sentry) {
st_set_status_by_sentry(status_flags, status_op, cached_sentry);
}
return OK;
}
PUBLIC int st_pair_by_function_id(unsigned long cached_id, unsigned long local_id, int status_flags, int status_op)
{
struct _magic_function *cached_function = NULL, *local_function = NULL;
ST_CHECK_INIT();
assert(cached_id || local_id);
if (cached_id) {
EXEC_WITH_MAGIC_VARS(
cached_function = magic_function_lookup_by_id(cached_id, NULL);
, &st_cached_magic_vars
);
if (!cached_function) {
return ENOENT;
}
}
if (local_id) {
EXEC_WITH_MAGIC_VARS(
local_function = magic_function_lookup_by_id(local_id, NULL);
, st_local_magic_vars_ptr
);
if (!local_function) {
return ENOENT;
}
}
st_pair_by_function(cached_function, local_function, status_flags, status_op);
return OK;
}
PUBLIC int st_pair_by_sentry_id(unsigned long cached_id, unsigned long local_id, int status_flags, int status_op)
{
struct _magic_sentry *cached_sentry = NULL, *local_sentry = NULL;
ST_CHECK_INIT();
assert(cached_id || local_id);
if (cached_id) {
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_id(cached_id, NULL);
, &st_cached_magic_vars
);
if (!cached_sentry) {
return ENOENT;
}
}
if (local_id) {
EXEC_WITH_MAGIC_VARS(
local_sentry = magic_sentry_lookup_by_id(local_id, NULL);
, st_local_magic_vars_ptr
);
if (!local_sentry) {
return ENOENT;
}
}
st_pair_by_sentry(cached_sentry, local_sentry, status_flags, status_op);
return OK;
}
PUBLIC int st_pair_by_name(char *cached_parent_name, char *cached_name,
char *local_parent_name, char *local_name, _magic_id_t dsentry_site_id,
int status_flags, int status_op)
{
struct _magic_function *cached_function = NULL, *local_function = NULL;
struct _magic_sentry *cached_sentry = NULL, *local_sentry = NULL;
ST_CHECK_INIT();
assert(cached_name || local_name);
if (cached_name) {
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_name(cached_parent_name ? cached_parent_name : "", cached_name, dsentry_site_id, NULL);
if (cached_sentry && MAGIC_SENTRY_IS_ALLOC(cached_sentry)) {
return EINVAL;
}
if (!cached_sentry) {
cached_function = magic_function_lookup_by_name(NULL, cached_name);
}
, &st_cached_magic_vars
);
if (!cached_sentry && !cached_function) {
return ENOENT;
}
}
if (local_name) {
EXEC_WITH_MAGIC_VARS(
if (!cached_function) {
local_sentry = magic_sentry_lookup_by_name(local_parent_name ? local_parent_name : "", local_name, dsentry_site_id, NULL);
if (local_sentry && MAGIC_SENTRY_IS_ALLOC(local_sentry)) {
return EINVAL;
}
}
if (!cached_sentry && !local_sentry) {
local_function = magic_function_lookup_by_name(NULL, local_name);
}
, st_local_magic_vars_ptr
);
if (!local_sentry && !local_function) {
return ENOENT;
}
}
if (cached_function || local_function) {
assert(!cached_sentry && !local_sentry);
st_pair_by_function(cached_function, local_function, status_flags, status_op);
return OK;
}
assert(cached_sentry || local_sentry);
st_pair_by_sentry(cached_sentry, local_sentry, status_flags, status_op);
return OK;
}
PUBLIC int st_pair_by_alloc_name_policies(st_init_info_t *info, char *cached_parent_name, char *cached_name, _magic_id_t cached_dsentry_site_id, char *local_parent_name, char *local_name, _magic_id_t local_dsentry_site_id, int num_elements, const union __alloc_flags *p_alloc_flags, int alloc_policies, int status_flags, int status_op)
{
int r, saved_policies = st_policies;
st_policies &= ~(ST_ON_ALLOC_UNPAIR_MASK);
st_policies |= (alloc_policies & ST_ON_ALLOC_UNPAIR_MASK);
r = st_pair_by_alloc_name(info, cached_parent_name, cached_name, cached_dsentry_site_id, local_parent_name, local_name, local_dsentry_site_id, num_elements, p_alloc_flags, status_flags, status_op);
st_policies = saved_policies;
return r;
}
PUBLIC int st_pair_by_alloc_name(st_init_info_t *info, char *cached_parent_name, char *cached_name, _magic_id_t cached_dsentry_site_id, char *local_parent_name, char *local_name, _magic_id_t local_dsentry_site_id, int num_elements, const union __alloc_flags *p_alloc_flags, int status_flags, int status_op)
{
struct _magic_sentry *cached_sentry = NULL, *local_sentry = NULL;
struct _magic_dsindex *local_dsindex = NULL;
struct _magic_dsentry *prev_dsentry, *dsentry, *head_dsentry;
struct _magic_sentry* sentry;
int r;
int is_cached_alloc = FALSE, is_local_alloc = FALSE;
ST_CHECK_INIT();
assert(cached_name || local_name);
assert(!((cached_name == NULL) ^ (cached_parent_name == NULL)));
assert(!((local_name == NULL) ^ (local_parent_name == NULL)));
if (cached_name) {
EXEC_WITH_MAGIC_VARS(
cached_sentry = magic_sentry_lookup_by_name(cached_parent_name,
cached_name, cached_dsentry_site_id, NULL);
if (cached_sentry && MAGIC_SENTRY_IS_ALLOC(cached_sentry)) {
is_cached_alloc = TRUE;
}
, &st_cached_magic_vars
);
}
if (local_name) {
EXEC_WITH_MAGIC_VARS(
local_sentry = magic_sentry_lookup_by_name(local_parent_name,
local_name, local_dsentry_site_id, NULL);
if (local_sentry && MAGIC_SENTRY_IS_ALLOC(local_sentry)) {
is_local_alloc = TRUE;
}
if (!local_sentry || is_local_alloc) {
local_dsindex = magic_dsindex_lookup_by_name(local_parent_name, local_name);
if (local_dsindex && !MAGIC_DSINDEX_IS_ALLOC(local_dsindex)) {
local_dsindex = NULL;
}
if (local_sentry) assert(local_dsindex);
is_local_alloc = is_local_alloc || local_dsindex != NULL;
}
, st_local_magic_vars_ptr
);
}
if (!is_cached_alloc && !is_local_alloc) {
if (cached_sentry || local_sentry) {
st_pair_by_sentry(cached_sentry, local_sentry, status_flags, status_op);
return OK;
}
return ENOENT;
}
if (local_sentry) {
if (!is_local_alloc) {
/* Alloc sentries may have multiple instances with the same name. */
assert(cached_sentry && is_cached_alloc);
head_dsentry = MAGIC_DSENTRY_NEXT(MAGIC_DSENTRY_FROM_SENTRY(cached_sentry));
assert(cached_parent_name && cached_name);
MAGIC_DSENTRY_ALIVE_NAME_ID_ITER(head_dsentry, prev_dsentry, dsentry, sentry, cached_parent_name, cached_name, cached_dsentry_site_id,
/* Cannot map multiple cached alloc sentries to a single local non-alloc sentry. */
return E2BIG;
);
/* Map a single cached alloc sentry to a single local non-alloc sentry. */
st_pair_by_sentry(cached_sentry, local_sentry, status_flags, status_op);
return OK;
}
else {
/* Unpair all the local alloc sentries. */
head_dsentry = MAGIC_DSENTRY_FROM_SENTRY(local_sentry);
assert(local_parent_name && local_name);
MAGIC_DSENTRY_ALIVE_NAME_ID_ITER(head_dsentry, prev_dsentry, dsentry, sentry, local_parent_name, local_name, local_dsentry_site_id,
st_pair_by_sentry(NULL, sentry, status_flags, status_op);
);
}
}
if (!cached_sentry) {
return OK;
}
/* Map a single cached non-alloc sentry to a local to-be-alloc sentry. */
if (!is_cached_alloc) {
assert(local_dsindex);
return st_pair_alloc_by_dsindex(info, cached_sentry, local_dsindex, num_elements, p_alloc_flags, status_flags, status_op);
}
/* Map all the cached alloc sentries to the corresponding local to-be-alloc sentries (or NULL). */
head_dsentry = MAGIC_DSENTRY_FROM_SENTRY(cached_sentry);
assert(cached_parent_name && cached_name);
MAGIC_DSENTRY_ALIVE_NAME_ID_ITER(head_dsentry, prev_dsentry, dsentry,
sentry, cached_parent_name, cached_name, cached_dsentry_site_id,
r = st_pair_alloc_by_dsindex(info, sentry, local_dsindex, num_elements, p_alloc_flags, status_flags, status_op);
if (r != OK) {
return r;
}
);
return OK;
}
/* Metadata transfer and adjustment functions */
PRIVATE int transfer_metadata_functions(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars,
struct _magic_vars_t *remote_magic_vars,
st_counterparts_t *counterparts)
{
int i;
struct _magic_function *cached_function;
/* transfer magic_functions */
MD_TRANSFER(info, remote_magic_vars->functions, (void **)&cached_magic_vars->functions, remote_magic_vars->functions_num * sizeof(struct _magic_function));
/* adjust magic_functions */
for (i = 0 ; i < cached_magic_vars->functions_num ; i++) {
cached_function = &cached_magic_vars->functions[i];
MD_TRANSFER_STR(info, &cached_function->name);
cached_function->type = &cached_magic_vars->types[cached_function->type - remote_magic_vars->types];
}
return OK;
}
PRIVATE int transfer_metadata_dfunctions(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars,
struct _magic_vars_t *remote_magic_vars,
st_counterparts_t *counterparts)
{
struct _magic_dfunction **dfunction_ptr;
struct _magic_dfunction *cached_dfunction, *prev_dfunction = NULL;
struct _magic_function *cached_function;
/* Transfer dfunctions. */
cached_magic_vars->first_dfunction = remote_magic_vars->first_dfunction;
dfunction_ptr = &cached_magic_vars->first_dfunction;
while (*dfunction_ptr != NULL) {
MD_TRANSFER(info, *dfunction_ptr, (void **)dfunction_ptr, sizeof(struct _magic_dfunction));
cached_dfunction = *dfunction_ptr;
/* Adjust dfunction parent_name and next/prev links. */
if (cached_dfunction->parent_name != NULL) {
MD_TRANSFER_STR(info, &cached_dfunction->parent_name);
if (strlen(cached_dfunction->parent_name) == 0) {
printf("ERROR. strlen(dfunction->parent_name) == 0.\n");
return EGENERIC;
}
} else {
printf("ERROR. dfunction->parent_name == NULL.\n");
return EGENERIC;
}
/* Adjust function name and type. */
cached_function = &cached_dfunction->function;
MD_TRANSFER_STR(info, &cached_function->name);
cached_function->type = &cached_magic_vars->types[cached_function->type - remote_magic_vars->types];
if (cached_dfunction->prev != NULL)
cached_dfunction->prev = prev_dfunction;
dfunction_ptr = &cached_dfunction->next;
prev_dfunction = cached_dfunction;
}
cached_magic_vars->last_dfunction = prev_dfunction;
return OK;
}
PUBLIC int st_transfer_metadata_types(st_init_info_t *info, struct _magic_vars_t *cached_magic_vars
, struct _magic_vars_t *remote_magic_vars, st_counterparts_t *counterparts)
{
int i;
/* transfer types */
MD_TRANSFER(info, remote_magic_vars->types, (void **)&cached_magic_vars->types, remote_magic_vars->types_num * sizeof(struct _magic_type));
/* type adjustments */
for (i = 0 ; i < cached_magic_vars->types_num ; i++) {
if (transfer_metadata_type_members(info, &cached_magic_vars->types[i], cached_magic_vars, remote_magic_vars)) {
printf("ERROR transferring type members metadata.\n");
return EGENERIC;
}
set_typename_key(&cached_magic_vars->types[i]);
}
return OK;
}
PRIVATE int transfer_metadata_type_value_set(st_init_info_t *info, struct _magic_type *type, struct _magic_vars_t *cached_magic_vars, struct _magic_vars_t *remote_magic_vars)
{
int num_elements;
/* MD_TRANSFER cannot be used, because it will allocate space for num_elements */
if (st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) type->value_set, sizeof(int), (uint32_t) &num_elements)) {
printf("ERROR transferring type value set metadata.\n");
return EGENERIC;
}
num_elements++;
MD_TRANSFER(info, type->value_set, (void **)&type->value_set, num_elements *sizeof(int));
return OK;
}
PRIVATE int transfer_metadata_type_members(st_init_info_t *info, struct _magic_type *type, struct _magic_vars_t *cached_magic_vars, struct _magic_vars_t *remote_magic_vars)
{
int r;
int num_child = MAGIC_TYPE_NUM_CONTAINED_TYPES(type), i;
MD_TRANSFER_STR(info, &type->name);
MD_TRANSFER_STR(info, &type->type_str);
if (type->names != NULL && type->num_names > 0) {
/* transfer array of name pointers */
MD_TRANSFER(info, type->names, (void **)&type->names, type->num_names * sizeof(char *));
for (i = 0 ; i < type->num_names ; i++) {
/* transfer individual name */
MD_TRANSFER_STR(info, &type->names[i]);
}
}
#define MD_TRANSFER_ADJUST_MEMBER_PTR(NUM_ELEMENTS, \
ELEMENT_SIZE,PTR_ARRAY,INDEX) \
if((NUM_ELEMENTS) > 0 && (PTR_ARRAY) != NULL) { \
MD_TRANSFER(info, PTR_ARRAY, (void **)&PTR_ARRAY, \
NUM_ELEMENTS * ELEMENT_SIZE); \
for(INDEX = 0 ; (INDEX) < (NUM_ELEMENTS) ; INDEX++) { \
PTR_ARRAY[INDEX] = ADJUST_POINTER(cached_magic_vars->types, \
remote_magic_vars->types, PTR_ARRAY[INDEX]); \
} \
}
MD_TRANSFER_ADJUST_MEMBER_PTR(
(type->type_id == MAGIC_TYPE_FUNCTION ? num_child + 1 : num_child),
sizeof(struct _magic_type *), type->contained_types, i
);
if (type->compatible_types) {
struct _magic_type *comp_types_element;
int comp_types_size=0;
/* determine size of array */
do {
if (st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) &type->compatible_types[comp_types_size]
, sizeof(struct _magic_type *), (uint32_t) &comp_types_element))
{
printf("ERROR transferring compatible types array metadata.\n");
return EGENERIC;
}
comp_types_size++;
} while(comp_types_element != NULL);
/* We know the size, now transfer the whole array */
MD_TRANSFER(info, type->compatible_types, (void **) &type->compatible_types, comp_types_size * sizeof(struct _magic_type *));
for (i = 0; i < comp_types_size; i++) {
if (type->compatible_types[i] != NULL) {
/* Adjust the pointer to point to the local counterpart */
type->compatible_types[i] = ADJUST_POINTER(cached_magic_vars->types, remote_magic_vars->types, type->compatible_types[i]);
}
}
}
if (num_child>0 && type->member_names != NULL) {
MD_TRANSFER(info, type->member_names, (void **)&type->member_names, num_child * sizeof(char *));
for (i = 0 ; i < num_child ; i++) {
MD_TRANSFER_STR(info, &type->member_names[i]);
}
}
if (num_child>0 && type->member_offsets != NULL) {
MD_TRANSFER(info, type->member_offsets, (void **)&type->member_offsets, num_child * sizeof(unsigned));
}
if (MAGIC_TYPE_HAS_VALUE_SET(type)) {
r = transfer_metadata_type_value_set(info, type, cached_magic_vars, remote_magic_vars);
if (r != OK) {
return r;
}
}
return OK;
}
PRIVATE int transfer_metadata_sentries(st_init_info_t *info, struct _magic_vars_t *cached_magic_vars
, struct _magic_vars_t *remote_magic_vars, st_counterparts_t *counterparts
, int *max_buff_sz)
{
int i;
int skipped_sentries = 0;
struct _magic_sentry *cached_sentry;
/* transfer sentries */
MD_TRANSFER(info, remote_magic_vars->sentries, (void **)&cached_magic_vars->sentries, remote_magic_vars->sentries_num * sizeof(struct _magic_sentry));
/* todo: try to use only remote_magic_vars or cached magic_vars */
/* todo: if transfer is complete, and argument 2 and 3 are always the same, remove 2nd argument */
/* adjust sentries */
for (i = 0 ; i < cached_magic_vars->sentries_num ; i++) {
cached_sentry = &cached_magic_vars->sentries[i];
if ((st_policies & ST_TRANSFER_DIRTY_ONLY) &&
!MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DIRTY_PAGE) &&
!MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_IMMUTABLE)) {
skipped_sentries++;
continue;
}
if (skipped_sentries > 0) {
cached_magic_vars->sentries[i - skipped_sentries] =
cached_magic_vars->sentries[i];
cached_magic_vars->sentries[i - skipped_sentries].id -=
skipped_sentries;
cached_sentry = &cached_magic_vars->sentries[i - skipped_sentries];
}
if (transfer_metadata_sentry_members(info, cached_sentry)) {
printf("ERROR transferring sentry members metadata.\n");
return EGENERIC;
}
/*
* We have to change the type to its cached counterpart,
* so that it may be compared to the local type of the local sentry counterpart.
*/
cached_sentry->type = &cached_magic_vars->types[cached_sentry->type - remote_magic_vars->types];
if (cached_sentry->type->size > *max_buff_sz) {
*max_buff_sz = cached_sentry->type->size;
}
}
if (skipped_sentries > 0)
cached_magic_vars->sentries_num -= skipped_sentries;
return OK;
}
PRIVATE int transfer_metadata_sentry_members(st_init_info_t *info, struct _magic_sentry *sentry)
{
if (sentry->name != NULL) {
MD_TRANSFER_STR(info, &sentry->name);
} else {
printf("ERROR. sentry->name == NULL.\n");
return EGENERIC;
}
return OK;
}
PUBLIC int st_transfer_metadata_dsentries(st_init_info_t *info, struct _magic_vars_t *cached_magic_vars
, struct _magic_vars_t *remote_magic_vars, st_counterparts_t *counterparts, int *max_buff_sz, int *dsentries_num)
{
struct _magic_dsentry **dsentry_ptr;
#if MAGIC_DSENTRY_ALLOW_PREV
struct _magic_dsentry *prev_dsentry = NULL;
#endif
int r;
*dsentries_num = 0;
cached_magic_vars->first_dsentry = remote_magic_vars->first_dsentry;
dsentry_ptr = &cached_magic_vars->first_dsentry;
while (*dsentry_ptr != NULL) {
struct _magic_dsentry *cached_dsentry, *remote_dsentry = *dsentry_ptr;
struct _magic_sentry *sentry;
/* transfer dsentry */
MD_TRANSFER(info, *dsentry_ptr, (void **) dsentry_ptr, sizeof(struct _magic_dsentry));
cached_dsentry = *dsentry_ptr;
if ((st_policies & ST_TRANSFER_DIRTY_ONLY) &&
!MAGIC_STATE_FLAG((&cached_dsentry->sentry), MAGIC_STATE_DIRTY_PAGE) &&
!MAGIC_STATE_FLAG((&cached_dsentry->sentry), MAGIC_STATE_IMMUTABLE)) {
*dsentry_ptr = cached_dsentry->next;
continue;
}
if (cached_magic_vars->first_stack_dsentry == remote_dsentry) {
cached_magic_vars->first_stack_dsentry = cached_dsentry;
} else if(cached_magic_vars->last_stack_dsentry == remote_dsentry) {
cached_magic_vars->last_stack_dsentry = cached_dsentry;
}
/* adjust dsentry */
if (cached_dsentry->parent_name != NULL) {
MD_TRANSFER_STR(info, &cached_dsentry->parent_name);
if (strlen(cached_dsentry->parent_name) == 0) {
printf("ERROR. strlen(dsentry->parent_name) == 0.\n");
#if TODO_DSENTRY_PARENT_NAME_BUG
if (cached_dsentry->next != NULL)
#endif
return EGENERIC;
}
} else {
printf("ERROR. dsentry->parent_name == NULL.\n");
return EGENERIC;
}
sentry = &cached_dsentry->sentry;
if (transfer_metadata_sentry_members(info, sentry)) {
printf("ERROR transferring sentry members metadata.\n");
return EGENERIC;
}
/* Override original id to simplify pairing later. */
sentry->id = cached_magic_vars->sentries_num + *dsentries_num + 1;
/*
* Report violations for all the pointers pointing to the initial stack area.
* This is to make sure no assumption is incorrectly made about this area.
*/
if (!strcmp(sentry->name, MAGIC_ALLOC_INITIAL_STACK_NAME)) {
sentry->flags |= MAGIC_STATE_ADDR_NOT_TAKEN;
}
/*
* Adjust the type, so that the local and remote type can be compared
* during a server version update
*/
if (sentry->type == &remote_dsentry->type) {
/*
* sentry->type is contained in dsentry.type. Therefore, this is an
* array type. In order to allocate a new memory region, we only
* need the size of the type, and the contained type as arguments
* to the magic allocation function. Therefore, other members of
* the type do need to be cached or adjusted.
*/
/* Adjust pointer to cached location */
sentry->type = &cached_dsentry->type;
/* Adjust contained_types to type_array located in dsentry struct. */
sentry->type->contained_types = cached_dsentry->type_array;
/*
* Adjust only pointer in type_array. It currently has the same
* value as the remote copy, but it has to point to the cached
* of the contained type.
*/
sentry->type->contained_types[0] = &cached_magic_vars->types[sentry->type->contained_types[0] - remote_magic_vars->types];
/* Adjust empty strings. */
sentry->type->name = "";
sentry->type->type_str = "";
/* Adjust value set if necessary. */
if (MAGIC_TYPE_HAS_VALUE_SET(sentry->type)) {
r = transfer_metadata_type_value_set(info, sentry->type, cached_magic_vars, remote_magic_vars);
if (r != OK) {
return r;
}
}
} else {
/*
* sentry.type must be in the global type array. Adjust pointer accordingly.
* The pointer is still pointing to the remote version.
* We have to change it to the cached version.
*/
sentry->type = &cached_magic_vars->types[sentry->type - remote_magic_vars->types];
}
/* see if the buffer needs to be bigger for the dsentry data region. */
if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_OUT_OF_BAND) && *max_buff_sz < sentry->type->size) {
*max_buff_sz = sentry->type->size;
}
dsentry_ptr = &cached_dsentry->next;
#if MAGIC_DSENTRY_ALLOW_PREV
if (cached_dsentry->prev != NULL)
cached_dsentry->prev = prev_dsentry;
prev_dsentry = cached_dsentry;
#endif
*dsentries_num = *dsentries_num + 1;
}
return OK;
}
PRIVATE int pair_metadata_types(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars, st_counterparts_t *counterparts, int allow_unpaired_types)
{
int i, j, num_unpaired_struct_types = 0;
int num_unpaired_types = 0;
int num_total_types = 0;
int num_struct_types = 0;
int num_unpaired_types_left, num_unpaired_struct_types_left;
if (st_unpaired_types_ratio > 0 || st_unpaired_struct_types_ratio > 0) {
for (i = 0 ; i < cached_magic_vars->types_num ; i++) {
struct _magic_type *type = &cached_magic_vars->types[i];
if (ST_IS_UNPAIRABLE_STRUCT_TYPE(type)) {
num_struct_types++;
}
if (ST_IS_UNPAIRABLE_TYPE(type)) {
num_total_types++;
}
}
num_unpaired_types = (int) (st_unpaired_types_ratio*num_total_types);
num_unpaired_struct_types = (int) (st_unpaired_struct_types_ratio*num_struct_types);
}
num_unpaired_types_left = num_unpaired_types;
num_unpaired_struct_types_left = num_unpaired_struct_types;
/* type pairing, remote->local */
for(i = 0 ; i < cached_magic_vars->types_num ; i++) {
struct _magic_type *type = &cached_magic_vars->types[i];
counterparts->types[i].counterpart = NULL;
if (num_unpaired_types_left > 0 && ST_IS_UNPAIRABLE_TYPE(type)) {
num_unpaired_types_left--;
continue;
}
else if (num_unpaired_struct_types_left > 0 && ST_IS_UNPAIRABLE_STRUCT_TYPE(type)) {
num_unpaired_struct_types_left--;
continue;
}
for (j = 0 ; j < _magic_types_num ; j++) {
/* A remote type may be paired to multiple local types.
* It is safe to index only the first type since counterparts
* are only used to speed up type matching.
*/
if (magic_type_compatible(type, &_magic_types[j], MAGIC_TYPE_COMPARE_ALL)) {
counterparts->types[i].counterpart = &_magic_types[j];
break;
}
}
if (!allow_unpaired_types && counterparts->types[i].counterpart == NULL) {
printf("ERROR, remote type cannot be paired with a local type: ");
MAGIC_TYPE_PRINT(type, MAGIC_EXPAND_TYPE_STR);
printf("\n");
return EGENERIC;
}
}
if (st_unpaired_types_ratio > 0 || st_unpaired_struct_types_ratio > 0) {
assert(num_unpaired_types_left == 0 && (st_unpaired_types_ratio > 0 || num_unpaired_struct_types == 0));
_magic_printf("Unpaired types stats: unpaired types: %d, total types: %d, unpaired struct types: %d, struct types: %d\n", num_unpaired_types, num_total_types, num_unpaired_struct_types, num_struct_types);
}
for (i = 0 ; i < cached_magic_vars->types_num ; i++) {
struct _magic_type *type = &cached_magic_vars->types[i];
struct _magic_type *local_type = (struct _magic_type*) counterparts->types[i].counterpart;
counterparts->ptr_types[i].counterpart = NULL;
if (local_type && type->type_id == MAGIC_TYPE_POINTER) {
if (MAGIC_TYPE_HAS_COMP_TYPES(type) != MAGIC_TYPE_HAS_COMP_TYPES(local_type)) {
continue;
}
if (MAGIC_TYPE_HAS_COMP_TYPES(type)) {
j = 0;
while (MAGIC_TYPE_HAS_COMP_TYPE(type, j) && MAGIC_TYPE_HAS_COMP_TYPE(local_type, j)) {
struct _magic_type *ctype = MAGIC_TYPE_COMP_TYPE(type, j);
struct _magic_type *local_ctype = MAGIC_TYPE_COMP_TYPE(local_type, j);
if (!ST_TYPE_IS_CACHED_COUNTERPART(ctype, local_ctype)) {
break;
}
j++;
}
if (MAGIC_TYPE_HAS_COMP_TYPE(type, j) || MAGIC_TYPE_HAS_COMP_TYPE(local_type, j)) {
continue;
}
}
counterparts->ptr_types[i].counterpart = local_type;
}
}
return OK;
}
PRIVATE int pair_metadata_functions(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars, st_counterparts_t *counterparts)
{
int i;
struct _magic_function *cached_function, *local_function;
#if ST_DEBUG_LEVEL > 0
int num_relocated = 0;
#endif
/* map remote functions to local functions */
for(i = 0 ; i < cached_magic_vars->functions_num ; i++) {
cached_function = &cached_magic_vars->functions[i];
local_function = NULL;
st_map_functions(&cached_function, &local_function);
ST_SET_CACHED_COUNTERPART(cached_function, functions, functions, local_function);
#if CHECK_SENTITY_PAIRS
if (local_function) {
/* debug: see if the function is paired more than once */
struct _magic_function *cfunction = NULL;
st_map_functions(&cfunction, &local_function);
if (cfunction != cached_function) {
printf("function pairing failed for (1) local function linked to multiple remote functions (2), (3)\n");
printf("(1) "); MAGIC_FUNCTION_PRINT(local_function, 0); printf("\n");
printf("(2) "); MAGIC_FUNCTION_PRINT(cached_function, 0); printf("\n");
printf("(3) "); MAGIC_FUNCTION_PRINT(cfunction, 0); printf("\n");
return EGENERIC;
}
}
#endif
#if ST_DEBUG_LEVEL > 0
if (local_function && cached_function->address != local_function->address) {
num_relocated++;
if (ST_DEBUG_LEVEL > 1) {
printf("- relocated function: '%s'\n", cached_magic_vars->functions[i].name);
}
}
#endif
}
#if ST_DEBUG_LEVEL > 0
printf("total remote functions: %d. relocated: %d\n", cached_magic_vars->functions_num, num_relocated);
#endif
return OK;
}
PRIVATE int pair_metadata_sentries(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars, st_counterparts_t *counterparts)
{
int i, r;
struct _magic_sentry *cached_sentry, *local_sentry;
#if ST_DEBUG_LEVEL > 0
int num_relocated_str = 0, num_relocated_normal = 0;
#endif
/* pair sentries remote->local */
for (i = 0 ; i < cached_magic_vars->sentries_num ; i++) {
void *local_data_addr = NULL;
cached_sentry = &cached_magic_vars->sentries[i];
/* String data is transferred directly. */
if (MAGIC_SENTRY_IS_STRING(cached_sentry)) {
char *string = st_buff_allocate(info, cached_sentry->type->size);
if (!string) {
printf("ERROR allocating string.\n");
return EGENERIC;
}
r = st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) cached_sentry->address,
cached_sentry->type->size, (uint32_t) string);
if(r != OK) {
printf("ERROR transferring string.\n");
return EGENERIC;
}
local_data_addr = string;
}
ST_SET_CACHED_COUNTERPART(cached_sentry, sentries, sentries_data, local_data_addr);
local_sentry = NULL;
st_map_sentries(&cached_sentry, &local_sentry);
ST_SET_CACHED_COUNTERPART(cached_sentry, sentries, sentries, local_sentry);
#if CHECK_SENTITY_PAIRS
if (local_sentry && !MAGIC_SENTRY_IS_STRING(cached_sentry)) {
/* debug: see if the non-string sentry is paired more than once */
struct _magic_sentry *csentry = NULL;
st_map_sentries(&csentry, &local_sentry);
if (csentry != cached_sentry) {
printf("sentry pairing failed for (1) local sentry linked to multiple remote sentries (2), (3)\n");
printf("(1) "); MAGIC_SENTRY_PRINT(local_sentry, 0); printf("\n");
printf("(2) "); MAGIC_SENTRY_PRINT(cached_sentry, 0); printf("\n");
printf("(3) "); MAGIC_SENTRY_PRINT(csentry, 0); printf("\n");
return EGENERIC;
}
}
#endif
#if ST_DEBUG_LEVEL > 0
if (local_sentry && cached_sentry->address != local_sentry->address) {
if (MAGIC_SENTRY_IS_STRING(cached_sentry)) {
num_relocated_str++;
}
else {
num_relocated_normal++;
if (ST_DEBUG_LEVEL > 1) {
printf("- relocated non-string sentry: '%s'\n", cached_sentry->name);
}
}
}
#endif
}
#if ST_DEBUG_LEVEL > 0
printf("total remote sentries: %d. relocated normal: %d relocated string: %d\n", cached_magic_vars->sentries_num, num_relocated_normal, num_relocated_str);
#endif
return OK;
}
#if ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
PRIVATE int allocate_pair_metadata_dsentries_from_raw_copy(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars, st_counterparts_t *counterparts)
{
struct _magic_dsentry *dsentry;
int remote_dsentries = 0, unpaired_dsentries = 0;
#if ST_DEBUG_LEVEL > 3
EXEC_WITH_MAGIC_VARS(
magic_print_dsentries();
, &st_cached_magic_vars
);
magic_print_dsentries();
#endif
dsentry = cached_magic_vars->first_dsentry;
while (dsentry != NULL) {
struct _magic_sentry *local_sentry = NULL, *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
/* Initialize counterpart to NULL. */
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, NULL);
remote_dsentries++;
if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_STACK) && !MAGIC_STATE_FLAG(sentry, MAGIC_STATE_LIB)) {
local_sentry = MAGIC_DSENTRY_TO_SENTRY((struct _magic_dsentry *)MAGIC_PTR_FROM_DATA(sentry->address));
} else {
#if MAGIC_LOOKUP_SENTRY_ALLOW_RANGE_INDEX
EXEC_WITH_MAGIC_VARS(
local_sentry = magic_sentry_lookup_by_range(sentry->address, NULL);
, &st_cached_magic_vars
);
#else
local_sentry = magic_sentry_lookup_by_addr(sentry->address, NULL);
#endif
}
if (!local_sentry) {
unpaired_dsentries++;
#if ST_DEBUG_LEVEL > 2
printf("allocate_pair_metadata_dsentries_from_raw_copy: found unpaired "); MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR); _magic_printf("\n");
#endif
}
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, local_sentry);
dsentry = dsentry->next;
}
#if ST_DEBUG_LEVEL > 0
printf("total remote dsentries: %d (%d unpaired)\n", remote_dsentries, unpaired_dsentries);
#endif
return OK;
}
#else
PRIVATE int allocate_pair_metadata_dsentries(st_init_info_t *info,
struct _magic_vars_t *cached_magic_vars, st_counterparts_t *counterparts)
{
struct _magic_dsentry *dsentry = cached_magic_vars->first_dsentry, *local_dsentry;
int remote_dsentries = 0;
#ifndef __MINIX
int *local_sentry_paired_by_id = st_buff_allocate(info, (_magic_sentries_next_id + 1) * sizeof(int));
#endif
#if ST_DEBUG_LEVEL > 3
EXEC_WITH_MAGIC_VARS(
magic_print_dsentries();
, &st_cached_magic_vars
);
magic_print_dsentries();
#endif
#ifdef __MINIX
/*
* Since on MINIX the mmaped regions are inherited in the new process,
* we must first deallocate them. This is not the case on Linux.
*/
while (dsentry != NULL) {
int res = 0;
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
int size = sentry->type->size;
/* For mmap first unmap the old region that is already mapped into this new instance */
if (!MAGIC_STATE_FLAG(sentry, MAGIC_STATE_OUT_OF_BAND)
&& MAGIC_STATE_REGION(sentry) == MAGIC_STATE_MAP
&& !USE_PRE_ALLOCATED_BUFFER(info)
)
{
/*
* The 'ext' field in the dsentry is used here to record
* the padding for ASR.
*/
size_t padding = (size_t) dsentry->ext;
/*
* call munmap(). ptr and size have to be altered,
* in order to free the preceding page, containing the dsentry struct, too.
*/
MAGIC_MEM_WRAPPER_BLOCK(
res = munmap((char *)sentry->address - magic_get_sys_pagesize(), size + magic_get_sys_pagesize() + padding);
);
if (res != 0) {
printf("ERROR, munmap returned NULL.\n");
return EGENERIC;
}
}
dsentry = dsentry->next;
}
#endif
/* Permute dsentries in case of ASR. */
if (info->flags & ST_LU_ASR) {
magic_asr_permute_dsentries(&cached_magic_vars->first_dsentry);
}
dsentry = cached_magic_vars->first_dsentry;
while (dsentry != NULL) {
struct _magic_sentry *local_sentry, *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
int is_alloc_dsentry = MAGIC_SENTRY_IS_ALLOC(sentry);
int res = 0;
struct _magic_dsindex *local_dsindex;
remote_dsentries++;
#ifdef __MINIX
/* Cannot deal with dead dsentries. */
assert(dsentry->magic_state == MAGIC_DSENTRY_MSTATE_ALIVE);
#else
/*
* If there are dead dsentries, we simply skip them.
*/
if (dsentry->magic_state != MAGIC_DSENTRY_MSTATE_ALIVE) {
dsentry = dsentry->next;
continue;
}
#endif
/* Initialize counterpart to NULL. */
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, NULL);
/* Handle non-alloc dsentries first. */
if (!is_alloc_dsentry) {
local_sentry = magic_sentry_lookup_by_name(dsentry->parent_name,
sentry->name, dsentry->site_id, NULL);
if (local_sentry) {
assert(!MAGIC_SENTRY_IS_ALLOC(local_sentry));
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, local_sentry);
}
dsentry = dsentry->next;
continue;
}
/* Out-of-band alloc dsentries next. */
if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_OUT_OF_BAND)) {
struct _magic_type *type;
/* We can only handle obdsentries with the magic void type, transferred as-is. */
if (sentry->type != &dsentry->type) {
/* Not an array type */
type = sentry->type;
} else {
/* This is an array type, use its contained type instead. */
type = sentry->type->contained_types[0];
}
/* We now have the cached version of the type. Compare it to magic void type */
if (!magic_type_compatible(type, MAGIC_VOID_TYPE, MAGIC_TYPE_COMPARE_ALL)) {
printf("Can't handle obdsentry with non-void type\n");
return EGENERIC;
}
#ifdef __MINIX
/* On MINIX we need to recreate all the obdsentries. */
struct _magic_obdsentry *obdsentry;
int size = sentry->type->size;
obdsentry = magic_create_obdsentry(sentry->address,
MAGIC_VOID_TYPE, size, MAGIC_STATE_REGION(sentry), sentry->name, dsentry->parent_name);
if (obdsentry == NULL) {
printf("ERROR, magic_create_obdsentry returned NULL.\n");
return EGENERIC;
}
local_dsentry = MAGIC_OBDSENTRY_TO_DSENTRY(obdsentry);
#else
/* On Linux we only need to pair them. */
local_sentry = magic_sentry_lookup_by_name(
MAGIC_SENTRY_PARENT(sentry), sentry->name,
MAGIC_SENTRY_SITE_ID(sentry), NULL);
if (local_sentry == NULL) {
printf("Unable to pair obdsentry.\n");
return EGENERIC;
}
local_dsentry = MAGIC_DSENTRY_FROM_SENTRY(local_sentry);
#endif
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, MAGIC_DSENTRY_TO_SENTRY(local_dsentry));
dsentry = dsentry->next;
continue;
}
/* Handle regular alloc dsentries last. */
#ifndef __MINIX
/*
* For Linux, first pair INIT time remote
* dsentries with local dsentries.
*/
if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_INIT)) {
local_sentry = NULL;
if (MAGIC_STATE_FLAG(sentry, MAGIC_STATE_IMMUTABLE)) {
/*
* Immutable init time dsentries should have already been
* preallocated, so just pair them by address.
*/
local_sentry = magic_sentry_lookup_by_addr(sentry->address, NULL);
} else {
#if MAGIC_LOOKUP_SENTRY_ALLOW_NAME_HASH
struct _magic_sentry_list *local_sentry_list;
local_sentry_list = magic_sentry_list_lookup_by_name_hash(
dsentry->parent_name, sentry->name, dsentry->site_id, NULL);
while (local_sentry_list) {
if (!local_sentry_paired_by_id[local_sentry_list->sentry->id]) {
local_sentry = local_sentry_list->sentry;
break;
}
local_sentry_list = local_sentry_list->next;
}
#else
do {
struct _magic_dsentry *prev_dsentry, *tmp_dsentry;
struct _magic_sentry *tmp_sentry;
MAGIC_DSENTRY_LOCK();
MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry,
tmp_dsentry, tmp_sentry,
if (!strcmp(tmp_sentry->name, sentry->name)) {
if (!dsentry->parent_name ||
!strcmp(MAGIC_SENTRY_PARENT(tmp_sentry), dsentry->parent_name)) {
if (dsentry->site_id == MAGIC_DSENTRY_SITE_ID_NULL ||
tmp_dsentry->site_id == dsentry->site_id) {
if (!local_sentry_paired_by_id[tmp_sentry->id]) {
local_sentry = tmp_sentry;
break;
}
}
}
}
);
MAGIC_DSENTRY_UNLOCK();
} while (0);
#endif
}
if (local_sentry) {
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, local_sentry);
local_sentry_paired_by_id[local_sentry->id] = 1;
dsentry = dsentry->next;
continue;
}
}
#endif
/*
* Just recreate all the other dsentries. Immutable objects will
* have already been inherited and allocate_local_dsentry() will
* not reallocate them, but instead it will just create a new
* local dsentry in the right place.
*/
local_dsindex = magic_dsindex_lookup_by_name(dsentry->parent_name, sentry->name);
if (local_dsindex || MAGIC_SENTRY_IS_LIB_ALLOC(sentry)) {
/* Allocate a new local dsentry and pair it with the remote. */
res = allocate_local_dsentry(info, local_dsindex, 0, 0, NULL, &local_dsentry, dsentry, NULL);
if (res != ENOSYS) {
if (res != OK) {
return res;
}
assert(local_dsentry);
ST_SET_CACHED_COUNTERPART(sentry, sentries, sentries, MAGIC_DSENTRY_TO_SENTRY(local_dsentry));
}
}
dsentry = dsentry->next;
}
#if ST_DEBUG_LEVEL > 0
printf("total remote dsentries: %d\n", remote_dsentries);
#endif
return OK;
}
PRIVATE int deallocate_nonxferred_dsentries(struct _magic_dsentry *first_dsentry, st_counterparts_t *counterparts)
{
struct _magic_dsentry *dsentry = first_dsentry;
struct _magic_sentry *local_sentry;
while (dsentry != NULL) {
struct _magic_sentry *sentry = MAGIC_DSENTRY_TO_SENTRY(dsentry);
int is_paired_dsentry = ST_HAS_CACHED_COUNTERPART(sentry, sentries);
int is_alloc_dsentry = MAGIC_SENTRY_IS_ALLOC(sentry);
ST_GET_CACHED_COUNTERPART(sentry, sentries, sentries, local_sentry);
if (MAGIC_STATE_EXTF_GET(sentry, ST_TRANSFER_DONE) || !is_alloc_dsentry) {
dsentry = dsentry->next;
continue;
}
/* Report non-transferred alloc dsentries when requested. */
if (is_paired_dsentry && (st_policies & ST_REPORT_NONXFERRED_ALLOCS)) {
printf("deallocate_nonxferred_dsentries: Non-transferred dsentry found: ");
MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR);
printf("\n");
}
if (!is_paired_dsentry && (st_policies & ST_REPORT_NONXFERRED_UNPAIRED_ALLOCS)) {
printf("deallocate_nonxferred_dsentries: Non-transferred unpaired dsentry found: ");
MAGIC_DSENTRY_PRINT(dsentry, MAGIC_EXPAND_TYPE_STR);
printf("\n");
}
if (!is_paired_dsentry) {
dsentry = dsentry->next;
continue;
}
assert(local_sentry);
if (MAGIC_SENTRY_IS_ALLOC(local_sentry)) {
deallocate_local_dsentry(MAGIC_DSENTRY_FROM_SENTRY(local_sentry));
}
dsentry = dsentry->next;
}
return OK;
}
#endif
PRIVATE void deallocate_local_dsentry(struct _magic_dsentry *local_dsentry)
{
int r, dsentry_type;
struct _magic_sentry *local_sentry = MAGIC_DSENTRY_TO_SENTRY(local_dsentry);
assert(MAGIC_SENTRY_IS_ALLOC(local_sentry));
dsentry_type = MAGIC_STATE_FLAG(local_sentry, MAGIC_STATE_OUT_OF_BAND) ? MAGIC_STATE_OUT_OF_BAND : MAGIC_STATE_REGION(local_sentry);
/* A MAP_SHARED region will have both MAGIC_STATE_MAP and MAGIC_STATE_SHM. */
if (dsentry_type == (MAGIC_STATE_MAP | MAGIC_STATE_SHM))
dsentry_type = MAGIC_STATE_MAP;
MAGIC_MEM_WRAPPER_BEGIN();
switch (dsentry_type) {
case MAGIC_STATE_HEAP:
/* free */
magic_free(local_sentry->address);
break;
case MAGIC_STATE_MAP:
/* munmap */
r = magic_munmap(local_sentry->address, local_sentry->type->size);
if (r != 0) {
printf("Warning: magic_munmap failed for ");
MAGIC_DSENTRY_PRINT(local_dsentry, 0);
printf("\n");
}
break;
#ifndef __MINIX
case MAGIC_STATE_SHM:
/* shmdt */
r = magic_shmdt(local_sentry->address);
if (r != 0) {
printf("Warning: magic_shmdt failed for ");
MAGIC_DSENTRY_PRINT(local_dsentry, 0);
printf("\n");
}
break;
#endif
case MAGIC_STATE_OUT_OF_BAND:
/* out-of-band dsentry. */
r = magic_destroy_obdsentry_by_addr(local_sentry->address);
if (r != 0) {
printf("Warning: magic_destroy_obdsentry_by_addr failed for ");
MAGIC_DSENTRY_PRINT(local_dsentry, 0);
printf("\n");
}
break;
default:
st_cbs_os.panic("ERROR. UNSUPPORTED DSENTRY TYPE: %d\n", dsentry_type);
}
MAGIC_MEM_WRAPPER_END();
}
PRIVATE int allocate_local_dsentry(st_init_info_t *info, struct _magic_dsindex *local_dsindex, int num_elements, int is_type_mismatch, const union __alloc_flags *p_alloc_flags, struct _magic_dsentry** local_dsentry_ptr, struct _magic_dsentry *cached_dsentry, void *ptr)
{
struct _magic_dsentry *local_dsentry = NULL;
struct _magic_sentry *cached_sentry = NULL;
char *name, *parent_name;
struct _magic_type *type;
int region;
size_t size;
union __alloc_flags alloc_flags;
/* Either a dsindex or a dsentry needs to be set. */
assert(local_dsindex || cached_dsentry);
if (cached_dsentry)
cached_sentry = MAGIC_DSENTRY_TO_SENTRY(cached_dsentry);
/* name, parent_name: local_dsindex || cached_dsentry. */
if (local_dsindex) {
assert(MAGIC_DSINDEX_IS_ALLOC(local_dsindex));
name = local_dsindex->name;
parent_name = local_dsindex->parent_name;
} else {
assert(MAGIC_SENTRY_IS_ALLOC(cached_sentry));
/*
* The external allocation parent_name needs to be readjusted.
* The external allocation name is adjusted after the new dsentry
* is created.
*/
name = cached_sentry->name;
if (!strcmp(cached_dsentry->parent_name, MAGIC_ALLOC_EXT_PARENT_NAME)) {
parent_name = MAGIC_ALLOC_EXT_PARENT_NAME;
} else {
int found_parent_name = 0;
struct _magic_sodesc *sodesc;
struct _magic_dsodesc *dsodesc;
MAGIC_DSODESC_LOCK();
MAGIC_SODESC_ITER(_magic_first_sodesc, sodesc,
if (!strcmp(cached_dsentry->parent_name, sodesc->lib.name)) {
parent_name = (char *)sodesc->lib.name;
found_parent_name = 1;
break;
}
);
if (!found_parent_name) {
MAGIC_DSODESC_ITER(_magic_first_dsodesc, dsodesc,
if (!strcmp(cached_dsentry->parent_name, dsodesc->lib.name)) {
parent_name = (char *)dsodesc->lib.name;
found_parent_name = 1;
break;
}
);
}
MAGIC_DSODESC_UNLOCK();
assert(found_parent_name && "Invalid parent name for cached dsentry!");
}
}
/* num_elements: args || cached_sentry. */
if (num_elements <= 0 && cached_sentry) {
num_elements = cached_sentry->type->type_id == MAGIC_TYPE_ARRAY ?
cached_sentry->type->num_child_types : 1;
}
assert(num_elements > 0);
/* alloc_flags: args || cached_dsentry. */
if (!p_alloc_flags) {
if (cached_dsentry && MAGIC_SENTRY_IS_ALLOC(cached_sentry)) {
alloc_flags = cached_dsentry->alloc_flags;
}
} else {
alloc_flags = *p_alloc_flags;
}
/* is_type_mismatch: args || cached_dsentry. */
if (!is_type_mismatch && cached_dsentry)
is_type_mismatch = MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_TYPE_SIZE_MISMATCH);
/*
* Use old address for immutable objects.
*/
/* ptr: args || cached_sentry. */
if (!ptr && cached_sentry &&
MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_IMMUTABLE))
ptr = cached_sentry->address;
/* region: local_dsindex || cached_sentry. */
if (local_dsindex)
region = MAGIC_STATE_REGION(local_dsindex);
else
region = MAGIC_STATE_REGION(cached_sentry);
/* Check if the region is ambigous. This shouldn't happen. */
assert(!((region & (MAGIC_STATE_HEAP | MAGIC_STATE_MAP)) ==
(MAGIC_STATE_HEAP | MAGIC_STATE_MAP)) &&
"MAGIC_STATE_HEAP | MAGIC_STATE_MAP detected!");
#if 0
if ((region & (MAGIC_STATE_HEAP | MAGIC_STATE_MAP)) ==
(MAGIC_STATE_HEAP | MAGIC_STATE_MAP)) {
/* Check call flags to determine what to do in the ambiguous cases. */
region = (alloc_flags.mmap_flags && alloc_flags.mmap_prot) ?
MAGIC_STATE_MAP : MAGIC_STATE_HEAP;
}
#endif
/* type: local_dsindex || cached_sentry. */
if (local_dsindex) {
type = local_dsindex->type;
if (num_elements > 1 && MAGIC_TYPE_FLAG(local_dsindex->type, MAGIC_TYPE_VARSIZE)) {
size = magic_type_alloc_get_varsized_array_size(local_dsindex->type, num_elements);
assert(size > 0);
} else {
if (is_type_mismatch) {
type = MAGIC_VOID_TYPE;
printf("WARNING: Type size mismatch dsentry detected! Ignoring dsindex type and reverting to MAGIC_TYPE_VOID.\n");
printf("name=%s, parent_name=%s\n", local_dsindex->name, local_dsindex->parent_name);
}
size = num_elements * type->size;
}
} else {
/*
* The type will need adjusting later.
*/
type = cached_sentry->type;
size = type->size;
}
*local_dsentry_ptr = NULL;
if (region & MAGIC_STATE_HEAP) {
/* malloc */
ptr = magic_malloc_positioned(type, name, parent_name, size, (ptr == NULL ? NULL : MAGIC_PTR_FROM_DATA(ptr)));
if (ptr == NULL) {
printf("ERROR, magic_malloc_positioned returned NULL.\n");
return ENOMEM;
}
memset(ptr, 0, size);
local_dsentry = MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(ptr));
}
else if (region & MAGIC_STATE_MAP) {
/* mmap */
if (!alloc_flags.mmap_flags || !alloc_flags.mmap_prot) {
/* We need call_flags to perform mmap. */
return ENOSYS;
}
ptr = persistent_mmap(type, name, parent_name, info, NULL, size,
alloc_flags.mmap_prot, alloc_flags.mmap_flags, -1, 0, ptr);
if (ptr == NULL) {
printf("ERROR, persistent_mmap returned NULL.\n");
return ENOMEM;
}
if (!(alloc_flags.mmap_flags & MAP_SHARED))
memset(ptr, 0, size);
local_dsentry = MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(ptr));
}
#ifndef __MINIX
else if (region & MAGIC_STATE_SHM) {
/* shmat */
if (!alloc_flags.shmat_flags || !alloc_flags.shmat_shmid) {
/* We need call_flags to perform shmat. */
return ENOSYS;
}
ptr = magic_shmat(type, name, parent_name, alloc_flags.shmat_shmid,
ptr, alloc_flags.shmat_flags);
if (ptr == NULL) {
printf("ERROR, magic_shmat returned NULL.\n");
return ENOMEM;
}
local_dsentry = MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(ptr));
}
#endif
else {
if (local_dsindex) {
printf("ERROR. UNSUPPORTED DSINDEX TYPE: ");
MAGIC_DSINDEX_PRINT(local_dsindex, MAGIC_EXPAND_TYPE_STR);
} else {
printf("ERROR. UNSUPPORTED DSENTRY: ");
MAGIC_DSENTRY_PRINT(cached_dsentry, MAGIC_EXPAND_TYPE_STR);
}
printf("\n");
return EINVAL;
}
if (!local_dsindex) {
/*
* This was an externally allocated type and, as such, needs adjusting.
*/
assert(cached_sentry->type == &cached_dsentry->type);
local_dsentry->type = cached_dsentry->type;
if (cached_dsentry->type_array[0]->type_id == MAGIC_TYPE_POINTER) {
ST_GET_CACHED_COUNTERPART(cached_dsentry->type_array[0], types, ptr_types, local_dsentry->type_array[0]);
} else {
ST_GET_CACHED_COUNTERPART(cached_dsentry->type_array[0], types, types, local_dsentry->type_array[0]);
}
local_dsentry->sentry.type = &local_dsentry->type;
local_dsentry->sentry.type->contained_types = local_dsentry->type_array;
}
assert(local_dsentry);
assert(local_dsentry->parent_name && strcmp(local_dsentry->parent_name, ""));
assert(local_dsentry->sentry.name && strcmp(local_dsentry->sentry.name, ""));
assert(magic_check_dsentry(local_dsentry, 0));
*local_dsentry_ptr = local_dsentry;
if (is_type_mismatch)
local_dsentry->sentry.flags |= MAGIC_STATE_TYPE_SIZE_MISMATCH;
/*
* Dsentries allocated by shared libraries have the names stored in dsentry
* buffers (for now).
* Readjust the local_sentry to do this as well, since after state transfer
* cleanup the existing names will become invalid.
*/
if (!local_dsindex && MAGIC_SENTRY_IS_LIB_ALLOC(cached_sentry)) {
strncpy(local_dsentry->name_ext_buff, local_dsentry->sentry.name,
MAGIC_DSENTRY_EXT_NAME_BUFF_SIZE);
local_dsentry->sentry.name = local_dsentry->name_ext_buff;
}
return OK;
}
PRIVATE int check_unpaired_sentry(st_init_info_t *info,
struct _magic_sentry* cached_sentry)
{
int sentry_needs_transfer = MAGIC_STATE_EXTF_GET(cached_sentry, ST_NEEDS_TRANSFER | ST_TRANSFER_DONE) == ST_NEEDS_TRANSFER;
int report;
if (!sentry_needs_transfer && !MAGIC_SENTRY_IS_STRING(cached_sentry)) {
return OK;
}
if (MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC)) {
report = st_policies & ST_REPORT_UNPAIRED_DSENTRIES;
}
else if(MAGIC_SENTRY_IS_STRING(cached_sentry)) {
report = st_policies & ST_REPORT_UNPAIRED_STRINGS;
}
else {
report = st_policies & ST_REPORT_UNPAIRED_SENTRIES;
}
if (report) {
printf("check_unpaired_sentry: Unpaired sentry found: ");
ST_SENTRY_PRINT(cached_sentry,MAGIC_EXPAND_TYPE_STR);
printf("\n");
}
return OK;
}
PUBLIC struct _magic_sentry* st_cached_to_remote_sentry(st_init_info_t *info, struct _magic_sentry *cached_sentry)
{
struct _magic_sentry *remote_sentry;
void *local_data_addr;
ST_CHECK_INIT();
/* Copy metadata into metadata buffer. */
if (MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_DYNAMIC)) {
magic_copy_dsentry(MAGIC_DSENTRY_FROM_SENTRY(cached_sentry), st_dsentry_buff);
remote_sentry = MAGIC_DSENTRY_TO_SENTRY(st_dsentry_buff);
}
else {
memcpy(&st_dsentry_buff->sentry, cached_sentry, sizeof(struct _magic_sentry));
remote_sentry = &st_dsentry_buff->sentry;
}
/* Have the remote sentry point to local data. */
local_data_addr = NULL;
/* See if we have the data locally already first. */
ST_GET_CACHED_COUNTERPART(cached_sentry, sentries, sentries_data, local_data_addr);
if (!local_data_addr) {
/* Copy remote data into local data buffer. */
if (st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) remote_sentry->address
, remote_sentry->type->size, (uint32_t) st_data_buff))
{
printf("ERROR transferring sentry data to local buffer.\n");
return NULL;
}
local_data_addr = st_data_buff;
}
remote_sentry->address = local_data_addr;
return remote_sentry;
}
PRIVATE int transfer_data_sentry(st_init_info_t *info,
struct _magic_sentry* cached_sentry)
{
int r;
int st_cb_flags = ST_CB_DEFAULT_FLAGS;
struct _magic_sentry *local_sentry, *remote_sentry;
int flags = ST_SEL_ANALYZE_FLAGS;
struct st_cb_info cb_info_buff;
struct st_cb_info *cb_info = &cb_info_buff;
static _magic_selement_t magic_local_selements[MAGIC_MAX_RECURSIVE_TYPES+1];
static int magic_flags_by_depth[MAGIC_MAX_RECURSIVE_TYPES+1];
/* Skip extern weak symbols. */
if (!cached_sentry->address) {
assert(MAGIC_STATE_FLAG(cached_sentry, MAGIC_STATE_EXTERNAL));
st_set_transfer_status(ST_TRANSFER_DONE, ST_OP_ADD, cached_sentry, NULL);
return OK;
}
/* Determine local and remote sentries from the cached version. */
local_sentry = NULL;
st_lookup_sentry_pair(&cached_sentry, &local_sentry);
assert(local_sentry && "Unexpected unpaired sentry!");
remote_sentry = st_cached_to_remote_sentry(info, cached_sentry);
if (!remote_sentry) {
printf("No remote sentry found for cached sentry: ");
MAGIC_SENTRY_PRINT(cached_sentry, 0);
printf("\n");
return EFAULT;
}
cb_info->local_selements = magic_local_selements;
cb_info->local_selement = magic_selement_from_sentry(local_sentry, &magic_local_selements[0]);
cb_info->walk_flags = MAGIC_TYPE_WALK_DEFAULT_FLAGS;
cb_info->st_cb_flags = st_cb_flags;
cb_info->init_info = info;
cb_info->st_cb_saved_flags = magic_flags_by_depth;
magic_flags_by_depth[0] = st_cb_flags;
EXEC_WITH_MAGIC_VARS(
r = magic_sentry_analyze(remote_sentry , flags, transfer_data_selement, cb_info, NULL);
, &st_cached_magic_vars
);
if (r < 0) {
return r;
}
st_set_transfer_status(ST_TRANSFER_DONE, ST_OP_ADD, cached_sentry, NULL);
return OK;
}
PRIVATE int transfer_data_selement(_magic_selement_t *selement, _magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, void *cb_args)
{
int r = ST_CB_NOT_PROCESSED;
int depth, cb_flags;
struct st_cb_info *cb_info = (struct st_cb_info *) cb_args;
_magic_selement_t *local_selement, *local_parent_selement;
st_cb_selement_transfer_t *cb;
register_typenames_and_callbacks();
if (!ST_CB_FLAG(ST_CB_CHECK_ONLY)) {
depth = selement->depth;
local_selement = &cb_info->local_selements[depth];
if (depth > 0) {
local_parent_selement = &cb_info->local_selements[depth-1];
local_selement->sentry = local_parent_selement->sentry;
local_selement->parent_type = local_parent_selement->type;
local_selement->parent_address = local_parent_selement->address;
cb_info->st_cb_flags = cb_info->st_cb_saved_flags[depth-1];
}
/* Map the cached and the local selement. */
st_map_selement(selement, local_selement, cb_info, FALSE);
if (local_selement->type == NULL) {
/* Unpaired selement. */
if (st_policies & ST_REPORT_UNPAIRED_SELEMENTS) {
printf("transfer_data_selement: Unpaired selement found: ");
MAGIC_SELEMENT_PRINT(selement, MAGIC_EXPAND_TYPE_STR);
printf("\n");
}
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
cb_info->local_selement = local_selement;
/* See if identity transfer has been requested. */
if (cb_info->st_cb_flags & ST_CB_FORCE_IXFER) {
r = transfer_identity_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
assert(r != ST_CB_NOT_PROCESSED);
cb_info->st_cb_saved_flags[depth] = cb_info->st_cb_flags;
return r;
}
}
cb_flags = ST_CB_TYPE_SELEMENT;
if (ST_TYPE_NAME_KEY(selement->type) != NULL) {
cb_flags |= ST_CB_TYPE_TYPENAME;
}
if (selement->num == 1) {
cb_flags |= ST_CB_TYPE_SENTRY;
}
cb = st_cbs.st_cb_selement_transfer[cb_flags];
while (TRUE) {
if (*cb != NULL) {
r = (*cb)(selement, sel_analyzed, sel_stats, cb_info);
} else {
r = default_transfer_selement_sel_cb(selement, sel_analyzed, sel_stats, cb_info);
assert(r != ST_CB_NOT_PROCESSED
&& "Default selement callback should always process the selement.");
}
if (r != ST_CB_NOT_PROCESSED) {
assert((r<0 || MAGIC_SENTRY_ANALYZE_IS_VALID_RET(r)) && "Invalid callback return code!");
if (!ST_CB_FLAG(ST_CB_CHECK_ONLY)) {
cb_info->st_cb_saved_flags[depth] = cb_info->st_cb_flags;
}
return r;
}
cb++;
}
/* Not reachable. */
return EINTR;
}
PRIVATE int lookup_trg_info(_magic_selement_t *selement,
_magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats, struct st_cb_info *cb_info,
_magic_selement_t *cached_trg_selement, _magic_selement_t *local_trg_selement)
{
_magic_selement_t *local_selement, *trg_selement;
struct _magic_sentry *cached_trg_sentry, *local_trg_sentry = NULL;
struct _magic_function *cached_trg_function, *local_trg_function = NULL;
_magic_sel_analyzed_t local_sel_analyzed;
_magic_sel_stats_t local_sel_stats;
void *local_trg_root_address;
struct _magic_type *cached_trg_root_type, *local_trg_root_type;
int first_legal_trg_type, is_same_type, is_same_trg_type, local_trg_has_addr_not_taken;
local_selement = cb_info->local_selement;
first_legal_trg_type = sel_analyzed->u.ptr.first_legal_trg_type;
assert(first_legal_trg_type >= 0);
trg_selement = &sel_analyzed->u.ptr.trg_selements[first_legal_trg_type];
local_trg_root_type = NULL;
/* Lookup cached and local targets. */
if (MAGIC_SEL_ANALYZED_PTR_HAS_TRG_SENTRY(sel_analyzed)) {
cached_trg_sentry = trg_selement->sentry;
local_trg_sentry = NULL;
st_lookup_sentry_pair(&cached_trg_sentry, &local_trg_sentry);
*cached_trg_selement = *trg_selement;
cached_trg_root_type = cached_trg_sentry->type;
local_trg_has_addr_not_taken = local_trg_sentry && MAGIC_STATE_FLAG(local_trg_sentry, MAGIC_STATE_ADDR_NOT_TAKEN);
local_trg_selement->sentry = local_trg_sentry;
if (local_trg_sentry) {
local_trg_root_address = local_trg_sentry->address;
local_trg_root_type = local_trg_sentry->type;
}
}
else if(MAGIC_SEL_ANALYZED_PTR_HAS_TRG_FUNCTION(sel_analyzed)) {
cached_trg_function = MAGIC_DFUNCTION_TO_FUNCTION(&sel_analyzed->u.ptr.trg.dfunction);
local_trg_function = NULL;
st_lookup_function_pair(&cached_trg_function, &local_trg_function);
*cached_trg_selement = *trg_selement;
cached_trg_root_type = cached_trg_function->type;
local_trg_has_addr_not_taken = local_trg_function && MAGIC_STATE_FLAG(local_trg_function, MAGIC_STATE_ADDR_NOT_TAKEN);
local_trg_selement->sentry = NULL;
if (local_trg_function) {
local_trg_root_address = local_trg_function->address;
local_trg_root_type = local_trg_function->type;
}
}
/* Check unpaired targets. */
if (!local_trg_root_type) {
local_trg_selement->type = NULL;
return OK;
}
/* Check address not taken violations. */
if (local_trg_has_addr_not_taken) {
ST_CB_PRINT(ST_CB_ERR, "uncaught ptr with paired target whose address is not taken", selement, sel_analyzed, sel_stats, cb_info);
return EFAULT;
}
/* Check types and return immediately in case of perfect pointer match. */
is_same_type = selement->type == local_selement->type || ST_PTR_TYPE_IS_CACHED_COUNTERPART(selement->type, local_selement->type);
is_same_trg_type = ST_TYPE_IS_CACHED_COUNTERPART(cached_trg_root_type, local_trg_root_type);
if (is_same_type && is_same_trg_type) {
local_trg_selement->type = cached_trg_selement->type;
local_trg_selement->address = (char*) local_trg_root_address + sel_analyzed->u.ptr.trg_offset;
return OK;
}
#if CHECK_ASR && !FORCE_SOME_UNPAIRED_TYPES
if (cb_info->init_info->flags & ST_LU_ASR) {
st_cbs_os.panic("ASR should never get here!");
}
#endif
/* Map sel_analyzed to its local counterpart. */
if (is_same_trg_type) {
local_sel_analyzed = *sel_analyzed;
local_sel_analyzed.u.ptr.trg_selements[0].address = (char*) local_trg_root_address + sel_analyzed->u.ptr.trg_offset;
}
else {
st_map_sel_analyzed_from_target(sel_analyzed, &local_sel_analyzed, local_trg_sentry, local_trg_function, cb_info);
if (local_sel_analyzed.u.ptr.num_trg_types == 0) {
/* Unpaired target selements. */
local_trg_selement->type = NULL;
return OK;
}
}
/* Check violations on the local target. */
memset(&local_sel_stats, 0, sizeof(local_sel_stats));
magic_selement_analyze_ptr_type_invs(local_selement, &local_sel_analyzed, &local_sel_stats);
if (MAGIC_SEL_STATS_HAS_VIOLATIONS(&local_sel_stats)) {
/* Local pointer with violations found */
ST_CB_PRINT(ST_CB_ERR, "uncaught ptr with after-transfer violations", selement, sel_analyzed, sel_stats, cb_info);
ST_CB_PRINT(ST_CB_ERR, "transferred ptr with violations", local_selement, &local_sel_analyzed, &local_sel_stats, cb_info);
return EFAULT;
}
/* All the targets mapped correctly. */
local_trg_selement->type = local_sel_analyzed.u.ptr.trg_selements[0].type;
local_trg_selement->address = local_sel_analyzed.u.ptr.trg_selements[0].address;
return OK;
}
/* transfer helper functions */
PRIVATE int md_transfer_str(st_init_info_t *info, char **str_pt)
{
char buff[ST_STR_BUFF_SIZE + 2];
if (st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) *str_pt, ST_STR_BUFF_SIZE + 1, (uint32_t) buff)) {
st_cbs_os.panic("md_transfer_str(): ERROR transferring string.\n");
return EGENERIC;
}
buff[ST_STR_BUFF_SIZE + 1] = '\0';
if (strlen(buff) > ST_STR_BUFF_SIZE) {
st_cbs_os.panic("md_transfer_str(): transferred string has a wrong size: %d\n", strlen(buff));
return EGENERIC;
}
*str_pt = st_buff_allocate(info, strlen(buff) + 1);
if (!*str_pt) {
st_cbs_os.panic("md_transfer_str(): string buffer could not be allocated.\n");
return EGENERIC;
}
strcpy(*str_pt, buff);
return OK;
}
PRIVATE int md_transfer(st_init_info_t *info, void *from, void **to, int len)
{
/* backup from value, in case &from == to */
void *from_backup = from;
*to = st_buff_allocate(info, len);
if (!*to) {
st_cbs_os.panic("md_transfer(): buffer could not be allocated.\n");
return EGENERIC;
}
if (st_cbs_os.copy_state_region(info->info_opaque, (uint32_t) from_backup, len, (uint32_t) *to)) {
st_cbs_os.panic("md_transfer(): ERROR transferring remote data to buffer.\n");
return EGENERIC;
}
return OK;
}
/* Buffer allocation */
PRIVATE void *persistent_mmap(__MA_ARGS__ st_init_info_t *info, void *start, size_t length, int prot, int flags, int fd, off_t offset, struct _magic_dsentry *dsentry) {
if (USE_PRE_ALLOCATED_BUFFER(info)) {
size_t alloc_length = length + (length % magic_get_sys_pagesize() == 0 ? 0 : magic_get_sys_pagesize() - (length % magic_get_sys_pagesize()));
char *ptr, *data_ptr;
assert(((char *)info->init_buff_cleanup_start) + alloc_length + magic_get_sys_pagesize() <= st_pre_allocated_page_pt && "mmap region hits temporary buffer.");
assert(((char *)info->init_buff_cleanup_start) + alloc_length + magic_get_sys_pagesize() <= ((char *) info->init_buff_start) + info->init_buff_len && "mmap region hits end of pre-allocated buffer");
ptr = ((char *)info->init_buff_cleanup_start) + magic_get_sys_pagesize() - MAGIC_SIZE_TO_REAL(0);
data_ptr = magic_alloc(__MA_VALUES__ ptr, alloc_length, (int) MAGIC_STATE_MAP);
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_flags = flags;
MAGIC_PTR_TO_DSENTRY(MAGIC_PTR_FROM_DATA(data_ptr))->alloc_mmap_prot = prot;
info->init_buff_cleanup_start = &data_ptr[alloc_length];
return data_ptr;
} else {
/* no pre-allocated mmap buffer. Call magic_mmap to allocate region. */
return magic_mmap_positioned(type, name, parent_name
, NULL, length, prot, flags, -1, 0, dsentry);
}
}
PUBLIC void *st_cb_pages_allocate(st_init_info_t *info, uint32_t *phys, int num_pages)
{
void *result;
int len = num_pages * magic_get_sys_pagesize();
if (USE_PRE_ALLOCATED_BUFFER(info)) {
if (!st_pre_allocated_page_pt) {
#if ST_DEBUG_LEVEL > 0
printf("st_pages_allocate: initializing pre-allocated page buffer.\n");
#endif
st_pre_allocated_page_pt = &((char *)info->init_buff_start)[info->init_buff_len];
}
st_pre_allocated_page_pt -= len;
assert(st_pre_allocated_page_pt >= (char *)info->init_buff_cleanup_start
&& "Temporary buffer ran into perminently pre-allocated mmapped pages.");
return st_pre_allocated_page_pt;
}
result = st_cbs_os.alloc_contig(len, 0, NULL);
if (result == NULL) {
printf("st_pages_allocate: alloc_contig(%d) failed.\n", len);
return NULL;
}
*phys = (uint32_t) NULL; /* we don't know or need the physical address in order to free */
return result;
}
PUBLIC void st_cb_pages_free(st_init_info_t *info, st_alloc_pages *current_page)
{
st_alloc_pages *to_be_freed;
int result;
if (USE_PRE_ALLOCATED_BUFFER(info)) {
/* nothing to do */
return;
}
while (current_page != NULL) {
to_be_freed = current_page;
current_page = current_page->previous;
result = st_cbs_os.free_contig(to_be_freed->virt_addr, to_be_freed->num_pages * magic_get_sys_pagesize());
if (result != OK) {
printf("munmap result != ok, using free()\n");
/*
* NOTE: in case this is moved out of a magic_* module it needs to be
* manually annotated so it doesn't get instrumented.
*/
free(to_be_freed->virt_addr);
}
}
}
PUBLIC void *st_buff_allocate(st_init_info_t *info, size_t size)
{
void *result;
if (size > st_alloc_buff_available) {
int pagesize = magic_get_sys_pagesize();
uint32_t phys;
st_alloc_pages *buff_previous_page = st_alloc_pages_current;
/* calculate number of pages needed */
int pages_needed = (size + sizeof(st_alloc_pages)) / pagesize;
if ((size + sizeof(st_alloc_pages)) % pagesize)
pages_needed++;
/* allocate pages */
st_alloc_pages_current
= st_cbs.st_cb_pages_allocate(info, &phys, pages_needed);
if (!st_alloc_pages_current) {
printf("Could not allocate buffer.\n");
return NULL;
}
/* set allocation struct */
st_alloc_pages_current->virt_addr = st_alloc_pages_current;
st_alloc_pages_current->phys_addr = phys;
st_alloc_pages_current->num_pages = pages_needed;
st_alloc_pages_current->previous = buff_previous_page;
/* requested space is right after the struct */
st_alloc_buff_pt = (char *) st_alloc_pages_current;
st_alloc_buff_pt += sizeof(st_alloc_pages);
/* subtract the struct size from the available buffer */
st_alloc_buff_available = pages_needed * pagesize - sizeof(st_alloc_pages);
}
/* return current buffer pointer */
result = st_alloc_buff_pt;
/* set buffer pointer after space that is requested, ready for next allocation */
st_alloc_buff_pt += size;
/* adjust available space */
st_alloc_buff_available -= size;
return result;
}
PUBLIC void st_buff_cleanup(st_init_info_t *info)
{
st_cbs.st_cb_pages_free(info, st_alloc_pages_current);
st_alloc_pages_current = NULL;
st_alloc_buff_available = 0;
st_alloc_buff_pt = NULL;
}
PUBLIC void st_cleanup(st_init_info_t *info)
{
#if MAGIC_LOOKUP_SENTRY_ALLOW_RANGE_INDEX
st_cleanup_rl_index(info, &st_cached_magic_vars);
st_cleanup_rl_index(info, _magic_vars);
#endif
#if MAGIC_LOOKUP_SENTRY_ALLOW_NAME_HASH
st_cleanup_sentry_hash(info, &st_cached_magic_vars);
st_cleanup_sentry_hash(info, _magic_vars);
#endif
#if MAGIC_LOOKUP_FUNCTION_ALLOW_ADDR_HASH
st_cleanup_function_hash(info, &st_cached_magic_vars);
st_cleanup_function_hash(info, _magic_vars);
#endif
#if !ST_ASSUME_RAW_COPY_BEFORE_TRANSFER
assert(
deallocate_nonxferred_dsentries(st_cached_magic_vars.first_dsentry,
&st_counterparts) == OK &&
"ERROR occurred during call to deallocate_nonxferred_dsentries().");
#endif
/*
* Free all temporary allocated memory.
*/
st_buff_cleanup(info);
/*
* Reset all values in case of successive state transfers.
*/
st_init_done = FALSE;
st_pre_allocated_page_pt = NULL;
st_dsentry_buff = NULL;
st_data_buff = NULL;
st_num_type_transformations = 0;
st_local_magic_vars_ptr = &_magic_vars_buff;
st_policies = ST_POLICIES_DEFAULT;
st_unpaired_types_ratio = ST_UNPAIRED_TYPES_RATIO_DEFAULT;
st_unpaired_struct_types_ratio = ST_UNPAIRED_STRUCT_TYPES_RATIO_DEFAULT;
/* Reallow mempool dsentries lookups. */
magic_lookup_nested_dsentries = 1;
}
/* State cleanup/checking functions. */
/*===========================================================================*
* do_st_before_receive *
*===========================================================================*/
PUBLIC void do_st_before_receive()
{
/* Handle State transfer before receive events. */
int num_violations;
assert(st_state_checking_before_receive_is_enabled());
num_violations = st_do_state_checking();
if (__st_before_receive_sc_max_cycles < LONG_MAX) {
__st_before_receive_sc_max_cycles--;
}
if (__st_before_receive_sc_max_violations < LONG_MAX) {
__st_before_receive_sc_max_violations -= num_violations;
}
if (__st_before_receive_sc_max_cycles <= 0) {
st_state_checking_before_receive_set_enabled(0, 0, 0);
printf("Maximum number of cycles reached\n");
}
if (__st_before_receive_sc_max_violations <= 0) {
st_state_checking_before_receive_set_enabled(0, 0, 0);
printf("Maximum number of violations reached\n");
}
}
/*===========================================================================*
* st_state_checking_before_receive_is_enabled *
*===========================================================================*/
PUBLIC int st_state_checking_before_receive_is_enabled()
{
return __st_before_receive_enabled;
}
/*===========================================================================*
* st_state_checking_before_receive_set_enabled *
*===========================================================================*/
PUBLIC int st_state_checking_before_receive_set_enabled(int enabled,
int max_cycles, int max_violations)
{
int was_enabled = __st_before_receive_enabled;
__st_before_receive_enabled = enabled;
if (enabled) {
if (max_cycles <= 0) {
max_cycles = ST_STATE_CHECKING_DEFAULT_MAX_CYCLES;
}
if (max_violations <= 0) {
max_violations = ST_STATE_CHECKING_DEFAULT_MAX_VIOLATIONS;
}
__st_before_receive_sc_max_cycles = max_cycles;
__st_before_receive_sc_max_violations = max_violations;
printf("Continuous state checking enabled, max cycles=%d, max violations=%d\n",
max_cycles == LONG_MAX ? 0 : max_cycles,
max_violations == LONG_MAX ? 0 : max_violations);
}
else {
printf("Continuous state checking disabled\n");
}
return was_enabled;
}
/*===========================================================================*
* st_cb_state_checking_wrapper *
*===========================================================================*/
PRIVATE int st_cb_state_checking_wrapper(_magic_selement_t* selement,
_magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats,
void* cb_args)
{
struct st_cb_info cb_info_buff;
struct st_cb_info *cb_info = &cb_info_buff;
int *num_violations = (int*) cb_args;
int ret;
cb_info->local_selements = NULL;
cb_info->local_selement = NULL;
cb_info->walk_flags = MAGIC_TYPE_WALK_DEFAULT_FLAGS;
cb_info->st_cb_flags = ST_CB_CHECK_ONLY;
cb_info->st_cb_saved_flags = NULL;
cb_info->init_info = NULL;
ret = transfer_data_selement(selement, sel_analyzed, sel_stats, cb_info);
if (ret < 0) {
ret = st_cbs.st_cb_state_checking(selement, sel_analyzed, sel_stats, cb_args);
(*num_violations)++;
}
return ret;
}
/*===========================================================================*
* st_do_state_checking *
*===========================================================================*/
PUBLIC int st_do_state_checking()
{
int num_violations = 0;
magic_sentries_analyze(ST_SEL_ANALYZE_FLAGS,
st_cb_state_checking_wrapper, &num_violations, NULL);
return num_violations;
}
/*===========================================================================*
* st_cb_state_checking_null *
*===========================================================================*/
PUBLIC int st_cb_state_checking_null(_magic_selement_t* selement,
_magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats,
void* cb_args)
{
return EINTR;
}
/*===========================================================================*
* st_cb_state_checking_print *
*===========================================================================*/
PUBLIC int st_cb_state_checking_print(_magic_selement_t* selement,
_magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats,
void* cb_args)
{
printf("%s. Found state violation:\n", st_cbs_os.debug_header());
magic_sentry_print_el_cb(selement, sel_analyzed, sel_stats, cb_args);
return MAGIC_SENTRY_ANALYZE_SKIP_PATH;
}
/*===========================================================================*
* st_cb_state_checking_panic *
*===========================================================================*/
PUBLIC int st_cb_state_checking_panic(_magic_selement_t* selement,
_magic_sel_analyzed_t *sel_analyzed, _magic_sel_stats_t *sel_stats,
void* cb_args)
{
st_cb_state_checking_print(selement, sel_analyzed, sel_stats, cb_args);
st_cbs_os.panic("Time to panic...");
return MAGIC_SENTRY_ANALYZE_STOP;
}
/*===========================================================================*
* st_do_state_cleanup *
*===========================================================================*/
PUBLIC int st_do_state_cleanup()
{
return st_cbs.st_cb_state_cleanup();
}
/*===========================================================================*
* st_cb_state_cleanup_null *
*===========================================================================*/
PUBLIC int st_cb_state_cleanup_null() {
return OK;
}
#ifndef __MINIX
/*===========================================================================*
* st_msync_all_shm_dsentries *
*===========================================================================*/
PUBLIC void st_msync_all_shm_dsentries(void) {
struct _magic_dsentry *prev_dsentry, *dsentry;
struct _magic_sentry *sentry;
MAGIC_DSENTRY_ALIVE_ITER(_magic_first_dsentry, prev_dsentry,
dsentry, sentry,
/*
* TODO:
* - Don't msync mmaps of /dev/zero
*/
if (MAGIC_STATE_FLAGS(sentry, MAGIC_STATE_SHM | MAGIC_STATE_MAP) &&
!(dsentry->alloc_mmap_flags & MAP_ANONYMOUS))
msync(MAGIC_PTR_TO_DATA(dsentry), sentry->type->size,
MS_SYNC | MS_INVALIDATE);
);
}
#endif