Search code examples
multithreadingmultiprocessingwindows-xpdriverwdk

KeSetSystemAffinityThread behavior


Some questions about KeSetSystemAffinityThread function, since MSDN is quite laconic.

NOTE: I can't use the more complete KeSetSystemAffinityThreadEx because I must still support Windows XP.

1) How can I restore the previous affinity? The function does not return the old value, how can I obtain it?

2) Is it true that passing 0 to the function restores the default system affinity? I have found such assertion in some forums, but I can't find it in official MS documentation.

3) Is the new thread's system affinity mask maintained after a return to user mode, or is it restored to the default each time the thread enters in system mode?

4) What happens if previous system affinity mask is not restored?

(I'd rather post four different questions, but they seem to me too interdependent)


Solution

  • Use the undocumented KeRevertToUserAffinityThread(void) in WinXP. A quick search yields little information about the API but I found an implementation of the same function in ReastOS :

    ReactOS KeRevertToUserAffinityThread

    It is rather simple so I copy & paste it here:

    VOID NTAPI KeRevertToUserAffinityThread     (   VOID        )   
    {
        KIRQL OldIrql;
        PKPRCB Prcb;
        PKTHREAD NextThread, CurrentThread = KeGetCurrentThread();
        ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
        ASSERT(CurrentThread->SystemAffinityActive != FALSE);
    
        /* Lock the Dispatcher Database */
        OldIrql = KiAcquireDispatcherLock();
    
        /* Set the user affinity and processor and disable system affinity */
        CurrentThread->Affinity = CurrentThread->UserAffinity;
        CurrentThread->IdealProcessor = CurrentThread->UserIdealProcessor;
        CurrentThread->SystemAffinityActive = FALSE;
    
        /* Get the current PRCB and check if it doesn't match this affinity */
        Prcb = KeGetCurrentPrcb();
        if (!(Prcb->SetMember & CurrentThread->Affinity))
        {
            /* Lock the PRCB */
            KiAcquirePrcbLock(Prcb);
    
            /* Check if there's no next thread scheduled */
            if (!Prcb->NextThread)
            {
                /* Select a new thread and set it on standby */
                NextThread = KiSelectNextThread(Prcb);
                NextThread->State = Standby;
                Prcb->NextThread = NextThread;
            }
    
            /* Release the PRCB lock */
            KiReleasePrcbLock(Prcb);
        }
    
        /* Unlock dispatcher database */
        KiReleaseDispatcherLock(OldIrql);
    }
    

    Note the function takes no argument and just restore the affinity from some elements in the currrent KTHREAD struct. I guess this answer your question 1 & 2. Just call this function with no argument. I have done a test in 32bit WinXP and confirmed this. Question 4 is simple, your thread will continue to run using the processor affinity your've set.

    I have no idea to your question 3. But most likely a switch between user and kernel mode has no effect on the current processor affinity in effect since this is something stored in the KTHREAD struct.