1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <blobfs/journal.h>
6 #include <unittest/unittest.h>
7
8 namespace blobfs {
9 namespace {
10
11 // Mock journal implementation which can be used to test JournalEntry / JournalProcessor
12 // functionality.
13 class MockJournal : public JournalBase {
14 public:
MockJournal()15 MockJournal() : readonly_(false), capacity_(0) {}
16
SendSignal(zx_status_t status)17 void SendSignal(zx_status_t status) final {
18 if (status != ZX_OK) {
19 readonly_ = true;
20 }
21 }
22
CreateDefaultWork()23 fbl::unique_ptr<blobfs::WritebackWork> CreateDefaultWork() {
24 return CreateWork();
25 }
26
CreateBufferedWork(size_t block_count)27 fbl::unique_ptr<WritebackWork> CreateBufferedWork(size_t block_count) {
28 fbl::unique_ptr<WritebackWork> work = CreateWork();
29
30 zx::vmo vmo;
31 ZX_ASSERT(zx::vmo::create(PAGE_SIZE, 0, &vmo) == ZX_OK);
32 work->Enqueue(vmo, 0, 0, block_count);
33 work->SetBuffer(2);
34 return work;
35 }
36
37 private:
GetCapacity() const38 size_t GetCapacity() const final {
39 return capacity_;
40 }
41
IsReadOnly() const42 bool IsReadOnly() const final {
43 return readonly_;
44 }
45
CreateWork()46 fbl::unique_ptr<WritebackWork> CreateWork() final {
47 return fbl::make_unique<blobfs::WritebackWork>(nullptr, nullptr);
48 }
49
50 // The following functions are no-ops, and only exist so they can be called by the
51 // JournalProcessor.
PrepareBuffer(JournalEntry * entry)52 void PrepareBuffer(JournalEntry* entry) final {}
PrepareDelete(JournalEntry * entry,WritebackWork * work)53 void PrepareDelete(JournalEntry* entry, WritebackWork* work) final {}
EnqueueEntryWork(fbl::unique_ptr<WritebackWork> work)54 zx_status_t EnqueueEntryWork(fbl::unique_ptr<WritebackWork> work) final {
55 return ZX_OK;
56 }
57
58 bool readonly_;
59 size_t capacity_;
60 };
61
JournalEntryLifetimeTest()62 static bool JournalEntryLifetimeTest() {
63 BEGIN_TEST;
64
65 // Create a dummy journal and journal processor.
66 MockJournal journal;
67 blobfs::JournalProcessor processor(&journal);
68
69 // Create and process a 'work' entry.
70 fbl::unique_ptr<blobfs::JournalEntry> entry(
71 new blobfs::JournalEntry(&journal, blobfs::EntryStatus::kInit, 0, 0,
72 journal.CreateBufferedWork(1)));
73 fbl::unique_ptr<blobfs::WritebackWork> first_work = journal.CreateDefaultWork();
74 first_work->SetSyncCallback(entry->CreateSyncCallback());
75 processor.ProcessWorkEntry(std::move(entry));
76
77 // Create and process another 'work' entry.
78 entry.reset(new blobfs::JournalEntry(&journal, blobfs::EntryStatus::kInit, 0, 0,
79 journal.CreateBufferedWork(1)));
80 fbl::unique_ptr<blobfs::WritebackWork> second_work = journal.CreateDefaultWork();
81 second_work->SetSyncCallback(entry->CreateSyncCallback());
82 processor.ProcessWorkEntry(std::move(entry));
83
84 // Enqueue the processor's work (this is a no-op).
85 processor.EnqueueWork();
86
87 // Simulate an error in the writeback thread by calling the first entry's callback with an
88 // error status.
89 first_work->Reset(ZX_ERR_BAD_STATE);
90
91 // Process the wait queue.
92 processor.ProcessWaitQueue();
93
94 // Now, attempt to call the second entry's callback with the error. If we are incorrectly
95 // disposing of entries before their callbacks have been invoked, this should trigger a
96 // "use-after-free" asan error, since the JournalEntry referenced by second_work will have
97 // already been deleted (see ZX-2940).
98 second_work->Reset(ZX_ERR_BAD_STATE);
99
100 // Additionally, we should check that the processor queues are not empty - i.e., there is still
101 // one entry waiting to be processed.
102 ASSERT_FALSE(processor.IsEmpty());
103
104 // Process the rest of the queues.
105 processor.ProcessWaitQueue();
106 processor.ProcessDeleteQueue();
107 processor.ProcessSyncQueue();
108
109 END_TEST;
110 }
111
112 } // namespace
113 } // namespace blobfs
114
115 BEGIN_TEST_CASE(blobfsJournalTests)
116 RUN_TEST(blobfs::JournalEntryLifetimeTest)
117 END_TEST_CASE(blobfsJournalTests);
118