summaryrefslogtreecommitdiff
path: root/src/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer.c')
-rw-r--r--src/buffer.c93
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,