dns/dnsmessage: don't use untrusted data to pre-allocate slices

We mustn't use data from p.header to pre-allocate slices for Message.Question, etc.
Otherwise an attacker can force the allocation of several MiB per parsed message,
which can lead to a DoS via putting pressure on the GC.

Fixes golang/go#23214

Change-Id: I6c99577f625b08331b438533adb6b8167bcd1ec5
Reviewed-on: https://go-review.googlesource.com/85135
Reviewed-by: Ian Gudger <igudger@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/dns/dnsmessage/message.go b/dns/dnsmessage/message.go
index ea94bd4..e98fda6 100644
--- a/dns/dnsmessage/message.go
+++ b/dns/dnsmessage/message.go
@@ -436,7 +436,13 @@
 
 // AllQuestions parses all Questions.
 func (p *Parser) AllQuestions() ([]Question, error) {
-	qs := make([]Question, 0, p.header.questions)
+	// Multiple questions are valid according to the spec,
+	// but servers don't actually support them. There will
+	// be at most one question here.
+	//
+	// Do not pre-allocate based on info in p.header, since
+	// the data is untrusted.
+	qs := []Question{}
 	for {
 		q, err := p.Question()
 		if err == ErrSectionDone {
@@ -492,7 +498,16 @@
 
 // AllAnswers parses all Answer Resources.
 func (p *Parser) AllAnswers() ([]Resource, error) {
-	as := make([]Resource, 0, p.header.answers)
+	// The most common query is for A/AAAA, which usually returns
+	// a handful of IPs.
+	//
+	// Pre-allocate up to a certain limit, since p.header is
+	// untrusted data.
+	n := int(p.header.answers)
+	if n > 20 {
+		n = 20
+	}
+	as := make([]Resource, 0, n)
 	for {
 		a, err := p.Answer()
 		if err == ErrSectionDone {
@@ -533,7 +548,16 @@
 
 // AllAuthorities parses all Authority Resources.
 func (p *Parser) AllAuthorities() ([]Resource, error) {
-	as := make([]Resource, 0, p.header.authorities)
+	// Authorities contains SOA in case of NXDOMAIN and friends,
+	// otherwise it is empty.
+	//
+	// Pre-allocate up to a certain limit, since p.header is
+	// untrusted data.
+	n := int(p.header.authorities)
+	if n > 10 {
+		n = 10
+	}
+	as := make([]Resource, 0, n)
 	for {
 		a, err := p.Authority()
 		if err == ErrSectionDone {
@@ -574,7 +598,16 @@
 
 // AllAdditionals parses all Additional Resources.
 func (p *Parser) AllAdditionals() ([]Resource, error) {
-	as := make([]Resource, 0, p.header.additionals)
+	// Additionals usually contain OPT, and sometimes A/AAAA
+	// glue records.
+	//
+	// Pre-allocate up to a certain limit, since p.header is
+	// untrusted data.
+	n := int(p.header.additionals)
+	if n > 10 {
+		n = 10
+	}
+	as := make([]Resource, 0, n)
 	for {
 		a, err := p.Additional()
 		if err == ErrSectionDone {