capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

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