0
0
mirror of https://github.com/zeromq/libzmq.git synced 2024-12-26 23:01:04 +08:00

Problem: zmq_z85_decode does not validate its input (#2322)

* Problem: zmq_z85_decode does not validate its input
Solution: added checks for invalid characters and overflows

* Added tests, added further check for range overflow, removed (multiple) calls to strlen

* Problem: gcc fails to build
Solution: added missing include directive

* Added VS2015 test_utils_z85 project

* Fixed indentation and copyright notice

* Resolved garbage from merge

* Revert "Added VS2015 test_utils_z85 project"

This reverts commit c58b3c664c144326e77135aa1184b6f0dee11143.

* Problem: test calls zmq_z85_decode with a NULL dest
Solution: call zmq_z85_decode with a properly sized buffer

* Problem: tests for zmq_z85_* scattered over two files
Solution: merged files

* Removed reference to removed test file from CMakeLists.txt

* Problem: Missing include directive to stdint.h
Solution: Added include directive

* Define __STDC_LIMIT_MACROS before including stdint.h

* Problem: Wrong variable is checked for invalid character marker
Solution: Use correct variable
This commit is contained in:
sigiesec 2017-01-25 15:28:03 +01:00 committed by Luca Boccassi
parent 673bb506cf
commit c6c21cf197
2 changed files with 113 additions and 22 deletions

View File

@ -27,6 +27,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define __STDC_LIMIT_MACROS
#include "precompiled.hpp"
#include "macros.hpp"
@ -36,6 +38,7 @@
#include "atomic_counter.hpp"
#include "atomic_ptr.hpp"
#include <assert.h>
#include <stdint.h>
#if !defined ZMQ_HAVE_WINDOWS
#include <unistd.h>
@ -97,19 +100,20 @@ static char encoder [85 + 1] = {
// Maps base 85 to base 256
// We chop off lower 32 and higher 128 ranges
// 0xFF denotes invalid characters within this range
static uint8_t decoder [96] = {
0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
0xFF, 0x44, 0xFF, 0x54, 0x53, 0x52, 0x48, 0xFF,
0x4B, 0x4C, 0x46, 0x41, 0xFF, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0xFF, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0xFF, 0x4E, 0x43, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0xFF, 0x50, 0xFF, 0xFF
};
// --------------------------------------------------------------------------
@ -154,17 +158,27 @@ char *zmq_z85_encode (char *dest, const uint8_t *data, size_t size)
uint8_t *zmq_z85_decode (uint8_t *dest, const char *string)
{
if (strlen (string) % 5 != 0) {
errno = EINVAL;
return NULL;
}
unsigned int byte_nbr = 0;
unsigned int char_nbr = 0;
size_t string_len = strlen (string);
uint32_t value = 0;
while (char_nbr < string_len) {
while (string[char_nbr]) {
// Accumulate value in base 85
value = value * 85 + decoder [(uint8_t) string [char_nbr++] - 32];
if (UINT32_MAX / 85 < value) {
// Invalid z85 encoding, represented value exceeds 0xffffffff
goto error_inval;
}
value *= 85;
uint8_t index = string [char_nbr++] - 32;
if (index >= sizeof(decoder)) {
// Invalid z85 encoding, character outside range
goto error_inval;
}
uint32_t summand = decoder [index];
if (summand == 0xFF || summand > (UINT32_MAX - value)) {
// Invalid z85 encoding, invalid character or represented value exceeds 0xffffffff
goto error_inval;
}
value += summand;
if (char_nbr % 5 == 0) {
// Output value in base 256
unsigned int divisor = 256 * 256 * 256;
@ -175,8 +189,15 @@ uint8_t *zmq_z85_decode (uint8_t *dest, const char *string)
value = 0;
}
}
if (char_nbr % 5 != 0) {
goto error_inval;
}
assert (byte_nbr == strlen (string) * 4 / 5);
return dest;
error_inval:
errno = EINVAL;
return NULL;
}
// --------------------------------------------------------------------------

View File

@ -70,14 +70,51 @@ void test__zmq_z85_decode__valid__success ()
assert (memcmp (out_decoded, expected, size) == 0);
}
// String length must be evenly divisible by 5 or must fail with EINVAL.
void test__zmq_z85_decode__invalid__failure (const char *encoded)
// Invalid input data must fail with EINVAL.
template<size_t SIZE>
void test__zmq_z85_decode__invalid__failure (const char (&encoded)[SIZE])
{
uint8_t decoded[SIZE * 4 / 5 + 1];
errno = 0;
assert (zmq_z85_decode(NULL, encoded) == NULL);
assert (zmq_z85_decode(decoded, encoded) == NULL);
assert (zmq_errno () == EINVAL);
}
// call zmq_z85_encode, then zmq_z85_decode, and compare the results with the original
template<size_t SIZE>
void test__zmq_z85_encode__zmq_z85_decode__roundtrip(const uint8_t (&test_data)[SIZE])
{
char test_data_z85[SIZE * 5 / 4 + 1];
char *res1 = zmq_z85_encode(test_data_z85, test_data, SIZE);
assert(res1 != NULL);
uint8_t test_data_decoded[SIZE];
uint8_t *res2 = zmq_z85_decode(test_data_decoded, test_data_z85);
assert(res2 != NULL);
int res3 = memcmp(test_data, test_data_decoded, SIZE);
assert(res3 == 0);
}
// call zmq_z85_encode, then zmq_z85_decode, and compare the results with the original
template<size_t SIZE>
void test__zmq_z85_decode__zmq_z85_encode__roundtrip(const char (&test_data)[SIZE])
{
const size_t decoded_size = (SIZE - 1) * 4 / 5;
uint8_t test_data_decoded[decoded_size];
uint8_t *res1 = zmq_z85_decode(test_data_decoded, test_data);
assert(res1 != NULL);
char test_data_z85[SIZE];
char *res2 = zmq_z85_encode(test_data_z85, test_data_decoded, decoded_size);
assert(res2 != NULL);
int res3 = memcmp(test_data, test_data_z85, SIZE);
assert(res3 == 0);
}
int main (void)
{
test__zmq_z85_encode__valid__success ();
@ -85,8 +122,41 @@ int main (void)
test__zmq_z85_encode__invalid__failure (42);
test__zmq_z85_decode__valid__success ();
// String length must be evenly divisible by 5 or must fail with EINVAL.
test__zmq_z85_decode__invalid__failure ("01234567");
test__zmq_z85_decode__invalid__failure ("0");
// decode invalid data with the maximum representable value
test__zmq_z85_decode__invalid__failure ("#####");
// decode invalid data with the minimum value beyond the limit
// "%nSc0" is 0xffffffff
test__zmq_z85_decode__invalid__failure ("%nSc1");
// decode invalid data with an invalid character in the range of valid
// characters
test__zmq_z85_decode__invalid__failure ("####\0047");
// decode invalid data with an invalid character just below the range of valid
// characters
test__zmq_z85_decode__invalid__failure ("####\0200");
// decode invalid data with an invalid character just above the range of valid
// characters
test__zmq_z85_decode__invalid__failure ("####\0037");
// round-trip encoding and decoding with minimum value
{
const uint8_t test_data[] = {0x00, 0x00, 0x00, 0x00};
test__zmq_z85_encode__zmq_z85_decode__roundtrip(test_data);
}
// round-trip encoding and decoding with maximum value
{
const uint8_t test_data[] = {0xff, 0xff, 0xff, 0xff};
test__zmq_z85_encode__zmq_z85_decode__roundtrip(test_data);
}
test__zmq_z85_decode__zmq_z85_encode__roundtrip("r^/rM9M=rMToK)63O8dCvd9D<PY<7iGlC+{BiSnG");
return 0;
}