fix: usr: Fix dig -x crash on excessively long arguments

dig -x crashed with a segmentation fault rather than printing an
error when given an argument with thousands of dot-separated
components. dig -x now rejects such inputs cleanly with "Invalid IP
address".

Closes #5917

Merge branch '5917-dig-reverse-octets-stack-overflow' into 'main'

See merge request isc-projects/bind9!11928
This commit is contained in:
Ondřej Surý 2026-04-30 14:02:08 +02:00
commit 62ced63e22
2 changed files with 40 additions and 9 deletions

View file

@ -288,16 +288,33 @@ append(const char *text, size_t len, char **p, char *end) {
static isc_result_t
reverse_octets(const char *in, char **p, char *end) {
const char *dot = strchr(in, '.');
size_t len;
if (dot != NULL) {
RETERR(reverse_octets(dot + 1, p, end));
RETERR(append(".", 1, p, end));
len = (int)(dot - in);
} else {
len = (int)strlen(in);
const char *parts[DNS_NAME_MAXLABELS];
size_t lens[DNS_NAME_MAXLABELS];
size_t n = 0;
const char *cursor = in;
while (true) {
const char *dot = strchr(cursor, '.');
if (n >= DNS_NAME_MAXLABELS) {
return DNS_R_NAMETOOLONG;
}
parts[n] = cursor;
lens[n] = (dot != NULL) ? (size_t)(dot - cursor)
: strlen(cursor);
n++;
if (dot == NULL) {
break;
}
cursor = dot + 1;
}
return append(in, len, p, end);
for (size_t i = n; i-- > 0;) {
if (i + 1 < n) {
RETERR(append(".", 1, p, end));
}
RETERR(append(parts[i], lens[i], p, end));
}
return ISC_R_SUCCESS;
}
isc_result_t

View file

@ -148,6 +148,20 @@ if [ -x "$DIG" ]; then
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "checking dig -x rejects deeply nested input cleanly ($n)"
ret=0
longinput="$(printf '1.%.0s' $(seq 1 6400))1"
# Pre-fix: SIGSEGV (139) or ASan abort (134) from unbounded recursion in
# reverse_octets() on the dots. Post-fix: structured rejection via
# DNS_R_NAMETOOLONG -> "Invalid IP address" -> exit 1.
rc=0
dig_with_opts -x "$longinput" >dig.out.test$n 2>&1 || rc=$?
[ $rc -ge 128 ] && ret=1
grep "Invalid IP address" dig.out.test$n >/dev/null || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "checking dig over TCP works ($n)"
ret=0