Re: httpd: don't send the fastcgi param struct over imsg and tweaks
Kirill A. Korinsky <[email protected]> Fri, 17 Jul 2026 15:51:52 +0200
| Newsgroups | gmane.os.openbsd.tech |
|---|---|
| Message-ID | <[email protected]> |
On Thu, 16 Jul 2026 11:30:09 +0200, Rafael Sadowski <[email protected]> wrote: > > Based on the "custom header" idea and found issues. > > Send one imsg per param with a small fixed header (fastcgi_param_imsg) > followed by the name and value bytes, and read it back with imsg_get_ibuf() > and ibuf_get_string(). The struct now uses dynamic buffer instead of fixed, > and the value limit is raised to 8192. Parse order is now preserved end-to-end > instead of being reversed twice. This follows the config order: > > ... > fcgi_add_param: HTTP_USER_AGENT[15] => curl/8.21.0[11], total_len: 189 > fcgi_add_param: FCGI_TEST_A[11] => A[1], total_len: 217 > fcgi_add_param: FCGI_TEST_B[11] => B[1], total_len: 231 > fcgi_add_param: FCGI_TEST_C[11] => C[1], total_len: 245 > fcgi_add_param: FCGI_TEST_D[11] => D[1], total_len: 259 > fcgi_add_param: FCGI_TEST_E[11] => E[1], total_len: 273 > fcgi_add_param: FCGI_TEST_BB[12] => BB[2], total_len: 287 > ... > > This diff not include the change in "httpd: do NOT send location > fcgiparams ...": https://marc.info/?l=openbsd-tech&m=178397190636339&w=2 > > Feedback, OK? > > Rafael > > diff --git a/config.c b/config.c > index 146ed2c..ec1ec26 100644 > --- a/config.c > +++ b/config.c > @@ -348,67 +348,52 @@ config_settls(struct httpd *env, struct server *srv, enum tls_config_type type, > int > config_getserver_fcgiparams(struct httpd *env, struct imsg *imsg) > { > - struct server *srv; > - struct server_config *srv_conf, *iconf; > - struct fastcgi_param *fp; > - uint32_t id; > - size_t c, nc, len; > - uint8_t *p = imsg->data; > - > - len = sizeof(nc) + sizeof(id); > - if (IMSG_DATA_SIZE(imsg) < len) { > - log_debug("%s: invalid message length", __func__); > + struct server_config *srv_conf; > + struct fastcgi_param *fp; > + struct fastcgi_param_imsg fpmsg; > + struct ibuf ibuf; > + > + if (imsg_get_ibuf(imsg, &ibuf) == -1 || > + ibuf_get(&ibuf, &fpmsg, sizeof(fpmsg)) == -1) { > + log_debug("%s: invalid message", __func__); > return (-1); > } > > - memcpy(&nc, p, sizeof(nc)); /* number of params */ > - p += sizeof(nc); > - > - memcpy(&id, p, sizeof(id)); /* server conf id */ > - srv_conf = serverconfig_byid(id); > - p += sizeof(id); > - > - len += nc*sizeof(*fp); > - if (IMSG_DATA_SIZE(imsg) < len) { > - log_debug("%s: invalid message length", __func__); > + if ((srv_conf = serverconfig_byid(fpmsg.id)) == NULL) { > + log_debug("%s: invalid config id", __func__); > return (-1); > } > > - /* Find associated server config */ > - TAILQ_FOREACH(srv, env->sc_servers, srv_entry) { > - if (srv->srv_conf.id == id) { > - srv_conf = &srv->srv_conf; > - break; > - } > - TAILQ_FOREACH(iconf, &srv->srv_hosts, entry) { > - if (iconf->id == id) { > - srv_conf = iconf; > - break; > - } > - } > + if (fpmsg.namelen > HTTPD_FCGI_NAME_MAX - 1 || > + fpmsg.vallen > HTTPD_FCGI_VAL_MAX - 1) { > + log_debug("%s: fastcgi_param too long", __func__); > + return (-1); > } > > - /* Fetch FCGI parameters */ > - for (c = 0; c < nc; c++) { > - if ((fp = calloc(1, sizeof(*fp))) == NULL) > - fatalx("fcgiparams out of memory"); > - memcpy(fp, p, sizeof(*fp)); > - TAILQ_INSERT_HEAD(&srv_conf->fcgiparams, fp, entry); > + if ((fp = calloc(1, sizeof(*fp))) == NULL) > + fatal("fastcgi_param out of memory"); > > - p += sizeof(*fp); > + fp->name = ibuf_get_string(&ibuf, fpmsg.namelen); > + fp->value = ibuf_get_string(&ibuf, fpmsg.vallen); > + if (fp->name == NULL || fp->value == NULL) { > + free(fp->name); > + free(fp->value); > + free(fp); > + return (-1); > } > > + TAILQ_INSERT_TAIL(&srv_conf->fcgiparams, fp, entry); > return (0); > } > > int > config_setserver_fcgiparams(struct httpd *env, struct server *srv) > { > - struct privsep *ps = env->sc_ps; > - struct server_config *srv_conf = &srv->srv_conf; > - struct fastcgi_param *fp; > - struct iovec *iov; > - size_t c = 0, nc = 0; > + struct privsep *ps = env->sc_ps; > + struct server_config *srv_conf = &srv->srv_conf; > + struct fastcgi_param *fp; > + struct fastcgi_param_imsg fpmsg; > + struct iovec iov[3]; > > DPRINTF("%s: sending fcgiparam for \"%s[%u]\" to %s fd %d", __func__, > srv_conf->name, srv_conf->id, ps->ps_title[PROC_SERVER], > @@ -418,28 +403,24 @@ config_setserver_fcgiparams(struct httpd *env, struct server *srv) > return (0); > > TAILQ_FOREACH(fp, &srv_conf->fcgiparams, entry) { > - nc++; > - } > - if ((iov = calloc(nc + 2, sizeof(*iov))) == NULL) > - return (-1); > - > - iov[c].iov_base = &nc; /* number of params */ > - iov[c++].iov_len = sizeof(nc); > - iov[c].iov_base = &srv_conf->id; /* server config id */ > - iov[c++].iov_len = sizeof(srv_conf->id); > - > - TAILQ_FOREACH(fp, &srv_conf->fcgiparams, entry) { /* push FCGI params */ > - iov[c].iov_base = fp; > - iov[c++].iov_len = sizeof(*fp); > - } > - if (proc_composev(ps, PROC_SERVER, IMSG_CFG_FCGI, iov, c) != 0) { > - log_warn("%s: failed to compose IMSG_CFG_FCGI imsg for " > - "`%s'", __func__, srv_conf->name); > - free(iov); > - return (-1); > + fpmsg.id = srv_conf->id; > + fpmsg.namelen = strlen(fp->name); > + fpmsg.vallen = strlen(fp->value); > + > + iov[0].iov_base = &fpmsg; > + iov[0].iov_len = sizeof(fpmsg); > + iov[1].iov_base = fp->name; > + iov[1].iov_len = fpmsg.namelen; > + iov[2].iov_base = fp->value; > + iov[2].iov_len = fpmsg.vallen; > + > + if (proc_composev(ps, PROC_SERVER, IMSG_CFG_FCGI, iov, 3) > + != 0) { > + log_warn("%s: failed to compose IMSG_CFG_FCGI " > + "for `%s'", __func__, srv_conf->name); > + return (-1); > + } > } > - free(iov); > - > return (0); > } > > @@ -727,6 +708,8 @@ config_getserver(struct httpd *env, struct imsg *imsg) > memcpy(&srv->srv_conf, &srv_conf, sizeof(srv->srv_conf)); > srv->srv_s = fd; > > + TAILQ_INIT(&srv->srv_conf.fcgiparams); > + > if (config_getserver_auth(env, &srv->srv_conf) != 0) > goto fail; > > diff --git a/httpd.h b/httpd.h > index 740721e..3cce5d1 100644 > --- a/httpd.h > +++ b/httpd.h > @@ -65,8 +65,8 @@ > #define HTTPD_TLS_CIPHERS "secure" > #define HTTPD_TLS_DHE_PARAMS "none" > #define HTTPD_TLS_ECDHE_CURVES "default" > -#define HTTPD_FCGI_NAME_MAX 511 > -#define HTTPD_FCGI_VAL_MAX 511 > +#define HTTPD_FCGI_NAME_MAX 512 > +#define HTTPD_FCGI_VAL_MAX 8192 > #define FD_RESERVE 5 > > #define SERVER_MAX_CLIENTS 1024 > @@ -440,9 +440,15 @@ struct server_tls_ticket { > unsigned char tt_key[TLS_TICKET_KEY_SIZE]; > }; > > +struct fastcgi_param_imsg { > + uint32_t id; /* server conf id */ > + uint16_t namelen; > + uint16_t vallen; > +}; > + > struct fastcgi_param { > - char name[HTTPD_FCGI_NAME_MAX]; > - char value[HTTPD_FCGI_VAL_MAX]; > + char *name; > + char *value; > > TAILQ_ENTRY(fastcgi_param) entry; > }; > diff --git a/parse.y b/parse.y > index 65664e1..257ecb7 100644 > --- a/parse.y > +++ b/parse.y > @@ -813,29 +813,33 @@ fcgiflags : SOCKET STRING { > if ((param = calloc(1, sizeof(*param))) == NULL) > fatal("out of memory"); > > - if (strlcpy(param->name, $2, sizeof(param->name)) >= > - sizeof(param->name)) { > - yyerror("fastcgi_param name truncated"); > + if (strlen($2) > HTTPD_FCGI_NAME_MAX - 1) { > + yyerror("fastcgi param name too long (max %d)", > + HTTPD_FCGI_NAME_MAX - 1); > free($2); > free($3); > - free(param); > YYERROR; > } > - if (strlcpy(param->value, $3, sizeof(param->value)) >= > - sizeof(param->value)) { > - yyerror("fastcgi_param value truncated"); > + > + if (strlen($3) > HTTPD_FCGI_VAL_MAX - 1) { > + yyerror("fastcgi param value too long (max %d)", > + HTTPD_FCGI_VAL_MAX - 1); > free($2); > free($3); > - free(param); > YYERROR; > } > + > + if ((param->name = strdup($2)) == NULL || > + (param->value = strdup($3)) == NULL) > + fatal("out of memory"); > + > free($2); > free($3); > > DPRINTF("[%s,%s,%d]: adding param \"%s\" value \"%s\"", > srv_conf->location, srv_conf->name, srv_conf->id, > param->name, param->value); > - TAILQ_INSERT_HEAD(&srv_conf->fcgiparams, param, entry); > + TAILQ_INSERT_TAIL(&srv_conf->fcgiparams, param, entry); Are you sure that here no possible null case which will lead to crash? Probably some tricky things like some fastcgi { param ... } inside a location may attempt to write to null. > } > | STRIP NUMBER { > if ($2 < 0 || $2 > INT_MAX) { > diff --git a/server.c b/server.c > index 0731730..a12d6f9 100644 > --- a/server.c > +++ b/server.c > @@ -483,8 +483,11 @@ serverconfig_free(struct server_config *srv_conf) > freezero(srv_conf->tls_cert, srv_conf->tls_cert_len); > freezero(srv_conf->tls_key, srv_conf->tls_key_len); > > - TAILQ_FOREACH_SAFE(param, &srv_conf->fcgiparams, entry, tparam) > + TAILQ_FOREACH_SAFE(param, &srv_conf->fcgiparams, entry, tparam) { > + free(param->name); > + free(param->value); > free(param); > + } > } > > void > -- wbr, Kirill