Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 860c9a8

Browse files
committed
Merge pull request #2859 from nguerrera/shim-exec
Move ForkAndExecProcess shim from coreclr to corefx
2 parents 61e0211 + 7b55568 commit 860c9a8

File tree

6 files changed

+181
-6
lines changed

6 files changed

+181
-6
lines changed

src/Common/src/Interop/Unix/libcoreclr/Interop.ForkAndExecProcess.cs renamed to src/Common/src/Interop/Unix/System.Native/Interop.ForkAndExecProcess.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
internal static partial class Interop
1010
{
11-
internal static partial class libcoreclr
11+
internal static partial class Sys
1212
{
1313
internal static unsafe int ForkAndExecProcess(
1414
string filename, string[] argv, string[] envp, string cwd,
@@ -32,7 +32,7 @@ internal static unsafe int ForkAndExecProcess(
3232
}
3333
}
3434

35-
[DllImport(Libraries.LibCoreClr, SetLastError = true)]
35+
[DllImport(Libraries.SystemNative, SetLastError = true)]
3636
private static extern unsafe int ForkAndExecProcess(
3737
string filename, byte** argv, byte** envp, string cwd,
3838
int redirectStdin, int redirectStdout, int redirectStderr,

src/Native/System.Native/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ project(System.Native)
22

33
set(NATIVE_SOURCES
44
pal_errno.cpp
5+
pal_exec.cpp
56
pal_stat.cpp
67
)
78

src/Native/System.Native/pal_exec.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
#include "../config.h"
5+
#include "pal_exec.h"
6+
7+
#include <assert.h>
8+
#include <errno.h>
9+
#include <stdlib.h>
10+
#include <unistd.h>
11+
12+
enum
13+
{
14+
READ_END_OF_PIPE = 0,
15+
WRITE_END_OF_PIPE = 1,
16+
};
17+
18+
static void CloseIfOpen(int fd)
19+
{
20+
// Ignoring errors from close is a deliberate choice and we musn't
21+
// let close during cleanup on a failure path disturb errno.
22+
if (fd >= 0)
23+
{
24+
int priorErrno = errno;
25+
close(fd);
26+
errno = priorErrno;
27+
}
28+
}
29+
30+
extern "C"
31+
int32_t ForkAndExecProcess(
32+
const char* filename,
33+
char* const argv[],
34+
char* const envp[],
35+
const char* cwd,
36+
int32_t redirectStdin,
37+
int32_t redirectStdout,
38+
int32_t redirectStderr,
39+
int32_t* childPid,
40+
int32_t* stdinFd,
41+
int32_t* stdoutFd,
42+
int32_t* stderrFd)
43+
{
44+
int success = true;
45+
int stdinFds[2] = { -1, -1 }, stdoutFds[2] = { -1, -1 }, stderrFds[2] = { -1, -1 };
46+
int processId = -1;
47+
48+
// Validate arguments
49+
if (nullptr == filename || nullptr == argv || nullptr == envp ||
50+
nullptr == stdinFd || nullptr == stdoutFd || nullptr == stderrFd ||
51+
nullptr == childPid)
52+
{
53+
assert(!"null argument.");
54+
errno = EINVAL;
55+
success = false;
56+
goto done;
57+
}
58+
59+
if ((redirectStdin & ~1) != 0 ||
60+
(redirectStdout & ~1) != 0 ||
61+
(redirectStderr & ~1) != 0)
62+
{
63+
assert(!"Boolean redirect* inputs must be 0 or 1.");
64+
errno = EINVAL;
65+
success = false;
66+
goto done;
67+
}
68+
69+
// Open pipes for any requests to redirect stdin/stdout/stderr
70+
if ((redirectStdin && pipe(stdinFds) != 0) ||
71+
(redirectStdout && pipe(stdoutFds) != 0) ||
72+
(redirectStderr && pipe(stderrFds) != 0))
73+
{
74+
assert(!"pipe() failed.");
75+
success = false;
76+
goto done;
77+
}
78+
79+
// Fork the child process
80+
if ((processId = fork()) == -1)
81+
{
82+
assert(!"fork() failed.");
83+
success = false;
84+
goto done;
85+
}
86+
87+
if (processId == 0) // processId == 0 if this is child process
88+
{
89+
// Close the child's copy of the parent end of any open pipes
90+
CloseIfOpen(stdinFds[WRITE_END_OF_PIPE]);
91+
CloseIfOpen(stdoutFds[READ_END_OF_PIPE]);
92+
CloseIfOpen(stderrFds[READ_END_OF_PIPE]);
93+
94+
// For any redirections that should happen, dup the pipe descriptors onto stdin/out/err.
95+
// Then close out the old pipe descriptrs, which we no longer need.
96+
if ((redirectStdin && dup2(stdinFds[READ_END_OF_PIPE], STDIN_FILENO) == -1) ||
97+
(redirectStdout && dup2(stdoutFds[WRITE_END_OF_PIPE], STDOUT_FILENO) == -1) ||
98+
(redirectStderr && dup2(stderrFds[WRITE_END_OF_PIPE], STDERR_FILENO) == -1))
99+
{
100+
_exit(errno != 0 ? errno : EXIT_FAILURE);
101+
}
102+
CloseIfOpen(stdinFds[READ_END_OF_PIPE]);
103+
CloseIfOpen(stdoutFds[WRITE_END_OF_PIPE]);
104+
CloseIfOpen(stderrFds[WRITE_END_OF_PIPE]);
105+
106+
// Change to the designated working directory, if one was specified
107+
if (nullptr != cwd && chdir(cwd) == -1)
108+
{
109+
_exit(errno != 0 ? errno : EXIT_FAILURE);
110+
}
111+
112+
// Finally, execute the new process. execve will not return if it's successful.
113+
execve(filename, (char**)argv, (char**)envp);
114+
_exit(errno != 0 ? errno : EXIT_FAILURE); // execve failed
115+
}
116+
117+
// This is the parent process. processId == pid of the child
118+
*childPid = processId;
119+
*stdinFd = stdinFds[WRITE_END_OF_PIPE];
120+
*stdoutFd = stdoutFds[READ_END_OF_PIPE];
121+
*stderrFd = stderrFds[READ_END_OF_PIPE];
122+
123+
done:
124+
// Regardless of success or failure, close the parent's copy of the child's end of
125+
// any opened pipes. The parent doesn't need them anymore.
126+
CloseIfOpen(stdinFds[READ_END_OF_PIPE]);
127+
CloseIfOpen(stdoutFds[WRITE_END_OF_PIPE]);
128+
CloseIfOpen(stderrFds[WRITE_END_OF_PIPE]);
129+
130+
// If we failed, close everything else and give back error values in all out arguments.
131+
if (!success)
132+
{
133+
CloseIfOpen(stdinFds[WRITE_END_OF_PIPE]);
134+
CloseIfOpen(stdoutFds[READ_END_OF_PIPE]);
135+
CloseIfOpen(stderrFds[READ_END_OF_PIPE]);
136+
137+
*stdinFd = -1;
138+
*stdoutFd = -1;
139+
*stderrFd = -1;
140+
*childPid = -1;
141+
}
142+
143+
return success ? 0 : -1;
144+
}

src/Native/System.Native/pal_exec.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
#pragma once
5+
6+
#include <stdint.h>
7+
8+
/**
9+
* Used by System.Diagnostics.Process.Start to fork/exec a new process.
10+
*
11+
* This function takes the place of directly using fork and execve from managed code,
12+
* in order to avoid executing managed code in the child process in the window between
13+
* fork and execve, which is not safe.
14+
*
15+
* As would have been the case with fork/execve, a return value of 0 is success and -1
16+
* is failure; if failure, error information is provided in errno.
17+
*/
18+
extern "C"
19+
int32_t ForkAndExecProcess(
20+
const char* filename, // filename argument to execve
21+
char* const argv[], // argv argument to execve
22+
char* const envp[], // envp argument to execve
23+
const char* cwd, // path passed to chdir in child process
24+
int32_t redirectStdin, // whether to redirect standard input from the parent
25+
int32_t redirectStdout, // whether to redirect standard output to the parent
26+
int32_t redirectStderr, // whether to redirect standard error to the parent
27+
int32_t* childPid, // [out] the child process' id
28+
int32_t* stdinFd, // [out] if redirectStdin, the parent's fd for the child's stdin
29+
int32_t* stdoutFd, // [out] if redirectStdout, the parent's fd for the child's stdout
30+
int32_t* stderrFd); // [out] if redirectStderr, the parent's fd for the child's stderr

src/System.Diagnostics.Process/src/System.Diagnostics.Process.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@
258258
<Compile Include="$(CommonPath)\Interop\Unix\libc\Interop.waitpid.cs">
259259
<Link>Common\Interop\Unix\Interop.waitpid.cs</Link>
260260
</Compile>
261-
<Compile Include="$(CommonPath)\Interop\Unix\libcoreclr\Interop.ForkAndExecProcess.cs">
261+
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.ForkAndExecProcess.cs">
262262
<Link>Common\Interop\Unix\Interop.ForkAndExecProcess.cs</Link>
263263
</Compile>
264264
<Compile Include="$(CommonPath)\Interop\Unix\libc\Interop.getsid.cs">

src/System.Diagnostics.Process/src/System/Diagnostics/Process.Unix.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,13 @@ private bool StartCore(ProcessStartInfo startInfo)
206206
string[] envp = CreateEnvp(startInfo);
207207
string cwd = !string.IsNullOrWhiteSpace(startInfo.WorkingDirectory) ? startInfo.WorkingDirectory : null;
208208

209-
// Invoke the runtime's fork/execve routine. It will create pipes for all requested
209+
// Invoke the shim fork/execve routine. It will create pipes for all requested
210210
// redirects, fork a child process, map the pipe ends onto the appropriate stdin/stdout/stderr
211-
// descriptors, and execve to execute the requested process. The runtime's implementation
211+
// descriptors, and execve to execute the requested process. The shim implementation
212212
// is used to fork/execve as executing managed code in a forked process is not safe (only
213213
// the calling thread will transfer, thread IDs aren't stable across the fork, etc.)
214214
int childPid, stdinFd, stdoutFd, stderrFd;
215-
if (Interop.libcoreclr.ForkAndExecProcess(
215+
if (Interop.Sys.ForkAndExecProcess(
216216
filename, argv, envp, cwd,
217217
startInfo.RedirectStandardInput, startInfo.RedirectStandardOutput, startInfo.RedirectStandardError,
218218
out childPid,

0 commit comments

Comments
 (0)