CVE-2023-25568 (Go, published 2023-05-10T14:15Z)
Description: value: Boxo, formerly known as go-libipfs, is a library for building IPFS applications and implementations. In versions 0.4.0 and 0.5.0, if an attacker is able allocate arbitrary many bytes in the Bitswap server, those allocations are lasting even if the connection is closed. This affects users accepting untrusted connections with the Bitswap server and also affects users using the old API stubs at `github.com/ipfs/go-libipfs/bitswap` because users then transitively import `github.com/ipfs/go-libipfs/bitswap/server`. Boxo versions 0.6.0 and 0.4.1 contain a patch for this issue. As a workaround, those who are using the stub object at `github.com/ipfs/go-libipfs/bitswap` not taking advantage of the features provided by the server can refactor their code to use the new split API that will allow them to run in a client only mode: `github.com/ipfs/go-libipfs/bitswap/client`.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
Repository: https://github.com/ipfs/boxo
Files changed:
bitswap/internal/defaults/defaults.go
bitswap/network/ipfs_impl.go
bitswap/options.go
bitswap/server/internal/decision/engine.go
bitswap/server/server.go
Commit message: bitswap/server/internal/decision: add filtering on CIDs
- Ignore cids that are too big.
- Kill connection for peers that are using inline CIDs.


Vulnerable code:
	panic("Not implemented: DisconnectFrom() is only used by tests")
// It defaults to DefaultMaxQueuedWantlistEntiresPerPeer.
func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwapMessage) {
	bs.engine.MessageReceived(ctx, p, incoming)

Fixed code:
	"encoding/binary"

	// Copied from github.com/ipfs/go-verifcid#maximumHashLength
	// FIXME: expose this in go-verifcid.
	MaximumHashLength = 128
	MaximumAllowedCid = binary.MaxVarintLen64*4 + MaximumHashLength
	return bsnet.host.Network().ClosePeer(p)
// MaxCidSize only affects the server.
// If it is 0 no limit is applied.
func MaxCidSize(n uint) Option {
	return Option{server.MaxCidSize(n)}
}

	mh "github.com/multiformats/go-multihash"
	maxCidSize                      uint
// WithMaxQueuedWantlistEntriesPerPeer limits how much individual entries each peer is allowed to send.
// If a peer send us more than this we will truncate newest entries.
func WithMaxCidSize(n uint) Option {
	return func(e *Engine) {
		e.maxCidSize = n
	}
}

		maxCidSize:                      defaults.MaximumAllowedCid,
func (e *Engine) MessageReceived(ctx context.Context, p peer.ID, m bsmsg.BitSwapMessage) (mustKillConnection bool) {
	filteredWants := wants[:0] // shift inplace

		if entry.Cid.Prefix().MhType == mh.IDENTITY {
			// This is a truely broken client, let's kill the connection.
			e.lock.Unlock()
			log.Warnw("peer wants an identity CID", "local", e.self, "remote", p)
			return true
		}
		if e.maxCidSize != 0 && uint(entry.Cid.ByteLen()) > e.maxCidSize {
			// Ignore requests about CIDs that big.
			continue
		}

		filteredWants = append(filteredWants, entry)
	}
	clear := wants[len(filteredWants):]
	for i := range clear {
		clear[i] = bsmsg.Entry{} // early GC
	wants = filteredWants
		if entry.Cid.Prefix().MhType == mh.IDENTITY {
			// This is a truely broken client, let's kill the connection.
			e.lock.Unlock()
			log.Warnw("peer canceled an identity CID", "local", e.self, "remote", p)
			return true
		}
		if e.maxCidSize != 0 && uint(entry.Cid.ByteLen()) > e.maxCidSize {
			// Ignore requests about CIDs that big.
			continue
		}

	return false
// MaxCidSize limits how big CIDs we are willing to serve.
// We will ignore CIDs over this limit.
// It defaults to [defaults.MaxCidSize].
// If it is 0 no limit is applied.
func MaxCidSize(n uint) Option {
	o := decision.WithMaxCidSize(n)
	return func(bs *Server) {
		bs.engineOptions = append(bs.engineOptions, o)
	}
}

	mustKillConnection := bs.engine.MessageReceived(ctx, p, incoming)
	if mustKillConnection {
		bs.network.DisconnectFrom(ctx, p)
	}