Przeglądaj źródła

accounts/abi: harden unpacking against malicious input

Martin Holst Swende 7 lat temu
rodzic
commit
bd6ed23899
2 zmienionych plików z 83 dodań i 4 usunięć
  1. 13 4
      accounts/abi/unpack.go
  2. 70 0
      accounts/abi/unpack_test.go

+ 13 - 4
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
 }

+ 70 - 0
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)
+		}
+	}
+}