CVE-2023-23626 (Go, published 2023-02-09T21:15Z)
Description: value: go-bitfield is a simple bitfield package for the go language aiming to be more performant that the standard library. When feeding untrusted user input into the size parameter of `NewBitfield` and `FromBytes` functions, an attacker can trigger `panic`s. This happen when the `size` is a not a multiple of `8` or is negative. There were already a note in the `NewBitfield` documentation, however known users of this package are subject to this issue. Users are advised to upgrade. Users unable to upgrade should ensure that `size` is a multiple of 8 before calling `NewBitfield` or `FromBytes`.

Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-1284 \u2014 Improper Validation of Specified Quantity in Input
The product receives input that is expected to specify a quantity (such as size or length), but it does not validate or incorrectly validates that the quantity has the required properties.
Repository: https://github.com/ipfs/go-bitfield
Files changed:
bitfield.go
bitfield_test.go
Commit message: refactor: return errors instead of panics

It is clearer and simpler for consumers if we use the type system instead of relying on them for extra checks.

Fixes https://github.com/ipfs/go-bitfield/security/advisories/GHSA-2h6c-j3gf-xp9r


Vulnerable code:
//
// Panics if size is not a multiple of 8.
func NewBitfield(size int) Bitfield {
		panic("Bitfield size must be a multiple of 8")
	return make([]byte, size/8)
func FromBytes(size int, bits []byte) Bitfield {
	bf := NewBitfield(size)
		panic("bitfield too small")
	return bf
	bf := NewBitfield(24)
	bf := NewBitfield(128)
	bf := NewBitfield(benchmarkSize)
func BenchmarkOnes(t *testing.B) {
	bf := NewBitfield(benchmarkSize)
	t.ResetTimer()
	for i := 0; i < t.N; i++ {
				t.Fatal("bad", i)
func BenchmarkBytes(t *testing.B) {
	bfa := NewBitfield(211)
	bfb := NewBitfield(211)
	for j := 0; j*4 < 211; j++ {
	t.ResetTimer()
	for i := 0; i < t.N; i++ {

Fixed code:
	"fmt"
func NewBitfield(size int) (Bitfield, error) {
	if size < 0 {
		return nil, fmt.Errorf("bitfield size must be positive; got %d", size)
	}
		return nil, fmt.Errorf("bitfield size must be a multiple of 8; got %d", size)
	return make([]byte, size/8), nil
func FromBytes(size int, bits []byte) (Bitfield, error) {
	bf, err := NewBitfield(size)
	if err != nil {
		return nil, err
	}
		return nil, fmt.Errorf("bitfield too small: got %d; need %d", size, len(bits)*8)
	return bf, nil
	bf, err := NewBitfield(24)
	assertNoError(t, err)
	bf, err := NewBitfield(128)
	assertNoError(t, err)
func TestBadSizeFails(t *testing.T) {
	for _, size := range [...]int{-8, 2, 1337, -3} {
		_, err := NewBitfield(size)
		if err == nil {
			t.Fatalf("missing error for %d sized bitfield", size)
		}
	}
}

	bf, err := NewBitfield(benchmarkSize)
	assertNoError(t, err)
func BenchmarkOnes(b *testing.B) {
	bf, err := NewBitfield(benchmarkSize)
	assertNoError(b, err)
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
				b.Fatal("bad", i)
func BenchmarkBytes(b *testing.B) {
	bfa, err := NewBitfield(216)
	assertNoError(b, err)
	bfb, err := NewBitfield(216)
	assertNoError(b, err)
	for j := 0; j*4 < 216; j++ {
	b.ResetTimer()
	for i := 0; i < b.N; i++ {

func FuzzFromBytes(f *testing.F) {
	f.Fuzz(func(_ *testing.T, size int, bytes []byte) {
		if size > 1<<20 { // We relly on consumers for limit checks, hopefully they understand that a New... factory allocates memory.
			return
		}
		FromBytes(size, bytes)
	})
}

func assertNoError(t testing.TB, e error) {
	if e != nil {
		t.Fatal(e)
	}
}