From d35f4c1c6be4293741abdc075e50f3ace8b65630 Mon Sep 17 00:00:00 2001 From: chuanying chen Date: Thu, 22 Oct 2026 11:07:52 +0800 Subject: [PATCH] Missing data read in super block extra parsing (#8392) * storage: fail on short super block extra read even without an error Some backends (e.g. s3_backend) clear io.EOF after a short read, returning n < len(p) with a nil error. Checking n only when the error is non-nil lets truncated extra data reach proto.Unmarshal with a zero-filled tail. * storage: apply the same short-read check to the super block header A backend that returns n < SuperBlockSize with a nil error previously slipped through and parsed a partially zero-filled header. * storage: test super block extra reads Covers a round trip through ReadSuperBlock, a truncated extra region, a backend returning io.EOF alongside a full read, and a backend returning a short read with a nil error. --------- Co-authored-by: Chris Lu --- .../super_block/super_block_read.go.go | 19 ++++++----- weed/storage/super_block/super_block_test.go | 33 +++++++++++++++++++ 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/weed/storage/super_block/super_block_read.go.go b/weed/storage/super_block/super_block_read.go.go index 69a9c108ef..3ded9e8398 100644 --- a/weed/storage/super_block/super_block_read.go.go +++ b/weed/storage/super_block/super_block_read.go.go @@ -2,6 +2,7 @@ package super_block import ( "fmt" + "io" "google.golang.org/protobuf/proto" @@ -15,11 +16,12 @@ import ( func ReadSuperBlock(datBackend backend.BackendStorageFile) (superBlock SuperBlock, err error) { header := make([]byte, SuperBlockSize) - if n, e := datBackend.ReadAt(header, 0); e != nil { - if n != SuperBlockSize { - err = fmt.Errorf("cannot read volume %s super block: %v", datBackend.Name(), e) - return + if n, e := datBackend.ReadAt(header, 0); n != SuperBlockSize { + if e == nil { + e = io.ErrUnexpectedEOF } + err = fmt.Errorf("cannot read volume %s super block: %v", datBackend.Name(), e) + return } superBlock.Version = needle.Version(header[0]) @@ -34,11 +36,12 @@ func ReadSuperBlock(datBackend backend.BackendStorageFile) (superBlock SuperBloc if superBlock.ExtraSize > 0 { // read more extraData := make([]byte, int(superBlock.ExtraSize)) - if n, e := datBackend.ReadAt(extraData, SuperBlockSize); e != nil { - if n != len(extraData) { - err = fmt.Errorf("cannot read volume %s super block extra: %v", datBackend.Name(), e) - return + if n, e := datBackend.ReadAt(extraData, SuperBlockSize); n != len(extraData) { + if e == nil { + e = io.ErrUnexpectedEOF } + err = fmt.Errorf("cannot read volume %s super block extra: %v", datBackend.Name(), e) + return } superBlock.Extra = &master_pb.SuperBlockExtra{} err = proto.Unmarshal(extraData, superBlock.Extra) diff --git a/weed/storage/super_block/super_block_test.go b/weed/storage/super_block/super_block_test.go index cdfcceefa2..6dd1d268c2 100644 --- a/weed/storage/super_block/super_block_test.go +++ b/weed/storage/super_block/super_block_test.go @@ -142,3 +142,36 @@ func TestReadSuperBlockTruncatedExtra(t *testing.T) { t.Fatalf("expected read error for truncated extra, got %v", err) } } + +// s3_backend.ReadAt clears io.EOF even when it returns fewer bytes than requested. +type shortReadBackendStorageFile struct { + backend.DiskFile + data []byte +} + +func (f *shortReadBackendStorageFile) ReadAt(p []byte, off int64) (n int, err error) { + return copy(p, f.data[off:]), nil +} + +func TestReadSuperBlockShortReadWithoutError(t *testing.T) { + rp, _ := NewReplicaPlacementFromByte(byte(001)) + ttl, _ := needle.ReadTTL("15d") + s := &SuperBlock{ + Version: needle.GetCurrentVersion(), + ReplicaPlacement: rp, + Ttl: ttl, + Extra: &master_pb.SuperBlockExtra{ + ErasureCoding: &master_pb.SuperBlockExtra_ErasureCoding{ + Data: 10, + Parity: 4, + VolumeIds: []uint32{1, 2, 3}, + }, + }, + } + + data := s.Bytes() + _, err := ReadSuperBlock(&shortReadBackendStorageFile{data: data[:len(data)-1]}) + if err == nil || !strings.Contains(err.Error(), "cannot read") { + t.Fatalf("expected read error for short extra read, got %v", err) + } +}