Mercurial > dovecot > core-2.3
changeset 26310:e5643ff5adc1
lib: base64 - Fix incremental/streaming Base64 encoding with CRLF line endings.
Line lengths became inconsistent due to a design error. The encoding itself
remained valid. The linefeed is no longer appended to the write buffer when the
destination buffer is full. Rather, a flag is set that makes the encoder emit
the linefeed immediately next time the encoder is called with more buffer
space. Appending it to the write buffer was wrong; it in fact needs to be
prepended and in that case a flag is more efficient.
author | Stephan Bosch <stephan.bosch@open-xchange.com> |
---|---|
date | Wed, 04 Sep 2019 22:44:03 +0200 |
parents | 379a7356c19c |
children | cef3ed72c74f |
files | src/lib/base64.c src/lib/base64.h |
diffstat | 2 files changed, 48 insertions(+), 17 deletions(-) [+] |
line wrap: on
line diff
--- a/src/lib/base64.c Thu Sep 05 01:17:55 2019 +0200 +++ b/src/lib/base64.c Wed Sep 04 22:44:03 2019 +0200 @@ -131,6 +131,9 @@ out_size += lines * (crlf ? 2 : 1); } + if (enc->pending_lf) + out_size++; + return out_size; } @@ -145,6 +148,8 @@ unsigned char *start, *ptr, *end; size_t src_pos; + i_assert(!enc->pending_lf); + /* determine how much we can write in destination buffer */ if (dst_avail == 0) { *src_pos_r = 0; @@ -295,6 +300,7 @@ const void *src, size_t src_size, size_t *src_pos_r, buffer_t *dest) { + bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); const unsigned char *src_c, *src_p; size_t src_pos; @@ -309,6 +315,16 @@ if (dst_avail == 0) break; + /* Emit pending newline immediately */ + if (enc->pending_lf) { + i_assert(crlf); + buffer_append_c(dest, '\n'); + enc->pending_lf = FALSE; + dst_avail--; + if (dst_avail == 0) + break; + } + i_assert(enc->max_line_len > 0); i_assert(enc->cur_line_len <= enc->max_line_len); line_avail = I_MIN(enc->max_line_len - enc->cur_line_len, @@ -335,15 +351,16 @@ if (dst_avail == 0) break; - i_assert(enc->w_buf_len < sizeof(enc->w_buf)); if (src_size > 0 && enc->cur_line_len == enc->max_line_len) { - if (HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF)) { - if (dst_avail >= 2) + if (crlf) { + if (dst_avail >= 2) { + /* emit the full CRLF sequence */ buffer_append(dest, "\r\n", 2); - else { + } else { + /* emit CR */ buffer_append_c(dest, '\r'); - enc->w_buf[enc->w_buf_len] = '\n'; - enc->w_buf_len++; + /* remember the LF */ + enc->pending_lf = TRUE; } } else { buffer_append_c(dest, '\n'); @@ -366,7 +383,7 @@ bool crlf = HAS_ALL_BITS(enc->flags, BASE64_ENCODE_FLAG_CRLF); bool padding = HAS_NO_BITS(enc->flags, BASE64_ENCODE_FLAG_NO_PADDING); unsigned char *ptr, *end; - size_t dst_avail, line_avail, write; + size_t dst_avail, line_avail, write_full, write; unsigned int w_buf_pos = 0; dst_avail = 0; @@ -375,7 +392,7 @@ i_assert(!enc->finished); - if (enc->w_buf_len > 0) { + if (enc->w_buf_len > 0 || enc->pending_lf) { if (dst_avail == 0) return FALSE; i_assert(enc->w_buf_len <= sizeof(enc->w_buf)); @@ -412,39 +429,49 @@ } enc->sub_pos = 0; - write = enc->w_buf_len; + write_full = write = enc->w_buf_len; + if (enc->pending_lf) + write_full++; if (enc->max_line_len < SIZE_MAX && line_avail < write) { unsigned int lines; lines = I_MAX((write - line_avail) / enc->max_line_len, 1); - write += lines * (crlf ? 2 : 1); + write_full += lines * (crlf ? 2 : 1); } else { line_avail = write; } - if (write == 0) { + if (write_full == 0) { enc->finished = TRUE; return TRUE; } i_assert(dest != NULL); - if (write > dst_avail) - write = dst_avail; + if (write_full > dst_avail) + write_full = dst_avail; - ptr = buffer_append_space_unsafe(dest, write); - end = ptr + write; + ptr = buffer_append_space_unsafe(dest, write_full); + end = ptr + write_full; + if (enc->pending_lf) { + ptr[0] = '\n'; + dst_avail--; + ptr++; + enc->pending_lf = FALSE; + } + if (line_avail > dst_avail) + line_avail = dst_avail; if (line_avail > 0) { memcpy(ptr, enc->w_buf, line_avail); ptr += line_avail; w_buf_pos += line_avail; } while (ptr < end && w_buf_pos < enc->w_buf_len) { + enc->cur_line_len = 0; if (crlf) { ptr[0] = '\r'; ptr++; if (ptr == end) { - i_assert(enc->w_buf_len < sizeof(enc->w_buf)); - enc->w_buf[enc->w_buf_len++] = '\n'; + enc->pending_lf = TRUE; break; } } @@ -458,6 +485,7 @@ memcpy(ptr, &enc->w_buf[w_buf_pos], write); ptr += write; w_buf_pos += write; + enc->cur_line_len += write; i_assert(ptr <= end); } i_assert(ptr == end); @@ -466,6 +494,8 @@ memmove(enc->w_buf, enc->w_buf + w_buf_pos, enc->w_buf_len); return FALSE; } + if (enc->pending_lf) + return FALSE; enc->finished = TRUE; return TRUE; }