Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260418222814.7408-1-me@ziyao.cc>
Date: Sat, 18 Apr 2026 22:28:13 +0000
From: Yao Zi <me@...ao.cc>
To: musl@...ts.openwall.com
Cc: Yao Zi <me@...ao.cc>
Subject: [PATCH] dns: Avoid division-by-zero when zero attempts is specified in resolv.conf

DNS query retry interval is calculated through timeout / attempts in
__res_msend_rc(), both are loaded from resolv.conf in
__get_resolv_conf(), while value of attempts isn't checked. This would
trigger an undefined behavior if attempts is set to zero in
configuration, causing misfunction or termination with SIGFPE.

Gracefully handle it by returning early, with errno set to ECONNREFUSED
to make res_send(), which invokes __res_msend_rc() and inherits its
errno, fail with the same errno as glibc in this case.

Fixes: d6cb08bcaca4 ("factor resolv.conf parsing out of res_msend to its own file")
Signed-off-by: Yao Zi <me@...ao.cc>
---

I have to admit this bug is found by auditing musl source code with
Large Language Model's help. However, LLM is only used to seek for
problems, and I've confirmed the issue by hand and reproduced it. This
fix, and the commit message are all written by myself.

I've viewed AI policies[1] proposed by Rich before auditing. Besides a
little worried about authorship problems since I did view AI-generated
analysis and an initial reproducer, I'd prefer to say this doesn't
violate it. It would be good if we could agree on a clear policy for
such AI usage for code auditing.

Back to the technical part, this problem could be easily reproduced by
adding

	options attempts:0

to resolv.conf (do not close the editor since many stuff might
segfault!), then basically every program recursively invoking
__res_msend_rc() through either gethostbyname*() or res_send() would
crash on x86_64 with SIGFPE.

It does sound weird to make zero attempt on DNS requests, but glibc
accepts so, and fails with errno = ECONNREFUSED, which seems an
unintended behavior. I chose to follow it in this patch, while
considering errno = EINVAL to be a better outcome.

It's worth noting that before the commit introduced this bug, musl
silently clamps attempts to be at least one, which makes sense but
introducing such an implicit behavior might be unexpected.

Thanks for your time and review.

[1]: https://www.openwall.com/lists/musl/2024/10/19/3

 src/network/res_msend.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/network/res_msend.c b/src/network/res_msend.c
index 51d42ecb76f2..f26a632bd68a 100644
--- a/src/network/res_msend.c
+++ b/src/network/res_msend.c
@@ -99,10 +99,14 @@ int __res_msend_rc(int nqueries, const unsigned char *const *queries,
 	int r;
 	unsigned long t0, t1, t2;
 
-	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
-
 	timeout = 1000*conf->timeout;
 	attempts = conf->attempts;
+	if (!attempts) {
+		errno = ECONNREFUSED;
+		return -1;
+	}
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
 
 	for (nns=0; nns<conf->nns; nns++) {
 		const struct address *iplit = &conf->ns[nns];
-- 
2.53.0

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.