diff options
Diffstat (limited to 'src/or/buffers.c')
-rw-r--r-- | src/or/buffers.c | 191 |
1 files changed, 160 insertions, 31 deletions
diff --git a/src/or/buffers.c b/src/or/buffers.c index 89382d1d8e..12a6c0239b 100644 --- a/src/or/buffers.c +++ b/src/or/buffers.c @@ -1,7 +1,7 @@ /* Copyright (c) 2001 Matej Pfajfar. * Copyright (c) 2001-2004, Roger Dingledine. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. - * Copyright (c) 2007-2016, The Tor Project, Inc. */ + * Copyright (c) 2007-2017, The Tor Project, Inc. */ /* See LICENSE for licensing information */ /** @@ -83,7 +83,11 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, #define CHUNK_HEADER_LEN STRUCT_OFFSET(chunk_t, mem[0]) /* We leave this many NUL bytes at the end of the buffer. */ +#ifdef DISABLE_MEMORY_SENTINELS +#define SENTINEL_LEN 0 +#else #define SENTINEL_LEN 4 +#endif /* Header size plus NUL bytes at the end */ #define CHUNK_OVERHEAD (CHUNK_HEADER_LEN + SENTINEL_LEN) @@ -97,18 +101,22 @@ static int parse_socks_client(const uint8_t *data, size_t datalen, #define DEBUG_SENTINEL -#ifdef DEBUG_SENTINEL +#if defined(DEBUG_SENTINEL) && !defined(DISABLE_MEMORY_SENTINELS) #define DBG_S(s) s #else #define DBG_S(s) (void)0 #endif +#ifdef DISABLE_MEMORY_SENTINELS +#define CHUNK_SET_SENTINEL(chunk, alloclen) STMT_NIL +#else #define CHUNK_SET_SENTINEL(chunk, alloclen) do { \ uint8_t *a = (uint8_t*) &(chunk)->mem[(chunk)->memlen]; \ DBG_S(uint8_t *b = &((uint8_t*)(chunk))[(alloclen)-SENTINEL_LEN]); \ DBG_S(tor_assert(a == b)); \ memset(a,0,SENTINEL_LEN); \ } while (0) +#endif /** Return the next character in <b>chunk</b> onto which data can be appended. * If the chunk is full, this might be off the end of chunk->mem. */ @@ -281,6 +289,7 @@ buf_pullup(buf_t *buf, size_t bytes) } #ifdef TOR_UNIT_TESTS +/* Return the data from the first chunk of buf in cp, and its length in sz. */ void buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz) { @@ -292,6 +301,53 @@ buf_get_first_chunk_data(const buf_t *buf, const char **cp, size_t *sz) *sz = buf->head->datalen; } } + +/* Write sz bytes from cp into a newly allocated buffer buf. + * Returns NULL when passed a NULL cp or zero sz. + * Asserts on failure: only for use in unit tests. + * buf must be freed using buf_free(). */ +buf_t * +buf_new_with_data(const char *cp, size_t sz) +{ + /* Validate arguments */ + if (!cp || sz <= 0) { + return NULL; + } + + tor_assert(sz < SSIZE_T_CEILING); + + /* Allocate a buffer */ + buf_t *buf = buf_new_with_capacity(sz); + tor_assert(buf); + assert_buf_ok(buf); + tor_assert(!buf->head); + + /* Allocate a chunk that is sz bytes long */ + buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz)); + buf->tail = buf->head; + tor_assert(buf->head); + assert_buf_ok(buf); + tor_assert(buf_allocation(buf) >= sz); + + /* Copy the data and size the buffers */ + tor_assert(sz <= buf_slack(buf)); + tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head)); + memcpy(&buf->head->mem[0], cp, sz); + buf->datalen = sz; + buf->head->datalen = sz; + buf->head->data = &buf->head->mem[0]; + assert_buf_ok(buf); + + /* Make sure everything is large enough */ + tor_assert(buf_allocation(buf) >= sz); + tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf)); + /* Does the buffer implementation allocate more than the requested size? + * (for example, by rounding up). If so, these checks will fail. */ + tor_assert(buf_datalen(buf) == sz); + tor_assert(buf_slack(buf) == 0); + + return buf; +} #endif /** Remove the first <b>n</b> bytes from buf. */ @@ -562,6 +618,11 @@ read_to_buf(tor_socket_t s, size_t at_most, buf_t *buf, int *reached_eof, tor_assert(reached_eof); tor_assert(SOCKET_OK(s)); + if (BUG(buf->datalen >= INT_MAX)) + return -1; + if (BUG(buf->datalen >= INT_MAX - at_most)) + return -1; + while (at_most > total_read) { size_t readlen = at_most - total_read; chunk_t *chunk; @@ -619,6 +680,11 @@ read_to_buf_tls(tor_tls_t *tls, size_t at_most, buf_t *buf) check(); + if (BUG(buf->datalen >= INT_MAX)) + return -1; + if (BUG(buf->datalen >= INT_MAX - at_most)) + return -1; + while (at_most > total_read) { size_t readlen = at_most - total_read; chunk_t *chunk; @@ -813,6 +879,11 @@ write_to_buf(const char *string, size_t string_len, buf_t *buf) return (int)buf->datalen; check(); + if (BUG(buf->datalen >= INT_MAX)) + return -1; + if (BUG(buf->datalen >= INT_MAX - string_len)) + return -1; + while (string_len) { size_t copy; if (!buf->tail || !CHUNK_REMAINING_CAPACITY(buf->tail)) @@ -962,6 +1033,12 @@ move_buf_to_buf(buf_t *buf_out, buf_t *buf_in, size_t *buf_flushlen) /* We can do way better here, but this doesn't turn up in any profiles. */ char b[4096]; size_t cp, len; + + if (BUG(buf_out->datalen >= INT_MAX)) + return -1; + if (BUG(buf_out->datalen >= INT_MAX - *buf_flushlen)) + return -1; + len = *buf_flushlen; if (len > buf_in->datalen) len = buf_in->datalen; @@ -1090,6 +1167,52 @@ buf_find_string_offset(const buf_t *buf, const char *s, size_t n) return -1; } +/** + * Scan the HTTP headers in the <b>headerlen</b>-byte memory range at + * <b>headers</b>, looking for a "Content-Length" header. Try to set + * *<b>result_out</b> to the numeric value of that header if possible. + * Return -1 if the header was malformed, 0 if it was missing, and 1 if + * it was present and well-formed. + */ +STATIC int +buf_http_find_content_length(const char *headers, size_t headerlen, + size_t *result_out) +{ + const char *p, *newline; + char *len_str, *eos=NULL; + size_t remaining, result; + int ok; + *result_out = 0; /* The caller shouldn't look at this unless the + * return value is 1, but let's prevent confusion */ + +#define CONTENT_LENGTH "\r\nContent-Length: " + p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH); + if (p == NULL) + return 0; + + tor_assert(p >= headers && p < headers+headerlen); + remaining = (headers+headerlen)-p; + p += strlen(CONTENT_LENGTH); + remaining -= strlen(CONTENT_LENGTH); + + newline = memchr(p, '\n', remaining); + if (newline == NULL) + return -1; + + len_str = tor_memdup_nulterm(p, newline-p); + /* We limit the size to INT_MAX because other parts of the buffer.c + * code don't like buffers to be any bigger than that. */ + result = (size_t) tor_parse_uint64(len_str, 10, 0, INT_MAX, &ok, &eos); + if (eos && !tor_strisspace(eos)) { + ok = 0; + } else { + *result_out = result; + } + tor_free(len_str); + + return ok ? 1 : -1; +} + /** There is a (possibly incomplete) http statement on <b>buf</b>, of the * form "\%s\\r\\n\\r\\n\%s", headers, body. (body may contain NULs.) * If a) the headers include a Content-Length field and all bytes in @@ -1115,9 +1238,10 @@ fetch_from_buf_http(buf_t *buf, char **body_out, size_t *body_used, size_t max_bodylen, int force_complete) { - char *headers, *p; - size_t headerlen, bodylen, contentlen; + char *headers; + size_t headerlen, bodylen, contentlen=0; int crlf_offset; + int r; check(); if (!buf->head) @@ -1153,17 +1277,12 @@ fetch_from_buf_http(buf_t *buf, return -1; } -#define CONTENT_LENGTH "\r\nContent-Length: " - p = (char*) tor_memstr(headers, headerlen, CONTENT_LENGTH); - if (p) { - int i; - i = atoi(p+strlen(CONTENT_LENGTH)); - if (i < 0) { - log_warn(LD_PROTOCOL, "Content-Length is less than zero; it looks like " - "someone is trying to crash us."); - return -1; - } - contentlen = i; + r = buf_http_find_content_length(headers, headerlen, &contentlen); + if (r == -1) { + log_warn(LD_PROTOCOL, "Content-Length is bogus; maybe " + "someone is trying to crash us."); + return -1; + } else if (r == 1) { /* if content-length is malformed, then our body length is 0. fine. */ log_debug(LD_HTTP,"Got a contentlen of %d.",(int)contentlen); if (bodylen < contentlen) { @@ -1176,7 +1295,11 @@ fetch_from_buf_http(buf_t *buf, bodylen = contentlen; log_debug(LD_HTTP,"bodylen reduced to %d.",(int)bodylen); } + } else { + tor_assert(r == 0); + /* Leave bodylen alone */ } + /* all happy. copy into the appropriate places, and return 1 */ if (headers_out) { *headers_out = tor_malloc(headerlen+1); @@ -1196,7 +1319,7 @@ fetch_from_buf_http(buf_t *buf, /** * Wait this many seconds before warning the user about using SOCKS unsafely - * again (requires that WarnUnsafeSocks is turned on). */ + * again. */ #define SOCKS_WARN_INTERVAL 5 /** Warn that the user application has made an unsafe socks request using @@ -1208,9 +1331,6 @@ log_unsafe_socks_warning(int socks_protocol, const char *address, { static ratelim_t socks_ratelim = RATELIM_INIT(SOCKS_WARN_INTERVAL); - const or_options_t *options = get_options(); - if (! options->WarnUnsafeSocks) - return; if (safe_socks) { log_fn_ratelim(&socks_ratelim, LOG_WARN, LD_APP, "Your application (using socks%d to port %d) is giving " @@ -1966,13 +2086,13 @@ fetch_from_buf_line(buf_t *buf, char *data_out, size_t *data_len) } /** Compress on uncompress the <b>data_len</b> bytes in <b>data</b> using the - * zlib state <b>state</b>, appending the result to <b>buf</b>. If + * compression state <b>state</b>, appending the result to <b>buf</b>. If * <b>done</b> is true, flush the data in the state and finish the * compression/uncompression. Return -1 on failure, 0 on success. */ int -write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state, - const char *data, size_t data_len, - int done) +write_to_buf_compress(buf_t *buf, tor_compress_state_t *state, + const char *data, size_t data_len, + const int done) { char *next; size_t old_avail, avail; @@ -1986,22 +2106,31 @@ write_to_buf_zlib(buf_t *buf, tor_zlib_state_t *state, } next = CHUNK_WRITE_PTR(buf->tail); avail = old_avail = CHUNK_REMAINING_CAPACITY(buf->tail); - switch (tor_zlib_process(state, &next, &avail, &data, &data_len, done)) { - case TOR_ZLIB_DONE: + switch (tor_compress_process(state, &next, &avail, + &data, &data_len, done)) { + case TOR_COMPRESS_DONE: over = 1; break; - case TOR_ZLIB_ERR: + case TOR_COMPRESS_ERROR: return -1; - case TOR_ZLIB_OK: - if (data_len == 0) + case TOR_COMPRESS_OK: + if (data_len == 0) { + tor_assert_nonfatal(!done); over = 1; + } break; - case TOR_ZLIB_BUF_FULL: + case TOR_COMPRESS_BUFFER_FULL: if (avail) { - /* Zlib says we need more room (ZLIB_BUF_FULL). Start a new chunk - * automatically, whether were going to or not. */ + /* The compression module says we need more room + * (TOR_COMPRESS_BUFFER_FULL). Start a new chunk automatically, + * whether were going to or not. */ need_new_chunk = 1; } + if (data_len == 0 && !done) { + /* We've consumed all the input data, though, so there's no + * point in forging ahead right now. */ + over = 1; + } break; } buf->datalen += old_avail - avail; |