Skip to content

chore: add fuzzing across the codebase - #5602

Open
acud wants to merge 2 commits into
masterfrom
fuzzings
Open

acud wants to merge 2 commits into
masterfrom
fuzzings

Conversation

@acud

@acud acud commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Description

Adds fuzz testing across the codebase and fixes a few paths that needed patching.

AI Disclosure

  • This PR contains code that has been generated by an LLM.
  • I have reviewed the AI generated code thoroughly.
  • I possess the technical expertise to responsibly review the code generated in this PR.

Elad Nachmias and others added 2 commits September 14, 2026 07:51
Co-authored-by: Alok Nerurkar <alok@no-reply.com>
@acud
acud marked this pull request as ready for review September 16, 2026 19:29
func deserialize(data []byte) []*ecdsa.PublicKey {
if len(data) == 0 {
if len(data) == 0 || len(data)%publicKeyLen != 0 {
return []*ecdsa.PublicKey{}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deserialize returns an empty slice [] if the stored data has an invalid length or corrupted keys.

NewGranteeListReference returns &GranteeListStruct{grantees: []}, nil. Corrupted data is silently masked as an empty ACL. Should we split the IFs and return error if len(data)%publicKeyLen != 0

Comment thread Makefile
$(GOLANGCI_LINT) run ./...

.PHONY: nilaway
nilaway:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing nilaway Target:
+.PHONY: nilaway-bin
+nilaway-bin:

  • test -f $(GOBIN)/nilaway || $(GO) install go.uber.org/nilaway/cmd/nilaway@latest

.PHONY: nilaway
-nilaway:
+nilaway: nilaway-bin
$(GOBIN)/nilaway ./...

Comment thread pkg/file/joiner/joiner.go
@@ -147,8 +148,14 @@ func New(ctx context.Context, g storage.Getter, putter storage.Putter, address s
// A Joiner provides Read, Seek and Size functionalities.
func NewJoiner(ctx context.Context, g storage.Getter, putter storage.Putter, address swarm.Address, rootChunk swarm.Chunk) (file.Joiner, int64, error) {
chunkData := rootChunk.Data()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we also check rootChunk if nil ?

pool := make([]byte, swarm.ChunkWithSpanSize*2)
f.Add(append(inter, pool...))

f.Fuzz(func(t *testing.T, data []byte) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test encrypted references (64 bytes) alongside standard 32-byte references:

-	f.Fuzz(func(t *testing.T, data []byte) {
+	f.Fuzz(func(t *testing.T, data []byte, encryptedRef bool) { 
....

Comment thread pkg/postage/batch.go
// the fields are copied into fixed size windows; an oversized field would
// either silently overwrite a neighbouring field or, for the value, index
// out of range.
if len(value) > 32 || len(b.ID) > 32 || len(b.Owner) > 20 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking len(b.ID) > 32 and len(b.Owner) > 20 allows short byte slices. A 10-byte ID or 12-byte Owner is copied into the binary output left-aligned. Deserialization slices 32 and 20 bytes respectively, which mutates the data.
Should we have: if len(value) > 32 || len(b.ID) != 32 || len(b.Owner) != 20 { ?

Comment thread pkg/postage/batch.go
func (b *Batch) MarshalBinary() ([]byte, error) {
out := make([]byte, 95)
copy(out, b.ID)
if b.Value == nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If b.Value is negative, big.Int.Bytes() serializes the absolute magnitude, and deserialization recovers it as a positive number

Comment thread pkg/postage/batch.go
if len(value) > 32 || len(b.ID) > 32 || len(b.Owner) > 20 {
return nil, ErrBatchInvalid
}
out := make([]byte, batchSize)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have this checks?

if b.BucketDepth > b.Depth || b.Depth > swarm.MaxPO {
		return nil, ErrBatchInvalid
}

Comment thread pkg/postage/batch.go
b.Start = binary.BigEndian.Uint64(buf[64:72])
b.Owner = buf[72:92]
b.BucketDepth = buf[92]
b.Depth = buf[93]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same check here?

if b.BucketDepth > b.Depth || b.Depth > swarm.MaxPO {
		return ErrBatchInvalid
}

Comment thread pkg/keystore/file/key.go
// maxScryptMem bounds the memory scrypt.Key is allowed to allocate for a
// keyfile supplied set of parameters (it allocates 128*N*r bytes), so that
// a malformed or hostile keyfile cannot exhaust the node's memory.
maxScryptMem = 1 << 30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI:
maxScryptP = 256 and maxScryptMem = 1 GiB. This permits hostile keyfiles to demand 256 sequential passes and up to 1 GiB of RAM, causing severe CPU exhaustion or OOM-killing low-memory nodes.

  • maxScryptMem = 1 << 30
  • maxScryptMem = 256 * (1 << 20) // 256 MiB: matches standard Ethereum N=262144, r=8
    // maxScryptP bounds the parallelization factor, which drives both the
    // number of sequential smix passes and the size of the pbkdf2 block.
  • maxScryptP = 1 << 8
  • maxScryptP = 1 // Ethereum keystores universally use p=1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants