Esempio n. 1
0
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);
    }
}
Esempio n. 2
0
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
}
Esempio n. 3
0
/* 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;
}