"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/lib/buffer.c" between
dovecot-2.3.16.tar.gz and dovecot-2.3.17.tar.gz

About: Dovecot is an IMAP and POP3 server, written with security primarily in mind.

buffer.c  (dovecot-2.3.16):buffer.c  (dovecot-2.3.17)
skipping to change at line 17 skipping to change at line 17
struct real_buffer { struct real_buffer {
union { union {
struct buffer buf; struct buffer buf;
struct { struct {
/* public: */ /* public: */
const void *r_buffer; const void *r_buffer;
size_t used; size_t used;
/* private: */ /* private: */
unsigned char *w_buffer; unsigned char *w_buffer;
size_t dirty, alloc, max_size; size_t dirty, alloc, writable_size, max_size;
pool_t pool; pool_t pool;
bool alloced:1; bool alloced:1;
bool dynamic:1; bool dynamic:1;
}; };
}; };
}; };
typedef int buffer_check_sizes[COMPILE_ERROR_IF_TRUE(sizeof(struct real_buffer) > sizeof(buffer_t)) ?1:1]; typedef int buffer_check_sizes[COMPILE_ERROR_IF_TRUE(sizeof(struct real_buffer) > sizeof(buffer_t)) ?1:1];
skipping to change at line 42 skipping to change at line 42
if (size == buf->alloc) if (size == buf->alloc)
return; return;
i_assert(size > buf->alloc); i_assert(size > buf->alloc);
if (buf->w_buffer == NULL) if (buf->w_buffer == NULL)
buf->w_buffer = p_malloc(buf->pool, size); buf->w_buffer = p_malloc(buf->pool, size);
else else
buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, s ize); buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc, s ize);
buf->alloc = size; buf->alloc = size;
buf->writable_size = size-1; /* -1 for str_c() NUL */
buf->r_buffer = buf->w_buffer; buf->r_buffer = buf->w_buffer;
buf->alloced = TRUE; buf->alloced = TRUE;
} }
static inline void static inline void
buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size) buffer_check_limits(struct real_buffer *buf, size_t pos, size_t data_size)
{ {
unsigned int extra;
size_t new_size; size_t new_size;
if (unlikely(buf->max_size - pos < data_size)) if (unlikely(buf->max_size - pos < data_size))
i_panic("Buffer write out of range (%zu + %zu)", pos, data_size); i_panic("Buffer write out of range (%zu + %zu)", pos, data_size);
new_size = pos + data_size; new_size = pos + data_size;
if (new_size > buf->used && buf->used < buf->dirty) { if (new_size > buf->used && buf->used < buf->dirty) {
/* clear used..dirty area */ /* clear used..dirty area */
size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size); size_t max = I_MIN(I_MIN(buf->alloc, buf->dirty), new_size);
memset(buf->w_buffer + buf->used, 0, max - buf->used); memset(buf->w_buffer + buf->used, 0, max - buf->used);
} }
/* always keep +1 byte allocated available in case str_c() is called /* Use buf->writable_size instead of buf->alloc to always keep +1 byte
for this buffer. this is mainly for cases where the buffer is available in case str_c() is called for this buffer. This is mainly
allocated from data stack, and str_c() is called in a separate stack for cases where the buffer is allocated from data stack, and str_c()
frame. */ is called in a separate stack frame. */
extra = buf->dynamic ? 1 : 0; if (new_size > buf->writable_size) {
if (new_size + extra > buf->alloc) {
if (unlikely(!buf->dynamic)) { if (unlikely(!buf->dynamic)) {
i_panic("Buffer full (%zu > %zu, pool %s)", i_panic("Buffer full (%zu > %zu, pool %s)",
pos + data_size, buf->alloc, pos + data_size, buf->alloc,
buf->pool == NULL ? "<none>" : buf->pool == NULL ? "<none>" :
pool_get_name(buf->pool)); pool_get_name(buf->pool));
} }
buffer_alloc(buf, pool_get_exp_grown_size(buf->pool, buf->alloc, size_t new_alloc_size =
new_size + extra)); pool_get_exp_grown_size(buf->pool, buf->alloc,
new_size + 1);
if (new_alloc_size > buf->max_size) {
/* limit to max_size, but do include +1 for
str_c() NUL */
new_alloc_size = buf->max_size + 1;
}
buffer_alloc(buf, new_alloc_size);
} }
#if 0 #if 0
else if (new_size > buf->used && buf->alloced && else if (new_size > buf->used && buf->alloced &&
!buf->pool->alloconly_pool && !buf->pool->datastack_pool) { !buf->pool->alloconly_pool && !buf->pool->datastack_pool) {
void *new_buf; void *new_buf;
/* buffer's size increased: move the buffer's memory elsewhere. /* buffer's size increased: move the buffer's memory elsewhere.
this should help catch bugs where old pointers are tried to this should help catch bugs where old pointers are tried to
be used to access the buffer's memory */ be used to access the buffer's memory */
new_buf = p_malloc(buf->pool, buf->alloc); new_buf = p_malloc(buf->pool, buf->alloc);
skipping to change at line 104 skipping to change at line 110
buf->r_buffer = new_buf; buf->r_buffer = new_buf;
} }
#endif #endif
if (new_size > buf->used) if (new_size > buf->used)
buf->used = new_size; buf->used = new_size;
i_assert(buf->used <= buf->alloc); i_assert(buf->used <= buf->alloc);
i_assert(buf->w_buffer != NULL); i_assert(buf->w_buffer != NULL);
} }
static inline void
buffer_check_append_limits(struct real_buffer *buf, size_t data_size)
{
/* Fast path: See if data to be appended fits into allocated buffer.
If it does, we don't even need to memset() the dirty buffer since
it's going to be filled with the newly appended data. */
if (buf->writable_size - buf->used < data_size)
buffer_check_limits(buf, buf->used, data_size);
else
buf->used += data_size;
}
#undef buffer_create_from_data #undef buffer_create_from_data
void buffer_create_from_data(buffer_t *buffer, void *data, size_t size) void buffer_create_from_data(buffer_t *buffer, void *data, size_t size)
{ {
struct real_buffer *buf; struct real_buffer *buf;
i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
buf = (struct real_buffer *)buffer; buf = container_of(buffer, struct real_buffer, buf);
i_zero(buf); i_zero(buf);
buf->alloc = buf->max_size = size; buf->alloc = buf->writable_size = buf->max_size = size;
buf->r_buffer = buf->w_buffer = data; buf->r_buffer = buf->w_buffer = data;
/* clear the whole memory area. unnecessary usually, but if the /* clear the whole memory area. unnecessary usually, but if the
buffer is used by e.g. str_c() it tries to access uninitialized buffer is used by e.g. str_c() it tries to access uninitialized
memory */ memory */
memset(data, 0, size); memset(data, 0, size);
} }
#undef buffer_create_from_const_data #undef buffer_create_from_const_data
void buffer_create_from_const_data(buffer_t *buffer, void buffer_create_from_const_data(buffer_t *buffer,
const void *data, size_t size) const void *data, size_t size)
{ {
struct real_buffer *buf; struct real_buffer *buf;
i_assert(sizeof(*buffer) >= sizeof(struct real_buffer)); i_assert(sizeof(*buffer) >= sizeof(struct real_buffer));
buf = (struct real_buffer *)buffer; buf = container_of(buffer, struct real_buffer, buf);
i_zero(buf); i_zero(buf);
buf->used = buf->alloc = buf->max_size = size; buf->used = buf->alloc = buf->writable_size = buf->max_size = size;
buf->r_buffer = data; buf->r_buffer = data;
i_assert(buf->w_buffer == NULL); i_assert(buf->w_buffer == NULL);
} }
buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size) buffer_t *buffer_create_dynamic(pool_t pool, size_t init_size)
{ {
return buffer_create_dynamic_max(pool, init_size, SIZE_MAX); return buffer_create_dynamic_max(pool, init_size, SIZE_MAX);
} }
buffer_t *buffer_create_dynamic_max(pool_t pool, size_t init_size, buffer_t *buffer_create_dynamic_max(pool_t pool, size_t init_size,
skipping to change at line 164 skipping to change at line 182
#endif #endif
buf = p_new(pool, struct real_buffer, 1); buf = p_new(pool, struct real_buffer, 1);
buf->pool = pool; buf->pool = pool;
buf->dynamic = TRUE; buf->dynamic = TRUE;
buf->max_size = max_size; buf->max_size = max_size;
/* buffer_alloc() reserves +1 for str_c() NIL, so add +1 here to /* buffer_alloc() reserves +1 for str_c() NIL, so add +1 here to
init_size so we can actually write that much to the buffer without init_size so we can actually write that much to the buffer without
realloc */ realloc */
buffer_alloc(buf, init_size+1); buffer_alloc(buf, init_size+1);
return (buffer_t *)buf; return &buf->buf;
} }
void buffer_free(buffer_t **_buf) void buffer_free(buffer_t **_buf)
{ {
struct real_buffer *buf = (struct real_buffer *)*_buf; struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf);
if (buf == NULL) if (buf == NULL)
return; return;
*_buf = NULL; *_buf = NULL;
if (buf->alloced) if (buf->alloced)
p_free(buf->pool, buf->w_buffer); p_free(buf->pool, buf->w_buffer);
if (buf->pool != NULL) if (buf->pool != NULL)
p_free(buf->pool, buf); p_free(buf->pool, buf);
} }
void *buffer_free_without_data(buffer_t **_buf) void *buffer_free_without_data(buffer_t **_buf)
{ {
struct real_buffer *buf = (struct real_buffer *)*_buf; struct real_buffer *buf = container_of(*_buf, struct real_buffer, buf);
void *data; void *data;
*_buf = NULL; *_buf = NULL;
data = buf->w_buffer; data = buf->w_buffer;
p_free(buf->pool, buf); p_free(buf->pool, buf);
return data; return data;
} }
pool_t buffer_get_pool(const buffer_t *_buf) pool_t buffer_get_pool(const buffer_t *_buf)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, const struct real_buffer, buf);
return buf->pool; return buf->pool;
} }
void buffer_write(buffer_t *_buf, size_t pos, void buffer_write(buffer_t *_buf, size_t pos,
const void *data, size_t data_size) const void *data, size_t data_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
buffer_check_limits(buf, pos, data_size); buffer_check_limits(buf, pos, data_size);
if (data_size > 0) if (data_size > 0)
memcpy(buf->w_buffer + pos, data, data_size); memcpy(buf->w_buffer + pos, data, data_size);
} }
void buffer_append(buffer_t *buf, const void *data, size_t data_size) void buffer_append(buffer_t *_buf, const void *data, size_t data_size)
{ {
buffer_write(buf, buf->used, data, data_size); struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
if (data_size > 0) {
size_t pos = buf->used;
buffer_check_append_limits(buf, data_size);
memcpy(buf->w_buffer + pos, data, data_size);
}
} }
void buffer_append_c(buffer_t *buf, unsigned char chr) void buffer_append_c(buffer_t *_buf, unsigned char chr)
{ {
buffer_append(buf, &chr, 1); struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
size_t pos = buf->used;
buffer_check_append_limits(buf, 1);
buf->w_buffer[pos] = chr;
} }
void buffer_insert(buffer_t *_buf, size_t pos, void buffer_insert(buffer_t *_buf, size_t pos,
const void *data, size_t data_size) const void *data, size_t data_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
if (pos >= buf->used) if (pos >= buf->used)
buffer_write(_buf, pos, data, data_size); buffer_write(_buf, pos, data, data_size);
else { else {
buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX); buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX);
memcpy(buf->w_buffer + pos, data, data_size); memcpy(buf->w_buffer + pos, data, data_size);
} }
} }
void buffer_delete(buffer_t *_buf, size_t pos, size_t size) void buffer_delete(buffer_t *_buf, size_t pos, size_t size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
size_t end_size; size_t end_size;
if (pos >= buf->used) if (pos >= buf->used)
return; return;
end_size = buf->used - pos; end_size = buf->used - pos;
if (size < end_size) { if (size < end_size) {
/* delete from between */ /* delete from between */
end_size -= size; end_size -= size;
memmove(buf->w_buffer + pos, memmove(buf->w_buffer + pos,
skipping to change at line 258 skipping to change at line 287
/* delete the rest of the buffer */ /* delete the rest of the buffer */
end_size = 0; end_size = 0;
} }
buffer_set_used_size(_buf, pos + end_size); buffer_set_used_size(_buf, pos + end_size);
} }
void buffer_replace(buffer_t *_buf, size_t pos, size_t size, void buffer_replace(buffer_t *_buf, size_t pos, size_t size,
const void *data, size_t data_size) const void *data, size_t data_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
size_t end_size; size_t end_size;
if (pos >= buf->used) { if (pos >= buf->used) {
buffer_write(_buf, pos, data, data_size); buffer_write(_buf, pos, data, data_size);
return; return;
} }
end_size = buf->used - pos; end_size = buf->used - pos;
if (size < end_size) { if (size < end_size) {
end_size -= size; end_size -= size;
skipping to change at line 290 skipping to change at line 319
/* overwrite the end */ /* overwrite the end */
end_size = 0; end_size = 0;
buffer_write(_buf, pos, data, data_size); buffer_write(_buf, pos, data, data_size);
} }
buffer_set_used_size(_buf, pos + data_size + end_size); buffer_set_used_size(_buf, pos + data_size + end_size);
} }
void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size) void buffer_write_zero(buffer_t *_buf, size_t pos, size_t data_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
buffer_check_limits(buf, pos, data_size); buffer_check_limits(buf, pos, data_size);
memset(buf->w_buffer + pos, 0, data_size); memset(buf->w_buffer + pos, 0, data_size);
} }
void buffer_append_zero(buffer_t *buf, size_t data_size) void buffer_append_zero(buffer_t *_buf, size_t data_size)
{ {
buffer_write_zero(buf, buf->used, data_size); struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
/* NOTE: When appending it's enough to check that the limits are
valid, because the data is already guaranteed to be zero-filled. */
buffer_check_limits(buf, buf->used, data_size);
} }
void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size) void buffer_insert_zero(buffer_t *_buf, size_t pos, size_t data_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
if (pos >= buf->used) if (pos >= buf->used)
buffer_write_zero(_buf, pos, data_size); buffer_write_zero(_buf, pos, data_size);
else { else {
buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX); buffer_copy(_buf, pos + data_size, _buf, pos, SIZE_MAX);
memset(buf->w_buffer + pos, 0, data_size); memset(buf->w_buffer + pos, 0, data_size);
} }
} }
void buffer_copy(buffer_t *_dest, size_t dest_pos, void buffer_copy(buffer_t *_dest, size_t dest_pos,
const buffer_t *_src, size_t src_pos, size_t copy_size) const buffer_t *_src, size_t src_pos, size_t copy_size)
{ {
struct real_buffer *dest = (struct real_buffer *)_dest; struct real_buffer *dest = container_of(_dest, struct real_buffer, buf);
const struct real_buffer *src = (const struct real_buffer *)_src; const struct real_buffer *src =
container_of(_src, const struct real_buffer, buf);
size_t max_size; size_t max_size;
i_assert(src_pos <= src->used); i_assert(src_pos <= src->used);
max_size = src->used - src_pos; max_size = src->used - src_pos;
if (copy_size > max_size) if (copy_size > max_size)
copy_size = max_size; copy_size = max_size;
buffer_check_limits(dest, dest_pos, copy_size); buffer_check_limits(dest, dest_pos, copy_size);
i_assert(src->r_buffer != NULL); i_assert(src->r_buffer != NULL);
skipping to change at line 346 skipping to change at line 380
} }
void buffer_append_buf(buffer_t *dest, const buffer_t *src, void buffer_append_buf(buffer_t *dest, const buffer_t *src,
size_t src_pos, size_t copy_size) size_t src_pos, size_t copy_size)
{ {
buffer_copy(dest, dest->used, src, src_pos, copy_size); buffer_copy(dest, dest->used, src, src_pos, copy_size);
} }
void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size) void *buffer_get_space_unsafe(buffer_t *_buf, size_t pos, size_t size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
buffer_check_limits(buf, pos, size); buffer_check_limits(buf, pos, size);
return buf->w_buffer + pos; return buf->w_buffer + pos;
} }
void *buffer_append_space_unsafe(buffer_t *buf, size_t size) void *buffer_append_space_unsafe(buffer_t *buf, size_t size)
{ {
/* NOTE: can't use buffer_check_append_limits() here because it doesn't
guarantee that the buffer is zero-filled. */
return buffer_get_space_unsafe(buf, buf->used, size); return buffer_get_space_unsafe(buf, buf->used, size);
} }
void *buffer_get_modifiable_data(const buffer_t *_buf, size_t *used_size_r) void *buffer_get_modifiable_data(const buffer_t *_buf, size_t *used_size_r)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, const struct real_buffer, buf);
if (used_size_r != NULL) if (used_size_r != NULL)
*used_size_r = buf->used; *used_size_r = buf->used;
i_assert(buf->used == 0 || buf->w_buffer != NULL); i_assert(buf->used == 0 || buf->w_buffer != NULL);
return buf->w_buffer; return buf->w_buffer;
} }
void buffer_set_used_size(buffer_t *_buf, size_t used_size) void buffer_set_used_size(buffer_t *_buf, size_t used_size)
{ {
struct real_buffer *buf = (struct real_buffer *)_buf; struct real_buffer *buf = container_of(_buf, struct real_buffer, buf);
i_assert(used_size <= buf->alloc); i_assert(used_size <= buf->alloc);
if (buf->used > buf->dirty) if (buf->used > buf->dirty)
buf->dirty = buf->used; buf->dirty = buf->used;
buf->used = used_size; buf->used = used_size;
} }
size_t buffer_get_size(const buffer_t *_buf) size_t buffer_get_size(const buffer_t *_buf)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, const struct real_buffer, buf);
return buf->alloc; return buf->alloc;
} }
size_t buffer_get_writable_size(const buffer_t *_buf) size_t buffer_get_writable_size(const buffer_t *_buf)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, const struct real_buffer, buf);
if (!buf->dynamic || buf->alloc == 0)
return buf->alloc;
/* we reserve +1 for str_c() NUL in buffer_check_limits(), so don't /* Use buf->writable_size instead of buf->alloc to reserve +1 for
include that in our return value. otherwise the caller might str_c() NUL in buffer_check_limits(). Otherwise the caller might
increase the buffer's alloc size unnecessarily when it just wants increase the buffer's alloc size unnecessarily when it just wants
to access the entire buffer. */ to access the entire buffer. */
return buf->alloc-1; return buf->writable_size;
} }
size_t buffer_get_avail_size(const buffer_t *_buf) size_t buffer_get_avail_size(const buffer_t *_buf)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, const struct real_buffer, buf);
i_assert(buf->alloc >= buf->used); i_assert(buf->alloc >= buf->used);
return ((buf->dynamic ? SIZE_MAX : buf->alloc) - buf->used); return ((buf->dynamic ? SIZE_MAX : buf->alloc) - buf->used);
} }
bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2) bool buffer_cmp(const buffer_t *buf1, const buffer_t *buf2)
{ {
if (buf1->used != buf2->used) if (buf1->used != buf2->used)
return FALSE; return FALSE;
if (buf1->used == 0) if (buf1->used == 0)
return TRUE; return TRUE;
return memcmp(buf1->data, buf2->data, buf1->used) == 0; return memcmp(buf1->data, buf2->data, buf1->used) == 0;
} }
void buffer_verify_pool(buffer_t *_buf) void buffer_verify_pool(buffer_t *_buf)
{ {
const struct real_buffer *buf = (const struct real_buffer *)_buf; const struct real_buffer *buf =
container_of(_buf, struct real_buffer, buf);
void *ret; void *ret;
if (buf->pool != NULL && buf->pool->datastack_pool && buf->alloc > 0) { if (buf->pool != NULL && buf->pool->datastack_pool && buf->alloc > 0) {
/* this doesn't really do anything except verify the /* this doesn't really do anything except verify the
stack frame */ stack frame */
ret = p_realloc(buf->pool, buf->w_buffer, ret = p_realloc(buf->pool, buf->w_buffer,
buf->alloc, buf->alloc); buf->alloc, buf->alloc);
i_assert(ret == buf->w_buffer); i_assert(ret == buf->w_buffer);
} }
} }
 End of changes. 37 change blocks. 
45 lines changed or deleted 83 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)