[9.20] fix: usr: Fix the processing of empty catalog zone ACLs

The :iscman:`named` process could terminate unexpectedly when
processing a catalog zone ACL in an APL resource record that
was completely empty. This has been fixed.

Closes #5801

Backport of MR !11740

Merge branch 'backport-5801-catz-empty-apl-rr-bug-fix-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!11759
This commit is contained in:
Arаm Sаrgsyаn 2026-03-27 14:34:35 +00:00
commit ce365083d9
4 changed files with 173 additions and 44 deletions

View file

@ -2679,6 +2679,102 @@ wait_for_soa @10.53.0.4 tls1.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
##########################################################################
# GL #5801
nextpart ns4/named.run >/dev/null
n=$((n + 1))
echo_i "Add empty APL allow-query to catalog-misc zone using nsupdate ($n)"
ret=0
# Using "\# 0" form as a workaround for nsupdate not parsing zero length rdata
$NSUPDATE -d <<END >>nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update add allow-query.ext.catalog-misc.example. 3600 IN APL \# 0
send
END
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "waiting for secondary to sync up ($n)"
ret=0
wait_for_message ns4/named.run "catz: catalog-misc.example: reload done: success" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "Adding a domain check-allow-query.example. to primary via RNDC ($n)"
ret=0
echo "@ 3600 IN SOA . . 1 3600 3600 3600 3600" >ns1/check-allow-query.example.db
echo "@ 3600 IN NS invalid." >>ns1/check-allow-query.example.db
rndccmd 10.53.0.1 addzone check-allow-query.example. in default '{ type primary; file "check-allow-query.example.db"; allow-transfer { any; }; allow-update { any; }; notify explicit; also-notify { 10.53.0.4; }; };' || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "checking that check-allow-query.example. is now served by primary ($n)"
ret=0
wait_for_soa @10.53.0.1 check-allow-query.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
nextpart ns4/named.run >/dev/null
n=$((n + 1))
echo_i "Adding domain check-allow-query.example. to catalog-misc zone ($n)"
ret=0
$NSUPDATE -d <<END >>nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update add check-allow-query.zones.catalog-misc.example. 3600 IN PTR check-allow-query.example.
update add primaries.ext.check-allow-query.zones.catalog-misc.example. 3600 IN A 10.53.0.1
send
END
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "waiting for secondary to sync up ($n)"
ret=0
wait_for_message ns4/named.run "catz: adding zone 'check-allow-query.example' from catalog 'catalog-misc.example'" \
&& wait_for_message ns4/named.run "transfer of 'check-allow-query.example/IN' from 10.53.0.1#${PORT}: Transfer status: success" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "checking that check-allow-query.example. is not served by secondary ($n)"
ret=0
wait_for_soa @10.53.0.4 check-allow-query.example. dig.out.test$n && ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
nextpart ns4/named.run >/dev/null
n=$((n + 1))
echo_i "Deleting empty allow-query property from catalog-misc zone ($n)"
ret=0
$NSUPDATE -d <<END >>nsupdate.out.test$n 2>&1 || ret=1
server 10.53.0.1 ${PORT}
update delete allow-query.ext.catalog-misc.example. 3600 IN APL
send
END
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "waiting for secondary to sync up ($n)"
ret=0
wait_for_message ns4/named.run "catz: catalog-misc.example: reload done: success" || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "checking that check-allow-query.example. is now served by secondary ($n)"
ret=0
wait_for_soa @10.53.0.4 check-allow-query.example. dig.out.test$n || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
##########################################################################
# GL #5658

View file

