Esempio n. 1
0
        //
        // METHODS
        //

        /// <devdoc>
        ///     Helper to close a thread handle.
        /// </devdoc>
        /// <internalonly/>
        private static void CloseThreadHandle(SafeThreadHandle handle)
        {
            if (handle != null)
            {
                handle.Dispose();
            }
        }
        public static SafeThreadHandle OpenThread(int threadId, int access)
        {
            SafeThreadHandle threadHandle = Interop.Kernel32.OpenThread(access, false, threadId);
            int result = Marshal.GetLastWin32Error();

            if (threadHandle.IsInvalid)
            {
                threadHandle.Dispose();
                if (result == Interop.Errors.ERROR_INVALID_PARAMETER)
                {
                    throw new InvalidOperationException(SR.Format(SR.ThreadExited, threadId.ToString()));
                }
                throw new Win32Exception(result);
            }
            return(threadHandle);
        }
        /// <summary>
        /// Calls <see cref="CreateProcessAsUserW"/> and safely stores the obtained handles.
        /// </summary>
        /// <param name="userToken">Token to impersonate the external process</param>
        /// <param name="createFlags">Flags used to create the external process</param>
        /// <param name="startupInfo">Startup information used to create the external process</param>
        /// <returns><c>true</c> if the call to <see cref="CreateProcessAsUserW"/> was successful; otherwise <c>false</c></returns>
        private bool SafeCreateProcessAsUserW(IntPtr userToken, CreateProcessFlags createFlags, StartupInfo startupInfo)
        {
            var  threadHandle = new SafeThreadHandle();
            bool success;

            // The following is necessary to make sure that processInformation.hProcess and processInformation.hThread
            // are safely stored in the respective SafeHandle classes. It is, unfortunately, not possible to define
            // processInformation.hProcess and processInformation.hThread as SafeHandles and use processInformation
            // as an out parameter, because the unmanaged code is not able to create these objects. We therefore use
            // IntPtr and ensure in the following that the IntPtrs are stored in SafeHandle objects immediately after
            // they have been obtained.
            // For details see here: https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions(v=vs.110).aspx
            RuntimeHelpers.PrepareConstrainedRegions();
            try { }
            finally
            {
                ProcessInformation processInformation;
                success = CreateProcessAsUserW(userToken, null, GetCommandLine(), IntPtr.Zero, IntPtr.Zero, true, createFlags, IntPtr.Zero, null, startupInfo, out processInformation);
                if (success)
                {
                    _processHandle = new SafeProcessHandle(processInformation.hProcess, true);
                    threadHandle.InitialSetHandle(processInformation.hThread);
                    _processId = processInformation.dwProcessId;
                }
            }

            // We don't need the threadHandle and therefore immediately dispose it.
            threadHandle.Dispose();

            if (success)
            {
                return(true);
            }

            if (_processHandle != null)
            {
                _processHandle.Dispose();
            }
            var error = Marshal.GetLastWin32Error();

            _debugLogger.Error("AsyncImpersonationProcess ({0}): Cannot start process. ErrorCode: {1} ({2})", StartInfo.FileName, error, new Win32Exception(error).Message);
            return(false);
        }
