From 1d787c39231e9d9c2e51c6d3aab0aa9b20efc175 Mon Sep 17 00:00:00 2001
From: Matt Joiner <anacrolix@gmail.com>
Date: Sat, 11 Dec 2021 13:43:25 +1100
Subject: [PATCH] Add generalized decodeJsonByteString and a fuzz target for it

---
 webtorrent/fuzz_test.go                       | 31 +++++++++++++++++++
 ...25a6f37ba920daf479f86bcfbbb880cd06cbb2ecf8 |  2 ++
 ...9a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 |  2 ++
 ...bf537d4d81f389524539f402d13aa01f93a65ac7e9 |  2 ++
 webtorrent/tracker_protocol.go                | 20 +++++++++---
 5 files changed, 52 insertions(+), 5 deletions(-)
 create mode 100644 webtorrent/fuzz_test.go
 create mode 100644 webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/195b11403204772a785dfc25a6f37ba920daf479f86bcfbbb880cd06cbb2ecf8
 create mode 100644 webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
 create mode 100644 webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9

diff --git a/webtorrent/fuzz_test.go b/webtorrent/fuzz_test.go
new file mode 100644
index 00000000..14638fa2
--- /dev/null
+++ b/webtorrent/fuzz_test.go
@@ -0,0 +1,31 @@
+//go:build go1.18
+// +build go1.18
+
+package webtorrent
+
+import (
+	"encoding/json"
+	"testing"
+
+	qt "github.com/frankban/quicktest"
+)
+
+func FuzzJsonBinaryStrings(f *testing.F) {
+	f.Fuzz(func(t *testing.T, in []byte) {
+		jsonBytes, err := json.Marshal(binaryToJsonString(in))
+		if err != nil {
+			t.Fatal(err)
+		}
+		// t.Logf("%q", jsonBytes)
+		var jsonStr string
+		err = json.Unmarshal(jsonBytes, &jsonStr)
+		if err != nil {
+			t.Fatal(err)
+		}
+		// t.Logf("%q", jsonStr)
+		c := qt.New(t)
+		out, err := decodeJsonByteString(jsonStr, []byte{})
+		c.Assert(err, qt.IsNil)
+		c.Assert(out, qt.DeepEquals, in)
+	})
+}
diff --git a/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/195b11403204772a785dfc25a6f37ba920daf479f86bcfbbb880cd06cbb2ecf8 b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/195b11403204772a785dfc25a6f37ba920daf479f86bcfbbb880cd06cbb2ecf8
new file mode 100644
index 00000000..9afa08b6
--- /dev/null
+++ b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/195b11403204772a785dfc25a6f37ba920daf479f86bcfbbb880cd06cbb2ecf8
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("\x93")
diff --git a/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4 b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
new file mode 100644
index 00000000..a96f5599
--- /dev/null
+++ b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("0")
diff --git a/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9 b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9
new file mode 100644
index 00000000..67322c70
--- /dev/null
+++ b/webtorrent/testdata/fuzz/FuzzJsonBinaryStrings/caf81e9797b19c76c1fc4dbf537d4d81f389524539f402d13aa01f93a65ac7e9
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("")
diff --git a/webtorrent/tracker_protocol.go b/webtorrent/tracker_protocol.go
index b6ac1a5b..14be67e4 100644
--- a/webtorrent/tracker_protocol.go
+++ b/webtorrent/tracker_protocol.go
@@ -48,6 +48,17 @@ func binaryToJsonString(b []byte) string {
 }
 
 func jsonStringToInfoHash(s string) (ih [20]byte, err error) {
+	b, err := decodeJsonByteString(s, ih[:0])
+	if err != nil {
+		return
+	}
+	if len(b) != len(ih) {
+		err = fmt.Errorf("string decoded to %v bytes", len(b))
+	}
+	return
+}
+
+func decodeJsonByteString(s string, b []byte) ([]byte, error) {
 	defer func() {
 		r := recover()
 		if r == nil {
@@ -55,12 +66,11 @@ func jsonStringToInfoHash(s string) (ih [20]byte, err error) {
 		}
 		panic(fmt.Sprintf("%q", s))
 	}()
-	for i, c := range []rune(s) {
+	for _, c := range []rune(s) {
 		if c < 0 || c > math.MaxUint8 {
-			err = fmt.Errorf("bad infohash string: %v", s)
-			return
+			return b, fmt.Errorf("rune out of bounds: %v", c)
 		}
-		ih[i] = byte(c)
+		b = append(b, byte(c))
 	}
-	return
+	return b, nil
 }
-- 
2.51.0