From bd6ed23899c8a3b4b6d0db29f0f6298e492cedd6 Mon Sep 17 00:00:00 2001
From: Martin Holst Swende <martin@swende.se>
Date: Sat, 13 Jan 2018 16:03:24 +0100
Subject: [PATCH] accounts/abi: harden unpacking against malicious input

---
 accounts/abi/unpack.go      | 17 ++++++---
 accounts/abi/unpack_test.go | 70 +++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index 334245661..51fb9ab9b 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -95,6 +95,9 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
 
 // iteratively unpack elements
 func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+	if size < 0 {
+		return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
+	}
 	if start+32*size > len(output) {
 		return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
 	}
@@ -181,16 +184,22 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
 
 // interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
 func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
-	offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
+	offsetBig := big.NewInt(0).SetBytes(output[index : index+32])
+	if !offsetBig.IsInt64() {
+		return 0, 0, fmt.Errorf("abi offset larger than int64: %v", offsetBig)
+	}
+	offset := int(offsetBig.Int64())
 	if offset+32 > len(output) {
 		return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
 	}
-	length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
+	lengthBig := big.NewInt(0).SetBytes(output[offset : offset+32])
+	if !lengthBig.IsInt64() {
+		return 0, 0, fmt.Errorf("abi length larger than int64: %v", lengthBig)
+	}
+	length = int(lengthBig.Int64())
 	if offset+32+length > len(output) {
 		return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
 	}
 	start = offset + 32
-
-	//fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
 	return
 }
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index e9f910812..742211244 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -683,3 +683,73 @@ func TestUnmarshal(t *testing.T) {
 		t.Fatal("expected error:", err)
 	}
 }
+
+func TestOOMMaliciousInput(t *testing.T) {
+	oomTests := []unpackTest{
+		{
+			def: `[{"type": "uint8[]"}]`,
+			enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
+				"0000000000000000000000000000000000000000000000000000000000000003" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+		{ // Length larger than 64 bits
+			def: `[{"type": "uint8[]"}]`,
+			enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
+				"00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+		{ // Offset very large (over 64 bits)
+			def: `[{"type": "uint8[]"}]`,
+			enc: "00ffffffffffffffffffffffffffffffffffffffffffffff0000000000000020" + // offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+		{ // Offset very large (below 64 bits)
+			def: `[{"type": "uint8[]"}]`,
+			enc: "0000000000000000000000000000000000000000000000007ffffffffff00020" + // offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+		{ // Offset negative (as 64 bit)
+			def: `[{"type": "uint8[]"}]`,
+			enc: "000000000000000000000000000000000000000000000000f000000000000020" + // offset
+				"0000000000000000000000000000000000000000000000000000000000000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+
+		{ // Negative length
+			def: `[{"type": "uint8[]"}]`,
+			enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
+				"000000000000000000000000000000000000000000000000f000000000000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+		{ // Very large length
+			def: `[{"type": "uint8[]"}]`,
+			enc: "0000000000000000000000000000000000000000000000000000000000000020" + // offset
+				"0000000000000000000000000000000000000000000000007fffffffff000002" + // num elems
+				"0000000000000000000000000000000000000000000000000000000000000001" + // elem 1
+				"0000000000000000000000000000000000000000000000000000000000000002", // elem 2
+		},
+	}
+	for i, test := range oomTests {
+		def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
+		abi, err := JSON(strings.NewReader(def))
+		if err != nil {
+			t.Fatalf("invalid ABI definition %s: %v", def, err)
+		}
+		encb, err := hex.DecodeString(test.enc)
+		if err != nil {
+			t.Fatalf("invalid hex: %s" + test.enc)
+		}
+		_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
+		if err == nil {
+			t.Fatalf("Expected error on malicious input, test %d", i)
+		}
+	}
+}
-- 
GitLab