From 0f6b7556c039e7a9cfef975bf027f5a7d3eb6c57 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 18 May 2021 18:27:58 +0100 Subject: [PATCH] Limit oc_text_buf to 16MiB, start adding test cases We really ought to unit test this a lot harder. This is a start... Signed-off-by: David Woodhouse --- tests/Makefile.am | 2 +- tests/buftest.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ textbuf.c | 38 +++++++------ 3 files changed, 157 insertions(+), 16 deletions(-) create mode 100644 tests/buftest.c diff --git a/tests/Makefile.am b/tests/Makefile.am index a975cc5c..9e06b5db 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -143,7 +143,7 @@ TESTS_ENVIRONMENT = srcdir="$(srcdir)" \ EXEEXT=$(EXEEXT) \ LSAN_OPTIONS=suppressions=suppressions.lsan -C_TESTS = lzstest seqtest +C_TESTS = lzstest seqtest buftest if OPENCONNECT_WIN32 C_TESTS += list-taps diff --git a/tests/buftest.c b/tests/buftest.c new file mode 100644 index 00000000..b3148ad8 --- /dev/null +++ b/tests/buftest.c @@ -0,0 +1,133 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2021 David Woodhouse. + * + * Author: David Woodhouse + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include + +#include +#include +#include + +#define __OPENCONNECT_INTERNAL_H__ + +/* I always coded as if it worked like this. Now it does. */ +#define realloc_inplace(p, size) do { \ + void *__realloc_old = p; \ + p = realloc(p, size); \ + if (size && !p) \ + free(__realloc_old); \ + } while (0) + +struct oc_packed_uint32_t { + uint32_t d; +} __attribute__((packed)); +struct oc_packed_uint16_t { + uint16_t d; +} __attribute__((packed)); + +static inline uint32_t load_be32(const void *_p) +{ + const struct oc_packed_uint32_t *p = _p; + return ntohl(p->d); +} + +static inline uint16_t load_be16(const void *_p) +{ + const struct oc_packed_uint16_t *p = _p; + return ntohs(p->d); +} + +static inline void store_be32(void *_p, uint32_t d) +{ + struct oc_packed_uint32_t *p = _p; + p->d = htonl(d); +} + +static inline void store_be16(void *_p, uint16_t d) +{ + struct oc_packed_uint16_t *p = _p; + p->d = htons(d); +} + +static inline uint32_t load_le32(const void *_p) +{ + const unsigned char *p = _p; + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} + +static inline uint16_t load_le16(const void *_p) +{ + const unsigned char *p = _p; + return p[0] | (p[1] << 8); +} + +static inline void store_le32(void *_p, uint32_t d) +{ + unsigned char *p = _p; + p[0] = d; + p[1] = d >> 8; +} + +static inline void store_le16(void *_p, uint16_t d) +{ + unsigned char *p = _p; + p[0] = d; + p[1] = d >> 8; + p[2] = d >> 16; + p[3] = d >> 24; +} + +struct oc_text_buf { + char *data; + int pos; + int buf_len; + int error; +}; + +#include "../textbuf.c" + +#define assert(x) if (!(x)) { \ + fprintf(stderr, "assert(%s) failed at line %d\n", #x, __LINE__); \ + exit(1); } + + +static char testbytes[OC_BUF_MAX]; + +int main(void) +{ + struct oc_text_buf *buf = NULL; + + assert(buf_error(buf) == -ENOMEM); + + buf = buf_alloc(); + assert(!buf_error(buf)); + + int len = (OC_BUF_MAX - 1) / 4 * 3; + buf_append_base64(buf, testbytes, len, 0); + assert(!buf_error(buf)); + assert(buf->pos == OC_BUF_MAX - 4); + + buf_truncate(buf); + len++; + buf_append_base64(buf, testbytes, len, 0); + assert(buf_error(buf) == -E2BIG); + + buf->error = 0; + buf_append_base64(buf, testbytes, -1, 0); + assert(buf_error(buf) == -EINVAL); + + return 0; +} diff --git a/textbuf.c b/textbuf.c index 129475e5..ed9e2e8c 100644 --- a/textbuf.c +++ b/textbuf.c @@ -19,18 +19,20 @@ #include #include -#include -#include #include #include #include #include #include +#include +#include +#include #include #include "openconnect-internal.h" #define BUF_CHUNK_SIZE 4096 +#define OC_BUF_MAX ((unsigned)(16*1024*1024)) struct oc_text_buf *buf_alloc(void) { @@ -42,6 +44,18 @@ int buf_error(struct oc_text_buf *buf) return buf ? buf->error : -ENOMEM; } + +void buf_truncate(struct oc_text_buf *buf) +{ + if (!buf) + return; + + if (buf->data) + memset(buf->data, 0, buf->pos); + + buf->pos = 0; +} + int buf_free(struct oc_text_buf *buf) { int error = buf_error(buf); @@ -56,17 +70,6 @@ int buf_free(struct oc_text_buf *buf) return error; } -void buf_truncate(struct oc_text_buf *buf) -{ - if (!buf) - return; - - if (buf->data) - memset(buf->data, 0, buf->pos); - - buf->pos = 0; -} - int buf_ensure_space(struct oc_text_buf *buf, int len) { unsigned int new_buf_len; @@ -79,7 +82,7 @@ int buf_ensure_space(struct oc_text_buf *buf, int len) if (new_buf_len <= buf->buf_len) return 0; - if (new_buf_len > INT_MAX) { + if (new_buf_len > OC_BUF_MAX) { buf->error = -E2BIG; return buf->error; } else { @@ -416,11 +419,16 @@ void buf_append_base64(struct oc_text_buf *buf, const void *bytes, int len, if (!buf || buf->error) return; + if (len < 0) { + buf->error = -EINVAL; + return; + } + unsigned int needed = ((len + 2u) / 3) * 4 + 1; if (line_len) needed += needed / line_len; - if (needed >= (unsigned)(INT_MAX - buf->pos)) { + if (needed >= (unsigned)(OC_BUF_MAX - buf->pos)) { buf->error = -E2BIG; return; } -- 2.49.0