From c1fa521b58f5c6c21066424403cdb5fdc9ea4ad2 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 6 Jan 2018 13:25:45 -0500 Subject: [PATCH] Add buffer overflow detection to the buffer class when the inherent 2GB limit is reached for a single buffer. --- core/buffer.c | 59 ++++++++++++++++-------------- core/io.c | 93 ++++++++++++++++++++++++++++++++++++++--------- core/util.h | 6 +++ include/dst/dst.h | 14 +++---- 4 files changed, 120 insertions(+), 52 deletions(-) diff --git a/core/buffer.c b/core/buffer.c index 806cef71..705029e0 100644 --- a/core/buffer.c +++ b/core/buffer.c @@ -64,7 +64,11 @@ void dst_buffer_ensure(DstBuffer *buffer, int32_t capacity) { /* Adds capacity for enough extra bytes to the buffer. Ensures that the * next n bytes pushed to the buffer will not cause a reallocation */ -void dst_buffer_extra(DstBuffer *buffer, int32_t n) { +int dst_buffer_extra(DstBuffer *buffer, int32_t n) { + /* Check for buffer overflow */ + if ((int64_t)n + buffer->count > INT32_MAX) { + return -1; + } int32_t new_size = buffer->count + n; if (new_size > buffer->capacity) { int32_t new_capacity = new_size * 2; @@ -75,55 +79,55 @@ void dst_buffer_extra(DstBuffer *buffer, int32_t n) { buffer->data = new_data; buffer->capacity = new_capacity; } -} - -/* Push multiple bytes into the buffer */ -void dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t length) { - int32_t new_size = buffer->count + length; - dst_buffer_ensure(buffer, new_size); - memcpy(buffer->data + buffer->count, string, length); - buffer->count = new_size; + return 0; } /* Push a cstring to buffer */ -void dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring) { +int dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring) { int32_t len = 0; while (cstring[len]) ++len; - dst_buffer_push_bytes(buffer, (const uint8_t *) cstring, len); + return dst_buffer_push_bytes(buffer, (const uint8_t *) cstring, len); +} + +/* Push multiple bytes into the buffer */ +int dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t length) { + if (dst_buffer_extra(buffer, length)) return -1; + memcpy(buffer->data + buffer->count, string, length); + buffer->count += length; + return 0; } /* Push a single byte to the buffer */ -void dst_buffer_push_u8(DstBuffer *buffer, uint8_t byte) { - int32_t new_size = buffer->count + 1; - dst_buffer_ensure(buffer, new_size); +int dst_buffer_push_u8(DstBuffer *buffer, uint8_t byte) { + if (dst_buffer_extra(buffer, 1)) return -1; buffer->data[buffer->count] = byte; - buffer->count = new_size; + buffer->count++; + return 0; } /* Push a 16 bit unsigned integer to the buffer */ -void dst_buffer_push_u16(DstBuffer *buffer, uint16_t x) { - int32_t new_size = buffer->count + 1; - dst_buffer_ensure(buffer, new_size); +int dst_buffer_push_u16(DstBuffer *buffer, uint16_t x) { + if (dst_buffer_extra(buffer, 2)) return -1; buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; - buffer->count = new_size; + buffer->count += 2; + return 0; } /* Push a 32 bit unsigned integer to the buffer */ -void dst_buffer_push_u32(DstBuffer *buffer, uint32_t x) { - int32_t new_size = buffer->count + 4; - dst_buffer_ensure(buffer, new_size); +int dst_buffer_push_u32(DstBuffer *buffer, uint32_t x) { + if (dst_buffer_extra(buffer, 4)) return -1; buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; buffer->data[buffer->count + 2] = (x >> 16) & 0xFF; buffer->data[buffer->count + 3] = (x >> 24) & 0xFF; - buffer->count = new_size; + buffer->count += 4; + return 0; } /* Push a 64 bit unsigned integer to the buffer */ -void dst_buffer_push_u64(DstBuffer *buffer, uint64_t x) { - int32_t new_size = buffer->count + 8; - dst_buffer_ensure(buffer, new_size); +int dst_buffer_push_u64(DstBuffer *buffer, uint64_t x) { + if (dst_buffer_extra(buffer, 8)) return -1; buffer->data[buffer->count] = x & 0xFF; buffer->data[buffer->count + 1] = (x >> 8) & 0xFF; buffer->data[buffer->count + 2] = (x >> 16) & 0xFF; @@ -132,5 +136,6 @@ void dst_buffer_push_u64(DstBuffer *buffer, uint64_t x) { buffer->data[buffer->count + 5] = (x >> 40) & 0xFF; buffer->data[buffer->count + 6] = (x >> 48) & 0xFF; buffer->data[buffer->count + 7] = (x >> 56) & 0xFF; - buffer->count = new_size; + buffer->count += 8; + return 0; } diff --git a/core/io.c b/core/io.c index 80e1dda9..4c1d95e1 100644 --- a/core/io.c +++ b/core/io.c @@ -6,7 +6,7 @@ DstAbstractType dst_stl_filetype = { NULL }; -/* Open a a file and return a userdata wrapper arounf the C file API. */ +/* Open a a file and return a userdata wrapper around the C file API. */ int dst_stl_open(int32_t argn, Dst *argv, Dst *ret) { if (argn < 2) { *ret = dst_cstringv("expected at least 2 arguments"); @@ -17,47 +17,104 @@ int dst_stl_open(int32_t argn, Dst *argv, Dst *ret) { FILE *f; FILE **fp; f = fopen((const char *)fname, (const char *)fmode); - if (!f) - dst_c_throwc(vm, "could not open file"); - fp = dst_userdata(vm, sizeof(FILE *), &dst_stl_filetype); + if (!f) { + dst_c_throwc("could not open file"); + return 1; + } + fp = dst_astract(sizeof(FILE *), &dst_stl_filetype); *fp = f; - dst_c_return(vm, dst_wrap_userdata(fp)); + *ret = dst_wrap_abstract(fp); + return 0; +} + +/* Check file argument */ +static FILE **checkfile(int32_t argn, Dst *argv, Dst *ret, int32_t n) { + FILE **fp; + if (n >= argn) { + *ret = dst_cstringv("expected stl.file"); + return NULL; + } + if (!dst_checktype(argv[n], DST_ABSTRACT)) { + *ret = dst_cstringv("expected stl.file"); + return NULL; + } + fp = (FILE **) dst_unwrap_abstract(argv[n]); + if (dst_abstract_type(fp) != &dst_stl_filetype) { + *ret = dst_cstringv("expected stl.file"); + return NULL; + } + return fp; +} + +/* Check buffer argument */ +static DstBuffer *checkbuffer(int32_t argn, Dst *argv, Dst *ret, int32_t n, int optional) { + if (optional && n == argn) { + return dst_buffer(0); + } + if (n >= argn) { + *ret = dst_cstringv("expected buffer"); + return NULL; + } + if (!dst_checktype(argv[n], DST_BUFFER)) { + *ret = dst_cstringv("expected buffer"); + return NULL; + } + return dst_unwrap_abstract(argv[n]); } /* Read an entire file into memory */ -int dst_stl_slurp(Dst *vm) { +int dst_stl_slurp(int32_t argn, Dst *argv, Dst *ret) { DstBuffer *b; long fsize; FILE *f; - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - if (!dst_check_buffer(vm, 1, &b)) b = dst_buffer(vm, 10); + FILE **fp = checkfile(argn, argv, ret, 0); + if (!fp) return 1; + b = checkbuffer(argn, argv, ret, 1, 1); + if (!b) return 1; f = *fp; /* Read whole file */ fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); + if (fsize > INT32_MAX || fsize + b->count > INT32_MAX) { + *ret = dst_cstringv("buffer overflow"); + return 1; + } /* Ensure buffer size */ - dst_buffer_ensure(vm, b, b->count + fsize); + dst_buffer_extra(b, fsize); fread((char *)(b->data + b->count), fsize, 1, f); b->count += fsize; - dst_c_return(vm, dst_wrap_buffer(b)); + /* return */ + *ret = dst_wrap_buffer(b); + return 0; } /* Read a certain number of bytes into memory */ int dst_stl_read(Dst *vm) { DstBuffer *b; FILE *f; - int64_t len; - FILE **fp = dst_check_userdata(vm, 0, &dst_stl_filetype); - if (fp == NULL) dst_c_throwc(vm, "expected file"); - if (!(dst_check_integer(vm, 1, &len))) dst_c_throwc(vm, "expected integer"); - if (!dst_check_buffer(vm, 2, &b)) b = dst_buffer(vm, 10); + int32_t len; + FILE **fp = checkfile(argn, argv, ret, 0); + if (!fp) return 1; + if (!dst_checktype(argv[1], DST_INTEGER)) { + *ret = dst_cstringv("expected positive integer"); + return 1; + } + len = dst_unwrap_integer(argv[1]); + if (len < 0) { + *ret = dst_cstringv("expected positive integer"); + return 1; + } + b = checkbuffer(argn, argv, ret, 2, 1); + if (!b) return 1; + f = *fp; /* Ensure buffer size */ - dst_buffer_ensure(vm, b, b->count + len); + if (len + bcount + dst_buffer_extra(b, len); b->count += fread((char *)(b->data + b->count), len, 1, f) * len; - dst_c_return(vm, dst_wrap_buffer(b)); + *ret = dst_wrap_buffer(b); + return 0; } /* Write bytes to a file */ diff --git a/core/util.h b/core/util.h index b9e994ec..4bc51690 100644 --- a/core/util.h +++ b/core/util.h @@ -27,6 +27,12 @@ /* Utils internal to dst. */ +/* Type check Dst C Function arguments */ +typedef struct DstCSig { + int32_t flags; + DstAbstractType abs; +} DstCSig; + /* Utils */ extern const char dst_base64[65]; int32_t dst_array_calchash(const Dst *array, int32_t len); diff --git a/include/dst/dst.h b/include/dst/dst.h index a76a4528..3b173a53 100644 --- a/include/dst/dst.h +++ b/include/dst/dst.h @@ -47,13 +47,13 @@ DstBuffer *dst_buffer(int32_t capacity); DstBuffer *dst_buffer_init(DstBuffer *buffer, int32_t capacity); void dst_buffer_deinit(DstBuffer *buffer); void dst_buffer_ensure(DstBuffer *buffer, int32_t capacity); -void dst_buffer_extra(DstBuffer *buffer, int32_t n); -void dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t len); -void dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring); -void dst_buffer_push_u8(DstBuffer *buffer, uint8_t x); -void dst_buffer_push_u16(DstBuffer *buffer, uint16_t x); -void dst_buffer_push_u32(DstBuffer *buffer, uint32_t x); -void dst_buffer_push_u64(DstBuffer *buffer, uint64_t x); +int dst_buffer_extra(DstBuffer *buffer, int32_t n); +int dst_buffer_push_bytes(DstBuffer *buffer, const uint8_t *string, int32_t len); +int dst_buffer_push_cstring(DstBuffer *buffer, const char *cstring); +int dst_buffer_push_u8(DstBuffer *buffer, uint8_t x); +int dst_buffer_push_u16(DstBuffer *buffer, uint16_t x); +int dst_buffer_push_u32(DstBuffer *buffer, uint32_t x); +int dst_buffer_push_u64(DstBuffer *buffer, uint64_t x); /* Tuple */ #define dst_tuple_raw(t) ((int32_t *)(t) - 2)