From c9557b2822527cb07ba226eead32c6a0a146c163 Mon Sep 17 00:00:00 2001 From: greatroar <61184462+greatroar@users.noreply.github.com> Date: Mon, 6 Jun 2022 16:26:38 +0200 Subject: [PATCH] internal/repository: Fix LoadBlob + fuzz test When given a buf that is big enough for a compressed blob but not its decompressed contents, the copy at the end of LoadBlob would skip the last part of the contents. Fixes #3783. --- .gitattributes | 2 + internal/repository/fuzz_test.go | 43 +++++++++++++++++++ internal/repository/repository.go | 3 +- ...405e1ab2e1ef5d11d07c8aa4fe6814010294bffd33 | 3 ++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 internal/repository/fuzz_test.go create mode 100644 internal/repository/testdata/fuzz/FuzzSaveLoadBlob/62d79435b9ad1777d0562c405e1ab2e1ef5d11d07c8aa4fe6814010294bffd33 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..07a638105 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Workaround for https://github.com/golang/go/issues/52268. +**/testdata/fuzz/*/* eol=lf diff --git a/internal/repository/fuzz_test.go b/internal/repository/fuzz_test.go new file mode 100644 index 000000000..5af134d84 --- /dev/null +++ b/internal/repository/fuzz_test.go @@ -0,0 +1,43 @@ +//go:build go1.18 +// +build go1.18 + +package repository + +import ( + "context" + "testing" + + "github.com/restic/restic/internal/backend/mem" + "github.com/restic/restic/internal/restic" +) + +// Test saving a blob and loading it again, with varying buffer sizes. +// Also a regression test for #3783. +func FuzzSaveLoadBlob(f *testing.F) { + f.Fuzz(func(t *testing.T, blob []byte, buflen uint) { + if buflen > 64<<20 { + // Don't allocate enormous buffers. We're not testing the allocator. + t.Skip() + } + + id := restic.Hash(blob) + repo, _ := TestRepositoryWithBackend(t, mem.New(), 2) + + _, _, err := repo.SaveBlob(context.TODO(), restic.DataBlob, blob, id, false) + if err != nil { + t.Fatal(err) + } + err = repo.Flush(context.TODO()) + if err != nil { + t.Fatal(err) + } + + buf, err := repo.LoadBlob(context.TODO(), restic.DataBlob, id, make([]byte, buflen)) + if err != nil { + t.Fatal(err) + } + if restic.Hash(buf) != id { + t.Fatal("mismatch") + } + }) +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index dc03a3b14..6c095dcc5 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -303,8 +303,9 @@ func (r *Repository) LoadBlob(ctx context.Context, t restic.BlobType, id restic. return plaintext, nil } // move decrypted data to the start of the buffer + buf = buf[:len(plaintext)] copy(buf, plaintext) - return buf[:len(plaintext)], nil + return buf, nil } if lastError != nil { diff --git a/internal/repository/testdata/fuzz/FuzzSaveLoadBlob/62d79435b9ad1777d0562c405e1ab2e1ef5d11d07c8aa4fe6814010294bffd33 b/internal/repository/testdata/fuzz/FuzzSaveLoadBlob/62d79435b9ad1777d0562c405e1ab2e1ef5d11d07c8aa4fe6814010294bffd33 new file mode 100644 index 000000000..c6fcd9e9a --- /dev/null +++ b/internal/repository/testdata/fuzz/FuzzSaveLoadBlob/62d79435b9ad1777d0562c405e1ab2e1ef5d11d07c8aa4fe6814010294bffd33 @@ -0,0 +1,3 @@ +go test fuzz v1 +[]byte("\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6") +uint(109)