• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 29554550439

16 Jul 2026 05:33PM UTC coverage: 91.66% (+2.3%) from 89.402%
29554550439

push

github

web-flow
Merge pull request #5732 from Rohde-Schwarz/rene/update_bogo.py

116441 of 127036 relevant lines covered (91.66%)

10457847.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.27
/src/lib/compression/compress_utils.cpp
1
/*
2
* Compression Utils
3
* (C) 2014,2016 Jack Lloyd
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include <botan/internal/compress_utils.h>
9

10
#include <botan/exceptn.h>
11
#include <botan/mem_ops.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/int_utils.h>
14
#include <cstdlib>
15

16
namespace Botan {
17

18
Compression_Error::Compression_Error(const char* func_name, ErrorType type, int rc) :
×
19
      Exception(fmt("Compression API {} failed with return code {}", func_name, rc)), m_type(type), m_rc(rc) {}
×
20

21
void* Compression_Alloc_Info::do_malloc(size_t n, size_t size) noexcept {
1,263✔
22
   // Precheck for integer overflow in the multiplication
23
   // before passing to calloc, which may or may not check.
24
   if(!checked_mul(n, size)) {
2,526✔
25
      return nullptr;
26
   }
27

28
   void* ptr = std::calloc(n, size);  // NOLINT(*-no-malloc,*-owning-memory,*-const-correctness)
1,263✔
29

30
   if(ptr == nullptr) {
1,263✔
31
      return nullptr;
32
   }
33

34
   /*
35
   * Return null rather than throwing here as we are being called by a
36
   * C library and it may not be possible for an exception to unwind
37
   * the call stack from here. The compression library is expecting a
38
   * function written in C and a null return on error, which it will
39
   * send upwards to the compression wrappers. So if recording the
40
   * allocation throws (eg bad_alloc growing the map), free the block and
41
   * report failure the same way.
42
   */
43
   try {
1,263✔
44
      m_current_allocs[ptr] = n * size;
1,263✔
45
   } catch(...) {
×
46
      std::free(ptr);  // NOLINT(*-no-malloc,*-owning-memory)
×
47
      return nullptr;
×
48
   }
×
49

50
   return ptr;
1,263✔
51
}
52

53
void Compression_Alloc_Info::do_free(void* ptr) noexcept {
1,343✔
54
   if(ptr != nullptr) {
1,343✔
55
      auto i = m_current_allocs.find(ptr);
1,263✔
56

57
      if(i == m_current_allocs.end()) {
1,263✔
58
         /*
59
         * The compression library tried to free a pointer that was not allocated by
60
         * a call to the allocation function. We do not have any particularly good
61
         * options here.
62
         *
63
         * - Throwing is not possible since it would unwind through a C ABI library,
64
         *   which results in undefined behavior.
65
         *
66
         * - We could panic (fprintf to stderr and abort) but causing a unilateral
67
         *   and unrecoverable process crash isn't necessarily the right choice either.
68
         *
69
         * - We could skip the scrub step and pass the pointer along directly to
70
         *   std::free, but that risks corrupting the system heap.
71
         *
72
         * So instead we just ignore the request entirely
73
         */
74
         return;
1,343✔
75
      }
76

77
      secure_scrub_memory(ptr, i->second);
1,263✔
78
      std::free(ptr);  // NOLINT(*-no-malloc,*-owning-memory)
1,263✔
79
      m_current_allocs.erase(i);
1,263✔
80
   }
81
}
82

83
void Stream_Compression::clear() {
133✔
84
   m_stream.reset();
×
85
}
×
86

87
void Stream_Compression::start(size_t level) {
133✔
88
   m_stream = make_stream(level);
133✔
89
}
133✔
90

91
void Stream_Compression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
296✔
92
   BOTAN_ASSERT(m_stream, "Initialized");
296✔
93
   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
296✔
94

95
   // bzip doesn't like being called with no input and BZ_RUN
96
   if(buf.size() == offset && flags == m_stream->run_flag()) {
296✔
97
      return;
98
   }
99

100
   const size_t buffer_needed = add_or_throw(buf.size(), offset, "Compression input too large");
276✔
101
   if(m_buffer.size() < buffer_needed) {
276✔
102
      m_buffer.resize(buffer_needed);
113✔
103
   }
104

105
   // If the output buffer has zero length, .data() might return nullptr. This would
106
   // make some compression algorithms (notably those provided by zlib) fail.
107
   // Any small positive value works fine, but we choose 32 as it is the smallest power
108
   // of two that is large enough to hold all the headers and trailers of the common
109
   // formats, preventing further resizings to make room for output data.
110
   if(m_buffer.empty()) {
276✔
111
      m_buffer.resize(32);
70✔
112
   }
113

114
   m_stream->next_in(buf.data() + offset, buf.size() - offset);
276✔
115
   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
276✔
116

