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