minix/minix/llvm/static/magic/magic_st.c

4583 lines
172 KiB
C
Raw Normal View History

#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;
}
Fix mmap leak in malloc code upon state transfer The NetBSD libc malloc implementation uses a memory-mapped area for its page directory. Since the process heap is reconstructed upon state transfer for live update, this memory-mapped area must not be transferred to the new process. However, as the new instance of the process being updated inherits all memory-mapped areas of the old instance, it also automatically inherits the malloc implementation's page directory. Thus, we must explicitly free this area in order to avoid a memory leak. The magic pass already detects (de)allocation functions called from within other (de)allocation functions, which is why the mmap(2) and munmap(2) calls of the malloc code are not instrumented as it is. This patch changes that particular case to allow a different hook function to be called for such "nested" allocation calls, for a particular set of nested calls. In particular, the malloc(3) code's mmap(2) and munmap(2) calls are replaced with magic_nested_mmap and magic_nested_munmap calls, respectively. The magic library then tracks memory mapping allocations of the malloc code by providing an implementation for these two wrappers, and frees the allocations upon state transfer. This approach was chosen over various alternatives: - While it appears that nesting could be established by setting a flag while the malloc(3) wrapper is active, and testing the flag in the mmap(2)/munmap(2) wrappers, this approach would fail to detect memory-mapped allocations made from uninstrumented malloc(3) calls, and therefore not a viable option. - It would be possible to obtain the value of the variables that store the information about the memory-mapped area in the malloc code. However, this is rather difficult in practice due to the way the libc malloc implementation stores the size of the are, and it would make the solution more dependent on the specific libc malloc implementation. - It would be possible to use the special "nested" instrumentation for allocations made from certain marked sections. Since we mark the data section of the malloc code already, this would not be hard to do. Switching to this alternative would change very little, and if for any reason this approach yields more advantages in the future, we can still choose to do so. Change-Id: Id977405da86a72458dd10f18e076d8460fd2fb75
2015-08-26 07:57:03 +02:00
#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
Fix mmap leak in malloc code upon state transfer The NetBSD libc malloc implementation uses a memory-mapped area for its page directory. Since the process heap is reconstructed upon state transfer for live update, this memory-mapped area must not be transferred to the new process. However, as the new instance of the process being updated inherits all memory-mapped areas of the old instance, it also automatically inherits the malloc implementation's page directory. Thus, we must explicitly free this area in order to avoid a memory leak. The magic pass already detects (de)allocation functions called from within other (de)allocation functions, which is why the mmap(2) and munmap(2) calls of the malloc code are not instrumented as it is. This patch changes that particular case to allow a different hook function to be called for such "nested" allocation calls, for a particular set of nested calls. In particular, the malloc(3) code's mmap(2) and munmap(2) calls are replaced with magic_nested_mmap and magic_nested_munmap calls, respectively. The magic library then tracks memory mapping allocations of the malloc code by providing an implementation for these two wrappers, and frees the allocations upon state transfer. This approach was chosen over various alternatives: - While it appears that nesting could be established by setting a flag while the malloc(3) wrapper is active, and testing the flag in the mmap(2)/munmap(2) wrappers, this approach would fail to detect memory-mapped allocations made from uninstrumented malloc(3) calls, and therefore not a viable option. - It would be possible to obtain the value of the variables that store the information about the memory-mapped area in the malloc code. However, this is rather difficult in practice due to the way the libc malloc implementation stores the size of the are, and it would make the solution more dependent on the specific libc malloc implementation. - It would be possible to use the special "nested" instrumentation for allocations made from certain marked sections. Since we mark the data section of the malloc code already, this would not be hard to do. Switching to this alternative would change very little, and if for any reason this approach yields more advantages in the future, we can still choose to do so. Change-Id: Id977405da86a72458dd10f18e076d8460fd2fb75
2015-08-26 07:57:03 +02:00
#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