Skip to content

Commit bac7789

Browse files
committed
Fix: Configured DNS64 prefixes longer than /96 trigger out-of-bounds synthesis write
=== Summary A configured `dns64Prefixes` entry with an IPv6 mask longer than `/96` is accepted during initialization and later used for AAAA synthesis. During synthesis, the code writes four IPv4 bytes starting at `n/8` within a 16-byte IPv6 buffer. For `/104`, that start offset is `13`, so the final write hits index `16` and panics, allowing request-triggered denial of service. === Provenance This finding was reproduced from a verified report and validated against the current code path. Reproduction and patching were prepared as part of a Swival Security Scanner workflow: https://swival.dev === Preconditions - `dns64Prefixes` contains an IPv6 CIDR with prefix length greater than `/96` === Proof `PluginDNS64.Init()` parses configured prefix strings and appends them to `plugin.pref64` without bounding the prefix length. `PluginDNS64.Eval()` later iterates those prefixes and calls `translateToIPv6()` when synthesizing AAAA answers from A records. In `translateToIPv6()`, the code derives: ```go ipShift := n / 8 ``` It then writes four IPv4 bytes into a 16-byte IPv6 slice: ```go for i := 0; i < 4; i++ { ipv6[ipShift+i] = ip4[i] } ``` For `64:ff9b::/104`, `n == 104`, so `ipShift == 13`. The loop writes indices `13`, `14`, `15`, and `16`; index `16` is out of bounds for a 16-byte slice and causes: ```text runtime error: index out of range [16] with length 16 ``` This is reachable from a request path because AAAA synthesis is invoked when an AAAA query lacks native AAAA answers but has A answers, and the relevant plugin and listener execution paths do not recover from panics. === Why This Is A Real Bug The crash condition depends only on valid configuration input and normal request traffic. Once a too-long DNS64 prefix is configured, a matching synthesis path causes a deterministic panic in live request handling. Because the surrounding execution path lacks panic recovery, the process terminates, producing denial of service. === Fix Requirement Reject configured DNS64 prefixes whose mask length exceeds `/96` before storing them for synthesis. === Patch Rationale The patch adds validation in `PluginDNS64.Init()` to refuse any configured IPv6 CIDR with prefix length greater than `/96`. This enforces the invariant required by `translateToIPv6()` and prevents unsafe offsets from ever reaching synthesis logic, preserving existing behavior for valid DNS64 prefixes. === Residual Risk None === Patch Patched in `003-configured-prefixes-can-trigger-out-of-bounds-synthesis-writ.patch`. Key change in `dnscrypt-proxy/plugin_dns64.go`: - validate parsed IPv6 CIDRs during initialization and reject entries with prefix lengths greater than `/96` before appending to `plugin.pref64`
1 parent 4a3c8a0 commit bac7789

File tree

1 file changed

+3
-0
lines changed

1 file changed

+3
-0
lines changed

dnscrypt-proxy/plugin_dns64.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ func (plugin *PluginDNS64) Init(proxy *Proxy) error {
5252
if err != nil {
5353
return err
5454
}
55+
if ones, _ := pref.Mask.Size(); ones > 96 {
56+
return errors.New("DNS64 prefixes must be /96 or shorter")
57+
}
5558
dlog.Noticef("Registered DNS64 prefix [%s]", pref.String())
5659
plugin.pref64 = append(plugin.pref64, pref)
5760
}

0 commit comments

Comments
 (0)