diff options
Diffstat (limited to 'src/buffer.c')
-rw-r--r-- | src/buffer.c | 93 |
1 files changed, 37 insertions, 56 deletions
diff --git a/src/buffer.c b/src/buffer.c index 4efa97b..80ca49a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5,6 +5,7 @@ #include <stdio.h> #include <stdlib.h> #include <stdint.h> +#include <limits.h> #include "config.h" #include "cmark_ctype.h" @@ -28,68 +29,48 @@ void cmark_strbuf_init(cmark_strbuf *buf, bufsize_t initial_size) { cmark_strbuf_grow(buf, initial_size); } -void cmark_strbuf_overflow_err() { - fprintf(stderr, "String buffer overflow"); - abort(); +static CMARK_INLINE bool S_strbuf_grow_by(cmark_strbuf *buf, bufsize_t add) { + return cmark_strbuf_grow(buf, buf->size + add); } -static CMARK_INLINE void S_strbuf_grow_by(cmark_strbuf *buf, size_t add) { - size_t target_size = (size_t)buf->size + add; - - if (target_size < add /* Integer overflow. */ - || target_size > BUFSIZE_MAX /* Truncation overflow. */ - ) { - cmark_strbuf_overflow_err(); - return; /* unreachable */ - } - - if ((bufsize_t)target_size >= buf->asize) - cmark_strbuf_grow(buf, (bufsize_t)target_size); -} +#if BUFSIZE_MAX > (SIZE_MAX / 4) +# error "unsafe value for BUFSIZE_MAX" +#endif -void cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { - unsigned char *new_ptr; +bool cmark_strbuf_grow(cmark_strbuf *buf, bufsize_t target_size) { + assert(target_size > 0); if (target_size < buf->asize) - return; - - if (buf->asize == 0) { - new_ptr = NULL; - } else { - new_ptr = buf->ptr; - } + return true; + + /* + * Do not allow string buffers to grow past this "safe" value. + * + * Note that this is a soft cap to prevent unbounded memory growth: + * in practice, the buffer can get larger than this value because we + * overgrow it by 50% + * + * Note that there are no overflow checks for the realloc because + * the value of BUFSIZE_MAX is always assured to be impossible + * to overflow on both 32 and 64 bit systems, since it will never + * be larger than 1/4th of our address space. + */ + if (target_size > BUFSIZE_MAX) + return false; /* Oversize the buffer by 50% to guarantee amortized linear time * complexity on append operations. */ - size_t new_size = (size_t)target_size + (size_t)target_size / 2; - - /* Account for terminating null byte. */ + bufsize_t new_size = target_size + target_size / 2; new_size += 1; - - /* round allocation up to multiple of 8 */ new_size = (new_size + 7) & ~7; - if (new_size < (size_t)target_size /* Integer overflow. */ - || new_size > BUFSIZE_MAX /* Truncation overflow. */ - ) { - if (target_size >= BUFSIZE_MAX) { - /* No space for terminating null byte. */ - cmark_strbuf_overflow_err(); - return; /* unreachable */ - } - /* Oversize by the maximum possible amount. */ - new_size = BUFSIZE_MAX; - } + unsigned char *new_ptr = realloc(buf->asize ? buf->ptr : NULL, new_size); + if (!new_ptr) + return false; - new_ptr = (unsigned char *)realloc(new_ptr, new_size); - - if (!new_ptr) { - perror("realloc in cmark_strbuf_grow"); - abort(); - } - - buf->asize = (bufsize_t)new_size; + buf->asize = new_size; buf->ptr = new_ptr; + return true; } bufsize_t cmark_strbuf_len(const cmark_strbuf *buf) { return buf->size; } @@ -117,8 +98,8 @@ void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, cmark_strbuf_clear(buf); } else { if (data != buf->ptr) { - if (len >= buf->asize) - cmark_strbuf_grow(buf, len); + if (len >= buf->asize && !cmark_strbuf_grow(buf, len)) + return; memmove(buf->ptr, data, len); } buf->size = len; @@ -128,21 +109,21 @@ void cmark_strbuf_set(cmark_strbuf *buf, const unsigned char *data, void cmark_strbuf_sets(cmark_strbuf *buf, const char *string) { cmark_strbuf_set(buf, (const unsigned char *)string, - string ? cmark_strbuf_safe_strlen(string) : 0); + string ? strlen(string) : 0); } void cmark_strbuf_putc(cmark_strbuf *buf, int c) { - S_strbuf_grow_by(buf, 1); + if (!S_strbuf_grow_by(buf, 1)) + return; buf->ptr[buf->size++] = (unsigned char)(c & 0xFF); buf->ptr[buf->size] = '\0'; } void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, bufsize_t len) { - if (len <= 0) + if (len <= 0 || !S_strbuf_grow_by(buf, len)) return; - S_strbuf_grow_by(buf, len); memmove(buf->ptr + buf->size, data, len); buf->size += len; buf->ptr[buf->size] = '\0'; @@ -150,7 +131,7 @@ void cmark_strbuf_put(cmark_strbuf *buf, const unsigned char *data, void cmark_strbuf_puts(cmark_strbuf *buf, const char *string) { cmark_strbuf_put(buf, (const unsigned char *)string, - cmark_strbuf_safe_strlen(string)); + strlen(string)); } void cmark_strbuf_copy_cstr(char *data, bufsize_t datasize, |