summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-07-10 23:49:07 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-07-11 00:44:30 -0600
commitad9ac6d07ce1819260c2b7f090fd4fe742c80d9f (patch)
treeab6a607ea8575382c978f07de943ccf6c077de7c /lib
parent41ef03aabf8d6db4f926480fc5ddfec014e342d3 (diff)
Fuzz btrfsitem, and by consequence improve binstruct errors
Diffstat (limited to 'lib')
-rw-r--r--lib/binstruct/binint/builtins.go38
-rw-r--r--lib/binstruct/binutil/binutil.go16
-rw-r--r--lib/binstruct/errors.go48
-rw-r--r--lib/binstruct/marshal.go27
-rw-r--r--lib/binstruct/size.go22
-rw-r--r--lib/binstruct/structs.go5
-rw-r--r--lib/binstruct/unmarshal.go23
-rw-r--r--lib/btrfs/btrfsitem/item_chunk.go6
-rw-r--r--lib/btrfs/btrfsitem/item_dir.go50
-rw-r--r--lib/btrfs/btrfsitem/item_extent.go8
-rw-r--r--lib/btrfs/btrfsitem/item_extentcsum.go4
-rw-r--r--lib/btrfs/btrfsitem/item_inoderef.go13
-rw-r--r--lib/btrfs/btrfsitem/item_rootref.go13
-rw-r--r--lib/btrfs/btrfsitem/items_gen.go8
-rw-r--r--lib/btrfs/btrfsitem/items_test.go65
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/12de190a2cae6f0c72854a75c03ce8488d9d0508a66fa4de0817264e458d22602
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/1ecbbb90e92add091b31e3c765955d76d06074ea5dca8f6b4ce523a640d50a2d2
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/2ae21a60c209d36ee27afa2b253c5f0ae7389c05837daa91500317cec5122f2e2
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/7b05bfc61add24e74c86043bfd0dea657fa522bd2b161d6334effe624c8478502
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/990f7eb92226efeef4491768fae2b9260529446c07a34b99d7e384b5dd847d932
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/aff8a82fa526d6a55b4e13d9320aee5f9413109bbab24e3b2269cdd6abb97fb72
-rw-r--r--lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/c9330e1640e459c9f623fc40d643eb42f727ed9b0b744c011a39a40552417cad2
-rw-r--r--lib/btrfs/internal/misc.go5
-rw-r--r--lib/btrfs/io4_fs.go44
-rw-r--r--lib/btrfsprogs/btrfsinspect/print_tree.go18
25 files changed, 304 insertions, 123 deletions
diff --git a/lib/binstruct/binint/builtins.go b/lib/binstruct/binint/builtins.go
index 5363dbe..01186bc 100644
--- a/lib/binstruct/binint/builtins.go
+++ b/lib/binstruct/binint/builtins.go
@@ -6,15 +6,9 @@ package binint
import (
"encoding/binary"
- "fmt"
-)
-func needNBytes(t interface{}, dat []byte, n int) error {
- if len(dat) < n {
- return fmt.Errorf("%T.UnmarshalBinary: need at least %v bytes, only have %v", t, n, len(dat))
- }
- return nil
-}
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil"
+)
// unsigned
@@ -23,7 +17,7 @@ type U8 uint8
func (U8) BinaryStaticSize() int { return 1 }
func (x U8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil }
func (x *U8) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 1); err != nil {
+ if err := binutil.NeedNBytes(dat, 1); err != nil {
return 0, err
}
*x = U8(dat[0])
@@ -41,7 +35,7 @@ func (x U16le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U16le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, 2); err != nil {
return 0, err
}
*x = U16le(binary.LittleEndian.Uint16(dat))
@@ -57,7 +51,7 @@ func (x U32le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U32le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, 4); err != nil {
return 0, err
}
*x = U32le(binary.LittleEndian.Uint32(dat))
@@ -73,7 +67,7 @@ func (x U64le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U64le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, 8); err != nil {
return 0, err
}
*x = U64le(binary.LittleEndian.Uint64(dat))
@@ -91,7 +85,7 @@ func (x U16be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U16be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, 2); err != nil {
return 0, err
}
*x = U16be(binary.BigEndian.Uint16(dat))
@@ -107,7 +101,7 @@ func (x U32be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U32be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, 4); err != nil {
return 0, err
}
*x = U32be(binary.BigEndian.Uint32(dat))
@@ -123,7 +117,7 @@ func (x U64be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *U64be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, 8); err != nil {
return 0, err
}
*x = U64be(binary.BigEndian.Uint64(dat))
@@ -137,7 +131,7 @@ type I8 int8
func (I8) BinaryStaticSize() int { return 1 }
func (x I8) MarshalBinary() ([]byte, error) { return []byte{byte(x)}, nil }
func (x *I8) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 1); err != nil {
+ if err := binutil.NeedNBytes(dat, 1); err != nil {
return 0, err
}
*x = I8(dat[0])
@@ -155,7 +149,7 @@ func (x I16le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I16le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, 2); err != nil {
return 0, err
}
*x = I16le(binary.LittleEndian.Uint16(dat))
@@ -171,7 +165,7 @@ func (x I32le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I32le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, 4); err != nil {
return 0, err
}
*x = I32le(binary.LittleEndian.Uint32(dat))
@@ -187,7 +181,7 @@ func (x I64le) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I64le) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, 8); err != nil {
return 0, err
}
*x = I64le(binary.LittleEndian.Uint64(dat))
@@ -205,7 +199,7 @@ func (x I16be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I16be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 2); err != nil {
+ if err := binutil.NeedNBytes(dat, 2); err != nil {
return 0, err
}
*x = I16be(binary.BigEndian.Uint16(dat))
@@ -221,7 +215,7 @@ func (x I32be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I32be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 4); err != nil {
+ if err := binutil.NeedNBytes(dat, 4); err != nil {
return 0, err
}
*x = I32be(binary.BigEndian.Uint32(dat))
@@ -237,7 +231,7 @@ func (x I64be) MarshalBinary() ([]byte, error) {
return buf[:], nil
}
func (x *I64be) UnmarshalBinary(dat []byte) (int, error) {
- if err := needNBytes(*x, dat, 8); err != nil {
+ if err := binutil.NeedNBytes(dat, 8); err != nil {
return 0, err
}
*x = I64be(binary.BigEndian.Uint64(dat))
diff --git a/lib/binstruct/binutil/binutil.go b/lib/binstruct/binutil/binutil.go
new file mode 100644
index 0000000..684237f
--- /dev/null
+++ b/lib/binstruct/binutil/binutil.go
@@ -0,0 +1,16 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package binutil
+
+import (
+ "fmt"
+)
+
+func NeedNBytes(dat []byte, n int) error {
+ if len(dat) < n {
+ return fmt.Errorf("need at least %v bytes, only have %v", n, len(dat))
+ }
+ return nil
+}
diff --git a/lib/binstruct/errors.go b/lib/binstruct/errors.go
new file mode 100644
index 0000000..3914ec7
--- /dev/null
+++ b/lib/binstruct/errors.go
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package binstruct
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type InvalidTypeError struct {
+ Type reflect.Type
+ Err error
+}
+
+func (e *InvalidTypeError) Error() string {
+ return fmt.Sprintf("%v: %v", e.Type, e.Err)
+}
+func (e *InvalidTypeError) Unwrap() error { return e.Err }
+
+type UnmarshalError struct {
+ Type reflect.Type
+ Method string
+ Err error
+}
+
+func (e *UnmarshalError) Error() string {
+ if e.Method == "" {
+ return fmt.Sprintf("%v: %v", e.Type, e.Err)
+ }
+ return fmt.Sprintf("(%v).%v: %v", e.Type, e.Method, e.Err)
+}
+func (e *UnmarshalError) Unwrap() error { return e.Err }
+
+type MarshalError struct {
+ Type reflect.Type
+ Method string
+ Err error
+}
+
+func (e *MarshalError) Error() string {
+ if e.Method == "" {
+ return fmt.Sprintf("%v: %v", e.Type, e.Err)
+ }
+ return fmt.Sprintf("(%v).%v: %v", e.Type, e.Method, e.Err)
+}
+func (e *MarshalError) Unwrap() error { return e.Err }
diff --git a/lib/binstruct/marshal.go b/lib/binstruct/marshal.go
index 8159191..78a4bb5 100644
--- a/lib/binstruct/marshal.go
+++ b/lib/binstruct/marshal.go
@@ -14,7 +14,15 @@ type Marshaler = encoding.BinaryMarshaler
func Marshal(obj any) ([]byte, error) {
if mar, ok := obj.(Marshaler); ok {
- return mar.MarshalBinary()
+ dat, err := mar.MarshalBinary()
+ if err != nil {
+ err = &UnmarshalError{
+ Type: reflect.TypeOf(obj),
+ Method: "MarshalBinary",
+ Err: err,
+ }
+ }
+ return dat, err
}
return MarshalWithoutInterface(obj)
}
@@ -24,7 +32,15 @@ func MarshalWithoutInterface(obj any) ([]byte, error) {
switch val.Kind() {
case reflect.Uint8, reflect.Int8, reflect.Uint16, reflect.Int16, reflect.Uint32, reflect.Int32, reflect.Uint64, reflect.Int64:
typ := intKind2Type[val.Kind()]
- return val.Convert(typ).Interface().(Marshaler).MarshalBinary()
+ dat, err := val.Convert(typ).Interface().(Marshaler).MarshalBinary()
+ if err != nil {
+ err = &UnmarshalError{
+ Type: typ,
+ Method: "MarshalBinary",
+ Err: err,
+ }
+ }
+ return dat, err
case reflect.Ptr:
return Marshal(val.Elem().Interface())
case reflect.Array:
@@ -40,7 +56,10 @@ func MarshalWithoutInterface(obj any) ([]byte, error) {
case reflect.Struct:
return getStructHandler(val.Type()).Marshal(val)
default:
- panic(fmt.Errorf("type=%v does not implement binfmt.Marshaler and kind=%v is not a supported statically-sized kind",
- val.Type(), val.Kind()))
+ panic(&InvalidTypeError{
+ Type: val.Type(),
+ Err: fmt.Errorf("does not implement binfmt.Marshaler and kind=%v is not a supported statically-sized kind",
+ val.Kind()),
+ })
}
}
diff --git a/lib/binstruct/size.go b/lib/binstruct/size.go
index 03b42d8..365da85 100644
--- a/lib/binstruct/size.go
+++ b/lib/binstruct/size.go
@@ -5,6 +5,7 @@
package binstruct
import (
+ "errors"
"fmt"
"reflect"
)
@@ -31,6 +32,14 @@ func staticSize(typ reflect.Type) (int, error) {
if typ.Implements(staticSizerType) {
return reflect.New(typ).Elem().Interface().(StaticSizer).BinaryStaticSize(), nil
}
+ if typ.Implements(marshalerType) || typ.Implements(unmarshalerType) {
+ // If you implement binstruct.Marshaler or binstruct.Unmarshaler,
+ // then you must implement if you wish to be statically sized.
+ return 0, &InvalidTypeError{
+ Type: typ,
+ Err: errors.New("does not implement binstruct.StaticSizer but does implement binstruct.Marshaler or binstruct.Unmarshaler"),
+ }
+ }
switch typ.Kind() {
case reflect.Uint8, reflect.Int8:
return 1, nil
@@ -49,13 +58,12 @@ func staticSize(typ reflect.Type) (int, error) {
}
return elemSize * typ.Len(), nil
case reflect.Struct:
- if !(typ.Implements(marshalerType) || typ.Implements(unmarshalerType)) {
- return getStructHandler(typ).Size, nil
- }
- return 0, fmt.Errorf("type=%v (kind=%v) does not implement binfmt.StaticSizer but does implement binfmt.Marshaler or binfmt.Unmarshaler",
- typ, typ.Kind())
+ return getStructHandler(typ).Size, nil
default:
- return 0, fmt.Errorf("type=%v does not implement binfmt.StaticSizer and kind=%v is not a supported statically-sized kind",
- typ, typ.Kind())
+ return 0, &InvalidTypeError{
+ Type: typ,
+ Err: fmt.Errorf("does not implement binfmt.StaticSizer and kind=%v is not a supported statically-sized kind",
+ typ.Kind()),
+ }
}
}
diff --git a/lib/binstruct/structs.go b/lib/binstruct/structs.go
index 2f224dd..72fd5e5 100644
--- a/lib/binstruct/structs.go
+++ b/lib/binstruct/structs.go
@@ -183,7 +183,10 @@ func getStructHandler(typ reflect.Type) structHandler {
h, err := genStructHandler(typ)
if err != nil {
- panic(err)
+ panic(&InvalidTypeError{
+ Type: typ,
+ Err: err,
+ })
}
structCache[typ] = h
return h
diff --git a/lib/binstruct/unmarshal.go b/lib/binstruct/unmarshal.go
index c545137..61c2a4a 100644
--- a/lib/binstruct/unmarshal.go
+++ b/lib/binstruct/unmarshal.go
@@ -5,6 +5,7 @@
package binstruct
import (
+ "errors"
"fmt"
"reflect"
)
@@ -15,7 +16,15 @@ type Unmarshaler interface {
func Unmarshal(dat []byte, dstPtr any) (int, error) {
if unmar, ok := dstPtr.(Unmarshaler); ok {
- return unmar.UnmarshalBinary(dat)
+ n, err := unmar.UnmarshalBinary(dat)
+ if err != nil {
+ err = &UnmarshalError{
+ Type: reflect.TypeOf(dstPtr),
+ Method: "UnmarshalBinary",
+ Err: err,
+ }
+ }
+ return n, err
}
return UnmarshalWithoutInterface(dat, dstPtr)
}
@@ -23,7 +32,10 @@ func Unmarshal(dat []byte, dstPtr any) (int, error) {
func UnmarshalWithoutInterface(dat []byte, dstPtr any) (int, error) {
_dstPtr := reflect.ValueOf(dstPtr)
if _dstPtr.Kind() != reflect.Ptr {
- return 0, fmt.Errorf("not a pointer: %v", _dstPtr.Type())
+ panic(&InvalidTypeError{
+ Type: _dstPtr.Type(),
+ Err: errors.New("not a pointer"),
+ })
}
dst := _dstPtr.Elem()
@@ -52,7 +64,10 @@ func UnmarshalWithoutInterface(dat []byte, dstPtr any) (int, error) {
case reflect.Struct:
return getStructHandler(dst.Type()).Unmarshal(dat, dst)
default:
- panic(fmt.Errorf("type=%v does not implement binfmt.Unmarshaler and kind=%v is not a supported statically-sized kind",
- dst.Type(), dst.Kind()))
+ panic(&InvalidTypeError{
+ Type: _dstPtr.Type(),
+ Err: fmt.Errorf("does not implement binfmt.Unmarshaler and kind=%v is not a supported statically-sized kind",
+ dst.Kind()),
+ })
}
}
diff --git a/lib/btrfs/btrfsitem/item_chunk.go b/lib/btrfs/btrfsitem/item_chunk.go
index 7197fb3..2ccc860 100644
--- a/lib/btrfs/btrfsitem/item_chunk.go
+++ b/lib/btrfs/btrfsitem/item_chunk.go
@@ -5,8 +5,6 @@
package btrfsitem
import (
- "fmt"
-
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsvol"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/internal"
@@ -70,7 +68,7 @@ func (chunk *Chunk) UnmarshalBinary(dat []byte) (int, error) {
_n, err := binstruct.Unmarshal(dat[n:], &stripe)
n += _n
if err != nil {
- return n, fmt.Errorf("%T.UnmarshalBinary: %w", *chunk, err)
+ return n, err
}
chunk.Stripes = append(chunk.Stripes, stripe)
}
@@ -87,7 +85,7 @@ func (chunk Chunk) MarshalBinary() ([]byte, error) {
_ret, err := binstruct.Marshal(stripe)
ret = append(ret, _ret...)
if err != nil {
- return ret, fmt.Errorf("%T.MarshalBinary: %w", chunk, err)
+ return ret, err
}
}
return ret, nil
diff --git a/lib/btrfs/btrfsitem/item_dir.go b/lib/btrfs/btrfsitem/item_dir.go
index 859cd14..2fb2d41 100644
--- a/lib/btrfs/btrfsitem/item_dir.go
+++ b/lib/btrfs/btrfsitem/item_dir.go
@@ -9,47 +9,21 @@ import (
"hash/crc32"
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/internal"
)
-// key.objectid = inode of directory containing this entry
-// key.offset =
-// for DIR_ITEM and XATTR_ITEM = NameHash(name)
-// for DIR_INDEX = index id in the directory (starting at 2, because "." and "..")
-type DirEntries []DirEntry // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24
+const MaxNameLen = 255
func NameHash(dat []byte) uint64 {
return uint64(^crc32.Update(1, crc32.MakeTable(crc32.Castagnoli), dat))
}
-func (o *DirEntries) UnmarshalBinary(dat []byte) (int, error) {
- *o = nil
- n := 0
- for n < len(dat) {
- var ref DirEntry
- _n, err := binstruct.Unmarshal(dat, &ref)
- n += _n
- if err != nil {
- return n, err
- }
- *o = append(*o, ref)
- }
- return n, nil
-}
-
-func (o DirEntries) MarshalBinary() ([]byte, error) {
- var ret []byte
- for _, ref := range o {
- bs, err := binstruct.Marshal(ref)
- ret = append(ret, bs...)
- if err != nil {
- return ret, err
- }
- }
- return ret, nil
-}
-
-type DirEntry struct {
+// key.objectid = inode of directory containing this entry
+// key.offset =
+// for DIR_ITEM and XATTR_ITEM = NameHash(name)
+// for DIR_INDEX = index id in the directory (starting at 2, because "." and "..")
+type DirEntry struct { // DIR_ITEM=84 DIR_INDEX=96 XATTR_ITEM=24
Location internal.Key `bin:"off=0x0, siz=0x11"`
TransID int64 `bin:"off=0x11, siz=8"`
DataLen uint16 `bin:"off=0x19, siz=2"` // [ignored-when-writing]
@@ -61,10 +35,20 @@ type DirEntry struct {
}
func (o *DirEntry) UnmarshalBinary(dat []byte) (int, error) {
+ if err := binutil.NeedNBytes(dat, 0x1e); err != nil {
+ return 0, err
+ }
n, err := binstruct.UnmarshalWithoutInterface(dat, o)
if err != nil {
return n, err
}
+ if o.NameLen > MaxNameLen {
+ return 0, fmt.Errorf("maximum name len is %v, but .NameLen=%v",
+ MaxNameLen, o.NameLen)
+ }
+ if err := binutil.NeedNBytes(dat, 0x1e+int(o.DataLen)+int(o.NameLen)); err != nil {
+ return 0, err
+ }
o.Data = dat[n : n+int(o.DataLen)]
n += int(o.DataLen)
o.Name = dat[n : n+int(o.NameLen)]
diff --git a/lib/btrfs/btrfsitem/item_extent.go b/lib/btrfs/btrfsitem/item_extent.go
index 9257d2b..d49243d 100644
--- a/lib/btrfs/btrfsitem/item_extent.go
+++ b/lib/btrfs/btrfsitem/item_extent.go
@@ -74,8 +74,8 @@ type ExtentHeader struct {
type TreeBlockInfo struct {
Key internal.Key `bin:"off=0, siz=0x11"`
- Level uint8 `bin:"off=0x11, siz=0x8"`
- binstruct.End `bin:"off=0x19"`
+ Level uint8 `bin:"off=0x11, siz=0x1"`
+ binstruct.End `bin:"off=0x12"`
}
type ExtentFlags uint64
@@ -131,7 +131,7 @@ func (o *ExtentInlineRef) UnmarshalBinary(dat []byte) (int, error) {
return n, err
}
default:
- return n, fmt.Errorf("btrfsitem.ExtentInlineRef.UnmarshalBinary: unexpected item type %v", o.Type)
+ return n, fmt.Errorf("unexpected item type %v", o.Type)
}
return n, nil
}
@@ -163,7 +163,7 @@ func (o ExtentInlineRef) MarshalBinary() ([]byte, error) {
return dat, err
}
default:
- return dat, fmt.Errorf("btrfsitem.ExtentInlineRef.MarshalBinary: unexpected item type %v", o.Type)
+ return dat, fmt.Errorf("unexpected item type %v", o.Type)
}
return dat, nil
}
diff --git a/lib/btrfs/btrfsitem/item_extentcsum.go b/lib/btrfs/btrfsitem/item_extentcsum.go
index f9c546d..a945295 100644
--- a/lib/btrfs/btrfsitem/item_extentcsum.go
+++ b/lib/btrfs/btrfsitem/item_extentcsum.go
@@ -20,7 +20,7 @@ type ExtentCSum struct { // EXTENT_CSUM=128
func (o *ExtentCSum) UnmarshalBinary(dat []byte) (int, error) {
if o.ChecksumSize == 0 {
- return 0, fmt.Errorf("btrfs.ExtentCSum.UnmarshalBinary: .ChecksumSize must be set")
+ return 0, fmt.Errorf(".ChecksumSize must be set")
}
for len(dat) >= o.ChecksumSize {
var csum btrfssum.CSum
@@ -33,7 +33,7 @@ func (o *ExtentCSum) UnmarshalBinary(dat []byte) (int, error) {
func (o ExtentCSum) MarshalBinary() ([]byte, error) {
if o.ChecksumSize == 0 {
- return nil, fmt.Errorf("btrfs.ExtentCSum.MarshalBinary: .ChecksumSize must be set")
+ return nil, fmt.Errorf(".ChecksumSize must be set")
}
var dat []byte
for _, csum := range o.Sums {
diff --git a/lib/btrfs/btrfsitem/item_inoderef.go b/lib/btrfs/btrfsitem/item_inoderef.go
index e4edf4a..b1eaf1b 100644
--- a/lib/btrfs/btrfsitem/item_inoderef.go
+++ b/lib/btrfs/btrfsitem/item_inoderef.go
@@ -5,7 +5,10 @@
package btrfsitem
import (
+ "fmt"
+
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil"
)
// key.objectid = inode number of the file
@@ -18,10 +21,20 @@ type InodeRef struct { // INODE_REF=12
}
func (o *InodeRef) UnmarshalBinary(dat []byte) (int, error) {
+ if err := binutil.NeedNBytes(dat, 0xA); err != nil {
+ return 0, err
+ }
n, err := binstruct.UnmarshalWithoutInterface(dat, o)
if err != nil {
return n, err
}
+ if o.NameLen > MaxNameLen {
+ return 0, fmt.Errorf("maximum name len is %v, but .NameLen=%v",
+ MaxNameLen, o.NameLen)
+ }
+ if err := binutil.NeedNBytes(dat, 0xA+int(o.NameLen)); err != nil {
+ return 0, err
+ }
dat = dat[n:]
o.Name = dat[:o.NameLen]
n += int(o.NameLen)
diff --git a/lib/btrfs/btrfsitem/item_rootref.go b/lib/btrfs/btrfsitem/item_rootref.go
index 228ab55..1ee0ee4 100644
--- a/lib/btrfs/btrfsitem/item_rootref.go
+++ b/lib/btrfs/btrfsitem/item_rootref.go
@@ -5,7 +5,10 @@
package btrfsitem
import (
+ "fmt"
+
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct/binutil"
"git.lukeshu.com/btrfs-progs-ng/lib/btrfs/internal"
)
@@ -18,10 +21,20 @@ type RootRef struct { // ROOT_REF=156 ROOT_BACKREF=144
}
func (o *RootRef) UnmarshalBinary(dat []byte) (int, error) {
+ if err := binutil.NeedNBytes(dat, 0x12); err != nil {
+ return 0, err
+ }
n, err := binstruct.UnmarshalWithoutInterface(dat, o)
if err != nil {
return n, err
}
+ if o.NameLen > MaxNameLen {
+ return 0, fmt.Errorf("maximum name len is %v, but .NameLen=%v",
+ MaxNameLen, o.NameLen)
+ }
+ if err := binutil.NeedNBytes(dat, 0x12+int(o.NameLen)); err != nil {
+ return 0, err
+ }
o.Name = dat[n : n+int(o.NameLen)]
n += int(o.NameLen)
return n, nil
diff --git a/lib/btrfs/btrfsitem/items_gen.go b/lib/btrfs/btrfsitem/items_gen.go
index 82743b0..8573967 100644
--- a/lib/btrfs/btrfsitem/items_gen.go
+++ b/lib/btrfs/btrfsitem/items_gen.go
@@ -47,8 +47,8 @@ var keytype2gotype = map[Type]reflect.Type{
CHUNK_ITEM_KEY: reflect.TypeOf(Chunk{}),
DEV_EXTENT_KEY: reflect.TypeOf(DevExtent{}),
DEV_ITEM_KEY: reflect.TypeOf(Dev{}),
- DIR_INDEX_KEY: reflect.TypeOf(DirEntries{}),
- DIR_ITEM_KEY: reflect.TypeOf(DirEntries{}),
+ DIR_INDEX_KEY: reflect.TypeOf(DirEntry{}),
+ DIR_ITEM_KEY: reflect.TypeOf(DirEntry{}),
EXTENT_CSUM_KEY: reflect.TypeOf(ExtentCSum{}),
EXTENT_DATA_KEY: reflect.TypeOf(FileExtent{}),
EXTENT_DATA_REF_KEY: reflect.TypeOf(ExtentDataRef{}),
@@ -70,7 +70,7 @@ var keytype2gotype = map[Type]reflect.Type{
TREE_BLOCK_REF_KEY: reflect.TypeOf(Empty{}),
UUID_RECEIVED_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}),
UUID_SUBVOL_KEY: reflect.TypeOf(UUIDMap{}),
- XATTR_ITEM_KEY: reflect.TypeOf(DirEntries{}),
+ XATTR_ITEM_KEY: reflect.TypeOf(DirEntry{}),
}
var untypedObjID2gotype = map[internal.ObjID]reflect.Type{
internal.FREE_SPACE_OBJECTID: reflect.TypeOf(FreeSpaceHeader{}),
@@ -81,7 +81,7 @@ func (Chunk) isItem() {}
func (Dev) isItem() {}
func (DevExtent) isItem() {}
func (DevStats) isItem() {}
-func (DirEntries) isItem() {}
+func (DirEntry) isItem() {}
func (Empty) isItem() {}
func (Extent) isItem() {}
func (ExtentCSum) isItem() {}
diff --git a/lib/btrfs/btrfsitem/items_test.go b/lib/btrfs/btrfsitem/items_test.go
new file mode 100644
index 0000000..aba8ca2
--- /dev/null
+++ b/lib/btrfs/btrfsitem/items_test.go
@@ -0,0 +1,65 @@
+// Copyright (C) 2022 Luke Shumaker <lukeshu@lukeshu.com>
+//
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package btrfsitem_test
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/require"
+
+ "git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfsitem"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/btrfssum"
+ "git.lukeshu.com/btrfs-progs-ng/lib/btrfs/internal"
+)
+
+func FuzzRoundTrip(f *testing.F) {
+ keySize := binstruct.StaticSize(internal.Key{})
+ sumtypeSize := binstruct.StaticSize(btrfssum.CSumType(0))
+
+ f.Add(make([]byte, 256))
+
+ f.Fuzz(func(t *testing.T, inDat []byte) {
+ if len(inDat) < keySize+sumtypeSize {
+ t.Skip()
+ }
+ keyInDat, inDat := inDat[:keySize], inDat[keySize:]
+ sumtypeInDat, inDat := inDat[:sumtypeSize], inDat[sumtypeSize:]
+ itemInDat := inDat
+
+ // key
+
+ var key internal.Key
+ n, err := binstruct.Unmarshal(keyInDat, &key)
+ require.NoError(t, err, "binstruct.Unmarshal(dat, &key)")
+ require.Equal(t, keySize, n, "binstruct.Unmarshal(dat, &key)")
+
+ keyOutDat, err := binstruct.Marshal(key)
+ require.NoError(t, err, "binstruct.Marshal(key)")
+ require.Equal(t, keyInDat, keyOutDat, "binstruct.Marshal(key)")
+
+ // sumtype
+
+ var sumtype btrfssum.CSumType
+ n, err = binstruct.Unmarshal(sumtypeInDat, &sumtype)
+ require.NoError(t, err, "binstruct.Unmarshal(dat, &sumtype)")
+ require.Equal(t, sumtypeSize, n, "binstruct.Unmarshal(dat, &sumtype)")
+
+ sumtypeOutDat, err := binstruct.Marshal(sumtype)
+ require.NoError(t, err, "binstruct.Marshal(sumtype)")
+ require.Equal(t, sumtypeInDat, sumtypeOutDat, "binstruct.Marshal(sumtype)")
+
+ // item
+
+ t.Logf("key=%v sumtype=%v dat=%q", key, sumtype, itemInDat)
+
+ item := btrfsitem.UnmarshalItem(key, sumtype, itemInDat)
+ require.NotNil(t, item, "btrfsitem.UnmarshalItem")
+
+ itemOutDat, err := binstruct.Marshal(item)
+ require.NoError(t, err, "binstruct.Marshal(item)")
+ require.Equal(t, string(itemInDat), string(itemOutDat), "binstruct.Marshal(item)")
+ })
+}
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/12de190a2cae6f0c72854a75c03ce8488d9d0508a66fa4de0817264e458d2260 b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/12de190a2cae6f0c72854a75c03ce8488d9d0508a66fa4de0817264e458d2260
new file mode 100644
index 0000000..6fb6f2c
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/12de190a2cae6f0c72854a75c03ce8488d9d0508a66fa4de0817264e458d2260
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000\x9c0000000000000000000000000000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/1ecbbb90e92add091b31e3c765955d76d06074ea5dca8f6b4ce523a640d50a2d b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/1ecbbb90e92add091b31e3c765955d76d06074ea5dca8f6b4ce523a640d50a2d
new file mode 100644
index 0000000..5aa6619
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/1ecbbb90e92add091b31e3c765955d76d06074ea5dca8f6b4ce523a640d50a2d
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000\x180000000000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/2ae21a60c209d36ee27afa2b253c5f0ae7389c05837daa91500317cec5122f2e b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/2ae21a60c209d36ee27afa2b253c5f0ae7389c05837daa91500317cec5122f2e
new file mode 100644
index 0000000..68454dd
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/2ae21a60c209d36ee27afa2b253c5f0ae7389c05837daa91500317cec5122f2e
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000\x800000000000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/7b05bfc61add24e74c86043bfd0dea657fa522bd2b161d6334effe624c847850 b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/7b05bfc61add24e74c86043bfd0dea657fa522bd2b161d6334effe624c847850
new file mode 100644
index 0000000..2576860
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/7b05bfc61add24e74c86043bfd0dea657fa522bd2b161d6334effe624c847850
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000`00000000000000000000000000000000000\x00\x00\x00\x0000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/990f7eb92226efeef4491768fae2b9260529446c07a34b99d7e384b5dd847d93 b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/990f7eb92226efeef4491768fae2b9260529446c07a34b99d7e384b5dd847d93
new file mode 100644
index 0000000..3f5d16a
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/990f7eb92226efeef4491768fae2b9260529446c07a34b99d7e384b5dd847d93
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000\xa80000000000000000000000000020000000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/aff8a82fa526d6a55b4e13d9320aee5f9413109bbab24e3b2269cdd6abb97fb7 b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/aff8a82fa526d6a55b4e13d9320aee5f9413109bbab24e3b2269cdd6abb97fb7
new file mode 100644
index 0000000..b9c2ffe
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/aff8a82fa526d6a55b4e13d9320aee5f9413109bbab24e3b2269cdd6abb97fb7
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000`0000000000000000000000000000000000000000")
diff --git a/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/c9330e1640e459c9f623fc40d643eb42f727ed9b0b744c011a39a40552417cad b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/c9330e1640e459c9f623fc40d643eb42f727ed9b0b744c011a39a40552417cad
new file mode 100644
index 0000000..c117858
--- /dev/null
+++ b/lib/btrfs/btrfsitem/testdata/fuzz/FuzzRoundTrip/c9330e1640e459c9f623fc40d643eb42f727ed9b0b744c011a39a40552417cad
@@ -0,0 +1,2 @@
+go test fuzz v1
+[]byte("00000000\f00000000000000000000")
diff --git a/lib/btrfs/internal/misc.go b/lib/btrfs/internal/misc.go
index d4bd768..49fe2bd 100644
--- a/lib/btrfs/internal/misc.go
+++ b/lib/btrfs/internal/misc.go
@@ -5,6 +5,7 @@
package internal
import (
+ "fmt"
"time"
"git.lukeshu.com/btrfs-progs-ng/lib/binstruct"
@@ -20,6 +21,10 @@ type Key struct {
binstruct.End `bin:"off=0x11"`
}
+func (k Key) String() string {
+ return fmt.Sprintf("{%v %v %v}", k.ObjectID, k.ItemType, k.Offset)
+}
+
func (a Key) Cmp(b Key) int {
if d := util.CmpUint(a.ObjectID, b.ObjectID); d != 0 {
return d
diff --git a/lib/btrfs/io4_fs.go b/lib/btrfs/io4_fs.go
index cb6bd73..3099aca 100644
--- a/lib/btrfs/io4_fs.go
+++ b/lib/btrfs/io4_fs.go
@@ -201,42 +201,30 @@ func (ret *Dir) populate() {
}
ret.DotDot = &ref
case btrfsitem.DIR_ITEM_KEY:
- body := item.Body.(btrfsitem.DirEntries)
- if len(body) != 1 {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple direntries in single DIR_ITEM?"))
+ entry := item.Body.(btrfsitem.DirEntry)
+ namehash := btrfsitem.NameHash(entry.Name)
+ if namehash != item.Head.Key.Offset {
+ ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x",
+ item.Head.Key.Offset, entry.Name, namehash))
continue
}
- for _, entry := range body {
- namehash := btrfsitem.NameHash(entry.Name)
- if namehash != item.Head.Key.Offset {
- ret.Errs = append(ret.Errs, fmt.Errorf("direntry crc32c mismatch: key=%#x crc32c(%q)=%#x",
- item.Head.Key.Offset, entry.Name, namehash))
- continue
- }
- if other, exists := ret.ChildrenByName[string(entry.Name)]; exists {
- if !reflect.DeepEqual(entry, other) {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name))
- }
- continue
+ if other, exists := ret.ChildrenByName[string(entry.Name)]; exists {
+ if !reflect.DeepEqual(entry, other) {
+ ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry name %q", entry.Name))
}
- ret.ChildrenByName[string(entry.Name)] = entry
+ continue
}
+ ret.ChildrenByName[string(entry.Name)] = entry
case btrfsitem.DIR_INDEX_KEY:
index := item.Head.Key.Offset
- body := item.Body.(btrfsitem.DirEntries)
- if len(body) != 1 {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple direntries in single DIR_INDEX?"))
- continue
- }
- for _, entry := range body {
- if other, exists := ret.ChildrenByIndex[index]; exists {
- if !reflect.DeepEqual(entry, other) {
- ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry index %v", index))
- }
- continue
+ entry := item.Body.(btrfsitem.DirEntry)
+ if other, exists := ret.ChildrenByIndex[index]; exists {
+ if !reflect.DeepEqual(entry, other) {
+ ret.Errs = append(ret.Errs, fmt.Errorf("multiple instances of direntry index %v", index))
}
- ret.ChildrenByIndex[index] = entry
+ continue
}
+ ret.ChildrenByIndex[index] = entry
//case btrfsitem.XATTR_ITEM_KEY:
default:
panic(fmt.Errorf("TODO: handle item type %v", item.Head.Key.ItemType))
diff --git a/lib/btrfsprogs/btrfsinspect/print_tree.go b/lib/btrfsprogs/btrfsinspect/print_tree.go
index a950242..cc57ae6 100644
--- a/lib/btrfsprogs/btrfsinspect/print_tree.go
+++ b/lib/btrfsprogs/btrfsinspect/print_tree.go
@@ -136,16 +136,14 @@ func printTree(out, errout io.Writer, fs *btrfs.FS, treeID btrfs.ObjID) error {
body.Index, body.NameLen, body.Name)
//case btrfsitem.INODE_EXTREF_KEY:
// // TODO
- case btrfsitem.DirEntries:
- for _, dir := range body {
- fmt.Fprintf(out, "\t\tlocation %v type %v\n",
- fmtKey(dir.Location), dir.Type)
- fmt.Fprintf(out, "\t\ttransid %v data_len %v name_len %v\n",
- dir.TransID, dir.DataLen, dir.NameLen)
- fmt.Fprintf(out, "\t\tname: %s\n", dir.Name)
- if len(dir.Data) > 0 {
- fmt.Fprintf(out, "\t\tdata %v\n", dir.Data)
- }
+ case btrfsitem.DirEntry:
+ fmt.Fprintf(out, "\t\tlocation %v type %v\n",
+ fmtKey(body.Location), body.Type)
+ fmt.Fprintf(out, "\t\ttransid %v data_len %v name_len %v\n",
+ body.TransID, body.DataLen, body.NameLen)
+ fmt.Fprintf(out, "\t\tname: %s\n", body.Name)
+ if len(body.Data) > 0 {
+ fmt.Fprintf(out, "\t\tdata %v\n", body.Data)
}
//case btrfsitem.DIR_LOG_INDEX_KEY, btrfsitem.DIR_LOG_ITEM_KEY:
// // TODO