1 // Copyright 2017 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 #pragma once
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 #include <threads.h>
10 #include <unistd.h>
11 
12 #include <block-client/client.h>
13 #include <crypto/secret.h>
14 #include <fbl/macros.h>
15 #include <fbl/mutex.h>
16 #include <fbl/unique_fd.h>
17 #include <fvm/fvm.h>
18 #include <lib/zx/vmo.h>
19 #include <zircon/compiler.h>
20 #include <zircon/status.h>
21 #include <zircon/types.h>
22 
23 #include "crypto/utils.h"
24 
25 #define DEFINE_EACH_DEVICE(Test)                                                                   \
26     bool Test##Raw(Volume::Version version) { return Test(version, false /* not FVM */); }         \
27     DEFINE_EACH(Test##Raw);                                                                        \
28     bool Test##Fvm(Volume::Version version) { return Test(version, true /* FVM */); }              \
29     DEFINE_EACH(Test##Fvm);
30 
31 #define RUN_EACH_DEVICE(Test)                                                                      \
32     RUN_EACH(Test##Raw)                                                                            \
33     RUN_EACH(Test##Fvm)
34 
35 namespace zxcrypt {
36 namespace testing {
37 
38 // Default disk geometry to use when testing device block related code.
39 const uint32_t kBlockCount = 64;
40 const uint32_t kBlockSize = 512;
41 const size_t kDeviceSize = kBlockCount * kBlockSize;
42 const uint32_t kSliceCount = kDeviceSize / FVM_BLOCK_SIZE;
43 
44 // |zxcrypt::testing::Utils| is a collection of functions designed to make the zxcrypt
45 // unit test setup and tear down easier.
46 class TestDevice final {
47 public:
48     explicit TestDevice();
49     ~TestDevice();
50     DISALLOW_COPY_ASSIGN_AND_MOVE(TestDevice);
51 
52     // ACCESSORS
53 
54     // Returns the size of the zxcrypt volume.
size()55     size_t size() const { return block_count_ * block_size_; }
56 
57     // Returns a duplicated file descriptor representing the zxcrypt volume's underlying device;
58     // that is, the ramdisk or FVM partition.
parent()59     fbl::unique_fd parent() const {
60         return fbl::unique_fd(dup(fvm_part_ ? fvm_part_.get() : ramdisk_.get()));
61     }
62 
63     // Returns a duplicated file descriptor representing t the zxcrypt volume.
zxcrypt()64     fbl::unique_fd zxcrypt() const { return fbl::unique_fd(dup(zxcrypt_.get())); }
65 
66     // Returns the block size of the zxcrypt device.
block_size()67     size_t block_size() const { return block_size_; }
68 
69     // Returns the block size of the zxcrypt device.
block_count()70     size_t block_count() const { return block_count_; }
71 
72     // Returns space reserved for metadata
reserved_blocks()73     size_t reserved_blocks() const { return volume_->reserved_blocks(); }
reserved_slices()74     size_t reserved_slices() const { return volume_->reserved_slices(); }
75 
76     // Returns a reference to the root key generated for this device.
key()77     const crypto::Secret& key() const { return key_; }
78 
79     // API WRAPPERS
80 
81     // These methods mirror the POSIX API, except that the file descriptors and buffers are
82     // provided automatically. |off| and |len| are in bytes.
lseek(zx_off_t off)83     ssize_t lseek(zx_off_t off) { return ::lseek(zxcrypt_.get(), off, SEEK_SET); }
read(zx_off_t off,size_t len)84     ssize_t read(zx_off_t off, size_t len) {
85         return ::read(zxcrypt_.get(), as_read_.get() + off, len);
86     }
write(zx_off_t off,size_t len)87     ssize_t write(zx_off_t off, size_t len) {
88         return ::write(zxcrypt_.get(), to_write_.get() + off, len);
89     }
90 
91     // These methods mirror the syscall API, except that the VMO and buffers are provided
92     // automatically. |off| and |len| are in bytes.
vmo_read(zx_off_t off,size_t len)93     zx_status_t vmo_read(zx_off_t off, size_t len) {
94         return vmo_.read(as_read_.get() + off, 0, len);
95     }
vmo_write(uint64_t off,uint64_t len)96     zx_status_t vmo_write(uint64_t off, uint64_t len) {
97         return vmo_.write(to_write_.get() + off, 0, len);
98     }
99 
100     // Sends a request over the block fifo to read or write the blocks given by |off| and |len|,
101     // according to the given |opcode|.  The data sent or received can be accessed using |vmo_write|
102     // or |vmo_read|, respectively.  |off| and |len| are in blocks.
block_fifo_txn(uint16_t opcode,uint64_t off,uint64_t len)103     zx_status_t block_fifo_txn(uint16_t opcode, uint64_t off, uint64_t len) {
104         req_.opcode = opcode;
105         req_.length = static_cast<uint32_t>(len);
106         req_.dev_offset = off;
107         req_.vmo_offset = 0;
108         return ::block_fifo_txn(client_, &req_, 1);
109     }
110 
111     // Sends |num| requests over the block fifo to read or write blocks.
block_fifo_txn(block_fifo_request_t * requests,size_t num)112     zx_status_t block_fifo_txn(block_fifo_request_t* requests, size_t num) {
113         for (size_t i = 0; i < num; ++i) {
114             requests[i].group = req_.group;
115             requests[i].vmoid = req_.vmoid;
116         }
117         return ::block_fifo_txn(client_, requests, num);
118     }
119 
120     // TEST HELPERS
121 
122     // Allocates a new block device of at least |device_size| bytes grouped into blocks of
123     // |block_size| bytes each.  If |fvm| is true, it will be formatted as an FVM partition with the
124     // appropriates number of slices of |FVM_BLOCK_SIZE| each.  A file descriptor for the block
125     // device is returned via |out_fd|.
126     bool Create(size_t device_size, size_t block_size, bool fvm);
127 
128     // Test helper that generates a key and creates a device according to |version| and |fvm|.  It
129     // sets up the device as a zxcrypt volume and binds to it.
130     bool Bind(Volume::Version version, bool fvm);
131 
132     // Test helper that rebinds the ramdisk and its children.
133     bool Rebind();
134 
135     // Tells the underlying ramdisk to sleep until |num| transactions have been received.  If
136     // |deferred| is true, the transactions will be handled on waking; else they will be failed.
137     bool SleepUntil(uint64_t num, bool deferred) __TA_EXCLUDES(lock_);
138 
139     // Blocks until the ramdisk is awake.
140     bool WakeUp() __TA_EXCLUDES(lock_);
141 
142     // Test helpers that perform a |lseek| and a |read| or |write| together. |off| and |len| are in
143     // bytes.  |ReadFd| additionally checks that the data read matches what was written.
144     bool ReadFd(zx_off_t off, size_t len);
145     bool WriteFd(zx_off_t off, size_t len);
146 
147     // Test helpers that perform a |lseek| and a |vmo_read| or |vmo_write| together.  |off| and
148     // |len| are in blocks.  |ReadVmo| additionally checks that the data read matches what was
149     // written.
150     bool ReadVmo(zx_off_t off, size_t len);
151     bool WriteVmo(zx_off_t off, size_t len);
152 
153     // Test helper that flips a (pseudo)random bit in the key at the given |slot| in the given
154     // |block|. The call to |srand| in main.c guarantees the same bit will be chosen for a given
155     // test iteration.
156     bool Corrupt(uint64_t block, key_slot_t slot);
157 
158 private:
159     // Allocates a new ramdisk of at least |device_size| bytes arranged into blocks of |block_size|
160     // bytes, and opens it.
161     bool CreateRamdisk(size_t device_size, size_t block_size);
162 
163     // Destroys the ramdisk, killing any active transactions
164     void DestroyRamdisk();
165 
166     // Waits until idle, rebinds a ramdisk, and waits until it has been removed.
167     static zx_status_t RebindWatcher(int dirfd, int event, const char* fn, void* cookie);
168 
169     // Creates a ramdisk of with enough blocks of |block_size| bytes to hold both FVM metadata and
170     // an FVM partition of at least |device_size| bytes.  It formats the ramdisk to be an FVM
171     // device, and allocates a partition with a single slice of size FVM_BLOCK_SIZE.
172     bool CreateFvmPart(size_t device_size, size_t block_size);
173 
174     // Connects the block client to the block server.
175     bool Connect();
176 
177     // Disconnects the block client from the block server.
178     void Disconnect();
179 
180     // Thread body to wake up the underlying ramdisk.
181     static int WakeThread(void* arg);
182 
183     // The pathname of the ramdisk
184     char ramdisk_path_[PATH_MAX];
185     // The pathname of the FVM partition.
186     char fvm_part_path_[PATH_MAX];
187     // File descriptor for the underlying ramdisk.
188     fbl::unique_fd ramdisk_;
189     // File descriptor for the (optional) underlying FVM partition.
190     fbl::unique_fd fvm_part_;
191     // File descriptor for the zxcrypt volume.
192     fbl::unique_fd zxcrypt_;
193     // The zxcrypt volume
194     fbl::unique_ptr<Volume> volume_;
195     // The cached block count.
196     size_t block_count_;
197     // The cached block size.
198     size_t block_size_;
199     // The root key for this device.
200     crypto::Secret key_;
201     // Client for the block I/O protocol to the block server.
202     fifo_client_t* client_;
203     // Request structure used to send messages via the block I/O protocol.
204     block_fifo_request_t req_;
205     // VMO attached to the zxcrypt device for use with the block I/O protocol.
206     zx::vmo vmo_;
207     // An internal write buffer, initially filled with pseudo-random data
208     fbl::unique_ptr<uint8_t[]> to_write_;
209     // An internal write buffer,  initially filled with zeros.
210     fbl::unique_ptr<uint8_t[]> as_read_;
211     // Lock to coordinate waking thread
212     fbl::Mutex lock_;
213     // Thread used to manage sleeping/waking.
214     thrd_t tid_;
215     // It would be nice if thrd_t had a reserved invalid value...
216     bool need_join_;
217     // The number of transactions before waking.
218     uint64_t wake_after_ __TA_GUARDED(lock_);
219     // Timeout before waking regardless of transactions
220     zx::time wake_deadline_ __TA_GUARDED(lock_);
221 };
222 
223 } // namespace testing
224 } // namespace zxcrypt
225