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 #include <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <inttypes.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/types.h>
14 #include <threads.h>
15 #include <unistd.h>
16 
17 #include <fbl/algorithm.h>
18 #include <fbl/auto_call.h>
19 #include <fbl/auto_lock.h>
20 #include <fbl/string.h>
21 #include <fbl/unique_fd.h>
22 #include <fs-management/fvm.h>
23 #include <fs-management/mount.h>
24 #include <fs-management/ramdisk.h>
25 #include <fvm/fvm.h>
26 #include <lib/fdio/debug.h>
27 #include <lib/fdio/watcher.h>
28 #include <lib/zx/time.h>
29 #include <unittest/unittest.h>
30 #include <zircon/assert.h>
31 #include <zircon/types.h>
32 #include <zxcrypt/volume.h>
33 
34 #include "test-device.h"
35 
36 #define ZXDEBUG 0
37 
38 namespace zxcrypt {
39 namespace testing {
40 namespace {
41 
42 // No test step should take longer than this
43 const zx::duration kTimeout = zx::sec(3);
44 
45 // FVM driver library
46 const char* kFvmDriver = "/boot/driver/fvm.so";
47 
48 // Takes a given |result|, e.g. from an ioctl, and translates into a zx_status_t.
ToStatus(ssize_t result)49 zx_status_t ToStatus(ssize_t result) {
50     return result < 0 ? static_cast<zx_status_t>(result) : ZX_OK;
51 }
52 
53 // Helper function to build error messages
Error(const char * fmt,...)54 char* Error(const char* fmt, ...) {
55     static char err[256];
56     va_list ap;
57     va_start(ap, fmt);
58     vsnprintf(err, sizeof(err), fmt, ap);
59     va_end(ap);
60     return err;
61 }
62 
63 // Waits for the given |path| to be opened, opens it, and returns the file descriptor via |out|.
WaitAndOpen(char * path,fbl::unique_fd * out)64 bool WaitAndOpen(char* path, fbl::unique_fd* out) {
65     BEGIN_HELPER;
66 
67     ASSERT_EQ(wait_for_device(path, ZX_SEC(3)), ZX_OK,
68               Error("failed while waiting to bind %s", path));
69     fbl::unique_fd fd(open(path, O_RDWR));
70     ASSERT_TRUE(fd, Error("failed to open %s", path));
71     if (out) {
72         out->swap(fd);
73     }
74 
75     END_HELPER;
76 }
77 
78 } // namespace
79 
TestDevice()80 TestDevice::TestDevice()
81     : block_count_(0), block_size_(0), client_(nullptr), tid_(0), need_join_(false), wake_after_(0),
82       wake_deadline_(0) {
83     memset(ramdisk_path_, 0, sizeof(ramdisk_path_));
84     memset(fvm_part_path_, 0, sizeof(fvm_part_path_));
85     memset(&req_, 0, sizeof(req_));
86 }
87 
~TestDevice()88 TestDevice::~TestDevice() {
89     Disconnect();
90     ramdisk_.reset();
91     DestroyRamdisk();
92     if (need_join_) {
93         int res;
94         thrd_join(tid_, &res);
95     }
96 }
97 
Create(size_t device_size,size_t block_size,bool fvm)98 bool TestDevice::Create(size_t device_size, size_t block_size, bool fvm) {
99     BEGIN_HELPER;
100 
101     ASSERT_LT(device_size, SSIZE_MAX);
102     if (fvm) {
103         ASSERT_TRUE(CreateFvmPart(device_size, block_size));
104     } else {
105         ASSERT_TRUE(CreateRamdisk(device_size, block_size));
106     }
107 
108 // TODO(aarongreen): See ZX-1130. The code below should be enabled when that bug is fixed.
109 #if 0
110     crypto::digest::Algorithm digest;
111     switch (version) {
112     case Volume::kAES256_XTS_SHA256:
113         digest = crypto::digest::kSHA256;
114         break;
115     default:
116         digest = crypto::digest::kUninitialized;
117         break;
118     }
119 
120     size_t digest_len;
121     key_.Reset();
122     if ((rc = crypto::digest::GetDigestLen(digest, &digest_len)) != ZX_OK ||
123         (rc = key_.Randomize(digest_len)) != ZX_OK) {
124         return rc;
125     }
126 #else
127     uint8_t* buf;
128     ASSERT_OK(key_.Allocate(kZx1130KeyLen, &buf));
129     memset(buf, 0, key_.len());
130 #endif
131 
132     END_HELPER;
133 }
134 
Bind(Volume::Version version,bool fvm)135 bool TestDevice::Bind(Volume::Version version, bool fvm) {
136     BEGIN_HELPER;
137     ASSERT_TRUE(Create(kDeviceSize, kBlockSize, fvm));
138     ASSERT_OK(Volume::Create(parent(), key_));
139     ASSERT_TRUE(Connect());
140     END_HELPER;
141 }
142 
Rebind()143 bool TestDevice::Rebind() {
144     BEGIN_HELPER;
145 
146     const char* sep = strrchr(ramdisk_path_, '/');
147     ASSERT_NONNULL(sep);
148     fbl::String path(ramdisk_path_, sep - ramdisk_path_);
149     DIR* dir = opendir(path.c_str());
150     ASSERT_NONNULL(dir);
151     auto close_dir = fbl::MakeAutoCall([&] { closedir(dir); });
152 
153     zx::time deadline = zx::deadline_after(kTimeout);
154 
155     Disconnect();
156     zxcrypt_.reset();
157     fvm_part_.reset();
158     ASSERT_EQ(fdio_watch_directory(dirfd(dir), RebindWatcher, deadline.get(), this), ZX_ERR_STOP);
159     ASSERT_TRUE(WaitAndOpen(ramdisk_path_, &ramdisk_));
160     if (strlen(fvm_part_path_) != 0) {
161         ASSERT_TRUE(WaitAndOpen(fvm_part_path_, &fvm_part_));
162     }
163     ASSERT_TRUE(Connect());
164 
165     END_HELPER;
166 }
167 
RebindWatcher(int dirfd,int event,const char * fn,void * cookie)168 zx_status_t TestDevice::RebindWatcher(int dirfd, int event, const char* fn, void* cookie) {
169     TestDevice* device = static_cast<TestDevice*>(cookie);
170     switch (event) {
171     case WATCH_EVENT_IDLE:
172         return ToStatus(ioctl_block_rr_part(device->ramdisk_.get()));
173     case WATCH_EVENT_REMOVE_FILE:
174         return strcmp(fn, "block") == 0 ? ZX_ERR_STOP : ZX_OK;
175     default:
176         return ZX_OK;
177     }
178 }
179 
SleepUntil(uint64_t num,bool deferred)180 bool TestDevice::SleepUntil(uint64_t num, bool deferred) {
181     BEGIN_HELPER;
182     fbl::AutoLock lock(&lock_);
183     ASSERT_EQ(wake_after_, 0);
184     ASSERT_NE(num, 0);
185     wake_after_ = num;
186     wake_deadline_ = zx::deadline_after(kTimeout);
187     ASSERT_EQ(thrd_create(&tid_, TestDevice::WakeThread, this), thrd_success);
188     need_join_ = true;
189     if (deferred) {
190         uint32_t flags = RAMDISK_FLAG_RESUME_ON_WAKE;
191         ASSERT_OK(ToStatus(ioctl_ramdisk_set_flags(ramdisk_.get(), &flags)));
192     }
193     uint64_t sleep_after = 0;
194     ASSERT_OK(ToStatus(ioctl_ramdisk_sleep_after(ramdisk_.get(), &sleep_after)));
195     END_HELPER;
196 }
197 
WakeUp()198 bool TestDevice::WakeUp() {
199     BEGIN_HELPER;
200     if (need_join_) {
201         fbl::AutoLock lock(&lock_);
202         ASSERT_NE(wake_after_, 0);
203         int res;
204         ASSERT_EQ(thrd_join(tid_, &res), thrd_success);
205         need_join_ = false;
206         wake_after_ = 0;
207         EXPECT_EQ(res, 0);
208     }
209     END_HELPER;
210 }
211 
WakeThread(void * arg)212 int TestDevice::WakeThread(void* arg) {
213     TestDevice* device = static_cast<TestDevice*>(arg);
214     fbl::AutoLock lock(&device->lock_);
215 
216     // Always send a wake-up call; even if we failed to go to sleep.
217     auto cleanup = fbl::MakeAutoCall([&] { ioctl_ramdisk_wake_up(device->ramdisk_.get()); });
218 
219     // Loop until timeout, |wake_after_| txns received, or error getting counts
220     ramdisk_blk_counts_t counts;
221     ssize_t res;
222     do {
223         zx::nanosleep(zx::deadline_after(zx::msec(100)));
224         if (device->wake_deadline_ < zx::clock::get_monotonic()) {
225             printf("Received %lu of %lu transactions before timing out.\n", counts.received,
226                    device->wake_after_);
227             return ZX_ERR_TIMED_OUT;
228         }
229         if ((res = ioctl_ramdisk_get_blk_counts(device->ramdisk_.get(), &counts)) < 0) {
230             return static_cast<zx_status_t>(res);
231         }
232     } while (counts.received < device->wake_after_);
233     return ZX_OK;
234 }
235 
ReadFd(zx_off_t off,size_t len)236 bool TestDevice::ReadFd(zx_off_t off, size_t len) {
237     BEGIN_HELPER;
238     ASSERT_OK(ToStatus(lseek(off)));
239     ASSERT_OK(ToStatus(read(off, len)));
240     ASSERT_EQ(memcmp(as_read_.get() + off, to_write_.get() + off, len), 0);
241     END_HELPER;
242 }
243 
WriteFd(zx_off_t off,size_t len)244 bool TestDevice::WriteFd(zx_off_t off, size_t len) {
245     BEGIN_HELPER;
246     ASSERT_OK(ToStatus(lseek(off)));
247     ASSERT_OK(ToStatus(write(off, len)));
248     END_HELPER;
249 }
250 
ReadVmo(zx_off_t off,size_t len)251 bool TestDevice::ReadVmo(zx_off_t off, size_t len) {
252     BEGIN_HELPER;
253     ASSERT_OK(block_fifo_txn(BLOCKIO_READ, off, len));
254     off *= block_size_;
255     len *= block_size_;
256     ASSERT_OK(vmo_read(off, len));
257     ASSERT_EQ(memcmp(as_read_.get() + off, to_write_.get() + off, len), 0);
258     END_HELPER;
259 }
260 
WriteVmo(zx_off_t off,size_t len)261 bool TestDevice::WriteVmo(zx_off_t off, size_t len) {
262     BEGIN_HELPER;
263     ASSERT_OK(vmo_write(off * block_size_, len * block_size_));
264     ASSERT_OK(block_fifo_txn(BLOCKIO_WRITE, off, len));
265     END_HELPER;
266 }
267 
Corrupt(uint64_t blkno,key_slot_t slot)268 bool TestDevice::Corrupt(uint64_t blkno, key_slot_t slot) {
269     BEGIN_HELPER;
270     uint8_t block[block_size_];
271 
272     fbl::unique_fd fd = parent();
273     ASSERT_OK(ToStatus(::lseek(fd.get(), blkno * block_size_, SEEK_SET)));
274     ASSERT_OK(ToStatus(::read(fd.get(), block, block_size_)));
275 
276     fbl::unique_ptr<Volume> volume;
277     ASSERT_OK(Volume::Unlock(parent(), key_, 0, &volume));
278 
279     zx_off_t off;
280     ASSERT_OK(volume->GetSlotOffset(slot, &off));
281     int flip = 1U << (rand() % 8);
282     block[off] ^= static_cast<uint8_t>(flip);
283 
284     ASSERT_OK(ToStatus(::lseek(fd.get(), blkno * block_size_, SEEK_SET)));
285     ASSERT_OK(ToStatus(::write(fd.get(), block, block_size_)));
286     END_HELPER;
287 }
288 
289 // Private methods
290 
CreateRamdisk(size_t device_size,size_t block_size)291 bool TestDevice::CreateRamdisk(size_t device_size, size_t block_size) {
292     BEGIN_HELPER;
293 
294     fbl::AllocChecker ac;
295     size_t count = fbl::round_up(device_size, block_size) / block_size;
296     to_write_.reset(new (&ac) uint8_t[device_size]);
297     ASSERT_TRUE(ac.check());
298     for (size_t i = 0; i < device_size; ++i) {
299         to_write_[i] = static_cast<uint8_t>(rand());
300     }
301 
302     as_read_.reset(new (&ac) uint8_t[device_size]);
303     ASSERT_TRUE(ac.check());
304     memset(as_read_.get(), 0, block_size);
305 
306     ASSERT_EQ(create_ramdisk(block_size, count, ramdisk_path_), ZX_OK);
307     ramdisk_.reset(open(ramdisk_path_, O_RDWR));
308     ASSERT_TRUE(ramdisk_, Error("failed to open %s", ramdisk_path_));
309 
310     block_size_ = block_size;
311     block_count_ = count;
312 
313     END_HELPER;
314 }
315 
DestroyRamdisk()316 void TestDevice::DestroyRamdisk() {
317     if (strlen(ramdisk_path_) != 0) {
318         destroy_ramdisk(ramdisk_path_);
319         ramdisk_path_[0] = '\0';
320     }
321 }
322 
323 // Creates a ramdisk, formats it, and binds to it.
CreateFvmPart(size_t device_size,size_t block_size)324 bool TestDevice::CreateFvmPart(size_t device_size, size_t block_size) {
325     BEGIN_HELPER;
326 
327     // Calculate total size of data + metadata.
328     device_size = fbl::round_up(device_size, FVM_BLOCK_SIZE);
329     size_t old_meta = fvm::MetadataSize(device_size, FVM_BLOCK_SIZE);
330     size_t new_meta = fvm::MetadataSize(old_meta + device_size, FVM_BLOCK_SIZE);
331     while (old_meta != new_meta) {
332         old_meta = new_meta;
333         new_meta = fvm::MetadataSize(old_meta + device_size, FVM_BLOCK_SIZE);
334     }
335     ASSERT_TRUE(CreateRamdisk(device_size + (new_meta * 2), block_size));
336 
337     // Format the ramdisk as FVM and bind to it
338     ASSERT_OK(fvm_init(ramdisk_.get(), FVM_BLOCK_SIZE));
339     ASSERT_OK(ToStatus(ioctl_device_bind(ramdisk_.get(), kFvmDriver, strlen(kFvmDriver))));
340 
341     char path[PATH_MAX];
342     fbl::unique_fd fvm_fd;
343     snprintf(path, sizeof(path), "%s/fvm", ramdisk_path_);
344     ASSERT_TRUE(WaitAndOpen(path, &fvm_fd));
345 
346     // Allocate a FVM partition with the last slice unallocated.
347     alloc_req_t req;
348     memset(&req, 0, sizeof(alloc_req_t));
349     req.slice_count = (kDeviceSize / FVM_BLOCK_SIZE) - 1;
350     memcpy(req.type, zxcrypt_magic, sizeof(zxcrypt_magic));
351     for (uint8_t i = 0; i < GUID_LEN; ++i) {
352         req.guid[i] = i;
353     }
354     snprintf(req.name, NAME_LEN, "data");
355     fvm_part_.reset(fvm_allocate_partition(fvm_fd.get(), &req));
356     ASSERT_TRUE(fvm_part_);
357 
358     // Save the topological path for rebinding
359     ASSERT_OK(ToStatus(
360         ioctl_device_get_topo_path(fvm_part_.get(), fvm_part_path_, sizeof(fvm_part_path_))));
361 
362     END_HELPER;
363 }
364 
Connect()365 bool TestDevice::Connect() {
366     BEGIN_HELPER;
367     ZX_DEBUG_ASSERT(!zxcrypt_);
368 
369     ASSERT_OK(Volume::Unlock(parent(), key_, 0, &volume_));
370     ASSERT_OK(volume_->Open(kTimeout, &zxcrypt_));
371 
372     block_info_t blk;
373     ASSERT_OK(ToStatus(ioctl_block_get_info(zxcrypt_.get(), &blk)));
374     block_size_ = blk.block_size;
375     block_count_ = blk.block_count;
376 
377     zx_handle_t fifo;
378     ASSERT_OK(ToStatus(ioctl_block_get_fifos(zxcrypt_.get(), &fifo)));
379     req_.group = 0;
380     ASSERT_OK(block_fifo_create_client(fifo, &client_));
381 
382     // Create the vmo and get a transferable handle to give to the block server
383     ASSERT_OK(zx::vmo::create(size(), 0, &vmo_));
384     zx_handle_t xfer;
385     ASSERT_OK(zx_handle_duplicate(vmo_.get(), ZX_RIGHT_SAME_RIGHTS, &xfer));
386     ASSERT_OK(ToStatus(ioctl_block_attach_vmo(zxcrypt_.get(), &xfer, &req_.vmoid)));
387 
388     END_HELPER;
389 }
390 
Disconnect()391 void TestDevice::Disconnect() {
392     if (client_) {
393         memset(&req_, 0, sizeof(req_));
394         block_fifo_release_client(client_);
395         client_ = nullptr;
396     }
397     zxcrypt_.reset();
398     volume_.reset();
399     block_size_ = 0;
400     block_count_ = 0;
401     vmo_.reset();
402 }
403 
404 } // namespace testing
405 } // namespace zxcrypt
406