fuzz-test.c++ (9270B)
1 // Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors 2 // Licensed under the MIT License: 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a copy 5 // of this software and associated documentation files (the "Software"), to deal 6 // in the Software without restriction, including without limitation the rights 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 // copies of the Software, and to permit persons to whom the Software is 9 // furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 // THE SOFTWARE. 21 22 #ifndef _GNU_SOURCE 23 #define _GNU_SOURCE 24 #endif 25 26 #include <capnp/test-import.capnp.h> 27 #include <capnp/test-import2.capnp.h> 28 #include "message.h" 29 #include "serialize.h" 30 #include <kj/test.h> 31 #include <stdlib.h> 32 #include <kj/miniposix.h> 33 #include "test-util.h" 34 35 namespace capnp { 36 namespace _ { // private 37 namespace { 38 39 bool skipFuzzTest() { 40 if (getenv("CAPNP_SKIP_FUZZ_TEST") != nullptr) { 41 KJ_LOG(WARNING, "Skipping test because CAPNP_SKIP_FUZZ_TEST is set in environment."); 42 return true; 43 } else { 44 return false; 45 } 46 } 47 48 class DisableStackTraces: public kj::ExceptionCallback { 49 // This test generates a lot of exceptions. Performing a backtrace on each one can be slow, 50 // especially on Windows (where it is very, very slow). So, disable them. 51 52 public: 53 StackTraceMode stackTraceMode() override { 54 return StackTraceMode::NONE; 55 } 56 }; 57 58 uint64_t traverse(AnyPointer::Reader reader); 59 uint64_t traverse(AnyStruct::Reader reader); 60 uint64_t traverse(AnyList::Reader reader); 61 62 template <typename ListType> 63 uint64_t traverseList(ListType list) { 64 // Traverse in reverse order in order to trigger segfaults before exceptions if we go 65 // out-of-bounds. 66 67 uint64_t result = 0; 68 for (size_t i = list.size(); i != 0; i--) { 69 result += traverse(list[i-1]); 70 } 71 return result; 72 } 73 74 uint64_t traverse(AnyStruct::Reader reader) { 75 uint64_t result = 0; 76 for (byte b: reader.getDataSection()) { 77 result += b; 78 } 79 result += traverseList(reader.getPointerSection()); 80 return result; 81 } 82 83 uint64_t traverse(AnyList::Reader reader) { 84 uint64_t result = 0; 85 switch (reader.getElementSize()) { 86 case ElementSize::VOID: break; 87 case ElementSize::BIT: for (auto e: reader.as<List<bool >>()) result += e; break; 88 case ElementSize::BYTE: for (auto e: reader.as<List<uint8_t >>()) result += e; break; 89 case ElementSize::TWO_BYTES: for (auto e: reader.as<List<uint16_t>>()) result += e; break; 90 case ElementSize::FOUR_BYTES: for (auto e: reader.as<List<uint32_t>>()) result += e; break; 91 case ElementSize::EIGHT_BYTES: for (auto e: reader.as<List<uint64_t>>()) result += e; break; 92 case ElementSize::POINTER: 93 traverseList(reader.as<List<AnyPointer>>()); 94 break; 95 case ElementSize::INLINE_COMPOSITE: 96 traverseList(reader.as<List<AnyStruct>>()); 97 break; 98 } 99 return result; 100 } 101 102 uint64_t traverse(AnyPointer::Reader reader) { 103 if (reader.isStruct()) { 104 return traverse(reader.getAs<AnyStruct>()); 105 } else if (reader.isList()) { 106 return traverse(reader.getAs<AnyList>()); 107 } else { 108 return 0; 109 } 110 } 111 112 template <typename Checker> 113 void traverseCatchingExceptions(kj::ArrayPtr<const word> data) { 114 // Try traversing through Checker. 115 kj::runCatchingExceptions([&]() { 116 FlatArrayMessageReader reader(data); 117 KJ_ASSERT(Checker::check(reader) != 0) { break; } 118 }); 119 120 // Try traversing through AnyPointer. 121 kj::runCatchingExceptions([&]() { 122 FlatArrayMessageReader reader(data); 123 KJ_ASSERT(traverse(reader.getRoot<AnyPointer>()) != 0) { break; } 124 }); 125 126 // Try counting the size.. 127 kj::runCatchingExceptions([&]() { 128 FlatArrayMessageReader reader(data); 129 KJ_ASSERT(reader.getRoot<AnyPointer>().targetSize().wordCount != 0) { break; } 130 }); 131 132 // Try copying into a builder, and if that works, traversing it with Checker. 133 static word buffer[8192]; 134 kj::runCatchingExceptions([&]() { 135 FlatArrayMessageReader reader(data); 136 MallocMessageBuilder copyBuilder(buffer); 137 copyBuilder.setRoot(reader.getRoot<AnyPointer>()); 138 KJ_ASSERT(Checker::check(copyBuilder) != 0) { break; } 139 }); 140 } 141 142 template <typename Checker> 143 void fuzz(kj::ArrayPtr<word> data, uint flipCount, uint startAt, uint endAt) { 144 if (flipCount == 0) { 145 traverseCatchingExceptions<Checker>(data); 146 } else { 147 for (uint i = startAt; i < endAt; i++) { 148 byte bit = 1u << (i % 8); 149 byte old = data.asBytes()[i / 8]; 150 data.asBytes()[i / 8] |= bit; 151 fuzz<Checker>(data, flipCount - 1, i + 1, endAt); 152 data.asBytes()[i / 8] &= ~bit; 153 fuzz<Checker>(data, flipCount - 1, i + 1, endAt); 154 data.asBytes()[i / 8] = bit; 155 fuzz<Checker>(data, flipCount - 1, i + 1, endAt); 156 data.asBytes()[i / 8] = old; 157 } 158 } 159 } 160 161 struct StructChecker { 162 template <typename ReaderOrBuilder> 163 static uint check(ReaderOrBuilder& message) { 164 uint result = 0; 165 for (auto c: message.template getRoot<TestAllTypes>().getTextField()) { 166 result += c; 167 } 168 return result; 169 } 170 }; 171 172 KJ_TEST("fuzz-test struct pointer") { 173 if (skipFuzzTest()) return; 174 DisableStackTraces disableStackTraces; 175 176 MallocMessageBuilder builder; 177 builder.getRoot<TestAllTypes>().setTextField("foo"); 178 KJ_ASSERT(builder.getSegmentsForOutput().size() == 1); 179 fuzz<StructChecker>(messageToFlatArray(builder), 2, 64, 192); 180 } 181 182 struct ListChecker { 183 template <typename ReaderOrBuilder> 184 static uint check(ReaderOrBuilder& message) { 185 uint result = 0; 186 for (auto e: message.template getRoot<List<uint32_t>>()) { 187 result += e; 188 } 189 return result; 190 } 191 }; 192 193 KJ_TEST("fuzz-test list pointer") { 194 if (skipFuzzTest()) return; 195 DisableStackTraces disableStackTraces; 196 197 MallocMessageBuilder builder; 198 auto list = builder.getRoot<AnyPointer>().initAs<List<uint32_t>>(2); 199 list.set(0, 12345); 200 list.set(1, 67890); 201 fuzz<ListChecker>(messageToFlatArray(builder), 2, 64, 192); 202 } 203 204 struct StructListChecker { 205 template <typename ReaderOrBuilder> 206 static uint check(ReaderOrBuilder& message) { 207 uint result = 0; 208 auto l = message.template getRoot<List<TestAllTypes>>(); 209 for (size_t i = l.size(); i > 0; i--) { 210 for (auto c: l[i-1].getTextField()) { 211 result += c; 212 } 213 } 214 return result; 215 } 216 }; 217 218 KJ_TEST("fuzz-test struct list pointer") { 219 if (skipFuzzTest()) return; 220 DisableStackTraces disableStackTraces; 221 222 MallocMessageBuilder builder; 223 auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2); 224 list[0].setTextField("foo"); 225 list[1].setTextField("bar"); 226 KJ_ASSERT(builder.getSegmentsForOutput().size() == 1); 227 228 fuzz<StructListChecker>(messageToFlatArray(builder), 2, 64, 192); 229 } 230 231 struct TextChecker { 232 template <typename ReaderOrBuilder> 233 static uint check(ReaderOrBuilder& message) { 234 uint result = 0; 235 for (auto c: message.template getRoot<Text>()) { 236 result += c; 237 } 238 return result; 239 } 240 }; 241 242 KJ_TEST("fuzz-test text pointer") { 243 if (skipFuzzTest()) return; 244 DisableStackTraces disableStackTraces; 245 246 MallocMessageBuilder builder; 247 builder.template getRoot<AnyPointer>().setAs<Text>("foo"); 248 fuzz<TextChecker>(messageToFlatArray(builder), 2, 64, 192); 249 } 250 251 KJ_TEST("fuzz-test far pointer") { 252 if (skipFuzzTest()) return; 253 DisableStackTraces disableStackTraces; 254 255 MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE); 256 initTestMessage(builder.getRoot<TestAllTypes>()); 257 258 uint segmentCount = builder.getSegmentsForOutput().size(); 259 uint tableSize = segmentCount / 2 + 1; 260 261 // Fuzz the root far pointer plus its landing pad, which should be in the next word. 262 fuzz<StructChecker>(messageToFlatArray(builder), 2, tableSize * 64, tableSize * 64 + 128); 263 } 264 265 KJ_TEST("fuzz-test double-far pointer") { 266 if (skipFuzzTest()) return; 267 DisableStackTraces disableStackTraces; 268 269 MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE); 270 271 // Carefully arrange for a double-far pointer to be created. 272 auto root = builder.getRoot<AnyPointer>(); 273 root.adopt(builder.getOrphanage().newOrphanCopy(Text::Reader("foo"))); 274 275 // Verify that did what we expected. 276 KJ_ASSERT(builder.getSegmentsForOutput().size() == 3); 277 KJ_ASSERT(builder.getSegmentsForOutput()[0].size() == 1); // root pointer 278 KJ_ASSERT(builder.getSegmentsForOutput()[1].size() == 1); // "foo" 279 KJ_ASSERT(builder.getSegmentsForOutput()[2].size() == 2); // double-far landing pad 280 281 // Fuzz the root far pointer. 282 fuzz<TextChecker>(messageToFlatArray(builder), 2, 64, 128); 283 284 // Fuzz the landing pad. 285 fuzz<TextChecker>(messageToFlatArray(builder), 2, 192, 320); 286 } 287 288 } // namespace 289 } // namespace _ (private) 290 } // namespace capnp