Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions ext4/dmverity/dmverity.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,14 @@ func ReadDMVerityInfo(vhdPath string, offsetInBytes int64) (*VerityInfo, error)

func ReadDMVerityInfoReader(r io.Reader) (*VerityInfo, error) {
block := make([]byte, blockSize)
if s, err := r.Read(block); err != nil || s != blockSize {
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrSuperBlockReadFailure, err)
// io.Reader is allowed to return fewer bytes than requested without that
// being an error, so read the whole block rather than relying on a single
// Read filling it.
if s, err := io.ReadFull(r, block); err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) {
return nil, fmt.Errorf("unexpected bytes read expected=%d actual=%d: %w", blockSize, s, ErrSuperBlockReadFailure)
}
return nil, fmt.Errorf("unexpected bytes read expected=%d actual=%d: %w", blockSize, s, ErrSuperBlockReadFailure)
return nil, fmt.Errorf("%w: %w", ErrSuperBlockReadFailure, err)
}

dmvSB := &dmveritySuperblock{}
Expand All @@ -200,11 +203,11 @@ func ReadDMVerityInfoReader(r io.Reader) (*VerityInfo, error) {
return nil, ErrNotVeritySuperBlock
}

if s, err := r.Read(block); err != nil || s != blockSize {
if err != nil {
return nil, fmt.Errorf("%w: %w", ErrRootHashReadFailure, err)
if s, err := io.ReadFull(r, block); err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) {
return nil, fmt.Errorf("unexpected bytes read expected=%d, actual=%d: %w", blockSize, s, ErrRootHashReadFailure)
}
return nil, fmt.Errorf("unexpected bytes read expected=%d, actual=%d: %w", blockSize, s, ErrRootHashReadFailure)
return nil, fmt.Errorf("%w: %w", ErrRootHashReadFailure, err)
}

rootHash := hash2(dmvSB.Salt[:dmvSB.SaltSize], block)
Expand Down
41 changes: 41 additions & 0 deletions ext4/dmverity/dmverity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,47 @@ func TestInvalidReadNotEnoughBytes(t *testing.T) {
}
}

// shortReader returns at most n bytes per Read, which io.Reader explicitly
// permits. A reader like this still delivers the whole hash device.
type shortReader struct {
r io.Reader
n int
}

func (s *shortReader) Read(p []byte) (int, error) {
if len(p) > s.n {
p = p[:s.n]
}
return s.r.Read(p)
}

func TestReadDMVerityInfoReaderShortReads(t *testing.T) {
tmpFile := tempFileWithContentLength(t, blockSize)
targetFile, err := writeDMVeritySuperBlock(tmpFile.Name())
if err != nil {
t.Fatalf("failed to write dm-verity super-block: %s", err)
}
content, err := os.ReadFile(targetFile.Name())
if err != nil {
t.Fatalf("failed to read temp file: %s", err)
}
// super block plus one block of root hash data
content = append(content[blockSize:], bytes.Repeat([]byte{1}, blockSize)...)

want, err := ReadDMVerityInfoReader(bytes.NewReader(content))
if err != nil {
t.Fatalf("failed to read verity info from a whole-block reader: %s", err)
}

got, err := ReadDMVerityInfoReader(&shortReader{r: bytes.NewReader(content), n: 1})
if err != nil {
t.Fatalf("failed to read verity info from a short reader: %s", err)
}
if got.RootDigest != want.RootDigest {
t.Fatalf("root digest mismatch: short reader got %q, want %q", got.RootDigest, want.RootDigest)
}
}

func TestNotVeritySuperBlock(t *testing.T) {
tmpFile := tempFileWithContentLength(t, 2*blockSize)
_, err := ReadDMVerityInfo(tmpFile.Name(), blockSize)
Expand Down