Esempio n. 4
0
        /// <summary>Starts the process using the supplied start info.</summary>
        /// <param name="startInfo">The start info with which to start the process.</param>
        private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo)
        {
            // See knowledge base article Q190351 for an explanation of the following code.  Noteworthy tricky points:
            //    * The handles are duplicated as non-inheritable before they are passed to CreateProcess so
            //      that the child process can not close them
            //    * CreateProcess allows you to redirect all or none of the standard IO handles, so we use
            //      GetStdHandle for the handles that are not being redirected

            StringBuilder commandLine = BuildCommandLine(startInfo.FileName, StartInfo.Arguments);

            Process.AppendArguments(commandLine, StartInfo.ArgumentList);

            Interop.Kernel32.STARTUPINFO         startupInfo     = new Interop.Kernel32.STARTUPINFO();
            Interop.Kernel32.PROCESS_INFORMATION processInfo     = new Interop.Kernel32.PROCESS_INFORMATION();
            Interop.Kernel32.SECURITY_ATTRIBUTES unused_SecAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES();
            SafeProcessHandle procSH   = new SafeProcessHandle();
            SafeThreadHandle  threadSH = new SafeThreadHandle();
            // handles used in parent process
            SafeFileHandle parentInputPipeHandle  = null;
            SafeFileHandle childInputPipeHandle   = null;
            SafeFileHandle parentOutputPipeHandle = null;
            SafeFileHandle childOutputPipeHandle  = null;
            SafeFileHandle parentErrorPipeHandle  = null;
            SafeFileHandle childErrorPipeHandle   = null;

            lock (s_createProcessLock)
            {
                try
                {
                    startupInfo.cb = sizeof(Interop.Kernel32.STARTUPINFO);

                    // set up the streams
                    if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
                    {
                        if (startInfo.RedirectStandardInput)
                        {
                            CreatePipe(out parentInputPipeHandle, out childInputPipeHandle, true);
                        }
                        else
                        {
                            childInputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE), false);
                        }

                        if (startInfo.RedirectStandardOutput)
                        {
                            CreatePipe(out parentOutputPipeHandle, out childOutputPipeHandle, false);
                        }
                        else
                        {
                            childOutputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
                        }

                        if (startInfo.RedirectStandardError)
                        {
                            CreatePipe(out parentErrorPipeHandle, out childErrorPipeHandle, false);
                        }
                        else
                        {
                            childErrorPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_ERROR_HANDLE), false);
                        }

                        startupInfo.hStdInput  = childInputPipeHandle.DangerousGetHandle();
                        startupInfo.hStdOutput = childOutputPipeHandle.DangerousGetHandle();
                        startupInfo.hStdError  = childErrorPipeHandle.DangerousGetHandle();

                        startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
                    }

                    // set up the creation flags parameter
                    int creationFlags = 0;
                    if (startInfo.CreateNoWindow)
                    {
                        creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_NO_WINDOW;
                    }

                    // set up the environment block parameter
                    string environmentBlock = null;
                    if (startInfo._environmentVariables != null)
                    {
                        creationFlags   |= Interop.Advapi32.StartupInfoOptions.CREATE_UNICODE_ENVIRONMENT;
                        environmentBlock = GetEnvironmentVariablesBlock(startInfo._environmentVariables);
                    }
                    string workingDirectory = startInfo.WorkingDirectory;
                    if (workingDirectory == string.Empty)
                    {
                        workingDirectory = Directory.GetCurrentDirectory();
                    }

                    bool retVal;
                    int  errorCode = 0;

                    if (startInfo.UserName.Length != 0)
                    {
                        if (startInfo.Password != null && startInfo.PasswordInClearText != null)
                        {
                            throw new ArgumentException(SR.CantSetDuplicatePassword);
                        }

                        Interop.Advapi32.LogonFlags logonFlags = (Interop.Advapi32.LogonFlags) 0;
                        if (startInfo.LoadUserProfile)
                        {
                            logonFlags = Interop.Advapi32.LogonFlags.LOGON_WITH_PROFILE;
                        }

                        fixed(char *passwordInClearTextPtr = startInfo.PasswordInClearText ?? string.Empty)
                        fixed(char *environmentBlockPtr = environmentBlock)
                        {
                            IntPtr passwordPtr = (startInfo.Password != null) ?
                                                 Marshal.SecureStringToGlobalAllocUnicode(startInfo.Password) : IntPtr.Zero;

                            try
                            {
                                retVal = Interop.Advapi32.CreateProcessWithLogonW(
                                    startInfo.UserName,
                                    startInfo.Domain,
                                    (passwordPtr != IntPtr.Zero) ? passwordPtr : (IntPtr)passwordInClearTextPtr,
                                    logonFlags,
                                    null,            // we don't need this since all the info is in commandLine
                                    commandLine,
                                    creationFlags,
                                    (IntPtr)environmentBlockPtr,
                                    workingDirectory,
                                    ref startupInfo,        // pointer to STARTUPINFO
                                    ref processInfo         // pointer to PROCESS_INFORMATION
                                    );
                                if (!retVal)
                                {
                                    errorCode = Marshal.GetLastWin32Error();
                                }
                            }
                            finally
                            {
                                if (passwordPtr != IntPtr.Zero)
                                {
                                    Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr);
                                }
                            }
                        }
                    }
                    else
                    {
                        fixed(char *environmentBlockPtr = environmentBlock)
                        {
                            retVal = Interop.Kernel32.CreateProcess(
                                null,                        // we don't need this since all the info is in commandLine
                                commandLine,                 // pointer to the command line string
                                ref unused_SecAttrs,         // address to process security attributes, we don't need to inherit the handle
                                ref unused_SecAttrs,         // address to thread security attributes.
                                true,                        // handle inheritance flag
                                creationFlags,               // creation flags
                                (IntPtr)environmentBlockPtr, // pointer to new environment block
                                workingDirectory,            // pointer to current directory name
                                ref startupInfo,             // pointer to STARTUPINFO
                                ref processInfo              // pointer to PROCESS_INFORMATION
                                );
                            if (!retVal)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                            }
                        }
                    }

                    if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != new IntPtr(-1))
                    {
                        procSH.InitialSetHandle(processInfo.hProcess);
                    }
                    if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != new IntPtr(-1))
                    {
                        threadSH.InitialSetHandle(processInfo.hThread);
                    }

                    if (!retVal)
                    {
                        if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
                        {
                            throw new Win32Exception(errorCode, SR.InvalidApplication);
                        }
                        throw new Win32Exception(errorCode);
                    }
                }
                finally
                {
                    childInputPipeHandle?.Dispose();
                    childOutputPipeHandle?.Dispose();
                    childErrorPipeHandle?.Dispose();

                    threadSH?.Dispose();
                }
            }

            if (startInfo.RedirectStandardInput)
            {
                Encoding enc = startInfo.StandardInputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleCP());
                _standardInput           = new StreamWriter(new FileStream(parentInputPipeHandle, FileAccess.Write, 4096, false), enc, 4096);
                _standardInput.AutoFlush = true;
            }
            if (startInfo.RedirectStandardOutput)
            {
                Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
                _standardOutput = new StreamReader(new FileStream(parentOutputPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
            }
            if (startInfo.RedirectStandardError)
            {
                Encoding enc = startInfo.StandardErrorEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
                _standardError = new StreamReader(new FileStream(parentErrorPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
            }

            if (procSH.IsInvalid)
            {
                return(false);
            }

            SetProcessHandle(procSH);
            SetProcessId((int)processInfo.dwProcessId);
            return(true);
        }
Esempio n. 5
0
 public void Dispose() => handle.Dispose();
Esempio n. 6
0
        /// <summary>Starts the process using the supplied start info.</summary>
        /// <param name="startInfo">The start info with which to start the process.</param>
        private bool StartCore(ProcessStartInfo startInfo)
        {
            // See knowledge base article Q190351 for an explanation of the following code.  Noteworthy tricky points:
            //    * The handles are duplicated as non-inheritable before they are passed to CreateProcess so
            //      that the child process can not close them
            //    * CreateProcess allows you to redirect all or none of the standard IO handles, so we use
            //      GetStdHandle for the handles that are not being redirected

            StringBuilder commandLine = BuildCommandLine(startInfo.FileName, startInfo.Arguments);

            Interop.Kernel32.STARTUPINFO         startupInfo     = new Interop.Kernel32.STARTUPINFO();
            Interop.Kernel32.PROCESS_INFORMATION processInfo     = new Interop.Kernel32.PROCESS_INFORMATION();
            Interop.Kernel32.SECURITY_ATTRIBUTES unused_SecAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES();
            SafeProcessHandle procSH   = new SafeProcessHandle();
            SafeThreadHandle  threadSH = new SafeThreadHandle();
            bool retVal;
            int  errorCode = 0;
            // handles used in parent process
            SafeFileHandle standardInputWritePipeHandle = null;
            SafeFileHandle standardOutputReadPipeHandle = null;
            SafeFileHandle standardErrorReadPipeHandle  = null;
            GCHandle       environmentHandle            = new GCHandle();

            lock (s_createProcessLock)
            {
                try
                {
                    // set up the streams
                    if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
                    {
                        if (startInfo.RedirectStandardInput)
                        {
                            CreatePipe(out standardInputWritePipeHandle, out startupInfo.hStdInput, true);
                        }
                        else
                        {
                            startupInfo.hStdInput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE), false);
                        }

                        if (startInfo.RedirectStandardOutput)
                        {
                            CreatePipe(out standardOutputReadPipeHandle, out startupInfo.hStdOutput, false);
                        }
                        else
                        {
                            startupInfo.hStdOutput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
                        }

                        if (startInfo.RedirectStandardError)
                        {
                            CreatePipe(out standardErrorReadPipeHandle, out startupInfo.hStdError, false);
                        }
                        else
                        {
                            startupInfo.hStdError = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_ERROR_HANDLE), false);
                        }

                        startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
                    }

                    // set up the creation flags parameter
                    int creationFlags = 0;
                    if (startInfo.CreateNoWindow)
                    {
                        creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_NO_WINDOW;
                    }

                    // set up the environment block parameter
                    IntPtr environmentPtr = (IntPtr)0;
                    if (startInfo._environmentVariables != null)
                    {
                        creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_UNICODE_ENVIRONMENT;
                        byte[] environmentBytes = EnvironmentVariablesToByteArray(startInfo._environmentVariables);
                        environmentHandle = GCHandle.Alloc(environmentBytes, GCHandleType.Pinned);
                        environmentPtr    = environmentHandle.AddrOfPinnedObject();
                    }
                    string workingDirectory = startInfo.WorkingDirectory;
                    if (workingDirectory == string.Empty)
                    {
                        workingDirectory = Directory.GetCurrentDirectory();
                    }

                    if (startInfo.UserName.Length != 0)
                    {
                        Interop.Advapi32.LogonFlags logonFlags = (Interop.Advapi32.LogonFlags) 0;
                        if (startInfo.LoadUserProfile)
                        {
                            logonFlags = Interop.Advapi32.LogonFlags.LOGON_WITH_PROFILE;
                        }


                        try { }
                        finally
                        {
                            retVal = Interop.Advapi32.CreateProcessWithLogonW(
                                startInfo.UserName,
                                startInfo.Domain,
                                startInfo.PasswordInClearText,
                                logonFlags,
                                null,                // we don't need this since all the info is in commandLine
                                commandLine,
                                creationFlags,
                                environmentPtr,
                                workingDirectory,
                                startupInfo,            // pointer to STARTUPINFO
                                processInfo             // pointer to PROCESS_INFORMATION
                                );
                            if (!retVal)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                            }
                            if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
                            {
                                procSH.InitialSetHandle(processInfo.hProcess);
                            }
                            if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
                            {
                                threadSH.InitialSetHandle(processInfo.hThread);
                            }
                        }
                        if (!retVal)
                        {
                            if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
                            {
                                throw new Win32Exception(errorCode, SR.InvalidApplication);
                            }

                            throw new Win32Exception(errorCode);
                        }
                    }
                    else
                    {
                        try { }
                        finally
                        {
                            retVal = Interop.Kernel32.CreateProcess(
                                null,                // we don't need this since all the info is in commandLine
                                commandLine,         // pointer to the command line string
                                ref unused_SecAttrs, // address to process security attributes, we don't need to inherit the handle
                                ref unused_SecAttrs, // address to thread security attributes.
                                true,                // handle inheritance flag
                                creationFlags,       // creation flags
                                environmentPtr,      // pointer to new environment block
                                workingDirectory,    // pointer to current directory name
                                startupInfo,         // pointer to STARTUPINFO
                                processInfo          // pointer to PROCESS_INFORMATION
                                );
                            if (!retVal)
                            {
                                errorCode = Marshal.GetLastWin32Error();
                            }
                            if (processInfo.hProcess != (IntPtr)0 && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
                            {
                                procSH.InitialSetHandle(processInfo.hProcess);
                            }
                            if (processInfo.hThread != (IntPtr)0 && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
                            {
                                threadSH.InitialSetHandle(processInfo.hThread);
                            }
                        }
                        if (!retVal)
                        {
                            if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
                            {
                                throw new Win32Exception(errorCode, SR.InvalidApplication);
                            }

                            throw new Win32Exception(errorCode);
                        }
                    }
                }
                finally
                {
                    // free environment block
                    if (environmentHandle.IsAllocated)
                    {
                        environmentHandle.Free();
                    }

                    startupInfo.Dispose();
                }
            }

            if (startInfo.RedirectStandardInput)
            {
                Encoding enc = GetEncoding((int)Interop.Kernel32.GetConsoleCP());
                _standardInput           = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), enc, 4096);
                _standardInput.AutoFlush = true;
            }
            if (startInfo.RedirectStandardOutput)
            {
                Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
                _standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
            }
            if (startInfo.RedirectStandardError)
            {
                Encoding enc = startInfo.StandardErrorEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
                _standardError = new StreamReader(new FileStream(standardErrorReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
            }

            bool ret = false;

            if (!procSH.IsInvalid)
            {
                SetProcessHandle(procSH);
                SetProcessId((int)processInfo.dwProcessId);
                threadSH.Dispose();
                ret = true;
            }

            return(ret);
        }
Esempio n. 7
0
        public void Start(
            Guid payloadGuid,
            ArraySegment <byte> payloadData,
            SafeFileHandle inheritableReportHandle,
            string dllNameX64,
            string dllNameX86)
        {
            using (m_syncSemaphore.AcquireSemaphore())
            {
                if (m_starting || m_disposed)
                {
                    throw new InvalidOperationException("Cannot invoke start process more than once or after this instance has been Disposed.");
                }

                m_starting = true;

                // The process creation flags
                // We use CREATE_DEFAULT_ERROR_MODE to ensure that the hard error mode of the child process (i.e., GetErrorMode)
                // is deterministic. Inheriting error mode is the default, but there may be some concurrent operation that temporarily
                // changes it (process global). The CLR has been observed to do so.
                // We use CREATE_NO_WINDOW in case BuildXL is attached to a console windows to prevent child processes from messing up
                // the console window. If BuildXL itself is started without a console window the flag is not set to prevent creating
                // extra conhost.exe processes.
                int creationFlags =
                    ((s_consoleWindow == IntPtr.Zero && !this.m_disableConHostSharing) ?
                     0 : Native.Processes.ProcessUtilities.CREATE_NO_WINDOW) | Native.Processes.ProcessUtilities.CREATE_DEFAULT_ERROR_MODE;

                SafeFileHandle standardInputWritePipeHandle = null;
                SafeFileHandle standardOutputReadPipeHandle = null;
                SafeFileHandle standardErrorReadPipeHandle  = null;

                try
                {
                    // set up the environment block parameter
                    var environmentHandle = default(GCHandle);
                    var payloadHandle     = default(GCHandle);

                    SafeFileHandle   hStdInput    = null;
                    SafeFileHandle   hStdOutput   = null;
                    SafeFileHandle   hStdError    = null;
                    SafeThreadHandle threadHandle = null;
                    try
                    {
                        IntPtr environmentPtr = IntPtr.Zero;
                        if (m_unicodeEnvironmentBlock != null)
                        {
                            creationFlags    |= Native.Processes.ProcessUtilities.CREATE_UNICODE_ENVIRONMENT;
                            environmentHandle = GCHandle.Alloc(m_unicodeEnvironmentBlock, GCHandleType.Pinned);
                            environmentPtr    = environmentHandle.AddrOfPinnedObject();
                        }

                        Pipes.CreateInheritablePipe(
                            Pipes.PipeInheritance.InheritRead,
                            Pipes.PipeFlags.WriteSideAsync,
                            readHandle: out hStdInput,
                            writeHandle: out standardInputWritePipeHandle);
                        Pipes.CreateInheritablePipe(
                            Pipes.PipeInheritance.InheritWrite,
                            Pipes.PipeFlags.ReadSideAsync,
                            readHandle: out standardOutputReadPipeHandle,
                            writeHandle: out hStdOutput);
                        Pipes.CreateInheritablePipe(
                            Pipes.PipeInheritance.InheritWrite,
                            Pipes.PipeFlags.ReadSideAsync,
                            readHandle: out standardErrorReadPipeHandle,
                            writeHandle: out hStdError);

                        // We want a per-process job primarily. If nested job support is not available, then we make sure to not have a BuildXL-level job.
                        if (JobObject.OSSupportsNestedJobs)
                        {
                            JobObject.SetTerminateOnCloseOnCurrentProcessJob();
                        }

                        // Initialize the injector
                        m_processInjector = new ProcessTreeContext(payloadGuid, inheritableReportHandle, payloadData, dllNameX64, dllNameX86, m_loggingContext);

                        // If path remapping is enabled then we wrap the job object in a container, so the filter drivers get
                        // configured (and they get cleaned up when the container is disposed)
                        if (m_containerConfiguration.IsIsolationEnabled)
                        {
                            m_job = new Container(
                                name: null,
                                containerConfiguration: m_containerConfiguration,
                                loggingContext: m_loggingContext);
                        }
                        else
                        {
                            m_job = new JobObject(null);
                        }

                        // We want the effects of SEM_NOGPFAULTERRORBOX on all children (but can't set that with CreateProcess).
                        // That's not set otherwise (even if set in this process) due to CREATE_DEFAULT_ERROR_MODE above.
                        m_job.SetLimitInformation(terminateOnClose: true, failCriticalErrors: false);

                        m_processInjector.Listen();

                        if (m_containerConfiguration.IsIsolationEnabled)
                        {
                            // After calling SetLimitInformation, start up the container if present
                            // This will throw if the container is not set up properly
                            m_job.StartContainerIfPresent();
                        }

                        // The call to the CreateDetouredProcess below will add a newly created process to the job.
                        System.Diagnostics.Stopwatch m_startUpTimeWatch = System.Diagnostics.Stopwatch.StartNew();
                        var detouredProcessCreationStatus =
                            Native.Processes.ProcessUtilities.CreateDetouredProcess(
                                m_commandLine,
                                creationFlags,
                                environmentPtr,
                                m_workingDirectory,
                                hStdInput,
                                hStdOutput,
                                hStdError,
                                m_job,
                                m_processInjector.Injector,
                                m_containerConfiguration.IsIsolationEnabled,
                                out m_processHandle,
                                out threadHandle,
                                out m_processId,
                                out int errorCode);
                        m_startUpTimeWatch.Stop();
                        m_startUpTime = m_startUpTimeWatch.ElapsedMilliseconds;

                        if (detouredProcessCreationStatus != CreateDetouredProcessStatus.Succeeded)
                        {
                            // TODO: Indicating user vs. internal errors (and particular phase failures e.g. adding to job object or injecting detours)
                            //       is good progress on the transparency into these failures. But consider making this indication visible beyond this
                            //       function without throwing exceptions; consider returning a structured value or logging events.
                            string message;
                            if (detouredProcessCreationStatus.IsDetoursSpecific())
                            {
                                message = string.Format(
                                    CultureInfo.InvariantCulture,
                                    "Internal error during process creation: {0:G}",
                                    detouredProcessCreationStatus);
                            }
                            else if (detouredProcessCreationStatus == CreateDetouredProcessStatus.ProcessCreationFailed)
                            {
                                message = "Process creation failed";
                            }
                            else
                            {
                                message = string.Format(
                                    CultureInfo.InvariantCulture,
                                    "Process creation failed: {0:G}",
                                    detouredProcessCreationStatus);
                            }

                            throw new BuildXLException(
                                      message,
                                      new NativeWin32Exception(errorCode));
                        }

                        // TODO: We should establish good post-conditions for CreateDetouredProcess. As a temporary measure, it would be nice
                        //       to determine if we are sometimes getting invalid process handles with retVal == true. So for now we differentiate
                        //       that possible case with a unique error string.
                        if (m_processHandle.IsInvalid)
                        {
                            throw new BuildXLException("Unable to start or detour a process (process handle invalid)", new NativeWin32Exception(errorCode));
                        }
                    }
                    finally
                    {
                        if (environmentHandle.IsAllocated)
                        {
                            environmentHandle.Free();
                        }

                        if (payloadHandle.IsAllocated)
                        {
                            payloadHandle.Free();
                        }

                        if (hStdInput != null && !hStdInput.IsInvalid)
                        {
                            hStdInput.Dispose();
                        }

                        if (hStdOutput != null && !hStdOutput.IsInvalid)
                        {
                            hStdOutput.Dispose();
                        }

                        if (hStdError != null && !hStdError.IsInvalid)
                        {
                            hStdError.Dispose();
                        }

                        if (inheritableReportHandle != null && !inheritableReportHandle.IsInvalid)
                        {
                            inheritableReportHandle.Dispose();
                        }

                        if (threadHandle != null && !threadHandle.IsInvalid)
                        {
                            threadHandle.Dispose();
                        }
                    }

                    var standardInputStream = new FileStream(standardInputWritePipeHandle, FileAccess.Write, m_bufferSize, isAsync: true);
                    m_standardInputWriter = new StreamWriter(standardInputStream, m_standardInputEncoding, m_bufferSize)
                    {
                        AutoFlush = true
                    };

                    var standardOutputFile = AsyncFileFactory.CreateAsyncFile(
                        standardOutputReadPipeHandle,
                        FileDesiredAccess.GenericRead,
                        ownsHandle: true,
                        kind: FileKind.Pipe);
                    m_outputReader = new AsyncPipeReader(standardOutputFile, m_outputDataReceived, m_standardOutputEncoding, m_bufferSize);
                    m_outputReader.BeginReadLine();

                    var standardErrorFile = AsyncFileFactory.CreateAsyncFile(
                        standardErrorReadPipeHandle,
                        FileDesiredAccess.GenericRead,
                        ownsHandle: true,
                        kind: FileKind.Pipe);
                    m_errorReader = new AsyncPipeReader(standardErrorFile, m_errorDataReceived, m_standardErrorEncoding, m_bufferSize);
                    m_errorReader.BeginReadLine();

                    Contract.Assert(!m_processHandle.IsInvalid);
                    m_processWaitHandle = new SafeWaitHandleFromSafeHandle(m_processHandle);

                    m_waiting = true;

                    TimeSpan timeout = m_timeout ?? Timeout.InfiniteTimeSpan;
                    m_registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(
                        m_processWaitHandle,
                        CompletionCallback,
                        null,
                        timeout,
                        true);

                    m_started = true;
                }
                catch (Exception)
                {
                    // Dispose pipe handles in case they are not assigned to streams.
                    if (m_standardInputWriter == null)
                    {
                        standardInputWritePipeHandle?.Dispose();
                    }

                    if (m_outputReader == null)
                    {
                        standardOutputReadPipeHandle?.Dispose();
                    }

                    if (m_errorReader == null)
                    {
                        standardErrorReadPipeHandle?.Dispose();
                    }

                    throw;
                }
            }
        }
    /// <summary>
    /// Calls <see cref="CreateProcessAsUserW"/> and safely stores the obtained handles.
    /// </summary>
    /// <param name="userToken">Token to impersonate the external process</param>
    /// <param name="createFlags">Flags used to create the external process</param>
    /// <param name="startupInfo">Startup information used to create the external process</param>
    /// <returns><c>true</c> if the call to <see cref="CreateProcessAsUserW"/> was successful; otherwise <c>false</c></returns>
    private bool SafeCreateProcessAsUserW(IntPtr userToken, CreateProcessFlags createFlags, StartupInfo startupInfo)
    {
      _processHandle = new SafeProcessHandle();
      var threadHandle = new SafeThreadHandle();
      bool success;

      // The following is necessary to make sure that processInformation.hProcess and processInformation.hThread
      // are safely stored in the respective SafeHandle classes. It is, unfortunately, not possible to define
      // processInformation.hProcess and processInformation.hThread as SafeHandles and use processInformation
      // as an out parameter, because the unmanaged code is not able to create these objects. We therefore use
      // IntPtr and ensure in the following that the IntPtrs are stored in SafeHandle objects immediately after
      // they have been obtained.
      // For details see here: https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimehelpers.prepareconstrainedregions(v=vs.110).aspx
      RuntimeHelpers.PrepareConstrainedRegions();
      try { }
      finally
      {
        ProcessInformation processInformation;
        success = CreateProcessAsUserW(userToken, null, GetCommandLine(), IntPtr.Zero, IntPtr.Zero, true, createFlags, IntPtr.Zero, null, startupInfo, out processInformation);
        if (success)
        {
          _processHandle.InitialSetHandle(processInformation.hProcess);
          threadHandle.InitialSetHandle(processInformation.hThread);
          _processId = processInformation.dwProcessId;
        }
      }
      
      // We don't need the threadHandle and therefore immediately dispose it.
      threadHandle.Dispose();

      if (success)
        return true;

      _processHandle.Dispose();
      var error = Marshal.GetLastWin32Error();
      _debugLogger.Error("AsyncImpersonationProcess ({0}): Cannot start process. ErrorCode: {1} ({2})", StartInfo.FileName, error, new Win32Exception(error).Message);
      return false;
    }
Esempio n. 9
0
        public unsafe IChildProcessStateHolder SpawnProcess(
            ref ChildProcessStartInfoInternal startInfo,
            string resolvedPath,
            SafeHandle stdIn,
            SafeHandle stdOut,
            SafeHandle stdErr)
        {
            var arguments            = startInfo.Arguments;
            var environmentVariables = startInfo.EnvironmentVariables;
            var workingDirectory     = startInfo.WorkingDirectory;
            var flags = startInfo.Flags;

            Debug.Assert(startInfo.CreateNewConsole || ConsolePal.HasConsoleWindow());

            var commandLine      = WindowsCommandLineUtil.MakeCommandLine(resolvedPath, arguments ?? Array.Empty <string>(), !flags.HasDisableArgumentQuoting());
            var environmentBlock = startInfo.UseCustomEnvironmentVariables ? WindowsEnvironmentBlockUtil.MakeEnvironmentBlock(environmentVariables.Span) : null;

            // Objects that need cleanup
            InputWriterOnlyPseudoConsole?pseudoConsole   = null;
            SafeJobObjectHandle?         jobObjectHandle = null;
            SafeProcessHandle?           processHandle   = null;
            SafeThreadHandle?            threadHandle    = null;

            try
            {
                pseudoConsole = startInfo.CreateNewConsole ? InputWriterOnlyPseudoConsole.Create() : null;
                if (pseudoConsole is not null && flags.HasUseCustomCodePage())
                {
                    ChangeCodePage(pseudoConsole, startInfo.CodePage, workingDirectory);
                }

                bool killOnClose = startInfo.AllowSignal && WindowsVersion.NeedsWorkaroundForWindows1809;
                jobObjectHandle = CreateJobObject(killOnClose, startInfo.DisableWindowsErrorReportingDialog);

                using var inheritableHandleStore = new InheritableHandleStore(3);
                var childStdIn = stdIn != null?inheritableHandleStore.Add(stdIn) : null;

                var childStdOut = stdOut != null?inheritableHandleStore.Add(stdOut) : null;

                var childStdErr = stdErr != null?inheritableHandleStore.Add(stdErr) : null;

                IntPtr jobObjectHandles = jobObjectHandle.DangerousGetHandle();

                Span <IntPtr> inheritableHandles = stackalloc IntPtr[inheritableHandleStore.Count];
                inheritableHandleStore.DangerousGetHandles(inheritableHandles);
                fixed(IntPtr *pInheritableHandles = inheritableHandles)
                {
                    using var attr = new ProcThreadAttributeList(3);
                    if (pseudoConsole is not null)
                    {
                        attr.UpdatePseudoConsole(pseudoConsole.Handle.DangerousGetHandle());
                    }
                    attr.UpdateHandleList(pInheritableHandles, inheritableHandles.Length);
                    attr.UpdateJobList(&jobObjectHandles, 1);

                    const int CreationFlags =
                        Kernel32.CREATE_UNICODE_ENVIRONMENT
                        | Kernel32.EXTENDED_STARTUPINFO_PRESENT;

                    int processId;

                    (processId, processHandle, threadHandle) = InvokeCreateProcess(
                        commandLine,
                        CreationFlags,
                        environmentBlock,
                        workingDirectory,
                        childStdIn,
                        childStdOut,
                        childStdErr,
                        attr);

                    return(new WindowsChildProcessState(processId, processHandle, jobObjectHandle, pseudoConsole, startInfo.AllowSignal));
                }
            }
            catch
            {
                if (processHandle is not null)
                {
                    Kernel32.TerminateProcess(processHandle, -1);
                    processHandle.Dispose();
                }
                pseudoConsole?.Dispose();
                jobObjectHandle?.Dispose();
                throw;
            }
            finally
            {
                threadHandle?.Dispose();
            }
        }