Coverage Report

Created: 2025-01-10 06:32

/src/botan/src/fuzzer/mem_pool.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* (C) 2018 Jack Lloyd
3
*
4
* Botan is released under the Simplified BSD License (see license.txt)
5
*/
6
7
#include "fuzzers.h"
8
9
#include <botan/internal/bit_ops.h>
10
#include <botan/internal/mem_pool.h>
11
#include <map>
12
#include <utility>
13
#include <vector>
14
15
#include <cstdlib>
16
17
namespace {
18
19
18.5k
size_t compute_expected_alignment(size_t plen) {
20
18.5k
   if(Botan::is_power_of_2(plen)) {
21
6.47k
      return plen;
22
12.1k
   } else {
23
12.1k
      return 8;
24
12.1k
   }
25
18.5k
}
26
27
struct RawPage {
28
   public:
29
4
      RawPage(void* p) : m_p(p) {}
30
31
8
      ~RawPage() {
32
         // NOLINTNEXTLINE(*-no-malloc)
33
8
         std::free(m_p);
34
8
      }
35
36
      RawPage(const RawPage& other) = default;
37
      RawPage& operator=(const RawPage& other) = default;
38
39
4
      RawPage(RawPage&& other) noexcept : m_p(nullptr) { std::swap(m_p, other.m_p); }
40
41
0
      RawPage& operator=(RawPage&& other) noexcept {
42
0
         if(this != &other) {
43
0
            std::swap(m_p, other.m_p);
44
0
         }
45
0
         return (*this);
46
0
      }
47
48
1.66k
      void* ptr() const { return m_p; }
49
50
   private:
51
      void* m_p;
52
};
53
54
1
std::vector<RawPage> allocate_raw_pages(size_t count, size_t page_size) {
55
1
   std::vector<RawPage> pages;
56
1
   pages.reserve(count);
57
58
5
   for(size_t i = 0; i != count; ++i) {
59
4
      void* ptr = nullptr;
60
61
4
      int rc = ::posix_memalign(&ptr, page_size, page_size);
62
4
      FUZZER_ASSERT_EQUAL(rc, 0);
63
64
4
      if(ptr) {
65
4
         pages.push_back(RawPage(ptr));
66
4
      }
67
4
   }
68
69
1
   return pages;
70
1
}
71
72
}  // namespace
73
74
415
void fuzz(std::span<const uint8_t> in) {
75
415
   const size_t page_count = 4;
76
415
   const size_t page_size = 4096;
77
78
   // static to avoid repeated allocations
79
415
   static std::vector<RawPage> raw_mem = allocate_raw_pages(page_count, page_size);
80
81
415
   std::vector<void*> mem_pages;
82
415
   mem_pages.reserve(raw_mem.size());
83
2.07k
   for(size_t i = 0; i != raw_mem.size(); ++i) {
84
1.66k
      mem_pages.push_back(raw_mem[i].ptr());
85
1.66k
   }
86
87
415
   Botan::Memory_Pool pool(mem_pages, page_size);
88
415
   std::map<uint8_t*, size_t> ptrs;
89
90
415
   size_t in_len = in.size();
91
415
   auto x = in.data();
92
90.0k
   while(in_len > 0) {
93
89.6k
      const uint8_t op = in[0] % 2;
94
89.6k
      size_t idx = (in[0] >> 1);
95
89.6k
      x += 1;
96
89.6k
      in_len -= 1;
97
98
89.6k
      if(in_len > 0 && idx < 4) {
99
53.2k
         idx = idx * 256 + x[0];
100
53.2k
         x += 1;
101
53.2k
         in_len -= 1;
102
53.2k
      }
103
104
89.6k
      if(op == 0) {
105
80.7k
         const size_t plen = idx + 1;  // ensure non-zero
106
80.7k
         uint8_t* p = static_cast<uint8_t*>(pool.allocate(plen));
107
108
80.7k
         if(p) {
109
18.5k
            const size_t expected_alignment = compute_expected_alignment(plen);
110
18.5k
            const size_t alignment = reinterpret_cast<uintptr_t>(p) % expected_alignment;
111
18.5k
            if(alignment != 0) {
112
0
               FUZZER_WRITE_AND_CRASH("Pointer allocated non-aligned pointer "
113
0
                                      << static_cast<void*>(p) << " for len " << plen << " expected "
114
0
                                      << expected_alignment << " got " << alignment);
115
0
            }
116
117
            //printf("alloc %d -> %p\n", plen, p);
118
119
1.04M
            for(size_t i = 0; i != plen; ++i) {
120
1.03M
               if(p[i] != 0) {
121
0
                  FUZZER_WRITE_AND_CRASH("Pool gave out non-zeroed memory");
122
0
               }
123
1.03M
            }
124
125
            // verify it becomes zeroed later
126
18.5k
            std::memset(p, static_cast<int>(idx), plen);
127
128
18.5k
            auto insert = ptrs.insert(std::make_pair(p, plen));
129
18.5k
            if(insert.second == false) {
130
0
               FUZZER_WRITE_AND_CRASH("Pointer " << static_cast<void*>(p) << " already existed\n");
131
0
            }
132
133
18.5k
            auto itr = insert.first;
134
135
            // Verify this pointer doesn't overlap with the one before it
136
18.5k
            if(itr != ptrs.begin()) {
137
18.2k
               auto before = std::prev(itr);
138
18.2k
               auto ptr_before = *before;
139
140
18.2k
               if(ptr_before.first + ptr_before.second > p) {
141
0
                  FUZZER_WRITE_AND_CRASH("Previous " << static_cast<void*>(ptr_before.first) << "/" << ptr_before.second
142
0
                                                     << " overlaps with new " << static_cast<void*>(p));
143
0
               }
144
18.2k
            }
145
146
18.5k
            auto after = std::next(itr);
147
148
18.5k
            if(after != ptrs.end()) {
149
6.15k
               if(p + plen > after->first) {
150
0
                  FUZZER_WRITE_AND_CRASH("New " << static_cast<void*>(p) << "/" << plen << " overlaps following "
151
0
                                                << static_cast<void*>(after->first));
152
0
               }
153
6.15k
            }
154
18.5k
         }
155
80.7k
      } else if(op == 1) {
156
8.91k
         if(ptrs.empty()) {
157
8.91k
            continue;
158
8.91k
         }
159
160
0
         size_t which_ptr = idx % ptrs.size();
161
162
0
         auto itr = ptrs.begin();
163
164
0
         while(which_ptr-- > 0) {
165
0
            ++itr;
166
0
         }
167
168
         //printf("free %p %d\n", itr->first, itr->second);
169
0
         FUZZER_ASSERT_TRUE(pool.deallocate(itr->first, itr->second));
170
0
         ptrs.erase(itr);
171
0
      }
172
89.6k
   }
173
415
}