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