Commit 3054a7f0 authored by Sergey Lyubka's avatar Sergey Lyubka

Make sure child closes all pipe fds when executing CGI

parent 2de96bd5
...@@ -1293,8 +1293,8 @@ static void trim_trailing_whitespaces(char *s) { ...@@ -1293,8 +1293,8 @@ static void trim_trailing_whitespaces(char *s) {
} }
static pid_t spawn_process(struct mg_connection *conn, const char *prog, static pid_t spawn_process(struct mg_connection *conn, const char *prog,
char *envblk, char *envp[], int fd_stdin, char *envblk, char *envp[], int fdin,
int fd_stdout, const char *dir) { int fdout, const char *dir) {
HANDLE me; HANDLE me;
char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX],
cmdline[PATH_MAX], buf[PATH_MAX]; cmdline[PATH_MAX], buf[PATH_MAX];
...@@ -1312,9 +1312,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, ...@@ -1312,9 +1312,9 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
si.wShowWindow = SW_HIDE; si.wShowWindow = SW_HIDE;
me = GetCurrentProcess(); me = GetCurrentProcess();
DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdin), me, DuplicateHandle(me, (HANDLE) _get_osfhandle(fdin), me,
&si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS); &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
DuplicateHandle(me, (HANDLE) _get_osfhandle(fd_stdout), me, DuplicateHandle(me, (HANDLE) _get_osfhandle(fdout), me,
&si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS); &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
// If CGI file is a script, try to read the interpreter line // If CGI file is a script, try to read the interpreter line
...@@ -1356,10 +1356,6 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, ...@@ -1356,10 +1356,6 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
pi.hProcess = (pid_t) -1; pi.hProcess = (pid_t) -1;
} }
// Always close these to prevent handle leakage.
(void) close(fd_stdin);
(void) close(fd_stdout);
(void) CloseHandle(si.hStdOutput); (void) CloseHandle(si.hStdOutput);
(void) CloseHandle(si.hStdInput); (void) CloseHandle(si.hStdInput);
(void) CloseHandle(pi.hThread); (void) CloseHandle(pi.hThread);
...@@ -1414,8 +1410,8 @@ int mg_start_thread(mg_thread_func_t func, void *param) { ...@@ -1414,8 +1410,8 @@ int mg_start_thread(mg_thread_func_t func, void *param) {
#ifndef NO_CGI #ifndef NO_CGI
static pid_t spawn_process(struct mg_connection *conn, const char *prog, static pid_t spawn_process(struct mg_connection *conn, const char *prog,
char *envblk, char *envp[], int fd_stdin, char *envblk, char *envp[], int fdin,
int fd_stdout, const char *dir) { int fdout, const char *dir) {
pid_t pid; pid_t pid;
const char *interp; const char *interp;
...@@ -1428,15 +1424,15 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, ...@@ -1428,15 +1424,15 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
// Child // Child
if (chdir(dir) != 0) { if (chdir(dir) != 0) {
cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO));
} else if (dup2(fd_stdin, 0) == -1) { } else if (dup2(fdin, 0) == -1) {
cry(conn, "%s: dup2(%d, 0): %s", __func__, fd_stdin, strerror(ERRNO)); cry(conn, "%s: dup2(%d, 0): %s", __func__, fdin, strerror(ERRNO));
} else if (dup2(fd_stdout, 1) == -1) { } else if (dup2(fdout, 1) == -1) {
cry(conn, "%s: dup2(%d, 1): %s", __func__, fd_stdout, strerror(ERRNO)); cry(conn, "%s: dup2(%d, 1): %s", __func__, fdout, strerror(ERRNO));
} else { } else {
// Not redirecting stderr to stdout, to avoid output being littered // Not redirecting stderr to stdout, to avoid output being littered
// with the error messages. // with the error messages.
(void) close(fd_stdin); (void) close(fdin);
(void) close(fd_stdout); (void) close(fdout);
// After exec, all signal handlers are restored to their default values, // After exec, all signal handlers are restored to their default values,
// with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's // with one exception of SIGCHLD. According to POSIX.1-2001 and Linux's
...@@ -1457,10 +1453,6 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog, ...@@ -1457,10 +1453,6 @@ static pid_t spawn_process(struct mg_connection *conn, const char *prog,
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// Parent. Close stdio descriptors
(void) close(fd_stdin);
(void) close(fd_stdout);
return pid; return pid;
} }
#endif // !NO_CGI #endif // !NO_CGI
...@@ -3380,14 +3372,14 @@ static void prepare_cgi_environment(struct mg_connection *conn, ...@@ -3380,14 +3372,14 @@ static void prepare_cgi_environment(struct mg_connection *conn,
} }
static void handle_cgi_request(struct mg_connection *conn, const char *prog) { static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
int headers_len, data_len, i, fd_stdin[2], fd_stdout[2]; int headers_len, data_len, i, fdin[2], fdout[2];
const char *status, *status_text; const char *status, *status_text;
char buf[16384], *pbuf, dir[PATH_MAX], *p; char buf[16384], *pbuf, dir[PATH_MAX], *p;
struct mg_request_info ri; struct mg_request_info ri;
struct cgi_env_block blk; struct cgi_env_block blk;
FILE *in, *out; FILE *in = NULL, *out = NULL;
struct file fout = STRUCT_FILE_INITIALIZER; struct file fout = STRUCT_FILE_INITIALIZER;
pid_t pid; pid_t pid = (pid_t) -1;
prepare_cgi_environment(conn, prog, &blk); prepare_cgi_environment(conn, prog, &blk);
...@@ -3402,32 +3394,36 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) { ...@@ -3402,32 +3394,36 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
p = (char *) prog; p = (char *) prog;
} }
pid = (pid_t) -1; if (pipe(fdin) != 0 || pipe(fdout) != 0) {
fd_stdin[0] = fd_stdin[1] = fd_stdout[0] = fd_stdout[1] = -1;
in = out = NULL;
if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
send_http_error(conn, 500, http_500_error, send_http_error(conn, 500, http_500_error,
"Cannot create CGI pipe: %s", strerror(ERRNO)); "Cannot create CGI pipe: %s", strerror(ERRNO));
goto done; goto done;
} }
pid = spawn_process(conn, p, blk.buf, blk.vars, fd_stdin[0], fd_stdout[1], pid = spawn_process(conn, p, blk.buf, blk.vars, fdin[0], fdout[1], dir);
dir);
// spawn_process() must close those!
// If we don't mark them as closed, close() attempt before
// return from this function throws an exception on Windows.
// Windows does not like when closed descriptor is closed again.
fd_stdin[0] = fd_stdout[1] = -1;
if (pid == (pid_t) -1) { if (pid == (pid_t) -1) {
send_http_error(conn, 500, http_500_error, send_http_error(conn, 500, http_500_error,
"Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO)); "Cannot spawn CGI process [%s]: %s", prog, strerror(ERRNO));
goto done; goto done;
} }
if ((in = fdopen(fd_stdin[1], "wb")) == NULL || // Make sure child closes all pipe descriptors. It must dup them to 0,1
(out = fdopen(fd_stdout[0], "rb")) == NULL) { set_close_on_exec(fdin[0]);
set_close_on_exec(fdin[1]);
set_close_on_exec(fdout[0]);
set_close_on_exec(fdout[1]);
// Parent closes only one side of the pipes.
// If we don't mark them as closed, close() attempt before
// return from this function throws an exception on Windows.
// Windows does not like when closed descriptor is closed again.
(void) close(fdin[0]);
(void) close(fdout[1]);
fdin[0] = fdout[1] = -1;
if ((in = fdopen(fdin[1], "wb")) == NULL ||
(out = fdopen(fdout[0], "rb")) == NULL) {
send_http_error(conn, 500, http_500_error, send_http_error(conn, 500, http_500_error,
"fopen: %s", strerror(ERRNO)); "fopen: %s", strerror(ERRNO));
goto done; goto done;
...@@ -3446,7 +3442,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) { ...@@ -3446,7 +3442,7 @@ static void handle_cgi_request(struct mg_connection *conn, const char *prog) {
// Close so child gets an EOF. // Close so child gets an EOF.
fclose(in); fclose(in);
in = NULL; in = NULL;
fd_stdin[1] = -1; fdin[1] = -1;
// Now read CGI reply into a buffer. We need to set correct // Now read CGI reply into a buffer. We need to set correct
// status code, thus we need to see all HTTP headers first. // status code, thus we need to see all HTTP headers first.
...@@ -3503,23 +3499,23 @@ done: ...@@ -3503,23 +3499,23 @@ done:
if (pid != (pid_t) -1) { if (pid != (pid_t) -1) {
kill(pid, SIGKILL); kill(pid, SIGKILL);
} }
if (fd_stdin[0] != -1) { if (fdin[0] != -1) {
close(fd_stdin[0]); close(fdin[0]);
} }
if (fd_stdout[1] != -1) { if (fdout[1] != -1) {
close(fd_stdout[1]); close(fdout[1]);
} }
if (in != NULL) { if (in != NULL) {
fclose(in); fclose(in);
} else if (fd_stdin[1] != -1) { } else if (fdin[1] != -1) {
close(fd_stdin[1]); close(fdin[1]);
} }
if (out != NULL) { if (out != NULL) {
fclose(out); fclose(out);
} else if (fd_stdout[0] != -1) { } else if (fdout[0] != -1) {
close(fd_stdout[0]); close(fdout[0]);
} }
} }
#endif // !NO_CGI #endif // !NO_CGI
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment