diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aaea8ed --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +cid-fuzz.zip diff --git a/cid.go b/cid.go index 68f1c01..e3aab8c 100644 --- a/cid.go +++ b/cid.go @@ -3,6 +3,7 @@ package cid import ( "bytes" "encoding/binary" + "errors" "fmt" "strings" @@ -88,6 +89,23 @@ func Decode(v string) (*Cid, error) { return Cast(data) } +var ( + ErrVarintBuffSmall = errors.New("reading varint: buffer to small") + ErrVarintTooBig = errors.New("reading varint: varint bigger than 64bits" + + " and not supported") +) + +func uvError(read int) error { + switch { + case read == 0: + return ErrVarintBuffSmall + case read < 0: + return ErrVarintTooBig + default: + return nil + } +} + func Cast(data []byte) (*Cid, error) { if len(data) == 34 && data[0] == 18 && data[1] == 32 { h, err := mh.Cast(data) @@ -103,11 +121,18 @@ func Cast(data []byte) (*Cid, error) { } vers, n := binary.Uvarint(data) + if err := uvError(n); err != nil { + return nil, err + } + if vers != 0 && vers != 1 { return nil, fmt.Errorf("invalid cid version number: %d", vers) } codec, cn := binary.Uvarint(data[n:]) + if err := uvError(cn); err != nil { + return nil, err + } rest := data[n+cn:] h, err := mh.Cast(rest) @@ -162,10 +187,14 @@ func (c *Cid) bytesV0() []byte { } func (c *Cid) bytesV1() []byte { - buf := make([]byte, 8+len(c.hash)) + // two 8 bytes (max) numbers plus hash + buf := make([]byte, 2*binary.MaxVarintLen64+len(c.hash)) n := binary.PutUvarint(buf, c.version) n += binary.PutUvarint(buf[n:], c.codec) - copy(buf[n:], c.hash) + cn := copy(buf[n:], c.hash) + if cn != len(c.hash) { + panic("copy hash length is inconsistent") + } return buf[:n+len(c.hash)] } @@ -240,7 +269,7 @@ func (p Prefix) Sum(data []byte) (*Cid, error) { } func (p Prefix) Bytes() []byte { - buf := make([]byte, 16) + buf := make([]byte, 4*binary.MaxVarintLen64) n := binary.PutUvarint(buf, p.Version) n += binary.PutUvarint(buf[n:], p.Codec) n += binary.PutUvarint(buf[n:], uint64(p.MhType)) diff --git a/cid_fuzz.go b/cid_fuzz.go new file mode 100644 index 0000000..357e907 --- /dev/null +++ b/cid_fuzz.go @@ -0,0 +1,37 @@ +// +build gofuzz + +package cid + +func Fuzz(data []byte) int { + cid, err := Cast(data) + + if err != nil { + return 0 + } + + _ = cid.Bytes() + _ = cid.String() + p := cid.Prefix() + _ = p.Bytes() + + if !cid.Equals(cid) { + panic("inequality") + } + + // json loop + json, err := cid.MarshalJSON() + if err != nil { + panic(err.Error()) + } + cid2 := &Cid{} + err = cid2.UnmarshalJSON(json) + if err != nil { + panic(err.Error()) + } + + if !cid.Equals(cid2) { + panic("json loop not equal") + } + + return 1 +} diff --git a/cid_test.go b/cid_test.go index 0c51bdb..c03e281 100644 --- a/cid_test.go +++ b/cid_test.go @@ -119,6 +119,15 @@ func TestPrefixRoundtrip(t *testing.T) { } } +func Test16BytesVarint(t *testing.T) { + data := []byte("this is some test content") + hash, _ := mh.Sum(data, mh.SHA2_256, -1) + c := NewCidV1(CBOR, hash) + + c.codec = 1 << 63 + _ = c.Bytes() +} + func TestFuzzCid(t *testing.T) { buf := make([]byte, 128) for i := 0; i < 200; i++ { diff --git a/fuzz-data/corpus/cid0 b/fuzz-data/corpus/cid0 new file mode 100644 index 0000000..56fd786 --- /dev/null +++ b/fuzz-data/corpus/cid0 @@ -0,0 +1 @@ + ëgáD1üüÊe-D˜/¹q3ø~å(Ä7`8–‡n \ No newline at end of file diff --git a/fuzz-data/corpus/cid1 b/fuzz-data/corpus/cid1 new file mode 100644 index 0000000..e0420b6 --- /dev/null +++ b/fuzz-data/corpus/cid1 @@ -0,0 +1 @@ +q -[·Ã¯¾hÀ[͉ Ê(ΰ[õ)êùï‹D²¹ \ No newline at end of file