1 /* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
2
3 /* @UNSAFE: whole file */
4
5 #include "lib.h"
6 #include "buffer.h"
7
8 struct real_buffer {
9 union {
10 struct buffer buf;
11 struct {
12 /* public: */
13 const void *r_buffer;
14 size_t used;
15 /* private: */
16 unsigned char *w_buffer;
17 size_t dirty, alloc, writable_size, max_size;
18
19 pool_t pool;
20
21 bool alloced:1;
22 bool dynamic:1;
23 };
24 };
25 };
26 typedef int buffer_check_sizes[COMPILE_ERROR_IF_TRUE(sizeof(struct real_buffer) > sizeof(buffer_t)) ?1:1];
27
buffer_alloc(struct real_buffer * buf,size_t size)28 static void buffer_alloc(struct real_buffer *buf, size_t size)
29 {
30 i_assert(buf->w_buffer == NULL || buf->alloced);
31
32 if (size == buf->alloc)
33 return;
34
35 i_assert(size > buf->alloc);
36
37 if (buf->w_buffer == NULL)
38 buf->w_buffer = p_malloc(buf->pool, size);
39 else
40 buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, size);
41 buf->alloc = size;
42 buf->writable_size = size-1; /* -1 for str_c() NUL */
43
44 buf->r_buffer = buf->w_buffer;
45 buf->alloced = TRUE;
46 }
47
48 static inline void
buffer_check_limits(struct real_buffer * buf,size_t pos,size_t data_size)49 buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size)
50 {
51 size_t new_size;
52
53 if (unlikely(buf->max_size - pos < data_size))
54 i_panic("Buffer write out of range (%zu + %zu)", pos, data_size);
55
56 new_size = pos + data_size;
57
58 if (new_size > buf->used && buf->used < buf->dirty) {
59 /* clear used..dirty area */
60 size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size);
61
62 memset(buf->w_buffer + buf->used, 0, max - buf->used);
63 }
64
65 /* Use buf->writable_size instead of buf->alloc to always keep +1 byte
66 available in case str_c() is called for this buffer. This is mainly
67 for cases where the buffer is allocated from data stack, and str_c()
68 is called in a separate stack frame. */
69 if (new_size > buf->writable_size) {
70 if (unlikely(!buf->dynamic)) {
71 i_panic("Buffer full (%zu > %zu, pool %s)",
72 pos + data_size, buf->alloc,
73 buf->pool == NULL ? "<none>" :
74 pool_get_name(buf->pool));
75 }
76
77 size_t new_alloc_size =
78 pool_get_exp_grown_size(buf->pool, buf->alloc,
79 new_size + 1);
80 if (new_alloc_size > buf->max_size) {
81 /* limit to max_size, but do include +1 for
82 str_c() NUL */
83 new_alloc_size = buf->max_size + 1;
84 }
85 buffer_alloc(buf, new_alloc_size);
86 }
87 #if 0
88 else if (new_size > buf->used && buf->alloced &&
89 !buf->pool->alloconly_pool && !buf->pool->datastack_pool) {
90 void *new_buf;
91
92 /* buffer's size increased: move the buffer's memory elsewhere.
93 this should help catch bugs where old pointers are tried to
94 be used to access the buffer's memory */
95 new_buf = p_malloc(buf->pool, buf->alloc);
96 memcpy(new_buf, buf->w_buffer, buf->alloc);
97 p_free(buf->pool, buf->w_buffer);
98
99 buf->w_buffer = new_buf;
100 buf->r_buffer = new_buf;
101 }
102 #endif
103
104 if (new_size > buf->used)
105 buf->used = new_size;
106 i_assert(buf->used <= buf->alloc);
107 i_assert(buf->w_buffer != NULL);
108 }
109
110 static inline void
buffer_check_append_limits(struct real_buffer * buf,size_t data_size)111 buffer_check_append_limits(struct real_buffer *buf, size_t data_size)
112 {
113 /* Fast path: See if data to be appended fits into allocated buffer.
114 If it does, we don't even need to memset() the dirty buffer since
115 it's going to be filled with the newly appended data. */
116 if (buf->writable_size - buf->used < data_size)
117 buffer_check_limits(buf, buf->used, data_size);
118 else
119 buf->used += data_size;
120 }
121
122 #undef buffer_create_from_data
buffer_create_from_data(buffer_t * buffer,void * data,size_t size)123 void buffer_create_from_data(buffer_t *buffer, void *data, size_t size)
124 {
125 struct real_buffer *buf;
126
127 i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
128
129 buf = container_of(buffer, struct real_buffer, buf);
130 i_zero(buf);
131 buf->alloc = buf->writable_size = buf->max_size = size;
132 buf->r_buffer = buf->w_buffer = data;
133 /* clear the whole memory area. unnecessary usually, but if the
134 buffer is used by e.g. str_c() it tries to access uninitialized
135 memory */
136 memset(data, 0, size);
137 }
138
139 #undef buffer_create_from_const_data
buffer_create_from_const_data(buffer_t * buffer,const void * data,size_t size)140 void buffer_create_from_const_data(buffer_t *buffer,
141 const void *data, size_t size)
142 {
143 struct real_buffer *buf;
144
145 i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
146
147 buf = container_of(buffer, struct real_buffer, buf);
148 i_zero(buf);
149
150 buf->used = buf->alloc = buf->writable_size = buf->max_size = size;
151 buf->r_buffer = data;
152 i_assert(buf->w_buffer == NULL);
153 }
154
buffer_create_dynamic(pool_t pool,size_t init_size)155 buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size)
156 {
157 return buffer_create_dynamic_max(pool, init_size, SIZE_MAX);
158 }
159
buffer_create_dynamic_max(pool_t pool,size_t init_size,size_t max_size)160 buffer_t *buffer_create_dynamic_max(pool_t pool, size_t init_size,
161 size_t max_size)
162 {
163 struct real_buffer *buf;
164
165 #ifdef DEBUG
166 /* we increment this by 1 later on, so if it's SIZE_MAX
167 it turns into 0 and hides a potential bug.
168
169 Too scary to use in production for now, though. This
170 can change in future. */
171 i_assert(init_size < SIZE_MAX);
172 #endif
173
174 buf = p_new(pool, struct real_buffer, 1);
175 buf->pool = pool;
176 buf->dynamic = TRUE;
177 buf->max_size = max_size;
178 /* buffer_alloc() reserves +1 for str_c() NIL, so add +1 here to
179 init_size so we can actually write that much to the buffer without
180 realloc */
181 buffer_alloc(buf, init_size+1);
182 return &buf->buf;
183 }
184
buffer_free(buffer_t ** _buf)185 void buffer_free(buffer_t **_buf)
186 {
187 struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf);
188
189 if (buf == NULL)
190 return;
191
192 *_buf = NULL;
193 if (buf->alloced)
194 p_free(buf->pool, buf->w_buffer);
195 if (buf->pool != NULL)
196 p_free(buf->pool, buf);
197 }
198
buffer_free_without_data(buffer_t ** _buf)199 void *buffer_free_without_data(buffer_t **_buf)
200 {
201 struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf);
202 void *data;
203
204 *_buf = NULL;
205
206 data = buf->w_buffer;
207 p_free(buf->pool, buf);
208 return data;
209 }
210
buffer_get_pool(const buffer_t * _buf)211 pool_t buffer_get_pool(const buffer_t *_buf)
212 {
213 const struct real_buffer *buf =
214 container_of(_buf, const struct real_buffer, buf);
215
216 return buf->pool;
217 }
218
buffer_write(buffer_t * _buf,size_t pos,const void * data,size_t data_size)219 void buffer_write(buffer_t *_buf, size_t pos,
220 const void *data, size_t data_size)
221 {
222 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
223
224 buffer_check_limits(buf, pos, data_size);
225 if (data_size > 0)
226 memcpy(buf->w_buffer + pos, data, data_size);
227 }
228
buffer_append(buffer_t * _buf,const void * data,size_t data_size)229 void buffer_append(buffer_t *_buf, const void *data, size_t data_size)
230 {
231 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
232
233 if (data_size > 0) {
234 size_t pos = buf->used;
235 buffer_check_append_limits(buf, data_size);
236 memcpy(buf->w_buffer + pos, data, data_size);
237 }
238 }
239
buffer_append_c(buffer_t * _buf,unsigned char chr)240 void buffer_append_c(buffer_t *_buf, unsigned char chr)
241 {
242 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
243 size_t pos = buf->used;
244
245 buffer_check_append_limits(buf, 1);
246 buf->w_buffer[pos] = chr;
247 }
248
buffer_insert(buffer_t * _buf,size_t pos,const void * data,size_t data_size)249 void buffer_insert(buffer_t *_buf, size_t pos,
250 const void *data, size_t data_size)
251 {
252 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
253
254 if (pos >= buf->used)
255 buffer_write(_buf, pos, data, data_size);
256 else {
257 buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX);
258 memcpy(buf->w_buffer + pos, data, data_size);
259 }
260 }
261
buffer_delete(buffer_t * _buf,size_t pos,size_t size)262 void buffer_delete(buffer_t *_buf, size_t pos, size_t size)
263 {
264 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
265 size_t end_size;
266
267 if (pos >= buf->used)
268 return;
269 end_size = buf->used - pos;
270
271 if (size < end_size) {
272 /* delete from between */
273 end_size -= size;
274 memmove(buf->w_buffer + pos,
275 buf->w_buffer + pos + size, end_size);
276 } else {
277 /* delete the rest of the buffer */
278 end_size = 0;
279 }
280
281 buffer_set_used_size(_buf, pos + end_size);
282 }
283
buffer_replace(buffer_t * _buf,size_t pos,size_t size,const void * data,size_t data_size)284 void buffer_replace(buffer_t *_buf, size_t pos, size_t size,
285 const void *data, size_t data_size)
286 {
287 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
288 size_t end_size;
289
290 if (pos >= buf->used) {
291 buffer_write(_buf, pos, data, data_size);
292 return;
293 }
294 end_size = buf->used - pos;
295
296 if (size < end_size) {
297 end_size -= size;
298 if (data_size == 0) {
299 /* delete from between */
300 memmove(buf->w_buffer + pos,
301 buf->w_buffer + pos + size, end_size);
302 } else {
303 /* insert */
304 buffer_copy(_buf, pos + data_size, _buf, pos + size,
305 SIZE_MAX);
306 memcpy(buf->w_buffer + pos, data, data_size);
307 }
308 } else {
309 /* overwrite the end */
310 end_size = 0;
311 buffer_write(_buf, pos, data, data_size);
312 }
313
314 buffer_set_used_size(_buf, pos + data_size + end_size);
315 }
316
317
buffer_write_zero(buffer_t * _buf,size_t pos,size_t data_size)318 void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size)
319 {
320 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
321
322 buffer_check_limits(buf, pos, data_size);
323 memset(buf->w_buffer + pos, 0, data_size);
324 }
325
buffer_append_zero(buffer_t * _buf,size_t data_size)326 void buffer_append_zero(buffer_t *_buf, size_t data_size)
327 {
328 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
329
330 /* NOTE: When appending it's enough to check that the limits are
331 valid, because the data is already guaranteed to be zero-filled. */
332 buffer_check_limits(buf, buf->used, data_size);
333 }
334
buffer_insert_zero(buffer_t * _buf,size_t pos,size_t data_size)335 void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size)
336 {
337 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
338
339 if (pos >= buf->used)
340 buffer_write_zero(_buf, pos, data_size);
341 else {
342 buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX);
343 memset(buf->w_buffer + pos, 0, data_size);
344 }
345 }
346
buffer_copy(buffer_t * _dest,size_t dest_pos,const buffer_t * _src,size_t src_pos,size_t copy_size)347 void buffer_copy(buffer_t *_dest, size_t dest_pos,
348 const buffer_t *_src, size_t src_pos, size_t copy_size)
349 {
350 struct real_buffer *dest = container_of(_dest, struct real_buffer, buf);
351 const struct real_buffer *src =
352 container_of(_src, const struct real_buffer, buf);
353 size_t max_size;
354
355 i_assert(src_pos <= src->used);
356
357 max_size = src->used - src_pos;
358 if (copy_size > max_size)
359 copy_size = max_size;
360
361 buffer_check_limits(dest, dest_pos, copy_size);
362 i_assert(src->r_buffer != NULL);
363
364 if (src == dest) {
365 memmove(dest->w_buffer + dest_pos,
366 CONST_PTR_OFFSET(src->r_buffer, src_pos), copy_size);
367 } else {
368 memcpy(dest->w_buffer + dest_pos,
369 CONST_PTR_OFFSET(src->r_buffer, src_pos), copy_size);
370 }
371 }
372
buffer_append_buf(buffer_t * dest,const buffer_t * src,size_t src_pos,size_t copy_size)373 void buffer_append_buf(buffer_t *dest, const buffer_t *src,
374 size_t src_pos, size_t copy_size)
375 {
376 buffer_copy(dest, dest->used, src, src_pos, copy_size);
377 }
378
buffer_get_space_unsafe(buffer_t * _buf,size_t pos,size_t size)379 void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size)
380 {
381 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
382
383 buffer_check_limits(buf, pos, size);
384 return buf->w_buffer + pos;
385 }
386
buffer_append_space_unsafe(buffer_t * buf,size_t size)387 void *buffer_append_space_unsafe(buffer_t *buf, size_t size)
388 {
389 /* NOTE: can't use buffer_check_append_limits() here because it doesn't
390 guarantee that the buffer is zero-filled. */
391 return buffer_get_space_unsafe(buf, buf->used, size);
392 }
393
buffer_get_modifiable_data(const buffer_t * _buf,size_t * used_size_r)394 void *buffer_get_modifiable_data(const buffer_t *_buf, size_t *used_size_r)
395 {
396 const struct real_buffer *buf =
397 container_of(_buf, const struct real_buffer, buf);
398
399 if (used_size_r != NULL)
400 *used_size_r = buf->used;
401 i_assert(buf->used == 0 || buf->w_buffer != NULL);
402 return buf->w_buffer;
403 }
404
buffer_set_used_size(buffer_t * _buf,size_t used_size)405 void buffer_set_used_size(buffer_t *_buf, size_t used_size)
406 {
407 struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
408
409 i_assert(used_size <= buf->alloc);
410
411 if (buf->used > buf->dirty)
412 buf->dirty = buf->used;
413
414 buf->used = used_size;
415 }
416
buffer_get_size(const buffer_t * _buf)417 size_t buffer_get_size(const buffer_t *_buf)
418 {
419 const struct real_buffer *buf =
420 container_of(_buf, const struct real_buffer, buf);
421
422 return buf->alloc;
423 }
424
buffer_get_writable_size(const buffer_t * _buf)425 size_t buffer_get_writable_size(const buffer_t *_buf)
426 {
427 const struct real_buffer *buf =
428 container_of(_buf, const struct real_buffer, buf);
429
430 /* Use buf->writable_size instead of buf->alloc to reserve +1 for
431 str_c() NUL in buffer_check_limits(). Otherwise the caller might
432 increase the buffer's alloc size unnecessarily when it just wants
433 to access the entire buffer. */
434 return buf->writable_size;
435 }
436
buffer_get_avail_size(const buffer_t * _buf)437 size_t buffer_get_avail_size(const buffer_t *_buf)
438 {
439 const struct real_buffer *buf =
440 container_of(_buf, const struct real_buffer, buf);
441
442 i_assert(buf->alloc >= buf->used);
443 return ((buf->dynamic ? SIZE_MAX : buf->alloc) - buf->used);
444 }
445
buffer_cmp(const buffer_t * buf1,const buffer_t * buf2)446 bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2)
447 {
448 if (buf1->used != buf2->used)
449 return FALSE;
450 if (buf1->used == 0)
451 return TRUE;
452
453 return memcmp(buf1->data, buf2->data, buf1->used) == 0;
454 }
455
buffer_verify_pool(buffer_t * _buf)456 void buffer_verify_pool(buffer_t *_buf)
457 {
458 const struct real_buffer *buf =
459 container_of(_buf, struct real_buffer, buf);
460 void *ret;
461
462 if (buf->pool != NULL && buf->pool->datastack_pool && buf->alloc > 0) {
463 /* this doesn't really do anything except verify the
464 stack frame */
465 ret = p_realloc(buf->pool, buf->w_buffer,
466 buf->alloc, buf->alloc);
467 i_assert(ret == buf->w_buffer);
468 }
469 }
470
471 void ATTR_NO_SANITIZE_IMPLICIT_CONVERSION
472 ATTR_NO_SANITIZE_INTEGER
buffer_truncate_rshift_bits(buffer_t * buf,size_t bits)473 buffer_truncate_rshift_bits(buffer_t *buf, size_t bits)
474 {
475 /* no-op if it's shorten than bits in any case.. */
476 if (buf->used * 8 < bits) return;
477
478 if (bits > 0) {
479 /* truncate it to closest byte boundary */
480 size_t bytes = ((bits + 7) & -8U)/8;
481 /* remaining bits */
482 bits = bits % 8;
483 buffer_set_used_size(buf, I_MIN(bytes, buf->used));
484 unsigned char *ptr = buffer_get_modifiable_data(buf, &bytes);
485 /* right shift over byte array */
486 if (bits > 0) {
487 for(size_t i=bytes-1;i>0;i--)
488 ptr[i] = (ptr[i]>>(8-bits)) +
489 ((ptr[i-1]&(0xff>>(bits)))<<bits);
490 ptr[0] = ptr[0]>>(8-bits);
491 }
492 } else {
493 buffer_set_used_size(buf, 0);
494 }
495 }
496
497