void loader_thread_init(dcontext_t *dcontext) { privmod_t *mod; if (modlist == NULL) { #ifdef WINDOWS /* FIXME i#338: once restore order this will become nop */ /* os specific thread initilization prologue for loader with no lock */ os_loader_thread_init_prologue(dcontext); /* os specific thread initilization epilogue for loader with no lock */ os_loader_thread_init_epilogue(dcontext); #endif /* WINDOWS */ } else { /* os specific thread initilization prologue for loader with no lock */ os_loader_thread_init_prologue(dcontext); if (privload_has_thread_entry()) { /* We rely on lock isolation to prevent deadlock while we're here * holding privload_lock and the priv lib * DllMain may acquire the same lock that another thread acquired * in its app code before requesting a synchall (flush, exit). * FIXME i#875: we do not have ntdll!RtlpFlsLock isolated. * Living w/ it for now. It should be unlikely for the app to * hold RtlpFlsLock and then acquire privload_lock: privload_lock * is used for import redirection but those don't apply within * ntdll. */ ASSERT_OWN_NO_LOCKS(); acquire_recursive_lock(&privload_lock); /* Walk forward and call independent libs last. * We do notify priv libs of client threads. */ for (mod = modlist; mod != NULL; mod = mod->next) { if (!mod->externally_loaded) privload_call_entry(mod, DLL_THREAD_INIT); } release_recursive_lock(&privload_lock); } /* os specific thread initilization epilogue for loader with no lock */ os_loader_thread_init_epilogue(dcontext); } }
static #endif void handle_nudge(dcontext_t *dcontext, nudge_arg_t *arg) { uint nudge_action_mask = arg->nudge_action_mask; /* Future version checks would go here. */ ASSERT_CURIOSITY(arg->version == NUDGE_ARG_CURRENT_VERSION); /* Nudge shouldn't start with any locks held. Do this assert after the * dynamo_exited check, other wise the locks may be deleted. */ ASSERT_OWN_NO_LOCKS(); STATS_INC(num_nudges); #ifdef WINDOWS /* Linux does this in signal.c */ SYSLOG_INTERNAL_INFO("received nudge mask=0x%x id=0x%08x arg=0x"ZHEX64_FORMAT_STRING, arg->nudge_action_mask, arg->client_id, arg->client_arg); #endif if (nudge_action_mask == 0) { ASSERT_CURIOSITY(false && "Nudge: no action specified"); return; } else if (nudge_action_mask >= NUDGE_GENERIC(PARAMETRIZED_END)) { ASSERT(false && "Nudge: unknown nudge action"); return; } /* In -thin_client mode only detach and process_control nudges are allowed; * case 8888. */ #define VALID_THIN_CLIENT_NUDGES (NUDGE_GENERIC(process_control)|NUDGE_GENERIC(detach)) if (DYNAMO_OPTION(thin_client)) { if (TEST(VALID_THIN_CLIENT_NUDGES, nudge_action_mask)) { /* If it is a valid thin client nudge, then disable all others. */ nudge_action_mask &= VALID_THIN_CLIENT_NUDGES; } else { return; /* invalid nudge for thin_client, so mute it */ } } /* FIXME: NYI action handlers. As implemented move to desired order. */ if (TEST(NUDGE_GENERIC(upgrade), nudge_action_mask)) { /* FIXME: watch out for flushed clean-call fragment */ nudge_action_mask &= ~NUDGE_GENERIC(upgrade); ASSERT_NOT_IMPLEMENTED(false && "case 4179"); } if (TEST(NUDGE_GENERIC(kstats), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(kstats); ASSERT_NOT_IMPLEMENTED(false); } #ifdef INTERNAL if (TEST(NUDGE_GENERIC(stats), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(stats); ASSERT_NOT_IMPLEMENTED(false); } if (TEST(NUDGE_GENERIC(invalidate), nudge_action_mask)) { /* FIXME: watch out for flushed clean-call fragment */ nudge_action_mask &= ~NUDGE_GENERIC(invalidate); ASSERT_NOT_IMPLEMENTED(false); } if (TEST(NUDGE_GENERIC(recreate_pc), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(recreate_pc); ASSERT_NOT_IMPLEMENTED(false); } if (TEST(NUDGE_GENERIC(recreate_state), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(recreate_state); ASSERT_NOT_IMPLEMENTED(false); } if (TEST(NUDGE_GENERIC(reattach), nudge_action_mask)) { /* FIXME: watch out for flushed clean-call fragment */ nudge_action_mask &= ~NUDGE_GENERIC(reattach); ASSERT_NOT_IMPLEMENTED(false); } #endif /* INTERNAL */ if (TEST(NUDGE_GENERIC(diagnose), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(diagnose); ASSERT_NOT_IMPLEMENTED(false); } /* Implemented action handlers */ if (TEST(NUDGE_GENERIC(opt), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(opt); synchronize_dynamic_options(); } if (TEST(NUDGE_GENERIC(ldmp), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(ldmp); os_dump_core("Nudge triggered ldmp."); } if (TEST(NUDGE_GENERIC(freeze), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(freeze); coarse_units_freeze_all(true/*in-place: FIXME: separate nudge for non?*/); } if (TEST(NUDGE_GENERIC(persist), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(persist); coarse_units_freeze_all(false/*!in-place==persist*/); } #ifdef CLIENT_INTERFACE if (TEST(NUDGE_GENERIC(client), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(client); instrument_nudge(dcontext, arg->client_id, arg->client_arg); } #endif #ifdef PROCESS_CONTROL if (TEST(NUDGE_GENERIC(process_control), nudge_action_mask)) { /* Case 8594 */ nudge_action_mask &= ~NUDGE_GENERIC(process_control); /* Need to synchronize because process control can be switched between * on (white or black list) & off. FIXME - the nudge mask should specify this, * but doesn't hurt to do it again. */ synchronize_dynamic_options(); if (IS_PROCESS_CONTROL_ON()) process_control(); /* If process control is enforced then control won't come back. If * either -detect_mode is on or if there was nothing to enforce, control * comes back in which case it is safe to let remaining nudges be * processed because no core state would have been changed. */ } #endif #ifdef HOTPATCHING if (DYNAMO_OPTION(hot_patching) && DYNAMO_OPTION(liveshields) && TEST_ANY(NUDGE_GENERIC(policy)|NUDGE_GENERIC(mode)|NUDGE_GENERIC(lstats), nudge_action_mask)) { hotp_nudge_update(nudge_action_mask & (NUDGE_GENERIC(policy)|NUDGE_GENERIC(mode)|NUDGE_GENERIC(lstats))); nudge_action_mask &= ~(NUDGE_GENERIC(policy)|NUDGE_GENERIC(mode)|NUDGE_GENERIC(lstats)); } #endif #ifdef PROGRAM_SHEPHERDING if (TEST(NUDGE_GENERIC(violation), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(violation); /* Use nudge mechanism to trigger a security violation at an * arbitrary time. Note - is only useful for testing kill process attack * handling as this is not an app thread (we injected it). */ /* see bug 652 for planned improvements */ security_violation(dcontext, dcontext->next_tag, ATTACK_SIM_NUDGE_VIOLATION, OPTION_BLOCK|OPTION_REPORT); } #endif if (TEST(NUDGE_GENERIC(reset), nudge_action_mask)) { nudge_action_mask &= ~NUDGE_GENERIC(reset); if (DYNAMO_OPTION(enable_reset)) { mutex_lock(&reset_pending_lock); /* fcache_reset_all_caches_proactively() will unlock */ fcache_reset_all_caches_proactively(RESET_ALL); /* NOTE - reset is safe since we won't return to the code cache below (we * will in fact not return at all). */ } else { SYSLOG_INTERNAL_WARNING("nudge reset ignored since resets are disabled"); } } #ifdef WINDOWS /* The detach handler is last since in the common case it doesn't return. */ if (TEST(NUDGE_GENERIC(detach), nudge_action_mask)) { dcontext->free_app_stack = false; nudge_action_mask &= ~NUDGE_GENERIC(detach); detach_helper(DETACH_NORMAL_TYPE); } #endif }
/* exit_process is only honored if dcontext != NULL, and exit_code is only honored * if exit_process is true */ bool nudge_thread_cleanup(dcontext_t *dcontext, bool exit_process, uint exit_code) { /* Note - for supporting detach with CLIENT_INTERFACE and nudge threads we need that * no lock grabbing or other actions that would interfere with the detaching process * occur in the cleanup path here. */ /* Case 8901: this routine is currently called from the code cache, which may have * been reset underneath us, so we can't just blindly return. This also gives us * consistent behavior for handling stack freeing. */ /* Case 9020: no EXITING_DR() as os_terminate will do that for us */ /* FIXME - these nudge threads do hit dll mains for thread attach so app may have * allocated some TLS memory which won't end up being freed since this won't go * through dll main thread detach. The app may also object to unbalanced attach to * detach ratio though we haven't seen that in practice. Long term we should take * over and redirect the thread at the init apc so it doesn't go through the * DllMains to start with. */ /* We have a general problem on how to free the application stack for nudges. * Currently the app/os will never free a nudge thread's app stack: * On NT and 2k ExitThread would normally free the app stack, but we always * terminate nudge threads instead of allowing them to return and exit normally. * On XP and 2k3 none of our nudge creation routines inform csrss of the new thread * (which is who typically frees the stacks). * On Vista we don't use NtCreateThreadEx to create the nudge threads so the kernel * doesn't free the stack. * As such we are left with two options: free the app stack here (nudgee free) or * have the nudge thread creator free the app stack (nudger free). Going with * nudgee free means we leak exit race nudge stacks whereas if we go with nudger free * for external nudges then we'll leak timed out nudge stacks (for internal nudges * we pretty much have to do nudgee free). A nudge_arg_t flag is used to specify * which model we use, but currently we always nudgee free. * * dynamo_thread_exit_common() is where the app stack is actually freed, not here. */ if (dynamo_exited || !dynamo_initialized || dcontext == NULL) { /* FIXME - no cleanup so we'll leak any memory allocated for this thread * including the application's stack and arg if we were supposed to free them. * We only expect to get here in rare races where the nudge thread was created * before dr exited (i.e. before drmarker was freed) but didn't end up getting * scheduled till after dr exited. */ ASSERT(!exit_process); /* shouldn't happen */ #ifdef WINDOWS if (dcontext != NULL) swap_peb_pointer(dcontext, false/*to app*/); #endif os_terminate(dcontext, TERMINATE_THREAD); } else { /* Nudge threads should exit without holding any locks. */ ASSERT_OWN_NO_LOCKS(); #ifdef WINDOWS /* if exiting the process, os_loader_exit will swap to app, and we want to * remain private during exit (esp client exit) */ if (!exit_process && dcontext != NULL) swap_peb_pointer(dcontext, false/*to app*/); #endif /* if freeing the app stack we must be on the dstack when we cleanup */ if (dcontext->free_app_stack && !is_currently_on_dstack(dcontext)) { if (exit_process) { /* XXX: wasteful to use two dcontext fields just for this. * Extend call_switch_stack to support extra args or sthg? */ dcontext->nudge_terminate_process = true; dcontext->nudge_exit_code = exit_code; } call_switch_stack(dcontext, dcontext->dstack, (void(*)(void*))nudge_terminate_on_dstack, NULL /* not on initstack */, false /* don't return */); } else { /* Already on dstack or nudge creator will free app stack. */ if (exit_process) { os_terminate_with_code(dcontext, TERMINATE_PROCESS|TERMINATE_CLEANUP, exit_code); } else { os_terminate(dcontext, TERMINATE_THREAD|TERMINATE_CLEANUP); } } } ASSERT_NOT_REACHED(); /* we should never return */ return true; }