117
   while(true) {
381✔
118
      const bool stream_end = m_stream->run(flags);
381✔
119

120
      if(stream_end) {
381✔
121
         BOTAN_ASSERT(m_stream->avail_in() == 0, "After stream is done, no input remains to be processed");
143✔
122
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
143✔
123
         break;
143✔
124
      } else if(m_stream->avail_out() == 0) {
238✔
125
         const size_t added = add_or_throw<size_t>(m_buffer.size(), 8, "Compression output too large");
105✔
126
         const size_t new_size = add_or_throw(m_buffer.size(), added, "Compression output too large");
105✔
127
         m_buffer.resize(new_size);
105✔
128
         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
105✔
129
      } else if(m_stream->avail_in() == 0) {
133✔
130
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
133✔
131
         break;
133✔
132
      }
133
   }
134

135
   copy_mem(m_buffer.data(), buf.data(), offset);
276✔
136
   buf.swap(m_buffer);
276✔
137
}
138

139
void Stream_Compression::update(secure_vector<uint8_t>& buf, size_t offset, bool flush) {
163✔
140
   BOTAN_ASSERT(m_stream, "Initialized");
163✔
141
   process(buf, offset, flush ? m_stream->flush_flag() : m_stream->run_flag());
163✔
142
}
163✔
143

144
void Stream_Compression::finish(secure_vector<uint8_t>& buf, size_t offset) {
133✔
145
   BOTAN_ASSERT(m_stream, "Initialized");
133✔
146
   process(buf, offset, m_stream->finish_flag());
133✔
147
   clear();
133✔
148
}
133✔
149

150
void Stream_Decompression::clear() {
118✔
151
   m_stream.reset();
×
152
}
×
153

154
void Stream_Decompression::start() {
123✔
155
   m_stream = make_stream();
128✔
156
}
123✔
157

158
void Stream_Decompression::process(secure_vector<uint8_t>& buf, size_t offset, uint32_t flags) {
413✔
159
   BOTAN_ASSERT(m_stream, "Initialized");
413✔
160
   BOTAN_ASSERT(buf.size() >= offset, "Offset is sane");
413✔
161

162
   const size_t buffer_needed = add_or_throw(buf.size(), offset, "Compression input too large");
413✔
163
   if(m_buffer.size() < buffer_needed) {
413✔
164
      m_buffer.resize(buffer_needed);
38✔
165
   }
166

167
   m_stream->next_in(buf.data() + offset, buf.size() - offset);
413✔
168
   m_stream->next_out(m_buffer.data() + offset, m_buffer.size() - offset);
413✔
169

170
   while(true) {
576✔
171
      const bool stream_end = m_stream->run(flags);
576✔
172

173
      if(stream_end) {
576✔
174
         if(m_stream->avail_in() == 0) {
123✔
175
            // all data consumed
176
            m_buffer.resize(m_buffer.size() - m_stream->avail_out());
118✔
177
            clear();
118✔
178
            break;
179
         }
180

181
         // More data follows: try to process as a following stream
182
         // Remove stream1's unused output space so stream2's output
183
         // is placed immediately after stream1's data with no gap.
184
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
5✔
185
         const size_t read = (buf.size() - offset) - m_stream->avail_in();
5✔
186
         start();
5✔
187
         m_stream->next_in(buf.data() + offset + read, buf.size() - offset - read);
5✔
188
      }
189

190
      if(m_stream->avail_out() == 0) {
458✔
191
         const size_t added = add_or_throw<size_t>(m_buffer.size(), 8, "Compression output too large");
163✔
192
         const size_t new_size = add_or_throw(m_buffer.size(), added, "Compression output too large");
163✔
193
         m_buffer.resize(new_size);
163✔
194
         m_stream->next_out(m_buffer.data() + m_buffer.size() - added, added);
163✔
195
      } else if(m_stream->avail_in() == 0) {
295✔
196
         m_buffer.resize(m_buffer.size() - m_stream->avail_out());
295✔
197
         break;
295✔
198
      }
199
   }
200

201
   copy_mem(m_buffer.data(), buf.data(), offset);
413✔
202
   buf.swap(m_buffer);
413✔
203
}
413✔
204

205
void Stream_Decompression::update(secure_vector<uint8_t>& buf, size_t offset) {
413✔
206
   if(!m_stream) {
413✔
207
      if(buf.size() == offset) {
5✔
208
         return;
209
      }
210
      // Previous stream ended cleanly; re-initialize for a concatenated stream
211
      start();
5✔
212
   }
213
   process(buf, offset, m_stream->run_flag());
413✔
214
}
215

216
void Stream_Decompression::finish(secure_vector<uint8_t>& buf, size_t offset) {
113✔
217
   if(!m_stream) {
113✔
218
      if(buf.size() == offset) {
113✔
219
         return;
220
      }
221
      // Previous stream ended cleanly; re-initialize for a concatenated stream
222
      start();
×
223
   }
224

225
   process(buf, offset, m_stream->finish_flag());
×
226

227
   if(m_stream) {
×
228
      throw Invalid_State(fmt("{} finished but not at stream end", name()));
×
229
   }
230
}
231

232
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc