virtio_blk_fuzz.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * virtio-blk Fuzzing Target
  3. *
  4. * Copyright Red Hat Inc., 2020
  5. *
  6. * Based on virtio-scsi-fuzz target.
  7. *
  8. * This work is licensed under the terms of the GNU GPL, version 2 or later.
  9. * See the COPYING file in the top-level directory.
  10. */
  11. #include "qemu/osdep.h"
  12. #include "tests/qtest/libqtest.h"
  13. #include "tests/qtest/libqos/virtio-blk.h"
  14. #include "tests/qtest/libqos/virtio.h"
  15. #include "tests/qtest/libqos/virtio-pci.h"
  16. #include "standard-headers/linux/virtio_ids.h"
  17. #include "standard-headers/linux/virtio_pci.h"
  18. #include "standard-headers/linux/virtio_blk.h"
  19. #include "fuzz.h"
  20. #include "fork_fuzz.h"
  21. #include "qos_fuzz.h"
  22. #define TEST_IMAGE_SIZE (64 * 1024 * 1024)
  23. #define PCI_SLOT 0x02
  24. #define PCI_FN 0x00
  25. #define MAX_NUM_QUEUES 64
  26. /* Based on tests/qtest/virtio-blk-test.c. */
  27. typedef struct {
  28. int num_queues;
  29. QVirtQueue *vq[MAX_NUM_QUEUES + 2];
  30. } QVirtioBlkQueues;
  31. static QVirtioBlkQueues *qvirtio_blk_init(QVirtioDevice *dev, uint64_t mask)
  32. {
  33. QVirtioBlkQueues *vs;
  34. uint64_t features;
  35. vs = g_new0(QVirtioBlkQueues, 1);
  36. features = qvirtio_get_features(dev);
  37. if (!mask) {
  38. mask = ~((1u << VIRTIO_RING_F_INDIRECT_DESC) |
  39. (1u << VIRTIO_RING_F_EVENT_IDX) |
  40. (1u << VIRTIO_BLK_F_SCSI));
  41. }
  42. mask |= ~QVIRTIO_F_BAD_FEATURE;
  43. features &= mask;
  44. qvirtio_set_features(dev, features);
  45. vs->num_queues = 1;
  46. vs->vq[0] = qvirtqueue_setup(dev, fuzz_qos_alloc, 0);
  47. qvirtio_set_driver_ok(dev);
  48. return vs;
  49. }
  50. static void virtio_blk_fuzz(QTestState *s, QVirtioBlkQueues* queues,
  51. const unsigned char *Data, size_t Size)
  52. {
  53. /*
  54. * Data is a sequence of random bytes. We split them up into "actions",
  55. * followed by data:
  56. * [vqa][dddddddd][vqa][dddd][vqa][dddddddddddd] ...
  57. * The length of the data is specified by the preceding vqa.length
  58. */
  59. typedef struct vq_action {
  60. uint8_t queue;
  61. uint8_t length;
  62. uint8_t write;
  63. uint8_t next;
  64. uint8_t kick;
  65. } vq_action;
  66. /* Keep track of the free head for each queue we interact with */
  67. bool vq_touched[MAX_NUM_QUEUES + 2] = {0};
  68. uint32_t free_head[MAX_NUM_QUEUES + 2];
  69. QGuestAllocator *t_alloc = fuzz_qos_alloc;
  70. QVirtioBlk *blk = fuzz_qos_obj;
  71. QVirtioDevice *dev = blk->vdev;
  72. QVirtQueue *q;
  73. vq_action vqa;
  74. while (Size >= sizeof(vqa)) {
  75. /* Copy the action, so we can normalize length, queue and flags */
  76. memcpy(&vqa, Data, sizeof(vqa));
  77. Data += sizeof(vqa);
  78. Size -= sizeof(vqa);
  79. vqa.queue = vqa.queue % queues->num_queues;
  80. /* Cap length at the number of remaining bytes in data */
  81. vqa.length = vqa.length >= Size ? Size : vqa.length;
  82. vqa.write = vqa.write & 1;
  83. vqa.next = vqa.next & 1;
  84. vqa.kick = vqa.kick & 1;
  85. q = queues->vq[vqa.queue];
  86. /* Copy the data into ram, and place it on the virtqueue */
  87. uint64_t req_addr = guest_alloc(t_alloc, vqa.length);
  88. qtest_memwrite(s, req_addr, Data, vqa.length);
  89. if (vq_touched[vqa.queue] == 0) {
  90. vq_touched[vqa.queue] = 1;
  91. free_head[vqa.queue] = qvirtqueue_add(s, q, req_addr, vqa.length,
  92. vqa.write, vqa.next);
  93. } else {
  94. qvirtqueue_add(s, q, req_addr, vqa.length, vqa.write , vqa.next);
  95. }
  96. if (vqa.kick) {
  97. qvirtqueue_kick(s, dev, q, free_head[vqa.queue]);
  98. free_head[vqa.queue] = 0;
  99. }
  100. Data += vqa.length;
  101. Size -= vqa.length;
  102. }
  103. /* In the end, kick each queue we interacted with */
  104. for (int i = 0; i < MAX_NUM_QUEUES + 2; i++) {
  105. if (vq_touched[i]) {
  106. qvirtqueue_kick(s, dev, queues->vq[i], free_head[i]);
  107. }
  108. }
  109. }
  110. static void virtio_blk_fork_fuzz(QTestState *s,
  111. const unsigned char *Data, size_t Size)
  112. {
  113. QVirtioBlk *blk = fuzz_qos_obj;
  114. static QVirtioBlkQueues *queues;
  115. if (!queues) {
  116. queues = qvirtio_blk_init(blk->vdev, 0);
  117. }
  118. if (fork() == 0) {
  119. virtio_blk_fuzz(s, queues, Data, Size);
  120. flush_events(s);
  121. _Exit(0);
  122. } else {
  123. flush_events(s);
  124. wait(NULL);
  125. }
  126. }
  127. static void virtio_blk_with_flag_fuzz(QTestState *s,
  128. const unsigned char *Data, size_t Size)
  129. {
  130. QVirtioBlk *blk = fuzz_qos_obj;
  131. static QVirtioBlkQueues *queues;
  132. if (fork() == 0) {
  133. if (Size >= sizeof(uint64_t)) {
  134. queues = qvirtio_blk_init(blk->vdev, *(uint64_t *)Data);
  135. virtio_blk_fuzz(s, queues,
  136. Data + sizeof(uint64_t), Size - sizeof(uint64_t));
  137. flush_events(s);
  138. }
  139. _Exit(0);
  140. } else {
  141. flush_events(s);
  142. wait(NULL);
  143. }
  144. }
  145. static void virtio_blk_pre_fuzz(QTestState *s)
  146. {
  147. qos_init_path(s);
  148. counter_shm_init();
  149. }
  150. static void drive_destroy(void *path)
  151. {
  152. unlink(path);
  153. g_free(path);
  154. }
  155. static char *drive_create(void)
  156. {
  157. int fd, ret;
  158. char *t_path = g_strdup("/tmp/qtest.XXXXXX");
  159. /* Create a temporary raw image */
  160. fd = mkstemp(t_path);
  161. g_assert_cmpint(fd, >=, 0);
  162. ret = ftruncate(fd, TEST_IMAGE_SIZE);
  163. g_assert_cmpint(ret, ==, 0);
  164. close(fd);
  165. g_test_queue_destroy(drive_destroy, t_path);
  166. return t_path;
  167. }
  168. static void *virtio_blk_test_setup(GString *cmd_line, void *arg)
  169. {
  170. char *tmp_path = drive_create();
  171. g_string_append_printf(cmd_line,
  172. " -drive if=none,id=drive0,file=%s,"
  173. "format=raw,auto-read-only=off ",
  174. tmp_path);
  175. return arg;
  176. }
  177. static void register_virtio_blk_fuzz_targets(void)
  178. {
  179. fuzz_add_qos_target(&(FuzzTarget){
  180. .name = "virtio-blk-fuzz",
  181. .description = "Fuzz the virtio-blk virtual queues, forking "
  182. "for each fuzz run",
  183. .pre_vm_init = &counter_shm_init,
  184. .pre_fuzz = &virtio_blk_pre_fuzz,
  185. .fuzz = virtio_blk_fork_fuzz,},
  186. "virtio-blk",
  187. &(QOSGraphTestOptions){.before = virtio_blk_test_setup}
  188. );
  189. fuzz_add_qos_target(&(FuzzTarget){
  190. .name = "virtio-blk-flags-fuzz",
  191. .description = "Fuzz the virtio-blk virtual queues, forking "
  192. "for each fuzz run (also fuzzes the virtio flags)",
  193. .pre_vm_init = &counter_shm_init,
  194. .pre_fuzz = &virtio_blk_pre_fuzz,
  195. .fuzz = virtio_blk_with_flag_fuzz,},
  196. "virtio-blk",
  197. &(QOSGraphTestOptions){.before = virtio_blk_test_setup}
  198. );
  199. }
  200. fuzz_target_init(register_virtio_blk_fuzz_targets);