From 72da592b2706a097bfef3c8980bddc8a2dd267bd Mon Sep 17 00:00:00 2001 From: Mohd Quamar Tyagi <104281681+Tyagiquamar@users.noreply.github.com> Date: Sun, 11 Oct 2026 19:47:26 +0530 Subject: [PATCH] fix(filer): prevent nil pointer dereference in CompactFileChunks when context is cancelled (#11690) * filer: prevent nil pointer dereference in CompactFileChunks when context is cancelled Signed-off-by: Tyagiquamar * filer: propagate chunk resolution errors through ViewFromChunks A failed manifest resolution produced a nil interval list; with the nil guards this silently became an empty view, so replication sinks would write empty replicas and report success. Return the error so callers fail instead, and make ChunkStreamReader report it on first read. --------- Signed-off-by: Tyagiquamar Co-authored-by: Chris Lu Co-authored-by: Chris Lu --- weed/filer/filechunks.go | 6 ++++++ weed/filer/filechunks_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/weed/filer/filechunks.go b/weed/filer/filechunks.go index 3a61ed3b5..835c717f6 100644 --- a/weed/filer/filechunks.go +++ b/weed/filer/filechunks.go @@ -79,6 +79,9 @@ func CompactFileChunks(ctx context.Context, lookupFileIdFn wdclient.LookupFileId } func SeparateGarbageChunks(visibles *IntervalList[*VisibleInterval], chunks []*filer_pb.FileChunk) (compacted []*filer_pb.FileChunk, garbage []*filer_pb.FileChunk) { + if visibles == nil { + return chunks, nil + } fileIds := make(map[string]bool) for x := visibles.Front(); x != nil; x = x.Next { interval := x.Value @@ -225,6 +228,9 @@ func ViewFromVisibleIntervals(visibles *IntervalList[*VisibleInterval], offset i } chunkViews = NewIntervalList[*ChunkView]() + if visibles == nil { + return chunkViews + } for x := visibles.Front(); x != nil; x = x.Next { chunk := x.Value diff --git a/weed/filer/filechunks_test.go b/weed/filer/filechunks_test.go index 716e077a3..81eed9607 100644 --- a/weed/filer/filechunks_test.go +++ b/weed/filer/filechunks_test.go @@ -583,3 +583,36 @@ func TestCompactFileChunks3(t *testing.T) { t.Fatalf("unexpected compacted: %d", len(compacted)) } } + +func TestSeparateGarbageChunksNilVisibles(t *testing.T) { + chunks := []*filer_pb.FileChunk{ + {Offset: 10, Size: 100, FileId: "abc", ModifiedTsNs: 50}, + } + compacted, garbage := SeparateGarbageChunks(nil, chunks) + if len(compacted) != 1 || len(garbage) != 0 { + t.Fatalf("expected fallback to original chunks with nil garbage") + } +} + +func TestViewFromVisibleIntervalsNilVisibles(t *testing.T) { + views := ViewFromVisibleIntervals(nil, 0, 100) + if views == nil || views.Front() != nil { + t.Fatalf("expected empty interval list") + } +} + +func TestViewFromChunksPropagatesError(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + chunks := []*filer_pb.FileChunk{ + {Offset: 0, Size: 100, FileId: "abc", ModifiedTsNs: 50, IsChunkManifest: true}, + } + views, err := ViewFromChunks(ctx, nil, chunks, 0, math.MaxInt64) + if err == nil { + t.Fatal("expected error on cancelled context") + } + if views != nil { + t.Fatalf("expected nil views on error, got %v", views) + } +}