fix(sign): reject invalid UTF‑8 dbfile token (#7589)

The coredns/caddy lexer replaces invalid UTF‑8 bytes in tokens with
U+FFFD. When that lossy-decoded value is used as `dbfile` in the sign
plugin, the source zone file path never exists. On startup/refresh,
the `resign()` function sees the signed file missing and triggers
signing. Consequently `Sign()` then fails opening the bogus path,
the signed file is never created, and the cycle repeats across all
expanded origins (e.g., reverse CIDRs), causing unbounded churn/OOM.

Validate `dbfile` in setup and error if it contains U+FFFD. Add a
regression test.

Note: Unicode paths are supported; only U+FFFD (replacement-rune) is rejected.

Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
This commit is contained in:
Ville Vesilehto
2025-10-06 10:06:28 +03:00
committed by GitHub
parent 625f6c9307
commit 6676e6185d
2 changed files with 17 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"math/rand"
"path/filepath"
"strings"
"time"
"github.com/coredns/caddy"
@@ -50,6 +51,11 @@ func parse(c *caddy.Controller) (*Sign, error) {
dbfile = filepath.Join(config.Root, dbfile)
}
// Validate dbfile token to avoid infinite signing loops caused by invalid paths
if strings.ContainsRune(dbfile, '\uFFFD') {
return nil, fmt.Errorf("dbfile %q contains invalid characters", dbfile)
}
origins := plugin.OriginsFromArgsOrServerBlock(c.RemainingArgs(), c.ServerBlockKeys)
signers := make([]*Signer, len(origins))
for i := range origins {

View File

@@ -73,3 +73,14 @@ func TestParse(t *testing.T) {
}
}
}
// With setup validation in place, an invalid utf-8 dbfile token must cause parse() to error.
func TestParseRejectsInvalidDbfileToken(t *testing.T) {
input := "sign \"\xff\" 8.44.in-addr.arpa. 9.44.in-addr.arpa. {}"
c := caddy.NewTestController("dns", input)
_, err := parse(c)
if err == nil {
t.Fatalf("expected parse to fail for invalid dbfile token")
}
}