encoding/hex: fix potential incorrect Dumper output when Close is called multiple times

Fixes #23574

Change-Id: I69573de47daa6fd53cc99a78c0c4b867460242e3
Reviewed-on: https://go-review.googlesource.com/90275
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/encoding/hex/hex.go b/src/encoding/hex/hex.go
index e4df6cb..edc5395 100644
--- a/src/encoding/hex/hex.go
+++ b/src/encoding/hex/hex.go
@@ -211,6 +211,7 @@
 	buf        [14]byte
 	used       int  // number of bytes in the current line
 	n          uint // number of bytes, total
+	closed     bool
 }
 
 func toChar(b byte) byte {
@@ -221,6 +222,10 @@
 }
 
 func (h *dumper) Write(data []byte) (n int, err error) {
+	if h.closed {
+		return 0, errors.New("encoding/hex: dumper closed")
+	}
+
 	// Output lines look like:
 	// 00000010  2e 2f 30 31 32 33 34 35  36 37 38 39 3a 3b 3c 3d  |./0123456789:;<=|
 	// ^ offset                          ^ extra space              ^ ASCII of line.
@@ -277,9 +282,10 @@
 
 func (h *dumper) Close() (err error) {
 	// See the comments in Write() for the details of this format.
-	if h.used == 0 {
+	if h.used == 0 || h.closed {
 		return
 	}
+	h.closed = true
 	h.buf[0] = ' '
 	h.buf[1] = ' '
 	h.buf[2] = ' '
diff --git a/src/encoding/hex/hex_test.go b/src/encoding/hex/hex_test.go
index b6bab21..f222316 100644
--- a/src/encoding/hex/hex_test.go
+++ b/src/encoding/hex/hex_test.go
@@ -188,6 +188,22 @@
 	}
 }
 
+func TestDumper_doubleclose(t *testing.T) {
+	var out bytes.Buffer
+	dumper := Dumper(&out)
+
+	dumper.Write([]byte(`gopher`))
+	dumper.Close()
+	dumper.Close()
+	dumper.Write([]byte(`gopher`))
+	dumper.Close()
+
+	expected := "00000000  67 6f 70 68 65 72                                 |gopher|\n"
+	if out.String() != expected {
+		t.Fatalf("got:\n%#v\nwant:\n%#v", out.String(), expected)
+	}
+}
+
 func TestDump(t *testing.T) {
 	var in [40]byte
 	for i := range in {