The nsec3hash tool parsed its algorithm, flags, and iterations
arguments with atoi(), then range-checked the result. For values
that overflow int during digit-by-digit accumulation, atoi() is
undefined; in practice on musl libc the modular wrap leaves
n == 0, which silently passes the "iterations > 0xffffU" check.
On Alpine Linux this made nsec3hash succeed with iterations
treated as 0 for inputs like 4294967296 (2^32).
The latent bug only surfaced when the recent image rebuild pulled
in Hypothesis 6.152.9 (2026-05-19), which unified the distribution
used for bounded and unbounded integers() strategies. The new
smoother distribution explores the 2^32 boundary on unbounded
ranges like integers(min_value=65536); earlier versions did not
reach there, so test_nsec3hash_too_many_iterations only started
failing on Alpine after the image refresh.
Replace the three atoi() calls with isc_parse_uint8 /
isc_parse_uint16, which uniformly reject overflow, trailing
garbage, leading sign, and non-numeric input across libc
implementations. As a side effect, error messages now include
the offending argument and a specific reason ("out of range" vs
"not a valid number").
Assisted-by: Claude:claude-opus-4-7
Closes#6013
Backport of MR !12062
Merge branch 'backport-6013-nsec3hash-iterations-overflow-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12074
The nsec3hash tool parsed its algorithm, flags, and iterations
arguments with atoi(), then range-checked the result. For values
that overflow int during digit-by-digit accumulation, atoi() is
undefined; in practice on musl libc the modular wrap leaves
n == 0, which silently passes the "iterations > 0xffffU" check.
On Alpine Linux this made nsec3hash succeed with iterations
treated as 0 for inputs like 4294967296 (2^32).
The latent bug only surfaced when the recent image rebuild pulled
in Hypothesis 6.152.9 (2026-05-19), which unified the distribution
used for bounded and unbounded integers() strategies. The new
smoother distribution explores the 2^32 boundary on unbounded
ranges like integers(min_value=65536); earlier versions did not
reach there, so test_nsec3hash_too_many_iterations only started
failing on Alpine after the image refresh.
Replace the three atoi() calls with isc_parse_uint8 /
isc_parse_uint16, which uniformly reject overflow, trailing
garbage, leading sign, and non-numeric input across libc
implementations. As a side effect, error messages now include
the offending argument and a specific reason ("out of range" vs
"not a valid number").
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit e13302a6bc)
Apply assorted cleanups to `bin/tests/system/resend_loop/ans3/ans.py`.
Backport of MR !12063
Merge branch 'backport-michal/resend_loop-test-ans3-cleanup-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12071
Make the handlers defined in bin/tests/system/resend_loop/ans3/ans.py
follow canonical naming conventions used in other system tests. Keep
all server initialization code in the main() function.
(cherry picked from commit c5a30a7220)
Since the _get_cookie() function is only used by the CookieHandler
class, make the former a method of the latter to keep related logic
close in the source code.
(cherry picked from commit c3839e830c)
The "len(cookie.server) == 0" condition is superfluous for the
"resend_loop" system test, so remove it. Add a return type annotation
to the _get_cookie() function.
(cherry picked from commit 5fa2bd7e53)
dnspython 2.7.0 is now required to run the BIND 9 system test suite.
Drop the workarounds for older dnspython versions as they are now
redundant.
(cherry picked from commit c9ceb191e8)
The "yield" keyword does not cause a function to return. By design,
get_responses() may yield multiple DNS responses in a single call. As
currently implemented, CookieHandler.get_responses() sends two responses
to each client query that does not contain a COOKIE option. Make the
logic in that method consistent with code comments by only sending one
response to every query - either SERVFAIL or BADCOOKIE, never both.
(cherry picked from commit de42425bbd)
The ans3 custom server instance is created with default_aa=True. Do not
pass the authoritative=True keyword argument to the DnsResponseSend
constructor in CookieHandler.get_responses() as it is redundant.
(cherry picked from commit c61539279d)
The ans3 custom server does not have any zones defined, so the responses
passed to its handlers by core isctest.asyncserver code are guaranteed
to be empty. Remove a call to qctx.prepare_new_response() from
CookieHandler.get_responses() as it is redundant.
(cherry picked from commit 802c03313f)
The NoErrorHandler class does not get matched to any query sent by ns4
in the "resend_loop" test. Remove it as it is redundant.
(cherry picked from commit a296bcf587)
The CookieHandler class handles all traffic for the "example." domain.
Make it a subclass of DomainHandler to simplify its definition.
(cherry picked from commit ba6eee2b80)
- Rewrite cherry-pick references during autorebases
- Fix autorebase error reporting
- Limit post-push pipelines for autorebased branches
- Only autorebase when there is anything to rebase
- Conflate missing commit reference notifications
- Support autorebasing backported security MRs
Backport of MR !12024
Merge branch 'backport-michal/autorebase-improvements-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12069
Autorebasing a backported security fix enables convenient refreshing of
cherry-pick references, which makes it trivial for developers to satisfy
Danger rules just before the merge request is merged. Add a manual CI
job that is only created for backported merge requests targeting
security-* branches.
(cherry picked from commit dd723d93cb)
Instead of creating a separate (potentially lengthy) Danger notification
for every missing commit reference in a backport, produce a single
notification with a list of all unreferenced commit hashes. This makes
Danger output more concise while retaining all the relevant feedback for
the developer.
(cherry picked from commit 086780dcf0)
In an optimistic future, security-* branches will become empty, at least
intermittently. When that happens, there will be nothing left to rebase
on those branches, so when something gets merged into their base
branches, an autorebase will effectively be a fast-forward. While the
existing autorebase logic would handle such a case perfectly fine, it is
prudent to avoid creating a test pipeline after pushing such a
fast-forward update as the code revision getting pushed will have
already been tested by other pipelines. However, the push should still
happen as non-empty downstream autorebased branches may exist and those
will still need to be rebased. Achieve both of these objectives by
checking early whether there is anything to rebase and pushing the
fast-forwarded version of the branch without setting the AUTOREBASE CI
variable if there is not.
(cherry picked from commit 497f771ae3)
Current CI job triggering rules cause a full pipeline to be started
after every push to security-* branches. In this context, "push" means
"branch update", which covers both "git push" invocations and merging a
merge request. Meanwhile, running a test pipeline is only desired after
a rebase; if a branch is fast-forwarded, it means that a merge request
has been merged into it and a pipeline should have already been run for
that merge request itself. Limit resource use by only triggering
pipelines for security-* branches when they are pushed to with a "magic"
CI variable that is only set in autorebase jobs. Leave all the other
triggering rules (for scheduled/manual pipelines) intact.
(cherry picked from commit 5cd870053e)
The logic used for detecting the commit breaking an autorebase does not
work correctly if the offending commit is not the first one applied
during the "reverse rebase". Fix by using REBASE_HEAD instead of
processing the output of "git status" in a convoluted way.
Furthermore, the approach used for identifying the first offending merge
request in the case of a successful autorebase followed by a failed
build only works correctly if the base branch is not autorebased itself.
Since a solution that would work correctly for a branch autorebased on
top of a branch that only moves forward does not work correctly for a
branch autorebased on top of another autorebased branch and vice versa,
accurately identifying the most likely culprit after a successful
autorebase is a very complicated and brittle task. Since reporting no
details at all is arguably better than reporting false details, only
produce a minimal error notification if the build fails after a
successful autorebase.
(cherry picked from commit 4c0e93108e)
Use a custom rebasing script instead of "git rebase" to enable rewriting
cherry-pick references during autorebases.
(cherry picked from commit 98c3f339bf)
Configuring the root zone as a signed primary with parental agents (or with notify-on-cds-changes) caused named to exit on an internal assertion as soon as the DS-publication machinery tried to look up the parent NS RRset — the root has no parent. The lookup is now short-circuited cleanly.
Similar, a zone with no NS records in the parent caused named to exit in the same way.
Backport of MR !11909Closes#5910Closes#5996
Merge branch 'backport-5910-nsfetch-start-root-domain-assertion' into 'bind-9.20'
See merge request isc-projects/bind9!12053
Once the walk reaches the root, splitting one more label off would
trip an internal assertion and abort named. Stop cleanly with
ISC_R_NOTFOUND so the dispatcher cancels the fetch. Only reachable
through misconfiguration (root configured as a primary with parental
agents, or a parent zone that NODATAs its own NS).
Assisted-by: Claude:claude-opus-4-7
Manually edited to resolve conflicts.
(cherry picked from commit 141e8110f7)
This is required to AXFR and verify the root zone and it makes no
difference for non-root zones (dnssec-verify takes FQDN or makes the
provided name absolute).
(cherry picked from commit 1ce7cf2dd2)
Add a test case where the root zone has dnssec-policy configured, with
checkds enabled. This is a silly case because the root does not have
any parent NS records, but it should not crash the server.
The same is true for zones that do not have parent NS records, but
eventually they will hit the same code path.
(cherry picked from commit 9e9af18e4a)
When an authoritative server failed to respond to two consecutive
UDP queries, named marked the next retry as TCP but still sent it
over UDP, producing misleading dnstap records. The ineffective
retry path has been removed; a corrected TCP fallback will be
restored in future BIND 9 versions.
Closes#5529
Backport of MR !12022
Merge branch 'backport-5529-fix-tcp-fallback-after-udp-timeouts-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12049
The retry path in resquery_send() that flipped DNS_FETCHOPT_TCP on a
query whose dispatch had already been bound as UDP in fctx_query() had
no effect on the transport actually used, but did leave a stale TCP
bit visible to downstream consumers (dnstap framing, cookie checks,
the AUTHORITY-NS spoofability guard).
The ineffective code has been removed from resquery_send(). The
TCP fallback functionality will be corrected and restored in the
BIND 9.22.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 01523a078a)
Adds a CI job that runs PR-Agent against each merge request opened from the canonical repository, posting an automated review and code-improvement suggestions as MR comments. The job is gated to same-project source branches so the OpenAI key and personal access token are not exposed to fork pipelines.
Backport of MR!12032, MR!12033 and MR!12035
Merge branch 'ondrej/add-pr-agent-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12034
Run PR-Agent's `review` and `improve` commands against each merge
request from the canonical repository, posting an automated review
and code-improvement suggestions as MR comments. The rule restricts
the job to MRs whose source project matches CI_PROJECT_PATH so the
OpenAI key and GitLab personal access token are never exposed to
fork pipelines.
(cherry picked from commit 07345b25d9)
(cherry picked from commit 4257454262)
(cherry picked from commit 5550fb84ae)
The max-types-per-name cache eviction tests were flaky because two test steps were missing a sleep between queries, causing TTL-based cache verification to fail when both queries completed within the same second.
Backport of MR !11782
Merge branch 'backport-ondrej/fix-flaky-reclimit-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12027
The cache verification in steps 11 and 15 checks that the TTL has
decreased from its initial value to confirm the response was served
from cache, but the sleep between the two queries was missing. Both
queries could complete within the same second, leaving the TTL
unchanged and causing the test to incorrectly conclude the entry was
not cached.
(cherry picked from commit 80f04a9ee5)
BIND used to wait silently for the correct DNS message id on a UDP fetch
even after receiving a response from the expected server with the wrong
id, leaving room for off-path spoofing attempts to keep guessing within
that window. The resolver now retries the fetch over TCP on the first
such response, and a new MismatchTCP statistics counter tracks how
often the fallback fires.
Closes#5449
Backport of MR !12023
Merge branch 'backport-5449-immediate-tcp-fallback-on-id-mismatch-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12025
Until now, the dispatcher silently dropped UDP responses from the
expected peer that carried the wrong DNS message id and kept listening
for the correct id to arrive within the read timeout. An off-path
attacker who knows the destination address and source port of an
outgoing fetch could exploit that quiet retry window to flood the
resolver with guessed responses; with a gigabit link the per-query
success probability grows linearly with the number of guesses that
arrive before the legitimate answer or the timeout.
Treat any such mismatch as a possible spoofing attempt and let the
resolver immediately retry the same query over TCP, the same control
path the truncation handler already uses.
Add a resolver statistics counter - exposed as 'queries retried over TCP
after a response with mismatched query id' in rndc stats and
'MismatchTCP' in the statistics channel
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 11bca1051f)
The statistics charts where not displaying on some browsers.
This has been fixed.
Closes#5990
Backport of MR !12018
Merge branch 'backport-5990-disable-output-escaping-in-bind9-xsl-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12019
The statistics charts where not displaying on some browsers (e.g. Chrome)
due to '>' being escaped as '>'. Use disable-output-escaping="yes" to
turn this off.
(cherry picked from commit 9b6c018425)
'rndc dumpdb' against a server with zones, and async zone load,
had a timing window where the operation's completion could fire
before the server had finished registering the operation,
occasionally leading to a possible crash. The completion is now
delivered after the registration is in place.
Closes#5952
Backport of MR !11991
Merge branch 'backport-5952-fix-masterdump-async-ctx-race-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12021
Bouncing the offload itself to the target loop let the after-work
callback fire on the target thread and run the user's done callback
before the calling thread had published *dctxp / *lctxp. Enqueue on
the calling loop and bounce only the done callback instead, so the
publish is sequenced before the cross-thread hand-off by construction
and cannot be reintroduced by reordering the entry-point body.
(cherry picked from commit 8ae464d552)
The global RUNNER_SCRIPT_TIMEOUT: 55m in the parent pipeline was being
forwarded to the stress and tsan:stress child pipelines, where forwarded
yaml variables outrank job-level variables. That caused stress jobs with
BIND_STRESS_TESTS_RUN_TIME >= 60 to be killed at 55 minutes, regardless
of the per-job RUNNER_SCRIPT_TIMEOUT set in the generated child config.
Set forward:yaml_variables: false on both trigger jobs; the generated
configs already declare every variable they need.
Assisted-by: Claude:claude-opus-4-7
Backport of MR !12012
Merge branch 'backport-mnowak/fix-stress-test-script-timeout-9.20' into 'bind-9.20'
See merge request isc-projects/bind9!12015
The parent's global RUNNER_SCRIPT_TIMEOUT: 55m was reaching the stress
and tsan:stress child pipelines via inherited yaml variables, where
inherited values outrank the child's job-level variables. That caused
stress jobs with BIND_STRESS_TESTS_RUN_TIME >= 60 to be killed at 55
minutes, regardless of the per-job RUNNER_SCRIPT_TIMEOUT set in the
generated child config.
Use inherit:variables with a positive list on both trigger jobs:
inherit only CI_REGISTRY_IMAGE so the parent's registry override
(needed for image pulls in the child) flows through, while keeping
RUNNER_SCRIPT_TIMEOUT (and other globals) out of the child pipeline's
variable scope. The per-job RUNNER_SCRIPT_TIMEOUT values set by the
generated child config now take effect.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 73915b73d1)
Tell respdiff.sh where to find the respdiff Python tools (msgdiff.py,
diffsum.py, ...) so the in-tree copy from bind9-qa is used.
Assisted-by: Claude:claude-opus-4-7
(cherry picked from commit 4f410ee1e6)