fuzzer.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #include "command_file.hpp"
  19. #include "list.hpp"
  20. #include "object_schema.hpp"
  21. #include "property.hpp"
  22. #include "results.hpp"
  23. #include "schema.hpp"
  24. #include "impl/realm_coordinator.hpp"
  25. #include <realm/commit_log.hpp>
  26. #include <realm/disable_sync_to_disk.hpp>
  27. #include <realm/group_shared.hpp>
  28. #include <realm/link_view.hpp>
  29. #include <iostream>
  30. #include <sstream>
  31. #include <sys/types.h>
  32. #include <sys/uio.h>
  33. #include <unistd.h>
  34. using namespace realm;
  35. #ifndef FUZZ_SORTED
  36. #define FUZZ_SORTED 0
  37. #endif
  38. #ifndef FUZZ_LINKVIEW
  39. #define FUZZ_LINKVIEW 0
  40. #endif
  41. #define FUZZ_LOG 0
  42. // Read from a fd until eof into a string
  43. // Needs to use unbuffered i/o to work properly with afl
  44. static void read_all(std::string& buffer, int fd)
  45. {
  46. buffer.clear();
  47. size_t offset = 0;
  48. while (true) {
  49. buffer.resize(offset + 4096);
  50. ssize_t bytes_read = read(fd, &buffer[offset], 4096);
  51. if (bytes_read < 4096) {
  52. buffer.resize(offset + bytes_read);
  53. break;
  54. }
  55. offset += 4096;
  56. }
  57. }
  58. static Query query(fuzzer::RealmState& state)
  59. {
  60. #if FUZZ_LINKVIEW
  61. return state.table.where(state.lv);
  62. #else
  63. return state.table.where().greater(1, 100).less(1, 50000);
  64. #endif
  65. }
  66. static TableView tableview(fuzzer::RealmState& state)
  67. {
  68. auto tv = query(state).find_all();
  69. #if FUZZ_SORTED
  70. tv.sort({1, 0}, {true, true});
  71. #endif
  72. return tv;
  73. }
  74. // Apply the changes from the command file and then return whether a change
  75. // notification should occur
  76. static bool apply_changes(fuzzer::CommandFile& commands, fuzzer::RealmState& state)
  77. {
  78. auto tv = tableview(state);
  79. #if FUZZ_LOG
  80. for (size_t i = 0; i < tv.size(); ++i)
  81. fprintf(stderr, "pre: %lld\n", tv.get_int(0, i));
  82. #endif
  83. commands.run(state);
  84. auto tv2 = tableview(state);
  85. if (tv.size() != tv2.size())
  86. return true;
  87. for (size_t i = 0; i < tv.size(); ++i) {
  88. #if FUZZ_LOG
  89. fprintf(stderr, "%lld %lld\n", tv.get_int(0, i), tv2.get_int(0, i));
  90. #endif
  91. if (!tv.is_row_attached(i))
  92. return true;
  93. if (tv.get_int(0, i) != tv2.get_int(0, i))
  94. return true;
  95. if (find(begin(state.modified), end(state.modified), tv.get_int(0, i)) != end(state.modified))
  96. return true;
  97. }
  98. return false;
  99. }
  100. static auto verify(CollectionChangeIndices const& changes, std::vector<int64_t> values, fuzzer::RealmState& state)
  101. {
  102. auto tv = tableview(state);
  103. // Apply the changes from the transaction log to our copy of the
  104. // initial, using UITableView's batching rules (i.e. delete, then
  105. // insert, then update)
  106. auto it = util::make_reverse_iterator(changes.deletions.end());
  107. auto end = util::make_reverse_iterator(changes.deletions.begin());
  108. for (; it != end; ++it) {
  109. values.erase(values.begin() + it->first, values.begin() + it->second);
  110. }
  111. for (auto i : changes.insertions.as_indexes()) {
  112. values.insert(values.begin() + i, tv.get_int(1, i));
  113. }
  114. if (values.size() != tv.size()) {
  115. abort();
  116. }
  117. for (auto i : changes.modifications.as_indexes()) {
  118. if (changes.insertions.contains(i))
  119. abort();
  120. values[i] = tv.get_int(1, i);
  121. }
  122. #if FUZZ_SORTED
  123. if (!std::is_sorted(values.begin(), values.end()))
  124. abort();
  125. #endif
  126. for (size_t i = 0; i < values.size(); ++i) {
  127. if (values[i] != tv.get_int(1, i)) {
  128. #if FUZZ_LOG
  129. fprintf(stderr, "%lld %lld\n", values[i], tv.get_int(1, i));
  130. #endif
  131. abort();
  132. }
  133. }
  134. return values;
  135. }
  136. static void verify_no_op(CollectionChangeIndices const& changes, std::vector<int64_t> values, fuzzer::RealmState& state)
  137. {
  138. auto new_values = verify(changes, values, state);
  139. if (!std::equal(begin(values), end(values), begin(new_values), end(new_values)))
  140. abort();
  141. }
  142. static void test(Realm::Config const& config, SharedRealm& r, SharedRealm& r2, std::istream& input_stream)
  143. {
  144. fuzzer::RealmState state = {
  145. *r,
  146. *_impl::RealmCoordinator::get_existing_coordinator(r->config().path),
  147. *r->read_group()->get_table("class_object"),
  148. r->read_group()->get_table("class_linklist")->get_linklist(0, 0),
  149. 0,
  150. {}
  151. };
  152. fuzzer::CommandFile command(input_stream);
  153. if (command.initial_values.empty()) {
  154. return;
  155. }
  156. command.import(state);
  157. fuzzer::RealmState state2 = {
  158. *r2,
  159. state.coordinator,
  160. *r2->read_group()->get_table("class_object"),
  161. #if FUZZ_LINKVIEW
  162. r2->read_group()->get_table("class_linklist")->get_linklist(0, 0),
  163. #else
  164. {},
  165. #endif
  166. state.uid,
  167. {}
  168. };
  169. #if FUZZ_LINKVIEW && !FUZZ_SORTED
  170. auto results = List(r, ObjectSchema(), state.lv);
  171. #else
  172. auto results = Results(r, ObjectSchema(), query(state))
  173. #if FUZZ_SORTED
  174. .sort({{1, 0}, {true, true}})
  175. #endif
  176. ;
  177. #endif // FUZZ_LINKVIEW
  178. std::vector<int64_t> initial_values;
  179. for (size_t i = 0; i < results.size(); ++i)
  180. initial_values.push_back(results.get(i).get_int(1));
  181. CollectionChangeIndices changes;
  182. int notification_calls = 0;
  183. auto token = results.add_notification_callback([&](CollectionChangeIndices c, std::exception_ptr err) {
  184. if (notification_calls > 0 && c.empty())
  185. abort();
  186. changes = c;
  187. ++notification_calls;
  188. });
  189. state.coordinator.on_change(); r->notify();
  190. if (notification_calls != 1) {
  191. abort();
  192. }
  193. bool expect_notification = apply_changes(command, state2);
  194. state.coordinator.on_change(); r->notify();
  195. if (expect_notification) {
  196. if (notification_calls != 2)
  197. abort();
  198. verify(changes, initial_values, state);
  199. }
  200. else {
  201. if (notification_calls == 2)
  202. verify_no_op(changes, initial_values, state);
  203. }
  204. }
  205. int main(int argc, char** argv) {
  206. std::ios_base::sync_with_stdio(false);
  207. realm::disable_sync_to_disk();
  208. Realm::Config config;
  209. config.path = "fuzzer.realm";
  210. config.in_memory = true;
  211. config.automatic_change_notifications = false;
  212. Schema schema{
  213. {"object", "", {
  214. {"id", PropertyTypeInt},
  215. {"value", PropertyTypeInt}
  216. }},
  217. {"linklist", "", {
  218. {"list", PropertyTypeArray, "object"}
  219. }}
  220. };
  221. config.schema = std::make_unique<Schema>(schema);
  222. unlink(config.path.c_str());
  223. auto r = Realm::get_shared_realm(config);
  224. auto r2 = Realm::get_shared_realm(config);
  225. auto& coordinator = *_impl::RealmCoordinator::get_existing_coordinator(config.path);
  226. r->begin_transaction();
  227. r->read_group()->get_table("class_linklist")->add_empty_row();
  228. r->commit_transaction();
  229. auto test_on = [&](auto& buffer) {
  230. std::istringstream ss(buffer);
  231. test(config, r, r2, ss);
  232. if (r->is_in_transaction())
  233. r->cancel_transaction();
  234. r2->invalidate();
  235. coordinator.on_change();
  236. };
  237. if (argc > 1) {
  238. std::string buffer;
  239. for (int i = 1; i < argc; ++i) {
  240. int fd = open(argv[i], O_RDONLY);
  241. if (fd < 0)
  242. abort();
  243. read_all(buffer, fd);
  244. close(fd);
  245. test_on(buffer);
  246. }
  247. unlink(config.path.c_str());
  248. return 0;
  249. }
  250. #ifdef __AFL_HAVE_MANUAL_CONTROL
  251. std::string buffer;
  252. while (__AFL_LOOP(1000)) {
  253. read_all(buffer, 0);
  254. test_on(buffer);
  255. }
  256. #else
  257. std::string buffer;
  258. read_all(buffer, 0);
  259. test_on(buffer);
  260. #endif
  261. unlink(config.path.c_str());
  262. return 0;
  263. }