hastd: Use fixed-length protocol names

All communication between hastd nodes and internally between hastd and
its worker children passes through the same pair of send / receive
functions.  The receive function uses recv(2) with the MSG_WAITALL flag,
which in theory means we should never get a short read.  However, when
handing off a socket to a worker child, we also pass a variable-length
string identifying the type of socket we're passing, and reading this
string relies on a short read.  This used to work because the arrival of
the descriptor would interrupt the recv(2) call, but this bug was fixed
when the AF_UNIX code was rewritten a while ago and hastd has been
broken ever since.

Fixing the length of the protocol name to four characters including the
terminating null solves the short-read bug by never requiring a short
read (nothing else in hastd requires one).

Note that this issue appears to have been reported independently first
by Alessandro Sagratini in PR 292322 and then by Martin Vidovic in
D57511.  I ended up going in a different direction than Martin's patch,
but his analysis was invaluable, hence the double credit below.

Approved by:	so
Security:	FreeBSD-EN-26:26.hastd
PR:		292322
Reported by:	Martin Vidovic <[email protected]>
MFC after:	1 week
Event:		EuroBSDcon DevSummit 2026
Reviewed by:	xtronom_gmail.com, kevans, glebius, gjb
Differential Revision:	https://reviews.freebsd.org/D59521

(cherry picked from commit b7e5c2e008)
(cherry picked from commit 9401bfb061)
This commit is contained in:
Dag-Erling Smørgrav authored and Mark Johnston committed 2026-10-13 14:03:00 +00:00
1 parent c192c6e54d
commit 49baaf01c2
3 files changed
+7 -12

No files matched your search

+5 -10
View File
@@ -282,7 +282,6 @@ proto_recv(const struct proto_conn *conn, void *data, size_t size)
int
proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn)
{
const char *protoname;
int ret, fd;
PJDLOG_ASSERT(conn != NULL);
@@ -294,11 +293,9 @@ proto_connection_send(const struct proto_conn *conn, struct proto_conn *mconn)
PJDLOG_ASSERT(mconn->pc_proto != NULL);
fd = proto_descriptor(mconn);
PJDLOG_ASSERT(fd >= 0);
protoname = mconn->pc_proto->prt_name;
PJDLOG_ASSERT(protoname != NULL);
ret = conn->pc_proto->prt_send(conn->pc_ctx,
(const unsigned char *)protoname, strlen(protoname) + 1, fd);
mconn->pc_proto->prt_name, sizeof(mconn->pc_proto->prt_name), fd);
proto_close(mconn);
if (ret != 0) {
errno = ret;
@@ -311,7 +308,7 @@ int
proto_connection_recv(const struct proto_conn *conn, bool client,
struct proto_conn **newconnp)
{
char protoname[128];
char protoname[sizeof(conn->pc_proto->prt_name)];
struct proto *proto;
struct proto_conn *newconn;
int ret, fd;
@@ -322,10 +319,8 @@ proto_connection_recv(const struct proto_conn *conn, bool client,
PJDLOG_ASSERT(conn->pc_proto->prt_recv != NULL);
PJDLOG_ASSERT(newconnp != NULL);
bzero(protoname, sizeof(protoname));
ret = conn->pc_proto->prt_recv(conn->pc_ctx, (unsigned char *)protoname,
sizeof(protoname) - 1, &fd);
ret = conn->pc_proto->prt_recv(conn->pc_ctx, protoname,
sizeof(protoname), &fd);
if (ret != 0) {
errno = ret;
return (-1);
@@ -334,7 +329,7 @@ proto_connection_recv(const struct proto_conn *conn, bool client,
PJDLOG_ASSERT(fd >= 0);
TAILQ_FOREACH(proto, &protos, prt_next) {
if (strcmp(proto->prt_name, protoname) == 0)
if (memcmp(proto->prt_name, protoname, sizeof(protoname)) == 0)
break;
}
if (proto == NULL) {
+1 -1
View File
@@ -53,7 +53,7 @@ typedef void prt_remote_address_t(const void *, char *, size_t);
typedef void prt_close_t(void *);
struct proto {
const char *prt_name;
char prt_name[4];
prt_client_t *prt_client;
prt_connect_t *prt_connect;
prt_connect_wait_t *prt_connect_wait;
+1 -1
View File
@@ -220,7 +220,7 @@ sp_close(void *ctx)
}
static struct proto sp_proto = {
.prt_name = "socketpair",
.prt_name = "skp",
.prt_client = sp_client,
.prt_send = sp_send,
.prt_recv = sp_recv,