Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ambrop72/badvpn.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmbroz Bizjak <ambrop7@gmail.com>2016-08-17 22:08:55 +0300
committerAmbroz Bizjak <ambrop7@gmail.com>2016-08-17 22:10:02 +0300
commit1e45405d7e9355821e941e314f1b221556983f50 (patch)
tree2a2e9a5f053d20f57d2b802247cd75ad51dd60f5
parentc8e12822f805e4cbe82d27b2e36b27c0f98aa2ed (diff)
BProcess: Fix hazards doing various things between fork and exec.
-rw-r--r--system/BProcess.c136
1 files changed, 95 insertions, 41 deletions
diff --git a/system/BProcess.c b/system/BProcess.c
index 7efea25..368db4e 100644
--- a/system/BProcess.c
+++ b/system/BProcess.c
@@ -49,6 +49,8 @@
#include <generated/blog_channel_BProcess.h>
+#define INITIAL_NUM_GROUPS 24
+
static void call_handler (BProcess *o, int normally, uint8_t normally_exit_status)
{
DEBUGERROR(&o->d_err, o->handler(o->user, normally, normally_exit_status))
@@ -183,6 +185,8 @@ static int fds_contains (const int *fds, int fd, size_t *pos)
int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], struct BProcess_params params)
{
+ int res = 0;
+
// init arguments
o->m = m;
o->handler = handler;
@@ -192,6 +196,78 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
size_t num_fds;
for (num_fds = 0; params.fds[num_fds] >= 0; num_fds++);
+ // We retrieve all the needed info and allocate all needed memory
+ // before the fork, because doing these things in the child after
+ // the fork may be unsafe.
+
+ // Get the max FD number.
+ int max_fd = sysconf(_SC_OPEN_MAX);
+ if (max_fd < 0) {
+ BLog(BLOG_ERROR, "sysconf(_SC_OPEN_MAX)");
+ goto fail0;
+ }
+
+ char *pwnam_buf = NULL;
+ struct passwd pwd;
+ gid_t groups_static[INITIAL_NUM_GROUPS];
+ gid_t *groups = groups_static;
+ int num_groups = INITIAL_NUM_GROUPS;
+
+ if (params.username) {
+ // Get the max getpwnam_r buffer size.
+ long pwnam_bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (pwnam_bufsize < 0) {
+ pwnam_bufsize = 16384;
+ }
+
+ // Allocate memory for getpwnam_r.
+ pwnam_buf = malloc(pwnam_bufsize);
+ if (!pwnam_buf) {
+ BLog(BLOG_ERROR, "malloc failed");
+ goto fail0;
+ }
+
+ // Get information about the user.
+ struct passwd *getpwnam_res;
+ getpwnam_r(params.username, &pwd, pwnam_buf, pwnam_bufsize, &getpwnam_res);
+ if (!getpwnam_res) {
+ BLog(BLOG_ERROR, "getpwnam_r failed");
+ goto fail1;
+ }
+
+ // Retrieve the group list.
+ // Start with a small auto-allocated list and only if it turns out
+ // to be too small resort to malloc.
+ while (1) {
+ int groups_ret = getgrouplist(params.username, pwd.pw_gid, groups, &num_groups);
+ if ((groups_ret < 0) ? (num_groups <= 0) : (num_groups != groups_ret)) {
+ BLog(BLOG_ERROR, "getgrouplist behaved inconsistently");
+ goto fail2;
+ }
+
+ if (groups_ret >= 0) {
+ break;
+ }
+
+ if (groups != groups_static) {
+ free(groups);
+ }
+ groups = malloc(num_groups * sizeof(gid_t));
+ if (!groups) {
+ BLog(BLOG_ERROR, "malloc failed");
+ goto fail2;
+ }
+ }
+ }
+
+ // Make a copy of the file descriptors array.
+ int *fds2 = malloc((num_fds + 1) * sizeof(fds2[0]));
+ if (!fds2) {
+ BLog(BLOG_ERROR, "malloc failed");
+ goto fail2;
+ }
+ memcpy(fds2, params.fds, (num_fds + 1) * sizeof(fds2[0]));
+
// block signals
// needed to prevent parent's signal handlers from being called
// in the child
@@ -200,7 +276,7 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
sigset_t sset_old;
if (sigprocmask(SIG_SETMASK, &sset_all, &sset_old) < 0) {
BLog(BLOG_ERROR, "sigprocmask failed");
- goto fail0;
+ goto fail3;
}
// fork
@@ -225,27 +301,11 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
abort();
}
- // copy fds array
- int *fds2 = malloc((num_fds + 1) * sizeof(fds2[0]));
- if (!fds2) {
- abort();
- }
- memcpy(fds2, params.fds, (num_fds + 1) * sizeof(fds2[0]));
-
- // find maximum file descriptors
- int max_fd = sysconf(_SC_OPEN_MAX);
- if (max_fd < 0) {
- abort();
- }
-
- // close file descriptors
+ // close file descriptors, except the given fds
for (int i = 0; i < max_fd; i++) {
- // if it's one of the given fds, don't close it
- if (fds_contains(fds2, i, NULL)) {
- continue;
+ if (!fds_contains(fds2, i, NULL)) {
+ close(i);
}
-
- close(i);
}
// map fds to requested fd numbers
@@ -284,24 +344,7 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
// assume identity of username, if requested
if (params.username) {
- long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize < 0) {
- bufsize = 16384;
- }
-
- char *buf = malloc(bufsize);
- if (!buf) {
- abort();
- }
-
- struct passwd pwd;
- struct passwd *res;
- getpwnam_r(params.username, &pwd, buf, bufsize, &res);
- if (!res) {
- abort();
- }
-
- if (initgroups(params.username, pwd.pw_gid) < 0) {
+ if (setgroups(num_groups, groups) < 0) {
abort();
}
@@ -314,8 +357,10 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
}
}
+ // do the exec
execv(file, argv);
+ // if we're still here, something went wrong
abort();
}
@@ -324,7 +369,7 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
if (pid < 0) {
BLog(BLOG_ERROR, "fork failed");
- goto fail0;
+ goto fail3;
}
// remember pid
@@ -336,10 +381,19 @@ int BProcess_Init2 (BProcess *o, BProcessManager *m, BProcess_handler handler, v
DebugObject_Init(&o->d_obj);
DebugError_Init(&o->d_err, BReactor_PendingGroup(m->reactor));
- return 1;
+ // Returning success, but cleanup first.
+ res = 1;
+fail3:
+ free(fds2);
+fail2:
+ if (groups != groups_static) {
+ free(groups);
+ }
+fail1:
+ free(pwnam_buf);
fail0:
- return 0;
+ return res;
}
int BProcess_InitWithFds (BProcess *o, BProcessManager *m, BProcess_handler handler, void *user, const char *file, char *const argv[], const char *username, const int *fds, const int *fds_map)