Commit 3a717467 authored by Kenton Varda's avatar Kenton Varda

Add a simple fuzz test for pointer validation.

parent b2496f3a
......@@ -413,6 +413,7 @@ capnp_test_SOURCES = \
src/capnp/orphan-test.c++ \
src/capnp/serialize-test.c++ \
src/capnp/serialize-packed-test.c++ \
src/capnp/fuzz-test.c++ \
src/capnp/test-util.c++ \
src/capnp/test-util.h \
$(heavy_tests)
......
......@@ -195,6 +195,7 @@ if(BUILD_TESTING)
orphan-test.c++
serialize-test.c++
serialize-packed-test.c++
fuzz-test.c++
test-util.c++
${test_capnp_cpp_files}
${test_capnp_h_files}
......
// Copyright (c) 2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <capnp/test-import.capnp.h>
#include <capnp/test-import2.capnp.h>
#include "message.h"
#include "serialize.h"
#include <kj/test.h>
#include "test-util.h"
namespace capnp {
namespace _ { // private
namespace {
uint64_t traverse(AnyPointer::Reader reader);
uint64_t traverse(AnyStruct::Reader reader);
uint64_t traverse(AnyList::Reader reader);
template <typename ListType>
uint64_t traverseList(ListType list) {
// Traverse in reverse order in order to trigger segfaults before exceptions if we go
// out-of-bounds.
uint64_t result = 0;
for (size_t i = list.size(); i != 0; i--) {
result += traverse(list[i-1]);
}
return result;
}
uint64_t traverse(AnyStruct::Reader reader) {
uint64_t result = 0;
for (byte b: reader.getDataSection()) {
result += b;
}
result += traverseList(reader.getPointerSection());
return result;
}
uint64_t traverse(AnyList::Reader reader) {
uint64_t result = 0;
switch (reader.getElementSize()) {
case ElementSize::VOID: break;
case ElementSize::BIT: for (auto e: reader.as<List<bool >>()) result += e; break;
case ElementSize::BYTE: for (auto e: reader.as<List<uint8_t >>()) result += e; break;
case ElementSize::TWO_BYTES: for (auto e: reader.as<List<uint16_t>>()) result += e; break;
case ElementSize::FOUR_BYTES: for (auto e: reader.as<List<uint32_t>>()) result += e; break;
case ElementSize::EIGHT_BYTES: for (auto e: reader.as<List<uint64_t>>()) result += e; break;
case ElementSize::POINTER:
traverseList(reader.as<List<AnyPointer>>());
break;
case ElementSize::INLINE_COMPOSITE:
traverseList(reader.as<List<AnyStruct>>());
break;
}
return result;
}
uint64_t traverse(AnyPointer::Reader reader) {
if (reader.isStruct()) {
return traverse(reader.getAs<AnyStruct>());
} else if (reader.isList()) {
return traverse(reader.getAs<AnyList>());
} else {
return 0;
}
}
void traverseCatchingExceptions(kj::ArrayPtr<const word> data) {
kj::runCatchingExceptions([&]() {
FlatArrayMessageReader reader(data);
KJ_ASSERT(traverse(reader.getRoot<AnyPointer>()) != 0);
});
static word buffer[8192];
memset(buffer, 0, sizeof(buffer));
kj::runCatchingExceptions([&]() {
FlatArrayMessageReader reader(data);
MallocMessageBuilder copyBuilder(buffer);
copyBuilder.setRoot(reader.getRoot<AnyPointer>());
});
}
void fuzz(kj::ArrayPtr<word> data, uint flipCount, uint startAt, uint endAt) {
if (flipCount == 0) {
traverseCatchingExceptions(data);
} else {
for (uint i = startAt; i < endAt; i++) {
byte bit = 1u << (i % 8);
byte old = data.asBytes()[i / 8];
data.asBytes()[i / 8] |= bit;
fuzz(data, flipCount - 1, i + 1, endAt);
data.asBytes()[i / 8] &= ~bit;
fuzz(data, flipCount - 1, i + 1, endAt);
data.asBytes()[i / 8] = bit;
fuzz(data, flipCount - 1, i + 1, endAt);
data.asBytes()[i / 8] = old;
}
}
}
KJ_TEST("fuzz-test struct pointer") {
MallocMessageBuilder builder;
initTestMessage(builder.getRoot<TestAllTypes>());
KJ_ASSERT(builder.getSegmentsForOutput().size() == 1);
fuzz(messageToFlatArray(builder), 2, 64, 192);
}
KJ_TEST("fuzz-test struct list pointer") {
MallocMessageBuilder builder;
auto list = builder.getRoot<AnyPointer>().initAs<List<test::TestAllTypes>>(2);
initTestMessage(list[0]);
initTestMessage(list[1]);
KJ_ASSERT(builder.getSegmentsForOutput().size() == 1);
fuzz(messageToFlatArray(builder), 2, 64, 192);
}
KJ_TEST("fuzz-test far pointer") {
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
initTestMessage(builder.getRoot<TestAllTypes>());
uint segmentCount = builder.getSegmentsForOutput().size();
uint tableSize = segmentCount / 2 + 1;
// Fuzz the root far pointer plus its landing pad, which should be in the next word.
fuzz(messageToFlatArray(builder), 2, tableSize * 64, tableSize * 64 + 128);
}
KJ_TEST("fuzz-test double-far pointer") {
MallocMessageBuilder builder(1, AllocationStrategy::FIXED_SIZE);
// Carefully arrange for a double-far pointer to be created.
auto root = builder.getRoot<AnyPointer>();
root.adopt(builder.getOrphanage().newOrphanCopy(Text::Reader("foo")));
// Verify that did what we expected.
KJ_ASSERT(builder.getSegmentsForOutput().size() == 3);
KJ_ASSERT(builder.getSegmentsForOutput()[0].size() == 1); // root pointer
KJ_ASSERT(builder.getSegmentsForOutput()[1].size() == 1); // "foo"
KJ_ASSERT(builder.getSegmentsForOutput()[2].size() == 2); // double-far landing pad
// Fuzz the root far pointer.
fuzz(messageToFlatArray(builder), 2, 64, 128);
// Fuzz the landing pad.
fuzz(messageToFlatArray(builder), 2, 192, 320);
}
} // namespace
} // namespace _ (private)
} // namespace capnp
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment