give each server process its own socket for the executor

this fixes a bug introduced with the prefork mechanics: every server
process shared the same socket, and this would cause a race condition
when multiple server processes asked for a script cgi being executed.

This gives each server process its own socket to talk to the executor,
so the race cannot happen.
This commit is contained in:
Omar Polo 2021-03-03 17:22:01 +00:00
parent fda7b99fc7
commit 2c3e53dac6
5 changed files with 81 additions and 93 deletions

78
ex.c
View File

@ -19,6 +19,7 @@
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
@ -397,15 +398,50 @@ childerr:
_exit(1);
}
int
executor_main()
static void
handle_fork_req(int fd, short ev, void *data)
{
char *spath, *relpath, *addr, *ruser, *cissuer, *chash;
struct vhost *vhost;
struct vhost *vhost;
struct iri iri;
time_t notbefore, notafter;
int d;
if (!recv_iri(fd, &iri)
|| !recv_string(fd, &spath)
|| !recv_string(fd, &relpath)
|| !recv_string(fd, &addr)
|| !recv_string(fd, &ruser)
|| !recv_string(fd, &cissuer)
|| !recv_string(fd, &chash)
|| !recv_time(fd, &notbefore)
|| !recv_time(fd, &notafter)
|| !recv_vhost(fd, &vhost))
fatal("failure in handling fork request");
d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash,
notbefore, notafter, vhost);
if (!send_fd(fd, d))
fatal("failure in sending the fd to the server: %s",
strerror(errno));
close(d);
free_recvd_iri(&iri);
free(spath);
free(relpath);
free(addr);
free(ruser);
free(cissuer);
free(chash);
}
int
executor_main(void)
{
struct vhost *vhost;
struct event evs[PROC_MAX];
int i;
#ifdef __OpenBSD__
for (vhost = hosts; vhost->domain != NULL; ++vhost) {
/* r so we can chdir into the correct directory */
@ -419,39 +455,15 @@ executor_main()
err(1, "pledge");
#endif
while (!hupped) {
if (!recv_iri(exfd, &iri)
|| !recv_string(exfd, &spath)
|| !recv_string(exfd, &relpath)
|| !recv_string(exfd, &addr)
|| !recv_string(exfd, &ruser)
|| !recv_string(exfd, &cissuer)
|| !recv_string(exfd, &chash)
|| !recv_time(exfd, &notbefore)
|| !recv_time(exfd, &notafter)
|| !recv_vhost(exfd, &vhost))
break;
event_init();
d = launch_cgi(&iri, spath, relpath, addr, ruser, cissuer, chash,
notbefore, notafter, vhost);
if (!send_fd(exfd, d))
break;
close(d);
free_recvd_iri(&iri);
free(spath);
free(relpath);
free(addr);
free(ruser);
free(cissuer);
free(chash);
for (i = 0; i < conf.prefork; ++i) {
event_set(&evs[i], servpipes[i], EV_READ | EV_PERSIST,
handle_fork_req, NULL);
event_add(&evs[i], NULL);
}
if (hupped)
_exit(0);
event_dispatch();
/* kill all process in my group. This means the listener and
* every pending CGI script. */
kill(0, SIGINT);
return 1;
}

1
gmid.1
View File

@ -159,6 +159,7 @@ This increases the performance and prevents delays when connecting to
a server.
.Nm
runs 3 server processes by default, when not in config-less mode.
The maximum number allowed is 16.
.It Ic protocols Ar string
Specify the TLS protocols to enable.
Refer to

89
gmid.c
View File

@ -29,7 +29,7 @@ volatile sig_atomic_t hupped;
struct vhost hosts[HOSTSLEN];
int exfd, logfd, sock4, sock6;
int exfd, logfd, sock4, sock6, servpipes[PROC_MAX];
struct imsgbuf logpibuf, logcibuf;
@ -299,31 +299,6 @@ drop_priv(void)
log_warn(NULL, "not a good idea to run a network daemon as root");
}
static int
spawn_listeners(int *p)
{
int i;
close(p[0]);
exfd = p[1];
for (i = 0; i < conf.prefork -1; ++i) {
switch (fork()) {
case -1:
fatal("fork: %s", strerror(errno));
case 0: /* child */
setproctitle("server(%d)", i+1);
return listener_main();
}
}
if (conf.prefork == 0)
setproctitle("server");
else
setproctitle("server(%d)", conf.prefork);
return listener_main();
}
static void
usage(const char *me)
{
@ -359,13 +334,13 @@ logger_init(void)
}
}
static int
serve(int argc, char **argv, int *p)
serve(int argc, char **argv)
{
char path[PATH_MAX];
char path[PATH_MAX];
int i, p[2];
if (config_path == NULL) {
if (config_path == NULL) {
if (hostname == NULL)
hostname = "localhost";
if (certs_dir == NULL)
@ -395,28 +370,35 @@ serve(int argc, char **argv, int *p)
* to put private certs inside the chroot. */
setup_tls();
switch (fork()) {
case -1:
fatal("fork: %s", strerror(errno));
for (i = 0; i < conf.prefork; ++i) {
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
PF_UNSPEC, p) == -1)
fatal("socketpair: %s", strerror(errno));
case 0: /* child */
return spawn_listeners(p);
default: /* parent */
setproctitle("executor");
close(p[1]);
exfd = p[0];
drop_priv();
unblock_signals();
return executor_main();
switch (fork()) {
case -1:
fatal("fork: %s", strerror(errno));
case 0: /* child */
close(p[0]);
exfd = p[1];
setproctitle("server");
_exit(listener_main());
default:
servpipes[i] = p[0];
close(p[1]);
}
}
setproctitle("executor");
drop_priv();
unblock_signals();
_exit(executor_main());
}
int
main(int argc, char **argv)
{
int ch, p[2];
int conftest = 0, configless = 0;
int ch, conftest = 0, configless = 0;
int old_ipv6, old_port;
init_config();
@ -482,7 +464,7 @@ main(int argc, char **argv)
if (config_path == NULL) {
configless = 1;
conf.foreground = 1;
conf.prefork = 0;
conf.prefork = 1;
conf.verbose++;
}
@ -509,10 +491,6 @@ main(int argc, char **argv)
err(1, "daemon");
}
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
PF_UNSPEC, p) == -1)
err(1, "socketpair");
if (config_path != NULL)
parse_conf(config_path);
@ -524,7 +502,7 @@ main(int argc, char **argv)
sock6 = make_socket(conf.port, AF_INET6);
if (configless)
return serve(argc, argv, p);
return serve(argc, argv);
/* wait a sighup and reload the daemon */
for (;;) {
@ -535,12 +513,9 @@ main(int argc, char **argv)
case -1:
fatal("fork: %s", strerror(errno));
case 0:
return serve(argc, argv, p);
return serve(argc, argv);
}
close(p[0]);
close(p[1]);
wait_sighup();
unblock_signals();
log_info(NULL, "reloading configuration %s", config_path);
@ -568,9 +543,5 @@ main(int argc, char **argv)
sock4 = make_socket(conf.port, AF_INET);
if (sock6 == -1 && conf.ipv6)
sock6 = make_socket(conf.port, AF_INET6);
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
PF_UNSPEC, p) == -1)
fatal("socketpair: %s", strerror(errno));
}
}

4
gmid.h
View File

@ -56,6 +56,8 @@
#define DOMAIN_NAME_LEN (253+1)
#define LABEL_LEN (63+1)
#define PROC_MAX 16
struct location {
const char *match;
const char *lang;
@ -120,6 +122,8 @@ extern struct imsgbuf logpibuf, logcibuf;
extern volatile sig_atomic_t hupped;
extern int servpipes[PROC_MAX];
struct iri {
char *schema;
char *host;

View File

@ -299,7 +299,7 @@ check_strip_no(int n)
int
check_prefork_num(int n)
{
if (n <= 0)
if (n <= 0 || n >= PROC_MAX)
yyerror("invalid prefork number %d", n);
return n;
}