CVE-2023-23631: Uncontrolled Resource Consumption (CWE-400)
value: github.com/ipfs/go-unixfsnode is an ADL IPLD prime node that wraps go-codec-dagpb's implementation of protobuf to enable pathing. In versions priot to 1.5.2 trying to read malformed HAMT sharded directories can cause panics and virtual memory leaks.
If you are reading untrusted user input, an attacker can then trigger a panic. This is caused by bogus fanout parameter in the HAMT directory nodes. Users are advised to upgrade. There are no known workarounds for this vulnerability.
CWE description: The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
CVSS v3 base score: 7.5
Published: 2023-02-09T21:15Z
Repository: https://github.com/ipfs/go-unixfsnode
Language: Go
Files changed: hamt/shardeddir.go, hamt/util.go
Commit date: 2023-01-26 18:57:03 +0100
Commit message: Add size check on the bitfield before allocation

Diff: diff --git a/hamt/shardeddir.go b/hamt/shardeddir.go
index 97a833f..44af383 100644
--- a/hamt/shardeddir.go
+++ b/hamt/shardeddir.go
@@ -40,11 +40,14 @@ type _UnixFSHAMTShard struct {
 func NewUnixFSHAMTShard(ctx context.Context, substrate dagpb.PBNode, data data.UnixFSData, lsys *ipld.LinkSystem) (ipld.Node, error) {
 	if err := validateHAMTData(data); err != nil {
 		return nil, err
 	}
 	shardCache := make(map[ipld.Link]*_UnixFSHAMTShard, substrate.FieldLinks().Length())
-	bf := bitField(data)
+	bf, err := bitField(data)
+	if err != nil {
+		return nil, err
+	}
 	return &_UnixFSHAMTShard{
 		ctx:          ctx,
 		_substrate:   substrate,
 		data:         data,
 		lsys:         lsys,
diff --git a/hamt/util.go b/hamt/util.go
index bd72382..7132e03 100644
--- a/hamt/util.go
+++ b/hamt/util.go
@@ -86,14 +86,20 @@ func log2Size(nd data.UnixFSData) int {
 
 func maxPadLength(nd data.UnixFSData) int {
 	return len(fmt.Sprintf("%X", nd.FieldFanout().Must().Int()-1))
 }
 
-func bitField(nd data.UnixFSData) bitfield.Bitfield {
-	bf := bitfield.NewBitfield(int(nd.FieldFanout().Must().Int()))
+const maximumHamtWidth = 1 << 10
+
+func bitField(nd data.UnixFSData) (bitfield.Bitfield, error) {
+	fanout := int(nd.FieldFanout().Must().Int())
+	if fanout > maximumHamtWidth {
+		return nil, fmt.Errorf("hamt witdh (%d) exceed maximum allowed (%d)", fanout, maximumHamtWidth)
+	}
+	bf := bitfield.NewBitfield(fanout)
 	bf.SetBytes(nd.FieldData().Must().Bytes())
-	return bf
+	return bf, nil
 }
 
 func checkLogTwo(v int) error {
 	if v <= 0 {
 		return ErrHAMTSizeInvalid

Vulnerable code: 	bf := bitField(data)
func bitField(nd data.UnixFSData) bitfield.Bitfield {
	bf := bitfield.NewBitfield(int(nd.FieldFanout().Must().Int()))
	return bf
Fixed code: 	bf, err := bitField(data)
	if err != nil {
		return nil, err
	}
const maximumHamtWidth = 1 << 10

func bitField(nd data.UnixFSData) (bitfield.Bitfield, error) {
	fanout := int(nd.FieldFanout().Must().Int())
	if fanout > maximumHamtWidth {
		return nil, fmt.Errorf("hamt witdh (%d) exceed maximum allowed (%d)", fanout, maximumHamtWidth)
	}
	bf := bitfield.NewBitfield(fanout)
	return bf, nil