From b564fcd86942aff59c7ac64f5f770fb3fd47cbe7 Mon Sep 17 00:00:00 2001 From: Zhao Jianing <18910855655@163.com> Date: Sat, 5 Sep 2026 17:20:27 +0800 Subject: [PATCH] plugin/autopath: Fixes a nil pointer dereference panic in autopath during search path walk (#8517) * plugin/autopath: Fixes a nil pointer dereference panic in autopath during search path walk When a plugin later in the chain returns a ClientWrite rcode without writing a response (for example acl's drop action, which returns (dns.RcodeSuccess, nil) without calling WriteMsg), autopath dereferences a nil nw.Msg at nw.Msg.Rcode and panics. The final fallback w.WriteMsg(firstReply) can also receive a nil firstReply for the same reason. Skip search path elements that produced no message, and only write the first reply when it is non-nil. This mirrors the nil guards recently added in plugin/minimal (#8506), plugin/dns64 (#8511) and plugin/cache (#8512). Signed-off-by: zjncs <18910855655@163.com> * plugin/autopath: silence unused-parameter lint and assert no client write Address review feedback on #8517: rename the unused 'w' parameter in TestAutoPathNilMsgFromNext to '_w' so the revive unused-parameter check passes, and assert that the recorder receives no message so the intended drop/no-client-write behavior is explicit. Signed-off-by: zjncs <18910855655@163.com> --------- Signed-off-by: zjncs <18910855655@163.com> --- plugin/autopath/autopath.go | 6 +++++- plugin/autopath/autopath_test.go | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/plugin/autopath/autopath.go b/plugin/autopath/autopath.go index 59d0282bc..f221adc22 100644 --- a/plugin/autopath/autopath.go +++ b/plugin/autopath/autopath.go @@ -123,6 +123,10 @@ func (a *AutoPath) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms continue } + if nw.Msg == nil { + continue + } + if nw.Msg.Rcode == dns.RcodeNameError { continue } @@ -135,7 +139,7 @@ func (a *AutoPath) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms autoPathCount.WithLabelValues(metrics.WithServer(ctx)).Add(1) return rcode, err } - if plugin.ClientWrite(firstRcode) { + if plugin.ClientWrite(firstRcode) && firstReply != nil { w.WriteMsg(firstReply) } return firstRcode, firstErr diff --git a/plugin/autopath/autopath_test.go b/plugin/autopath/autopath_test.go index 89050bebf..673a6fb09 100644 --- a/plugin/autopath/autopath_test.go +++ b/plugin/autopath/autopath_test.go @@ -106,6 +106,29 @@ func TestAutoPathNoAnswer(t *testing.T) { } } +func TestAutoPathNilMsgFromNext(t *testing.T) { + ap := new(AutoPath) + ap.Zones = []string{"."} + // Simulate a plugin like acl's drop action that returns success + // without writing a response. + ap.Next = test.HandlerFunc(func(_ctx context.Context, _w dns.ResponseWriter, _r *dns.Msg) (int, error) { + return dns.RcodeSuccess, nil + }) + ap.search = []string{"example.org.", "example.com.", "com.", ""} + + m := new(dns.Msg) + m.SetQuestion("b.example.org.", dns.TypeA) + + rec := dnstest.NewRecorder(&test.ResponseWriter{}) + _, err := ap.ServeDNS(context.TODO(), rec, m) + if err != nil { + t.Fatalf("Expected no error, got %v", err) + } + if rec.Msg != nil { + t.Fatalf("Expected no client write, but the recorder got a message") + } +} + // nextHandler returns a Handler that returns an answer for the question in the // request per the domain->answer map. On success an RR will be returned: "qname 3600 IN A 127.0.0.53" func nextHandler(mm map[string]int) test.Handler {