@ -21,6 +21,7 @@ pytestmark = pytest.mark.extra_artifacts(
"ns*/*.nzd*",
"ns*/catalog*.example.db",
"ns*/*dom*.example.db",
"ns1/check-allow-query.example.db",
"ns1/longlong.longlong.long.long.name.example.db",
"ns1/tls1.example.db",
"ns2/__catz__*.db",

View file

@ -327,10 +327,12 @@ dns_rdata_apl_first(dns_rdata_in_apl_t *apl) {
/*
* If no APL return ISC_R_NOMORE.
*/
if (apl->apl == NULL) {
if (apl->apl == NULL || apl->apl_len == 0) {
return ISC_R_NOMORE;
}
apl->offset = 0;
/*
* Sanity check data.
*/
@ -338,7 +340,6 @@ dns_rdata_apl_first(dns_rdata_in_apl_t *apl) {
length = apl->apl[apl->offset + 3] & 0x7f;
INSIST(4 + length <= apl->apl_len);
apl->offset = 0;
return ISC_R_SUCCESS;
}

View file

@ -257,7 +257,6 @@ check_struct_conversions(dns_rdata_t *rdata, size_t structsize,
isc_buffer_t target;
void *rdata_struct;
char buf[1024];
unsigned int count = 0;
rdata_struct = isc_mem_allocate(mctx, structsize);
assert_non_null(rdata_struct);
@ -289,53 +288,82 @@ check_struct_conversions(dns_rdata_t *rdata, size_t structsize,
* https/svcb parameters.
*/
switch (type) {
case dns_rdatatype_apl: {
dns_rdata_in_apl_t *apl = rdata_struct;
for (size_t pass = 1; pass < 3; pass++) {
unsigned int count = 0;
for (result = dns_rdata_apl_first(apl);
result == ISC_R_SUCCESS;
result = dns_rdata_apl_next(apl))
{
dns_rdata_apl_ent_t apl_ent;
dns_rdata_apl_current(apl, &apl_ent);
count++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
}
break;
}
case dns_rdatatype_hip: {
dns_rdata_hip_t *hip = rdata_struct;
for (result = dns_rdata_hip_first(hip); result == ISC_R_SUCCESS;
result = dns_rdata_hip_next(hip))
{
dns_name_t name;
dns_name_init(&name, NULL);
dns_rdata_hip_current(hip, &name);
assert_int_not_equal(dns_name_countlabels(&name), 0);
assert_true(dns_name_isabsolute(&name));
count++;
for (size_t pass = 1; pass < 3; pass++) {
unsigned int count = 0;
for (result = dns_rdata_hip_first(hip);
result == ISC_R_SUCCESS;
result = dns_rdata_hip_next(hip))
{
dns_name_t name;
dns_name_init(&name, NULL);
dns_rdata_hip_current(hip, &name);
assert_int_not_equal(
dns_name_countlabels(&name), 0);
assert_true(dns_name_isabsolute(&name));
count++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
break;
}
case dns_rdatatype_https: {
dns_rdata_in_https_t *https = rdata_struct;
for (result = dns_rdata_in_https_first(https);
result == ISC_R_SUCCESS;
result = dns_rdata_in_https_next(https))
{
isc_region_t region;
dns_rdata_in_https_current(https, &region);
assert_true(region.length >= 4);
count++;
for (size_t pass = 1; pass < 3; pass++) {
unsigned int count = 0;
for (result = dns_rdata_in_https_first(https);
result == ISC_R_SUCCESS;
result = dns_rdata_in_https_next(https))
{
isc_region_t region;
dns_rdata_in_https_current(https, &region);
assert_true(region.length >= 4);
count++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
break;
}
case dns_rdatatype_svcb: {
dns_rdata_in_svcb_t *svcb = rdata_struct;
for (result = dns_rdata_in_svcb_first(svcb);
result == ISC_R_SUCCESS;
result = dns_rdata_in_svcb_next(svcb))
{
isc_region_t region;
dns_rdata_in_svcb_current(svcb, &region);
assert_true(region.length >= 4);
count++;
for (size_t pass = 1; pass < 3; pass++) {
unsigned int count = 0;
for (result = dns_rdata_in_svcb_first(svcb);
result == ISC_R_SUCCESS;
result = dns_rdata_in_svcb_next(svcb))
{
isc_region_t region;
dns_rdata_in_svcb_current(svcb, &region);
assert_true(region.length >= 4);
count++;
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
}
assert_int_equal(result, ISC_R_NOMORE);
assert_int_equal(count, loop);
break;
}
}
@ -874,23 +902,26 @@ key_required(void **state, dns_rdatatype_t type, size_t size) {
ISC_RUN_TEST_IMPL(apl) {
text_ok_t text_ok[] = {
/* empty list */
TEXT_VALID(""),
TEXT_VALID_LOOP(0, ""),
/* min,max prefix IPv4 */
TEXT_VALID("1:0.0.0.0/0"), TEXT_VALID("1:127.0.0.1/32"),
TEXT_VALID_LOOP(1, "1:0.0.0.0/0"),
TEXT_VALID_LOOP(1, "1:127.0.0.1/32"),
/* min,max prefix IPv6 */
TEXT_VALID("2:::/0"), TEXT_VALID("2:::1/128"),
TEXT_VALID_LOOP(1, "2:::/0"), TEXT_VALID_LOOP(1, "2:::1/128"),
/* negated */
TEXT_VALID("!1:0.0.0.0/0"), TEXT_VALID("!1:127.0.0.1/32"),
TEXT_VALID("!2:::/0"), TEXT_VALID("!2:::1/128"),
TEXT_VALID_LOOP(1, "!1:0.0.0.0/0"),
TEXT_VALID_LOOP(1, "!1:127.0.0.1/32"),
TEXT_VALID_LOOP(1, "!2:::/0"), TEXT_VALID_LOOP(1, "!2:::1/128"),
/* bits set after prefix length - not disallowed */
TEXT_VALID("1:127.0.0.0/0"), TEXT_VALID("2:8000::/0"),
TEXT_VALID_LOOP(1, "1:127.0.0.0/0"),
TEXT_VALID_LOOP(1, "2:8000::/0"),
/* multiple */
TEXT_VALID("1:0.0.0.0/0 1:127.0.0.1/32"),
TEXT_VALID("1:0.0.0.0/0 !1:127.0.0.1/32"),
TEXT_VALID_LOOP(2, "1:0.0.0.0/0 1:127.0.0.1/32"),
TEXT_VALID_LOOP(2, "1:0.0.0.0/0 !1:127.0.0.1/32"),
/* family 0, prefix 0, positive */
TEXT_VALID("\\# 4 00000000"),
TEXT_VALID_LOOP(1, "\\# 4 00000000"),
/* family 0, prefix 0, negative */
TEXT_VALID("\\# 4 00000080"),
TEXT_VALID_LOOP(1, "\\# 4 00000080"),
/* prefix too long */
TEXT_INVALID("1:0.0.0.0/33"), TEXT_INVALID("2:::/129"),
/*