/* * (C) 2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include "fuzzers.h" #include #include #include #include #include #include namespace { size_t compute_expected_alignment(size_t plen) { if(Botan::is_power_of_2(plen)) { return plen; } else { return 8; } } struct RawPage { public: RawPage(void* p) : m_p(p) {} ~RawPage() { std::free(m_p); } RawPage(const RawPage& other) = default; RawPage& operator=(const RawPage& other) = default; RawPage(RawPage&& other) : m_p(nullptr) { std::swap(m_p, other.m_p); } RawPage& operator=(RawPage&& other) { if(this != &other) { std::swap(m_p, other.m_p); } return (*this); } void* ptr() const { return m_p; } private: void* m_p; }; std::vector allocate_raw_pages(size_t count, size_t page_size) { std::vector pages; pages.reserve(count); for(size_t i = 0; i != count; ++i) { void* ptr = nullptr; int rc = ::posix_memalign(&ptr, page_size, page_size); FUZZER_ASSERT_EQUAL(rc, 0); if(ptr) { pages.push_back(RawPage(ptr)); } } return pages; } } void fuzz(const uint8_t in[], size_t in_len) { const size_t page_count = 4; const size_t page_size = 4096; // static to avoid repeated allocations static std::vector raw_mem = allocate_raw_pages(page_count, page_size); std::vector mem_pages; mem_pages.reserve(raw_mem.size()); for(size_t i = 0; i != raw_mem.size(); ++i) mem_pages.push_back(raw_mem[i].ptr()); Botan::Memory_Pool pool(mem_pages, page_size); std::map ptrs; while(in_len > 0) { const uint8_t op = in[0] % 2; size_t idx = (in[0] >> 1); in += 1; in_len -= 1; if(in_len > 0 && idx < 4) { idx = idx * 256 + in[0]; in += 1; in_len -= 1; } //printf("%d %d\n", op, idx); if(op == 0) { const size_t plen = idx + 1; // ensure non-zero uint8_t* p = static_cast(pool.allocate(plen)); if(p) { const size_t expected_alignment = compute_expected_alignment(plen); const size_t alignment = reinterpret_cast(p) % expected_alignment; if(alignment != 0) { FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer " << static_cast(p) << " for len " << plen << " expected " << expected_alignment << " got " << alignment); } //printf("alloc %d -> %p\n", plen, p); for(size_t i = 0; i != plen; ++i) { if(p[i] != 0) { FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory"); } } // verify it becomes zeroed later std::memset(p, idx, plen); auto insert = ptrs.insert(std::make_pair(p, plen)); if(insert.second == false) { FUZZER_WRITE_AND_CRASH("Pointer " << static_cast(p) << " already existed\n"); } auto itr = insert.first; // Verify this pointer doesn't overlap with the one before it if(itr != ptrs.begin()) { auto before = std::prev(itr); auto ptr_before = *before; if(ptr_before.first + ptr_before.second > p) { FUZZER_WRITE_AND_CRASH("Previous " << static_cast(ptr_before.first) << "/" << ptr_before.second << " overlaps with new " << static_cast(p)); } } auto after = std::next(itr); if(after != ptrs.end()) { if(p + plen > after->first) { FUZZER_WRITE_AND_CRASH("New " << static_cast(p) << "/" << plen << " overlaps following " << static_cast(after->first)); } } } } else if(op == 1) { if(ptrs.empty()) return; size_t which_ptr = idx % ptrs.size(); auto itr = ptrs.begin(); while(which_ptr-- > 0) { ++itr; } //printf("free %p %d\n", itr->first, itr->second); FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second)); ptrs.erase(itr); } } }