Alessio Podda [Mon, 3 Aug 2026 13:04:20 +0000 (13:04 +0000)]
chg: usr: Batch qp transaction for RPZ updates
RPZ was built around fine-grained locking, but that forces the use of many small qp transactions. With this MR, we switch qp transaction to handle the
full update to the rpz summary structure.
While this serializes the RPZ updates, the reduced overhead from batching qp transactions more than compensates for it and results in improvements for big RPZ zones.
Before this commit, maint_lock jointly protected both the "summary
structure" dns_rpz_zones_t and the individual zones. In particular,
locking maint_lock would block timer callbacks and shutdown requests,
which is undesirable.
Now that the update state has been moved to the baton, there is no
longer a reason to have a lock shared among all zones. This commit
splits the old rpzs->maint_lock into two.
First, rpz->update_lock protects callbacks and timers for individual
RPZ zones. Second, rpzs->data_lock protects joint operations on the QP
and CIDR trees. The latter replaces the old maint_lock.
Instead of embedding the "in progress" database inside the RPZ zone
struct, we can attach it to the baton passed to libuv. This simplifies
lifetimes and will make refactoring the locking possible.
Alessio Podda [Mon, 9 Mar 2026 23:12:54 +0000 (00:12 +0100)]
Delete optimistically from rpz nodes hashtable
Instead of checking whether an element is present and then deleting it,
we can try to delete it optimistically and then rely on the result to
know if the element was present or not.
Alessio Podda [Thu, 5 Mar 2026 15:55:00 +0000 (16:55 +0100)]
Batch qp transaction
Qp write transactions have a setup cost, so going from many small
transactions to a single one that covers the whole rpz zone processing
is a performance win, especially with big rpz zones.
Move from many small transactions to a single big transaction.
Alessio Podda [Thu, 5 Mar 2026 14:09:08 +0000 (15:09 +0100)]
Increase lock scope in rpzs construction
RPZ was built around fine-grained locking, but that forces the use of
many small qp transactions. This commit increases the scope of
rpzs->maint_lock to encompass the full processing of an RPZ zone.
Done as a preparation for a later commit.
Previously, the packet.pl script was used to send the a series of frames
to named; this worked by accident as most of these were refused by the
kernel with EAGAIN. packet.pl prints a dot every 1000 packets, so this
slowed the script down and allowed some frames to get through.
Reimplement the test in Python: build the packet with dnspython, send
~6 MiB of data over TCP discarding all replies and then check if the
server is still alive.
The server may close the connection before we have written the whole
stream. This shows up notably on Alma Linux 8: under concurrent load
its small default TCP buffers let the flow control between us and named
wedge, named then hits its idle timeout and resets the connection
mid-write, and drain() raises ConnectionError.
That is fine -- the connection being torn down is acceptable and the
only thing this test asserts is that named survives the flood, which
the final query checks -- so tolerate the server closing the connection
early.
Add Python helpers for inspecting `rndc status`, opening probe
connections, and waiting for counter changes, then use them to port the
TCP and recursive high-water checks from the shell script.
Connections are now managed by the test script directly removing the
need for the ans6 server. This also removes the need for the send.pl
script and the respective shell test helper as they were used to control
said server.
Add a helper that runs `rndc stats` and reads the TCP request counter
from named.stats, then use it to port the resolver and forwarder checks
from the shell script to tests_tcp.py. Record named.stats as an extra
artifact so the generated statistics remain available after test runs.
Use isctest.query.create() and a shared round-trip helper in
bin/tests/system/tcp/tests_tcp.py, add type hints, and reorganize the
existing tests to follow current style.
Store negative cache entries under their natural type
Negative cache entries were exposed through dns_rdataset_t in an
inverted shape (type=none, covers=<qtype>) while the cache internally
stored them under (<qtype>, none) with a NEGATIVE flag, so every
consumer had to convert between the two representations, and several
places relied on type==0 as an implicit negativity test.
Expose negative entries in their natural shape instead: type holds the
RR type whose nonexistence is cached (dns_rdatatype_any for NXDOMAIN
and NODATA(QTYPE=ANY) proofs), covers stays none, and the negative
attribute marks the entry. The implicit type==0 tests become explicit
attribute checks, and the cache database now rejects meta-types other
than ANY, which remains valid as a lookup type and as a negative
entry.
chg: dev: Delete cache rdatasets directly instead of tombstoning them
Deleting an rdataset from the cache left a placeholder entry
behind that every lookup had to skip until it aged out. Deletion
now removes the entry outright, and the placeholder mechanism is gone.
Merge branch 'ondrej/qpcache-cleanup-delete-tombstone' into 'main'
Delete cache rdatasets directly instead of tombstoning them
The refactoring that cleans the previous headers immediately from the
cache allows additional cleanup. The cache now does not require the
NONEXISTENT tombstone and dns_db_deleterdataset() can directly remove
the header with associate type from the cache. This simplifies all the
other code working with headers as the cache now only contains header
that carry information, so the only check we need to do now is ACTIVE()
check whether the TTL is still ok (+ serve-stale hacks).
chg: ci: Use meson native files for CI build configure flags
Make it easier to reproduce failed CI builds locally by using meson native files to configure the jobs, rather than composing variables in `.gitlab-ci.yml`.
To reproduce a build locally, the following can be used now: (with `$EXTRA_SETUP_FILES` being set per-job in `.gitlab-ci.yml`, single variable, no composition)
The `*tarball*` CI jobs build directly from the unpacked tarball, but
the entire ci/ directory was export-ignored. Add the required ci/*.ini
scripts so these jobs can pass in CI.
Use meson native files for CI build configure flags
CI build jobs carried compiler flags and project options as YAML
variables and EXTRA_CONFIGURE strings composed through inheritance and
shell interpolation, which is awkward to reproduce locally. Move the
shared flags into meson native files under ci/ so a build can be
reproduced directly:
ci/common.ini exports cflags_common as a [constants] value so per-job
overlays can extend rather than replace it:
c_args = cflags_common + ['-DDEBUG']
The cross build file ci/amd64cross32.ini repeats the c_args from
ci/common.ini, because that file only configures the host and the
[constants] section is not visible for the cross build.
ci/scan-build.ini likewise repeats ci/common.ini instead of layering on
it, and notably declares no [binaries] section. scan-build substitutes
its analyzer wrapper by setting $CC via --use-cc, but a machine file's
[binaries] c takes precedence over $CC; inheriting the c = 'gcc' from
ci/common.ini would pin the compiler back to gcc and leave the analyzer
unused, undoing 23a722db57.
ci/reprotest.ini is standalone too. `meson reprotest` builds twice
and compares the results byte for byte, so it configures the smallest
build that still exercises the compiler rather than the full CI option
set.
The tumbleweed and tumbleed-libuv options were unified. The only
difference was gssapi=disabled, which seems like it should apply to all
tumbleweed builds as per 1b2c191bed4097e1095de3bc2f3854b6db894a8e.
Nicki Křížek [Tue, 30 Jun 2026 15:00:36 +0000 (15:00 +0000)]
Drop the redundant readline configuration check from CI
.check_readline_setup grepped build/config.h to confirm the requested
libedit/readline backend was actually selected. It dates to 2020
(bc1c0d2ef4), the autotools era, when several backends
(readline/editline/libedit) were selectable and autoconf's detection
could silently fall back to a different one or to none.
main builds only with meson now, where "line" is a strict feature
option: -Dline=enabled makes libedit a hard dependency (configure fails
if it is missing) and -Dline=disabled guarantees HAVE_LIBEDIT is never
set. Both assertions the check makes are therefore tautological -- the
silent-misdetection failure mode it guarded against can no longer occur.
Ignore comments when comparing built-in root hints to named.root
InterNIC periodically republishes named.root with only the "last
update" and "related version of root zone" comments changed, which
failed the live-internet check even though the actual records were
unchanged. Updating the built-in hints for comment-only churn is
pointless, so compare just the resource records.
chg: dev: Reduce the memory used by each record set in the cache
Each record set in the cache carried a 32-byte table for
restoring its owner name's letter case, unused since the
case handling was reworked. Removing it makes every cache
entry 32 bytes smaller.
Merge branch 'ondrej/remove-upper-from-slabheader' into 'main'
Since the database ownercase became modifiable only through
dns_db_addrdataset(), nothing ever sets the CASESET attribute on a
slabheader, so the upper[] case bitmap was never written and the slab
getownercase method could never restore anything. Remove both,
shrinking every cache slabheader by 32 bytes.
Martin Basti [Wed, 29 Jul 2026 12:31:57 +0000 (12:31 +0000)]
chg: test: Convert the digdelv system test from shell to pytest
The digdelv system test was a 2000-line tests.sh script run through the shell-test wrapper. It is now four native pytest modules — tests_dig.py, tests_delv.py, tests_mdig.py and tests_others.py (nslookup, host, nsupdate) — with shared helpers in common.py, repeated checks parametrized, and the yamlget.py helper script replaced by direct PyYAML parsing. PyYAML is now a required test dependency: the +yaml checks fail instead of being skipped when it is missing, and each module is skipped as a whole when its tool is not built.
Merge branch 'mbasti/digdelv-tests-to-py' into 'main'
Martin Basti [Mon, 27 Jul 2026 14:51:02 +0000 (14:51 +0000)]
Drop executable-availability skips from the digdelv tests
The digdelv modules skipped themselves when the tool under test was
not built, but dig, delv, mdig, nslookup, host and nsupdate are
always built on the main branch, so the guards could never trigger
and would only hide genuine breakage as a skip.
Martin Basti [Thu, 23 Jul 2026 09:09:54 +0000 (09:09 +0000)]
Gate the dig source-address crash check on IPv6 availability
The check for the crash with an IPv4 source address and a server with
both IPv4 and IPv6 addresses relies on @localhost resolving to ::1 as
well as 127.0.0.1: without IPv6 the address-family mismatch under test
never happens and the test passes without exercising anything. Skip
it on hosts without IPv6 instead, like the other IPv6-dependent digdelv
tests. The remaining unmarked tests that mention IPv6 addresses only
parse them (the IPv4-mapped server refusal) or read them as record
data, so they work without IPv6.
Martin Basti [Thu, 23 Jul 2026 09:01:11 +0000 (09:01 +0000)]
Require PyYAML for the digdelv system test
Previously the digdelv +yaml checks were skipped when PyYAML was not
installed. Treat PyYAML as a hard test dependency instead.
Drop the conditional needs_pyyaml skips, so a missing module now
fails the tests at import time.
Martin Basti [Wed, 22 Jul 2026 22:26:06 +0000 (22:26 +0000)]
Migrate digdelv delv +ns checks to pytest, dropping tests.sh
Move the delv +ns checks from tests.sh into tests_delv.py: the NS
records in the iterative-mode output, lookups with and without
validation and query name minimization, the -4/-6 address family
selection, and the DNS COOKIE options. This migrates the last check
left in tests.sh, so tests.sh, the tests_sh_digdelv.py wrapper and the
now-unused yamlget.py helper are removed.
Martin Basti [Mon, 27 Jul 2026 14:12:10 +0000 (14:12 +0000)]
Migrate digdelv delv checks to pytest
Move the delv checks from tests.sh into a new tests_delv.py module:
+short/+sh, +split/+sp, +unknownformat, the rrcomments/multi matrix,
+nosplit field counts, +cookie/+nocookie, class handling, reverse
lookups, -q -m, -t ANY, -4/-6 handling and address family mismatches,
the malformed query name exit code, key-style and DS-style trust
anchors, REFUSED while chasing DS records, and the +yaml output
structure for positive and negative responses. check_ttl_range()
learns to look inside delv's commented records ("; name ttl class
type ..."), where its textual type comparison also matches delv's
\-ANY pseudo-type; the duplicated "-c CH" check is folded into one
test, and the module is skipped entirely when the delv executable is
not available.
Add __init__.py to avoid a module-name collision with
dnssec/tests_delv.py: without it, pytest imports the file as plain
tests_delv, which conflicts.
Martin Basti [Wed, 22 Jul 2026 22:04:19 +0000 (22:04 +0000)]
Migrate digdelv mdig checks to pytest
Move the mdig checks from tests.sh into a new tests_mdig.py module:
the source address and port handling over TCP, the malformed
'+ednsopt=:' option, the +multi +norrcomments output for DNSKEY and
SOA, and the +yaml output structure. Like tests_dig.py, the module is
skipped entirely when the mdig executable is not available.
Martin Basti [Wed, 22 Jul 2026 21:47:34 +0000 (21:47 +0000)]
Migrate digdelv dig +yaml structure checks to pytest
Move the remaining dig checks from tests.sh into tests_dig.py: the
structure of +yaml output for an ANY query, the printing of an IPv6
address ending in zeroes, the quoting of qnames containing characters
special to YAML, and the quoting of all 256 character values in TXT
rdata, whose expected string is now computed instead of spelled out
over thirty lines. This completes the dig section of tests.sh, so the
dig_with_opts and set_response_sequence helpers are removed with it.
Martin Basti [Wed, 22 Jul 2026 21:24:12 +0000 (21:24 +0000)]
Migrate digdelv dig failover checks to pytest
Move the dig connection-handling checks from tests.sh into
tests_dig.py: AXFR retries upon TCP EOF driven by the ans5 response
sequences, trying the next server after unreachable/read/connection
errors and timeouts, timeout followed by SERVFAIL, the retry comment
with +short +comments, the startup banner and +nocmd on the error
path, +yaml validity when no server is reached, the IPv4-mapped IPv6
server refusal, -4/-6 handling and the source-address crash check.
The ans5 sequence cases and the UDP/TCP pairs are parametrized, and
each case now arms its own response sequence so the tests are
order-independent.
Martin Basti [Wed, 22 Jul 2026 15:12:13 +0000 (15:12 +0000)]
Migrate digdelv dig +subnet checks to pytest
Move the dig EDNS client-subnet checks from tests.sh into
tests_dig.py: +subnet with IPv4, IPv6 and zero-length subnets, the
precedence of repeated +subnet options, prefix lengths 1-24 and
prefixes between byte boundaries, raw +ednsopt=8 encodings rejected by
the server, and the +yaml counterparts. The prefix-length loops are
parametrized and the expected masked address is computed with the
ipaddress module instead of a hardcoded table.
Martin Basti [Wed, 22 Jul 2026 14:52:10 +0000 (14:52 +0000)]
Migrate digdelv dig EDNS checks to pytest
Move the dig EDNS checks from tests.sh into tests_dig.py: the header
and EDNS flag options (+coflag/+raflag/+tcflag/+zflag, +ednsflags,
+showbadvers, +showtruncated), the +ednsopt family (NSID, KEY-TAG,
CLIENT-TAG, SERVER-TAG, CHAIN, EDE, UPDATE-LEASE, LLQ and malformed
options), +expire, +keepalive and +zoneversion, together with their
+yaml counterparts. The yamlget.py helper script is replaced by a
yaml_get() function which walks the parsed YAML directly, so the +yaml
checks now compare typed values instead of strings, and the client-tag,
server-tag and EDE cases are parametrized.
Martin Basti [Wed, 22 Jul 2026 14:08:43 +0000 (14:08 +0000)]
Migrate digdelv dig formatting checks to pytest
Move the dig output-formatting and command-line handling checks from
tests.sh into a new tests_dig.py module: +short, +split/+sp,
+unknownformat, reverse lookups, the rrcomments/multi matrix,
+header-only, +ttlunits, +expandaaaa, +bufsize, query-time units and
+yaml timestamp precision, +nocrypto, class CHAOS, query-name error
handling and the UPDATE-opcode rejection. Repeated checks are
parametrized and the duplicated "+short +rrcomments" check is folded
into a single test.
The shared check_ttl_range helper mirrors the original awk check: it
scans the output line by line, accepts the class tokens IN and CLASS1
(the RFC 3597 unknown-format spelling), and compares the RR type
token textually and the TTL numerically.
Martin Basti [Wed, 22 Jul 2026 12:27:36 +0000 (12:27 +0000)]
Migrate digdelv nslookup/host/nsupdate checks to pytest
Move the UPDATE-opcode rejection checks for nslookup, host and
nsupdate from tests.sh into a new tests_others.py module, which holds
the checks for the look-up tools other than dig, delv and mdig. The
new tests assert on the exact stream (stdout or stderr) each tool
prints to, instead of the combined output the shell version grepped.
BIND used to cache an NSEC3 closest encloser proof alongside positive wildcard answers so that a resolver could re-send it when answering from its cache. That stopped being used in
BIND 9.9 (2011), when positive wildcard responses were changed to omit that NSEC3 record — RFC 5155 requires only the next closer name proof — and the closest encloser came to be
derived during validation instead. The caching code has been unreachable ever since, so this removes it with no change in behaviour.
The addclosest/getclosest mechanism cached an NSEC3 closest encloser
proof alongside a positive wildcard answer so a recursive server could
re-emit it when answering from cache. It has been dead since changeset
3175 (RT #26200, 2011, first released in 9.9.0), which fixed both ends
of the exchange at once: the authoritative server stopped sending the
closest encloser NSEC3 in positive wildcard responses (RFC 5155 section
7.2.6 only requires the next closer name proof), and the validator
started deriving the closest encloser from the wildcard signature rather
than discovering it from a response record. Since then findnsec3proofs()
never sets DNS_VALIDATOR_CLOSESTENCLOSER for a positive wildcard -- 'val->
closest' is already known, so closest-encloser discovery is skipped -- so
the resolver's addclosest call and everything downstream of it (cache
storage, retrieval, and the query.c response path) can no longer be
reached. Remove the whole chain.
With it gone, the 'setclosest' output flag of dns_nsec3_noexistnodata()
has no remaining reader: findnsec3proofs() was its last consumer, and
findnoqname() in the resolver only ever inspected 'setnearest'. Drop the
flag and its scratch variables; the closest encloser name itself is still
discovered and returned via the 'closest' argument.
dns_private_chains wasn't looking for the private records that
indicate that a zone is being signed by a PRIVATE DNSSEC algorithm.
This has been fixed.
Closes #6205
Merge branch '6205-dns_private_chains-fix-private-algorithms-support' into 'main'
Martin Basti [Tue, 28 Jul 2026 15:22:20 +0000 (15:22 +0000)]
Add unit test for dns_private_chains
The private test only exercised dns_private_totext. Add coverage
for dns_private_chains in the no-NSEC, no-NSEC3PARAM case: the old
5-octet signing record form, the new 7-octet form used for PRIVATE
DNSSEC algorithms, records that must not trigger chain building
(mismatched algorithm octet, completed signing), and the combination
with a queued NSEC3 chain creation. The new-form case fails without
the preceding dns_private_chains fix.
dns_private_chains wasn't looking for the private records that
indicate that a zone is being signed by a PRIVATE DNSSEC algorithm.
This has been fixed.
Michal Nowak [Tue, 28 Jul 2026 10:38:21 +0000 (12:38 +0200)]
rem: ci: Drop FreeBSD 13 support
FreeBSD 13 has reached its end of life, so stop testing on it: remove
the CI build, system test, and unit test jobs together with the runner
tag anchor they used, and move the release from the regularly tested
platforms to the best-effort list in the ARM.
The is_host_freebsd_13 helper existed solely to grant one rerun to two
tests that were flaky on that release, so it goes away with them.
Assisted-by: Claude:claude-opus-5[1m]
Merge branch 'mnowak/drop-freebsd-13' into 'main'
Michal Nowak [Tue, 28 Jul 2026 08:47:41 +0000 (10:47 +0200)]
Drop FreeBSD 13 support
FreeBSD 13 has reached its end of life, so stop testing on it: remove
the CI build, system test, and unit test jobs together with the runner
tag anchor they used, and move the release from the regularly tested
platforms to the best-effort list in the ARM.
The is_host_freebsd_13 helper existed solely to grant one rerun to two
tests that were flaky on that release, so it goes away with them.
Michal Nowak [Tue, 28 Jul 2026 09:52:15 +0000 (11:52 +0200)]
chg: dev: Use sanitizer.contains() instead of the "in" operator
muon's static analyzer aborts on the four "'address' in sanitizer"
expressions because it cannot typecheck the "in" operator against the
complex combo type it infers for b_sanitize, which makes "muon analyze"
unusable on the whole tree. Reported upstream as
https://github.com/muon-build/muon/issues/289.
Assisted-by: Claude:claude-fable-5
Merge branch 'mnowak/meson-sanitizer-contains' into 'main'
Michal Nowak [Mon, 27 Jul 2026 14:44:40 +0000 (16:44 +0200)]
Use sanitizer.contains() instead of the "in" operator
muon's static analyzer aborts on the four "'address' in sanitizer"
expressions because it cannot typecheck the "in" operator against the
complex combo type it infers for b_sanitize, which makes "muon analyze"
unusable on the whole tree. Reported upstream as
https://github.com/muon-build/muon/issues/289.
Aydın Mercan [Wed, 22 Jul 2026 15:08:49 +0000 (18:08 +0300)]
use the lint test suite in misc job
The `misc` job in CI already needs to have a build directory configured
when checking for rdata registration. Thus, it is safe to transition the
CI job to use the `lint` test suite instead.
Michal Nowak [Thu, 23 Jul 2026 09:18:34 +0000 (11:18 +0200)]
Add missing putnull() to named_smf_add_message()
The control channel response text needs to be NUL-terminated.
named_smf_add_message() did not do that, so 'rndc stop' or
'rndc halt' on an SMF-managed named running chrooted could echo
uninitialized heap contents after the 'use svcadm(1M) to manage
named' message.
Michal Nowak [Thu, 23 Jul 2026 09:18:34 +0000 (11:18 +0200)]
Restore SMF support in the build system
libscf detection (AC_CHECK_LIB(scf, smf_enable_instance)) was lost
in the automake rewrite (978c7b2e89aa) and was not added back when
the build system was replaced with meson, so HAVE_LIBSCF was never
defined and the Solaris SMF code in bin/named was dead code.
Detect libscf on SunOS hosts and link named against it.
fix: test: Drop a redundant racy check in fetchlimit test
This particular `fetchlimit` failure has been happening quite often since https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/12124 removed the test re-runs with `flaky` (after fixing other instabilities inside the test).
Example failure: https://gitlab.isc.org/isc-projects/bind9/-/jobs/7780648
Merge branch 'nicki/fix-fetchlimit-recovery-quota-flaky' into 'main'
The "lame server recovery" step required the adaptive per-server quota
for the lame server to have dropped further after it started answering
again. That decrease is not reliable: the quota only drops while the
timeout ratio (atr) exceeds the high watermark, and once the server
answers atr falls, so a further drop cannot be relied upon.
Remove the check to improve the fetchlimit test stability in slower
platforms in CI. The quota at the start of the recovery is still
recorded -- it is checked later on that it is increased once the server
recovers.
In `ddns-confgen`, the user-supplied domain name to be updated was not properly escaped and put inside a double-quoted string when generating the example code fragment. This has been corrected.
Closes #6072
Merge branch '6072-ddns-confgen-self-domain-update-policy-injection' into 'main'
In ddns-confgen, the user-supplied domain name to be updated
was not properly escaped and put inside a double quoted string
when generating the example code fragment. This has been corrected.
Michal Nowak [Thu, 23 Jul 2026 16:24:10 +0000 (18:24 +0200)]
fix: dev: Restore arc4random() detection dropped in the v9.21.14 merge
Commit 4db9e5d90e2 ("Use arc4random for CSPRNG when available", part
of the CVE-2025-40780 fix) guarded the arc4random() code paths in
lib/isc/random.h and lib/isc/random.c with HAVE_ARC4RANDOM and added
the corresponding function check to meson.build. The manual conflict
resolution in merge c2a672bbaef ("Merge tag 'v9.21.14'") kept the code
changes but dropped the meson.build hunk, so HAVE_ARC4RANDOM was never
defined and platforms with arc4random() (macOS and the BSDs) silently
fell back to the internal ChaCha-based CSPRNG. Restore the check.
Assisted-by: Claude:claude-fable-5
Merge branch 'mnowak/restore-arc4random-meson-check' into 'main'
Michal Nowak [Thu, 23 Jul 2026 14:47:31 +0000 (16:47 +0200)]
Restore arc4random() detection dropped in the v9.21.14 merge
Commit 4db9e5d90e2 ("Use arc4random for CSPRNG when available", part
of the CVE-2025-40780 fix) guarded the arc4random() code paths in
lib/isc/random.h and lib/isc/random.c with HAVE_ARC4RANDOM and added
the corresponding function check to meson.build. The manual conflict
resolution in merge c2a672bbaef ("Merge tag 'v9.21.14'") kept the code
changes but dropped the meson.build hunk, so HAVE_ARC4RANDOM was never
defined and platforms with arc4random() (macOS and the BSDs) silently
fell back to the internal ChaCha-based CSPRNG. Restore the check.
Michal Nowak [Thu, 23 Jul 2026 12:24:19 +0000 (14:24 +0200)]
fix: ci: Make the scan-build CI job actually run the static analyzer
scan-build was a silent no-op: the native file pinned CC to a name ccc-analyzer never intercepts. Run meson and ninja under scan-build with --use-cc, drop the native file and LTO, and gate on all default checkers via --status-bugs.
Merge branch '6165-scan-build-actually-run-analyzer' into 'main'
Michal Nowak [Mon, 29 Jun 2026 12:41:03 +0000 (12:41 +0000)]
Silence scan-build leak false positive in isccc_cc_checkdup()
On success the symtab owns 'key' and frees it via symtab_undefine(), but
scan-build cannot see that ownership pass through the const key
parameter of isc_symtab_define() and reports a leak. free(key) under
__clang_analyzer__ only, which placates the checker without affecting
real builds:
lib/isccc/cc.c:1015:9: warning: Potential leak of memory pointed to by 'key' [unix.Malloc]
Michal Nowak [Mon, 29 Jun 2026 11:41:20 +0000 (11:41 +0000)]
Assert rdataset precondition in query_addrrset()
rdataset is always non-NULL and is dereferenced unconditionally below.
Make the precondition explicit to silence a scan-build false positive:
lib/ns/query.c:2254:11: warning: Access to field 'type' results in a dereference of a null pointer (loaded from variable 'rdataset') [core.NullDereference]
Michal Nowak [Mon, 29 Jun 2026 11:41:09 +0000 (11:41 +0000)]
Assert fd validity in start_tcp_child()
load_balance_sockets is fixed at netmgr init, so dup(fd) runs only when
the caller supplied a valid descriptor. Assert it to silence a
scan-build false positive:
lib/isc/netmgr/tcp.c:468:15: warning: The 1st argument to 'dup' is -1 but should be >= 0 [unix.StdCLibraryFunctions]
Michal Nowak [Mon, 29 Jun 2026 11:40:50 +0000 (11:40 +0000)]
Assert fd validity in start_udp_child()
load_balance_sockets is fixed at netmgr init, so dup(fd) runs only when
the caller supplied a valid descriptor. Assert it to silence a
scan-build false positive:
lib/isc/netmgr/udp.c:194:15: warning: The 1st argument to 'dup' is -1 but should be >= 0 [unix.StdCLibraryFunctions]
Michal Nowak [Mon, 22 Jun 2026 19:24:33 +0000 (21:24 +0200)]
Remove dead stores reported by scan-build
The syncplugin test driver overwrote two dns_name_fromstring() results
without checking them; wrap the calls in CHECK():
bin/tests/system/hooks/driver/test-syncplugin.c:156:3: warning: Value stored to 'result' is never read [deadcode.DeadStores]
bin/tests/system/hooks/driver/test-syncplugin.c:158:3: warning: Value stored to 'result' is never read [deadcode.DeadStores]
Michal Nowak [Mon, 22 Jun 2026 16:25:16 +0000 (18:25 +0200)]
Run scan-build static analysis in CI
scan-build was a silent no-op: the native file pinned CC to a name
ccc-analyzer never intercepts. Run meson and ninja under scan-build
with --use-cc, drop the native file and LTO, and gate on all default
checkers via --status-bugs.
new: test: Mark each test's boundaries in the named instance logs
Use `rndc null` command to mark each test boundaries in named.run logs.
Since calling the `rndc` binary caused a lot of overhead, import the rndc python interface from [python-rndc](https://gitlab.isc.org/isc-projects/python-rndc/) and adapt it to our use-case.
Merge branch 'nicki/rndc-null-in-system-tests' into 'main'
Mark each test's boundaries in the named instance logs
Send an `rndc null` command with a test ID marker at the start and end
of each test function to all named instances.
This makes it easy to identify the precise point where each test was
executed when reading the named logs. This becomes especially useful for
modules which run multiple test functions, since those are executed on
the same named instances.
Register the rndc module in the isctest package and provide a
NamedInstance.rndc_client() factory next to rndc(), so tests can grab
a client for an instance's control channel without repeating the
address and port details.
Rework the Python RNDC client API around isctest conventions
Rename the class to RNDCClient and align its interface with the
existing isctest.run helpers so that the binary and the Python RNDC
interfaces feel the same:
- take separate ip/port arguments and accept commands as str
- default the HMAC algorithm and secret to the _common/rndc.key
values used by virtually all control channels in the system tests
- call() returns an RNDCResult mirroring CmdResult (rc/out/err) and
raises RNDCException on a non-zero result code, with the same
raise_on_exception escape hatch as isctest.run.cmd(); the decoded
protocol response stays available as RNDCResult.response
- support the context manager protocol and add close(); the socket
gets a timeout so a stuck server can't hang a test, and a failed
login closes the socket instead of leaking it
- use sendall() and an exact-read loop to avoid short reads/writes
(MSG_WAITALL is ineffective on a socket with a timeout, which
operates in non-blocking mode)
- raise RNDCProtocolError for truncated, malformed, or
unauthenticated responses instead of overloading OSError
- draw the initial message serial from the full 32-bit space like
the rndc binary, keeping the odds of tripping named's duplicate
message suppression negligible
Annotate the module for the mypy CI gate. The locals in _command()
that were reused for values of a different type are split so each name
keeps a single type.
Rename locals that shadow the hash, input, and type builtins and drop
the double-underscore name mangling from private members in favor of
the single-underscore style used across isctest.
Replace percent formatting of str with f-strings and the deprecated
IOError alias with OSError. The bytes interpolation in the protocol
serialization is left alone; f-strings have no bytes counterpart.
Use the standard license header in the imported rndc.py
Replace the upstream header with the SPDX-tagged header used across the
repository and drop the stale __version__ marker, which has no meaning
for an in-tree copy. The module provenance is recorded in the import
commit.
Import rndc.py from the python-rndc project verbatim
Bring in the pure-Python RNDC protocol client as-is, with no
modifications, so that subsequent cleanup and integration changes can
be reviewed against the pristine upstream source.
Source: python-rndc rndc.py (version 0.2.0)
Co-authored-by: Matthew Pounsett <matt@conundrum.com> Assisted-by: Claude:claude-fable-5
chg: test: Deduplicate controls configuration in named.conf test templates
The named.conf.j2 files in system tests had a copy-pasted snippets for control channel configuration. These were replaced with the `_common/controls.conf.j2` template.
The control channel was turned on for all named test instances.
Merge branch 'nicki/pytest-named-conf-controls' into 'main'