Conversation
Co-authored-by: Alok Nerurkar <alok@no-reply.com>
| @@ -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() | |||
There was a problem hiding this comment.
should we also check rootChunk if nil ?
There was a problem hiding this comment.
not sure. if yes, we should also check: context not nil, storage.Getter not nil, putter not nil, address not nil. my assumption here is that this is not needed because if such problem exists you will see if very early in a panic etc. and a nil chunk is technically not possible imo
| pool := make([]byte, swarm.ChunkWithSpanSize*2) | ||
| f.Add(append(inter, pool...)) | ||
|
|
||
| f.Fuzz(func(t *testing.T, data []byte) { |
There was a problem hiding this comment.
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) {
....
| // 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 { |
There was a problem hiding this comment.
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 { ?
| func (b *Batch) MarshalBinary() ([]byte, error) { | ||
| out := make([]byte, 95) | ||
| copy(out, b.ID) | ||
| if b.Value == nil { |
There was a problem hiding this comment.
If b.Value is negative, big.Int.Bytes() serializes the absolute magnitude, and deserialization recovers it as a positive number
There was a problem hiding this comment.
how can it be negative?
| if len(value) > 32 || len(b.ID) > 32 || len(b.Owner) > 20 { | ||
| return nil, ErrBatchInvalid | ||
| } | ||
| out := make([]byte, batchSize) |
There was a problem hiding this comment.
Should we also have this checks?
if b.BucketDepth > b.Depth || b.Depth > swarm.MaxPO {
return nil, ErrBatchInvalid
}
| b.Start = binary.BigEndian.Uint64(buf[64:72]) | ||
| b.Owner = buf[72:92] | ||
| b.BucketDepth = buf[92] | ||
| b.Depth = buf[93] |
There was a problem hiding this comment.
Same check here?
if b.BucketDepth > b.Depth || b.Depth > swarm.MaxPO {
return ErrBatchInvalid
}
| // 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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
this presupposes that:
- the node's machine has been hacked
- the attacker forged a new keyfile and has put it into the node keyfile directory
?
aloknerurkar
left a comment
There was a problem hiding this comment.
Looks good overall apart from the minor comments. Main question is why we haven't ported the fuzz seed corpus files and if we should add this to CI for regression instead of relying on developers to run this themselves.
| fi | ||
|
|
||
| # Replay the seed corpus and the regression fixtures committed under | ||
| # testdata/fuzz/ without generating new inputs. |
There was a problem hiding this comment.
The seed corpus is missing in this PR? Without the seed corpus it only replays the in-line f.Add seeds and none of the values that actually caused the crashes.
Also I feel this should be enforced in CI. Maybe with a lower FUZZ_TIME?
There was a problem hiding this comment.
i removed them and converted them into actual regression tests. it doesn't make sense to include them because it just adds another level of abstraction on top of the fuzzer because it just seeds the fuzzer with the input needed to deterministically generate the test case inputs. so in other words, it is another layer of abstraction that makes you a regression test on the fly, nothing more. so they were converted into test vectors instead.
| // FuzzFeedPostHandler drives arbitrary HTTP feed creation requests through the feed | ||
| // router path, testing the control-flow bug (CF-01) where error handling in feed creation | ||
| // must never fall through to dereference a nil manifest. | ||
| func FuzzFeedPostHandler(f *testing.F) { |
There was a problem hiding this comment.
NewDefaultManifest only returns an error for ErrInvalidManifestType, and it is called with the hardcoded DefaultManifestType constant. So the error branch at pkg/api/feed.go:238 is unreachable through this HTTP path regardless of what the fuzzer sends for owner/topic. So not sure if the target provides any coverage of the bug it is documented as targeting.
pkg/api/feed.go:L238-L247 writes an HTTP response in the error branch but omits the return, falling through to feedManifest.Add(...) on a nil manifest. The branch is unreachable, but can become live if someone refactors the manifest pkg.
Either port the one-line return fix along with this target, or drop the CF-01 claim from this comment so it does not read as coverage that exists.
| wg.Wait() | ||
| } | ||
|
|
||
| func TestCurrentRatesConcurrentWithUpdatesSync(t *testing.T) { |
There was a problem hiding this comment.
Worth a comment on the test saying it is a no-op without -race, so nobody (including AI) later "fixes" it by adding a meaningless assertion or deletes it as dead weight.
| // isolating the target on the pre-signature validation arithmetic. An empty | ||
| // payout models a peer cheque whose JSON omitted CumulativePayout (decoding to a | ||
| // nil *big.Int) — exactly what the swap handler forwards after json.Unmarshal. | ||
| func FuzzReceiveCheque(f *testing.F) { |
There was a problem hiding this comment.
This test has no assertions at all. This matches the original intent (NIL-06) but it does mean this target cannot catch a logic regression, e.g. ReceiveCheque silently accepting a cheque it should reject. An assertion that a nil/ malformed cheque produces a non-nil error would make this meaningfully stronger.
The seed this is not ported is what made this target deterministic rather than probabilistic.
Description
Adds fuzz testing across the codebase and fixes a few paths that needed patching.
AI Disclosure