Re: httpd: don't send the fastcgi param struct over imsg and tweaks

Rafael Sadowski <[email protected]> Sat, 18 Jul 2026 08:58:49 +0200
Newsgroups gmane.os.openbsd.tech
Message-ID <[email protected]>
On Fri Jul 17, 2026 at 11:57:47PM +0200, Kirill A. Korinsky wrote:
> On Fri, 17 Jul 2026 22:02:13 +0200,
> Rafael Sadowski <[email protected]> wrote:
> > 
> >
> 
> Mostly good, a few minor findings:
> 
> > -	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);
> > +		}
> 
> Here tricky part: if not first proc_composev() call fails, early messages
> are alread commited.
> 
> Before that refactoring here was none or all contract, now it none,
> something or all.

Yes and No. That's a very good point if you look at the diff in
isolation, but we've already sorted that out with the [1] diff:

+	if ((srv->srv_conf.flags & SRVFLAG_LOCATION) == 0) {
+		/* Configure TLS if necessary. */
+		if (config_setserver_tls(env, srv) != 0)
+			return (-1);
+	}
+
+	/* Configure FCGI parameters if necessary. */
+	if (config_setserver_fcgiparams(env, srv) != 0)
+		return (-1);
+

You've given your OK for this. Here, we check the return value and exit
with -1 if a proc_composev fails. Sure we could quit with fail()
directly but I think it's better to push the error up the chain. In the
end with end up with a fail().

1: https://marc.info/?l=openbsd-tech&m=178429421655440&w=2

> 
> > @@ -813,29 +814,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);
> >  		}
> >  		| STRIP NUMBER			{
> >  			if ($2 < 0 || $2 > INT_MAX) {
> 
> I think here you're leaking param on not sucessfull exit.

Yes I fix it by placing the calloc after the _MAX checks.



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..6640d0b 100644
--- a/parse.y
+++ b/parse.y
@@ -657,6 +657,7 @@ serveroptsl	: LISTEN ON STRING opttls port	{
 			srv = s;
 			srv_conf = &srv->srv_conf;
 			SPLAY_INIT(&srv->srv_clients);
+			TAILQ_INIT(&srv_conf->fcgiparams);
 		} '{' optnl serveropts_l '}'	{
 			struct server	*s = NULL;
 			uint64_t	 f;
@@ -810,32 +811,37 @@ fcgiflags	: SOCKET STRING {
 		| PARAM STRING STRING	{
 			struct fastcgi_param	*param;
 
-			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 = calloc(1, sizeof(*param))) == NULL)
+				fatal("out of memory");
+
+			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);
 		}
 		| 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