diff --git a/.gitignore b/.gitignore index 97d442eb02d69fc8d40e8a5da3fd7da7ac00f513..3a8eab22df6b6117a4ffb0f327cf0299dc61b5a4 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ test/connector_c/update test/connector_c/snap test/box/protocol test/box/connector +test/unit/base64 test/unit/queue test/unit/mhash test/unit/objc_finally diff --git a/cmake/BuildMisc.cmake b/cmake/BuildMisc.cmake index e8975a5021977408b31ff9de8445b63e513472c5..8484ee83d2565344acbc502f7a57f5196bde3884 100644 --- a/cmake/BuildMisc.cmake +++ b/cmake/BuildMisc.cmake @@ -6,6 +6,7 @@ macro(libmisc_build) ${PROJECT_SOURCE_DIR}/third_party/proctitle.c ${PROJECT_SOURCE_DIR}/third_party/qsort_arg.c ${PROJECT_SOURCE_DIR}/third_party/PMurHash.c + ${PROJECT_SOURCE_DIR}/third_party/base64.c ) if (NOT HAVE_MEMMEM) diff --git a/include/pickle.h b/include/pickle.h index 8f3e6340a10314bf9f5bc1eb52bae081ea8cadbf..3aed167642faf75ebdfd8030449d7d61159de72a 100644 --- a/include/pickle.h +++ b/include/pickle.h @@ -187,35 +187,6 @@ pick_field_u32(const void **data, const void *end) return *(uint32_t *) pick_str(data, end, size); } - - -static inline uint32_t -valid_tuple(const void *data, const void *end, uint32_t field_count) -{ - const void *start = data; - - for (int i = 0; i < field_count; i++) - pick_field(&data, end); - - return data - start; -} - -/** - * Calculate size for a specified fields range - * - * @returns size of fields data including size of varint data - */ -static inline size_t -tuple_range_size(const void **begin, const void *end, size_t count) -{ - const void *start = *begin; - while (*begin < end && count-- > 0) { - size_t len = load_varint32(begin); - *begin += len; - } - return *begin - start; -} - static inline size_t varint32_sizeof(uint32_t value) { diff --git a/src/box/box.m b/src/box/box.m index dddce327c6062ad532c2aa5bd6dc718c8c39ebac..50a2a0a878776564f8f91fc01cfe41987da23691 100644 --- a/src/box/box.m +++ b/src/box/box.m @@ -47,6 +47,7 @@ #include "port.h" #include "request.h" #include "txn.h" +#include <third_party/base64.h> static void process_replica(struct port *port, u32 op, const void *reqdata, u32 reqlen); @@ -250,6 +251,27 @@ recover_snap_row(const void *data) const struct box_snap_row *row = data; + if (valid_tuple(row->data, row->data + row->data_size, + row->tuple_size) != row->data_size) { + say_error("\n" + "********************************************\n" + "* Found a corrupted tuple in the snapshot! *\n" + "* This can be either due to a memory *\n" + "* corruption or a bug in the server. *\n" + "* The tuple can not be loaded. *\n" + "********************************************\n" + "Tuple data, BAS64 encoded: \n"); + + int base64_buflen = base64_bufsize(row->data_size); + char *base64_buf = (char *) malloc(base64_buflen); + int len = base64_encode((const char *) row->data, row->data_size, + base64_buf, base64_buflen); + write(STDERR_FILENO, base64_buf, len); + free(base64_buf); + + tnt_raise(IllegalParams, :"invalid tuple length"); + } + struct tuple *tuple = tuple_alloc(row->data_size); memcpy(tuple->data, row->data, row->data_size); tuple->field_count = row->tuple_size; diff --git a/src/box/tuple.h b/src/box/tuple.h index 32f1c709d460f207a04beb1c3d8ea88b5574bc76..a9c215e9ac7ba629c9af8495cdecf4caef2c143b 100644 --- a/src/box/tuple.h +++ b/src/box/tuple.h @@ -29,6 +29,7 @@ * SUCH DAMAGE. */ #include <util.h> +#include <pickle.h> struct tbuf; @@ -93,5 +94,28 @@ static inline size_t tuple_len(struct tuple *tuple) } void tuple_free(struct tuple *tuple); + +/** + * Calculate size for a specified fields range + * + * @returns size of fields data including size of varint data + */ +static inline size_t +tuple_range_size(const void **begin, const void *end, size_t count) +{ + const void *start = *begin; + while (*begin < end && count-- > 0) { + size_t len = load_varint32(begin); + *begin += len; + } + return *begin - start; +} + +static inline uint32_t +valid_tuple(const void *data, const void *end, uint32_t field_count) +{ + return tuple_range_size(&data, end, field_count); +} + #endif /* TARANTOOL_BOX_TUPLE_H_INCLUDED */ diff --git a/src/box/tuple.m b/src/box/tuple.m index 4c939f0dac38adfd145dce3a8c27df18883847f4..fc029099d7a6c3a4f2091e960352dc8f6fbc6ef5 100644 --- a/src/box/tuple.m +++ b/src/box/tuple.m @@ -28,7 +28,6 @@ */ #include "tuple.h" -#include <pickle.h> #include <salloc.h> #include "tbuf.h" diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 982a932569cd0c8fb395dc2bd97d74c336c9b395..beb7c8fac63018ba5a4c505ffdb2da9353a4d4f9 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -29,3 +29,5 @@ target_link_libraries(objc_catchcxx ${LIBOBJC_LIBRARIES} ${LUAJIT_LIB} -lm -pthr if (TARGET_OS_LINUX OR TARGET_OS_DEBIAN_FREEBSD) target_link_libraries(objc_catchcxx dl) endif() + +add_executable(base64 base64.c ${CMAKE_SOURCE_DIR}/third_party/base64.c) diff --git a/test/unit/base64.c b/test/unit/base64.c new file mode 100644 index 0000000000000000000000000000000000000000..dca11da881db7b082e8e25aa444514984cf65bdc --- /dev/null +++ b/test/unit/base64.c @@ -0,0 +1,40 @@ +#include <third_party/base64.h> +#include "unit.h" +#include <string.h> + + +static void +base64_test(const char *str) +{ + header(); + + int len = strlen(str); + int base64_buflen = len * 4/3 + 4; + char *base64_buf = malloc(base64_buflen); + char *strbuf = malloc(len + 1); + + int res = base64_encode(str, len, base64_buf, base64_buflen); + + fail_unless(strlen(base64_buf) == res); + + base64_decode(base64_buf, strlen(base64_buf), strbuf, len + 1); + + fail_unless(strcmp(str, strbuf) == 0); + + free(base64_buf); + free(strbuf); + + footer(); +} + +int main(int argc, char *argv[]) +{ + base64_test(""); + base64_test("a"); + base64_test("Something that doesn't fit into a single line, " + "something that doesn't fit into a single line, " + "something that doesn't fit into a single line, " + "something that doesn't fit into a single line, " + "something that doesn't fit into a single line. "); + base64_test("\001\002\003\004\005\006\253\254\255"); +} diff --git a/test/unit/base64.result b/test/unit/base64.result new file mode 100644 index 0000000000000000000000000000000000000000..de91f62a78d9d1b119ae8bcb05e460ef21e867a8 --- /dev/null +++ b/test/unit/base64.result @@ -0,0 +1,9 @@ + *** base64_test *** + *** base64_test: done *** + *** base64_test *** + *** base64_test: done *** + *** base64_test *** + *** base64_test: done *** + *** base64_test *** + *** base64_test: done *** + \ No newline at end of file diff --git a/test/unit/base64.test b/test/unit/base64.test new file mode 100644 index 0000000000000000000000000000000000000000..0ed8d1b67d7103560b29ab48c9c8becec98619b8 --- /dev/null +++ b/test/unit/base64.test @@ -0,0 +1 @@ +run_test("base64") diff --git a/test/unit/unit.h b/test/unit/unit.h index add8a8e33439c8d2ffa9bfd005980f4c00d442c1..4ab24ea935bb68e71501c21a0e111b734dd9757c 100644 --- a/test/unit/unit.h +++ b/test/unit/unit.h @@ -30,6 +30,7 @@ */ #include <unistd.h> #include <stdlib.h> /* exit() */ +#include <stdio.h> #define header() printf("\t*** %s ***\n", __func__) #define footer() printf("\t*** %s: done ***\n ", __func__) diff --git a/third_party/base64.c b/third_party/base64.c new file mode 100644 index 0000000000000000000000000000000000000000..5ec20929050f6101012f2c736864f00ef15e3491 --- /dev/null +++ b/third_party/base64.c @@ -0,0 +1,296 @@ +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include "third_party/base64.h" +/* + * This is part of the libb64 project, and has been placed in the + * public domain. For details, see + * http://sourceforge.net/projects/libb64 + */ + + +/* {{{ encode */ + +enum base64_encodestep { step_A, step_B, step_C }; + +struct base64_encodestate { + enum base64_encodestep step; + char result; + int stepcount; +}; + +static inline void +base64_encodestate_init(struct base64_encodestate *state) +{ + state->step = step_A; + state->result = 0; + state->stepcount = 0; +} + +static inline char +base64_encode_value(char value) +{ + static const char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + unsigned codepos = (unsigned) value; + if (codepos > sizeof(encoding) - 1) + return '='; + return encoding[codepos]; +} + +static int +base64_encode_block(const char *in_bin, int in_len, + char *out_base64, int out_len, + struct base64_encodestate *state) +{ + const char *const in_end = in_bin + in_len; + const char *in_pos = in_bin; + char *out_pos = out_base64; + char *out_end = out_base64 + out_len; + char result; + char fragment; + + result = state->result; + + switch (state->step) + { + while (1) + { + case step_A: + if (in_pos == in_end || out_pos >= out_end) { + state->result = result; + state->step = step_A; + return out_pos - out_base64; + } + fragment = *in_pos++; + result = (fragment & 0x0fc) >> 2; + *out_pos++ = base64_encode_value(result); + result = (fragment & 0x003) << 4; + case step_B: + if (in_pos == in_end || out_pos >= out_end) { + state->result = result; + state->step = step_B; + return out_pos - out_base64; + } + fragment = *in_pos++; + result |= (fragment & 0x0f0) >> 4; + *out_pos++ = base64_encode_value(result); + result = (fragment & 0x00f) << 2; + case step_C: + if (in_pos == in_end || out_pos + 2 >= out_end) { + state->result = result; + state->step = step_C; + return out_pos - out_base64; + } + fragment = *in_pos++; + result |= (fragment & 0x0c0) >> 6; + *out_pos++ = base64_encode_value(result); + result = (fragment & 0x03f) >> 0; + *out_pos++ = base64_encode_value(result); + + /* + * Each full step (A->B->C) yields + * 4 characters. + */ + if (++state->stepcount * 4 == BASE64_CHARS_PER_LINE) { + if (out_pos >= out_end) + return out_pos - out_base64; + *out_pos++ = '\n'; + state->stepcount = 0; + } + } + } + /* control should not reach here */ + return out_pos - out_base64; +} + +static int +base64_encode_blockend(char *out_base64, int out_len, + struct base64_encodestate *state) +{ + char *out_pos = out_base64; + char *out_end = out_base64 + out_len; + + switch (state->step) { + case step_B: + if (out_pos + 2 >= out_end) + return out_pos - out_base64; + *out_pos++ = base64_encode_value(state->result); + *out_pos++ = '='; + *out_pos++ = '='; + break; + case step_C: + if (out_pos + 1 >= out_end) + return out_pos - out_base64; + *out_pos++ = base64_encode_value(state->result); + *out_pos++ = '='; + break; + case step_A: + break; + } + if (out_pos >= out_end) + return out_pos - out_base64; + *out_pos++ = '\n'; + if (out_pos >= out_end) + return out_pos - out_base64; + *out_pos = '\0'; + return out_pos - out_base64; +} + +int +base64_encode(const char *in_bin, int in_len, + char *out_base64, int out_len) +{ + struct base64_encodestate state; + base64_encodestate_init(&state); + int res = base64_encode_block(in_bin, in_len, out_base64, + out_len, &state); + return res + base64_encode_blockend(out_base64 + res, out_len - res, + &state); +} + +/* }}} */ + +/* {{{ decode */ + +enum base64_decodestep { step_a, step_b, step_c, step_d }; + +struct base64_decodestate +{ + enum base64_decodestep step; + char result; +}; + +static char +base64_decode_value(char value) +{ + static const char decoding[] = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, -1, -1, -1, -2, -1, -1, -1, 0, 1, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51 + }; + static const char decoding_size = sizeof(decoding); + int codepos = (signed char) value; + codepos -= 43; + if (codepos < 0 || codepos > decoding_size) + return -1; + return decoding[codepos]; +} + +static inline void +base64_decodestate_init(struct base64_decodestate *state) +{ + state->step = step_a; + state->result = 0; +} + +static int +base64_decode_block(const char *in_base64, int in_len, + char *out_bin, int out_len, + struct base64_decodestate *state) +{ + const char *in_pos = in_base64; + const char *in_end = in_base64 + in_len; + char *out_pos = out_bin; + char *out_end = out_bin + out_len; + char fragment; + + *out_pos = state->result; + + switch (state->step) + { + while (1) + { + case step_a: + do { + if (in_pos == in_end || out_pos >= out_end) + { + state->step = step_a; + state->result = *out_pos; + return out_pos - out_bin; + } + fragment = base64_decode_value(*in_pos++); + } while (fragment < 0); + *out_pos = (fragment & 0x03f) << 2; + case step_b: + do { + if (in_pos == in_end || out_pos + 1 >= out_end) + { + state->step = step_b; + state->result = *out_pos; + return out_pos - out_bin; + } + fragment = base64_decode_value(*in_pos++); + } while (fragment < 0); + *out_pos++ |= (fragment & 0x030) >> 4; + *out_pos = (fragment & 0x00f) << 4; + case step_c: + do { + if (in_pos == in_end || out_pos + 1 >= out_end) + { + state->step = step_c; + state->result = *out_pos; + return out_pos - out_bin; + } + fragment = base64_decode_value(*in_pos++); + } while (fragment < 0); + *out_pos++ |= (fragment & 0x03c) >> 2; + *out_pos = (fragment & 0x003) << 6; + case step_d: + do { + if (in_pos == in_end || out_pos >= out_end) + { + state->step = step_d; + state->result = *out_pos; + return out_pos - out_bin; + } + fragment = base64_decode_value(*in_pos++); + } while (fragment < 0); + *out_pos++ |= (fragment & 0x03f); + } + } + /* control should not reach here */ + return out_pos - out_bin; +} + + + +int +base64_decode(const char *in_base64, int in_len, + char *out_bin, int out_len) +{ + struct base64_decodestate state; + base64_decodestate_init(&state); + return base64_decode_block(in_base64, in_len, + out_bin, out_len, &state); +} + +/* }}} */ diff --git a/third_party/base64.h b/third_party/base64.h new file mode 100644 index 0000000000000000000000000000000000000000..c28ce6afba8a21278d23f9142339b3e9497c93df --- /dev/null +++ b/third_party/base64.h @@ -0,0 +1,85 @@ +#ifndef BASE64_H +#define BASE64_H +/* + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * This is part of the libb64 project, and has been placed in the + * public domain. For details, see + * http://sourceforge.net/projects/libb64 + */ + +#define BASE64_CHARS_PER_LINE 72 + +static inline int +base64_bufsize(int binsize) +{ + int datasize = binsize * 4/3 + 4; + int newlines = ((datasize + BASE64_CHARS_PER_LINE - 1)/ + BASE64_CHARS_PER_LINE); + return datasize + newlines; +} + +/** + * Encode a binary stream into BASE64 text. + * + * @pre the buffer size is at least 4/3 of the stream + * size + stream_size/72 (newlines) + 4 + * + * @param[in] in_bin the binary input stream to decode + * @param[in] in_len size of the input + * @param[out] out_base64 output buffer for the encoded data + * @param[in] out_len buffer size, must be at least + * 4/3 of the input size + * + * @return the size of encoded output + */ + +int +base64_encode(const char *in_bin, int in_len, + char *out_base64, int out_len); + +/** + * Decode a BASE64 text into a binary + * + * @param[in] in_base64 the BASE64 stream to decode + * @param[in] in_len size of the input + * @param[out] out_bin output buffer size + * @param[in] out_len buffer size + * + * @pre the output buffer size must be at least + * 3/4 + 1 of the size of the input + * + * @return the size of decoded output + */ + +int base64_decode(const char *in_base64, int in_len, + char *out_bin, int out_len); + +#endif /* BASE64_H */ +