VirtualBox

Changeset 27556 in vbox for trunk/src/VBox/Runtime


Ignore:
Timestamp:
Mar 20, 2010 3:05:06 PM (15 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
59072
Message:

process-win.cpp: must keep child process handles around or we risk races on NT4 and possibly W2K.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/win/process-win.cpp

    r27513 r27556  
    4444
    4545#include <iprt/assert.h>
     46#include <iprt/critsect.h>
    4647#include <iprt/file.h>
    4748#include <iprt/err.h>
    4849#include <iprt/env.h>
    4950#include <iprt/getopt.h>
     51#include <iprt/initterm.h>
    5052#include <iprt/ldr.h>
     53#include <iprt/mem.h>
     54#include <iprt/once.h>
    5155#include <iprt/pipe.h>
    5256#include <iprt/string.h>
     
    5458
    5559
    56 /*
    57  * This is from Winternl.h. It has been copied here
    58  * because the header does not define a calling convention for
    59  * its prototypes and just assumes that _stdcall is the standard
    60  * calling convention.
    61  */
    62 typedef struct _PEB
    63 {
    64     BYTE Reserved1[2];
    65     BYTE BeingDebugged;
    66     BYTE Reserved2[229];
    67     PVOID Reserved3[59];
    68     ULONG SessionId;
    69 } PEB, *PPEB;
    70 
    71 typedef struct _PROCESS_BASIC_INFORMATION
    72 {
    73     PVOID Reserved1;
    74     PPEB PebBaseAddress;
    75     PVOID Reserved2[2];
    76     ULONG_PTR UniqueProcessId;
    77     PVOID Reserved3;
    78 } PROCESS_BASIC_INFORMATION;
    79 
    80 typedef enum _PROCESSINFOCLASS
    81 {
    82     ProcessBasicInformation = 0,
    83     ProcessWow64Information = 26
    84 } PROCESSINFOCLASS;
    85 
    86 extern "C" LONG WINAPI
    87 NtQueryInformationProcess(
    88     IN HANDLE ProcessHandle,
    89     IN PROCESSINFOCLASS ProcessInformationClass,
    90     OUT PVOID ProcessInformation,
    91     IN ULONG ProcessInformationLength,
    92     OUT PULONG ReturnLength OPTIONAL);
    93 
     60/*******************************************************************************
     61*   Structures and Typedefs                                                    *
     62*******************************************************************************/
    9463typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
    9564                                                       LPCWSTR,
     
    10675
    10776
    108 #if 0  /** @todo Andy, could you enable and test this, please?  It should work, but I don't have a full windows install to test it on. */
     77/*******************************************************************************
     78*   Global Variables                                                           *
     79*******************************************************************************/
     80/** Init once structure. */
     81static RTONCE       g_rtProcWinInitOnce = RTONCE_INITIALIZER;
     82/** Critical section protecting the process array. */
     83static RTCRITSECT   g_CritSect;
     84/** The number of processes in the array. */
     85static uint32_t     g_cProcesses;
     86/** The current allocation size. */
     87static uint32_t     g_cProcessesAlloc;
     88/** Array containing the live or non-reaped child processes. */
     89static struct RTPROCWINENTRY
     90{
     91    /** The process ID. */
     92    ULONG_PTR       pid;
     93    /** The process handle. */
     94    HANDLE          hProcess;
     95}                  *g_paProcesses;
     96
     97
     98/**
     99 * Clean up the globals.
     100 *
     101 * @param   enmReason           Ignored.
     102 * @param   iStatus             Ignored.
     103 * @param   pvUser              Ignored.
     104 */
     105static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
     106{
     107    NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
     108
     109    RTCritSectDelete(&g_CritSect);
     110
     111    size_t i = g_cProcesses;
     112    while (i-- > 0)
     113    {
     114        CloseHandle(g_paProcesses[i].hProcess);
     115        g_paProcesses[i].hProcess = NULL;
     116    }
     117    RTMemFree(g_paProcesses);
     118
     119    g_paProcesses     = NULL;
     120    g_cProcesses      = 0;
     121    g_cProcessesAlloc = 0;
     122}
     123
     124
     125/**
     126 * Initialize the globals.
     127 *
     128 * @returns IPRT status code.
     129 * @param   pvUser1             Ignored.
     130 * @param   pvUser2             Ignored.
     131 */
     132static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
     133{
     134    NOREF(pvUser1); NOREF(pvUser2);
     135
     136    g_cProcesses        = 0;
     137    g_cProcessesAlloc   = 0;
     138    g_paProcesses       = NULL;
     139    int rc = RTCritSectInit(&g_CritSect);
     140    if (RT_SUCCESS(rc))
     141    {
     142        /** @todo init once, terminate once - this is a generic thing which should
     143         *        have some kind of static and simpler setup!  */
     144        rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
     145        if (RT_SUCCESS(rc))
     146            return rc;
     147        RTCritSectDelete(&g_CritSect);
     148    }
     149    return rc;
     150}
     151
     152
     153/**
     154 * Gets the process handle for a process from g_paProcesses.
     155 *
     156 * @returns Process handle if found, NULL if not.
     157 * @param   pid                 The process to remove (pid).
     158 */
     159static HANDLE rtProcWinFindPid(RTPROCESS pid)
     160{
     161    HANDLE hProcess = NULL;
     162
     163    RTCritSectEnter(&g_CritSect);
     164    uint32_t i = g_cProcesses;
     165    while (i-- > 0)
     166        if (g_paProcesses[i].pid == pid)
     167        {
     168            hProcess = g_paProcesses[i].hProcess;
     169            break;
     170        }
     171    RTCritSectLeave(&g_CritSect);
     172
     173    return hProcess;
     174}
     175
     176
     177/**
     178 * Removes a process from g_paProcesses.
     179 *
     180 * @param   pid                 The process to remove (pid).
     181 */
     182static void rtProcWinRemovePid(RTPROCESS pid)
     183{
     184    RTCritSectEnter(&g_CritSect);
     185    uint32_t i = g_cProcesses;
     186    while (i-- > 0)
     187        if (g_paProcesses[i].pid == pid)
     188        {
     189            g_cProcesses--;
     190            uint32_t cToMove = g_cProcesses - i;
     191            if (cToMove)
     192                memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
     193            break;
     194        }
     195    RTCritSectLeave(&g_CritSect);
     196}
     197
     198
     199/**
     200 * Adds a process to g_paProcesses.
     201 *
     202 * @returns IPRT status code.
     203 * @param   pid                 The process id.
     204 * @param   hProcess            The process handle.
     205 */
     206static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
     207{
     208    RTCritSectEnter(&g_CritSect);
     209
     210    uint32_t i = g_cProcesses;
     211    if (i >= g_cProcessesAlloc)
     212    {
     213        void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
     214        if (RT_UNLIKELY(!pvNew))
     215        {
     216            RTCritSectLeave(&g_CritSect);
     217            return VERR_NO_MEMORY;
     218        }
     219        g_paProcesses     = (struct RTPROCWINENTRY *)pvNew;
     220        g_cProcessesAlloc = i + 16;
     221    }
     222
     223    g_paProcesses[i].pid      = pid;
     224    g_paProcesses[i].hProcess = hProcess;
     225    g_cProcesses = i + 1;
     226
     227    RTCritSectLeave(&g_CritSect);
     228    return VINF_SUCCESS;
     229}
     230
     231
    109232RTR3DECL(int)   RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
    110233{
     
    114237                          pProcess);
    115238}
    116 #else
    117 /** @todo r=michael This function currently does not work correctly if the arguments
    118                     contain spaces. */
    119 RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
    120 {
    121     /*
    122      * Validate input.
    123      */
    124     AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
    125     AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
    126     AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
    127     AssertReturn(Env != NIL_RTENV, VERR_INVALID_PARAMETER);
    128     const char * const *papszEnv = RTEnvGetExecEnvP(Env);
    129     AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
    130     AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
    131     AssertPtrReturn(*papszArgs, VERR_INVALID_PARAMETER);
    132     /* later: path searching. */
    133 
    134     /*
    135      * Spawn the child.
    136      */
    137     /** @todo utf-8 considerations! */
    138     HANDLE hProcess = (HANDLE)_spawnve(_P_NOWAITO, pszExec, papszArgs, papszEnv);
    139     if (hProcess != 0 && hProcess != INVALID_HANDLE_VALUE)
    140     {
    141         if (pProcess)
    142         {
    143             /*
    144              * GetProcessId requires XP SP1 or later
    145              */
    146 #if defined(RT_ARCH_AMD64)
    147             *pProcess = GetProcessId(hProcess);
    148 #else /* !RT_ARCH_AMD64 */
    149             static bool           fInitialized = false;
    150             static DWORD (WINAPI *pfnGetProcessId)(HANDLE Thread) = NULL;
    151             if (!fInitialized)
    152             {
    153                 HMODULE hmodKernel32 = GetModuleHandle("KERNEL32.DLL");
    154                 if (hmodKernel32)
    155                     pfnGetProcessId = (DWORD (WINAPI*)(HANDLE))GetProcAddress(hmodKernel32, "GetProcessId");
    156                 fInitialized = true;
    157             }
    158             if (pfnGetProcessId)
    159             {
    160                 *pProcess = pfnGetProcessId(hProcess);
    161                 if (!*pProcess)
    162                 {
    163                     int rc = RTErrConvertFromWin32(GetLastError());
    164                     AssertMsgFailed(("failed to get pid from hProcess=%#x rc=%Rrc\n", hProcess, rc));
    165                     return rc;
    166                 }
    167             }
    168             else
    169             {
    170                 /*
    171                  * Fall back to the NT api for older versions.
    172                  */
    173                 PROCESS_BASIC_INFORMATION ProcInfo = {0};
    174                 ULONG Status = NtQueryInformationProcess(hProcess, ProcessBasicInformation,
    175                                                          &ProcInfo, sizeof(ProcInfo), NULL);
    176                 if (Status != 0)
    177                 {
    178                     int rc = ERROR_INTERNAL_ERROR; /* (we don't have a valid conversion here, but this shouldn't happen anyway.) */
    179                     AssertMsgFailed(("failed to get pid from hProcess=%#x rc=%Rrc Status=%#x\n", hProcess, rc, Status));
    180                     return rc;
    181                 }
    182                 *pProcess = ProcInfo.UniqueProcessId;
    183             }
    184 #endif  /* !RT_ARCH_AMD64 */
    185         }
    186         return VINF_SUCCESS;
    187     }
    188 
    189     int rc = RTErrConvertFromErrno(errno);
    190     AssertMsgFailed(("spawn/exec failed rc=%Rrc\n", rc)); /* this migth be annoying... */
    191     return rc;
    192 }
    193 #endif
    194239
    195240
     
    235280        dwErr = GetLastError();
    236281
    237 #ifndef IPRT_TARGET_NT4 /* We could dispense with the tediuos dynamic loading here if we wanted. */
     282#ifndef IPRT_TARGET_NT4
    238283    /*
    239284     * If we don't hold enough priviledges to spawn a new process with
     
    285330                               const char *pszPassword, PRTPROCESS phProcess)
    286331{
    287     int rc;
    288 
    289332    /*
    290333     * Input validation
     
    296339    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
    297340    /** @todo search the PATH (add flag for this). */
     341
     342    /*
     343     * Initialize the globals.
     344     */
     345    int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
     346    AssertRCReturn(rc, rc);
    298347
    299348    /*
     
    441490                    if (phProcess)
    442491                    {
    443                         /** @todo Remember the process handle and pick it up in RTProcWait. */
     492                        /*
     493                         * Add the process to the child process list so
     494                         * RTProcWait can reuse and close the process handle.
     495                         */
     496                        rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
    444497                        *phProcess = ProcInfo.dwProcessId;
    445498                    }
     
    472525{
    473526    AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
    474 
    475     /*
    476      * Open the process.
    477      */
    478     HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
     527    int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
     528    AssertRCReturn(rc, rc);
     529
     530    /*
     531     * Try find the process among the ones we've spawned, otherwise, attempt
     532     * opening the specified process.
     533     */
     534    HANDLE hProcess = rtProcWinFindPid(Process);
     535    if (hProcess == NULL)
     536        hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
    479537    if (hProcess != NULL)
    480538    {
     
    502560                        pProcStatus->iStatus = (int)dwExitCode;
    503561                    }
     562                    rtProcWinRemovePid(Process);
    504563                    return VINF_SUCCESS;
    505564                }
     
    527586    }
    528587    DWORD dwErr = GetLastError();
     588    if (dwErr == ERROR_INVALID_PARAMETER)
     589        return VERR_PROCESS_NOT_FOUND;
    529590    return RTErrConvertFromWin32(dwErr);
    530591}
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette