Merge pull request #5203 from OlegHahm/cbor_bound_checking

cbor: bounds checking on read and no_copy string deserialization
pr/spi.typo
Oleg Hahm 6 years ago committed by GitHub
commit b9b6e4934c

@ -78,6 +78,10 @@
if (stream->pos + bytes >= stream->size) { return 0; } \
} while(0)
#define CBOR_ENSURE_SIZE_READ(stream, bytes) do { \
if (bytes > stream->size) { return 0; } \
} while(0)
/* Extra defines not related to the protocol itself */
#define CBOR_STREAM_PRINT_BUFFERSIZE 1024 /* bytes */
@ -91,7 +95,20 @@
#define NAN (0.0/0.0)
#endif
/* pack to force aligned access on ARMv7 (buggy GCC) */
#pragma GCC diagnostic error "-Wcast-align"
typedef struct __attribute__((packed)) {
unsigned char u8;
union {
uint16_t u16;
uint32_t u32;
uint64_t u64;
} u;
} cast_align_u8_t;
#ifndef CBOR_NO_FLOAT
/**
* Convert float @p x to network format
*/
@ -323,10 +340,14 @@ static size_t decode_int(const cbor_stream_t *s, size_t offset, uint64_t *val)
*val = 0; /* clear val first */
CBOR_ENSURE_SIZE_READ(s, offset + 1);
unsigned char *in = &s->data[offset];
unsigned char additional_info = CBOR_ADDITIONAL_INFO(s, offset);
unsigned char bytes_follow = uint_bytes_follow(additional_info);
CBOR_ENSURE_SIZE_READ(s, offset + 1 + bytes_follow);
switch (bytes_follow) {
case 0:
*val = (in[0] & CBOR_INFO_MASK);
@ -337,15 +358,15 @@ static size_t decode_int(const cbor_stream_t *s, size_t offset, uint64_t *val)
break;
case 2:
*val = HTONS(*((uint16_t *)&in[1]));
*val = HTONS(((cast_align_u8_t *)in)->u.u16);
break;
case 4:
*val = HTONL(*((uint32_t *)&in[1]));
*val = HTONL(((cast_align_u8_t *)in)->u.u32);
break;
default:
*val = HTONLL(*((uint64_t *)&in[1]));
*val = HTONLL(((cast_align_u8_t *)in)->u.u64);
break;
}
@ -371,6 +392,8 @@ static size_t encode_bytes(unsigned char major_type, cbor_stream_t *s, const cha
static size_t decode_bytes(const cbor_stream_t *s, size_t offset, char *out, size_t length)
{
CBOR_ENSURE_SIZE_READ(s, offset + 1);
if ((CBOR_TYPE(s, offset) != CBOR_BYTES && CBOR_TYPE(s, offset) != CBOR_TEXT) || !out) {
return 0;
}
@ -386,13 +409,43 @@ static size_t decode_bytes(const cbor_stream_t *s, size_t offset, char *out, siz
return 0;
}
CBOR_ENSURE_SIZE_READ(s, offset + bytes_start + bytes_length);
memcpy(out, &s->data[offset + bytes_start], bytes_length);
out[bytes_length] = '\0';
return (bytes_start + bytes_length);
}
/* A zero copy version of decode_bytes.
Will not null termiante input, but will tell you the size of what you read.
Great for reading byte strings which could contain nulls inside of unknown size
without forced copies.
*/
static size_t decode_bytes_no_copy(const cbor_stream_t *s, size_t offset, unsigned char **out, size_t *length)
{
CBOR_ENSURE_SIZE_READ(s, offset + 1);
if ((CBOR_TYPE(s, offset) != CBOR_BYTES && CBOR_TYPE(s, offset) != CBOR_TEXT) || !out) {
return 0;
}
uint64_t bytes_length;
size_t bytes_start = decode_int(s, offset, &bytes_length);
if (!bytes_start) {
return 0;
}
CBOR_ENSURE_SIZE_READ(s, offset + bytes_start + bytes_length);
*out = &(s->data[offset + bytes_start]);
*length = bytes_length;
return (bytes_start + bytes_length);
}
size_t cbor_deserialize_int(const cbor_stream_t *stream, size_t offset, int *val)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if ((CBOR_TYPE(stream, offset) != CBOR_UINT && CBOR_TYPE(stream, offset) != CBOR_NEGINT) || !val) {
return 0;
}
@ -400,6 +453,10 @@ size_t cbor_deserialize_int(const cbor_stream_t *stream, size_t offset, int *val
uint64_t buf;
size_t read_bytes = decode_int(stream, offset, &buf);
if (!read_bytes) {
return 0;
}
if (CBOR_TYPE(stream, offset) == CBOR_UINT) {
*val = buf; /* resolve as CBOR_UINT */
}
@ -521,7 +578,7 @@ size_t cbor_deserialize_float(const cbor_stream_t *stream, size_t offset, float
unsigned char *data = &stream->data[offset];
if (*data == CBOR_FLOAT32) {
*val = ntohf(*(uint32_t *)(data + 1));
*val = ntohf(((cast_align_u8_t *)data)->u.u32);
return 5;
}
@ -540,6 +597,8 @@ size_t cbor_serialize_float(cbor_stream_t *s, float val)
size_t cbor_deserialize_double(const cbor_stream_t *stream, size_t offset, double *val)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if (CBOR_TYPE(stream, offset) != CBOR_7 || !val) {
return 0;
}
@ -547,7 +606,8 @@ size_t cbor_deserialize_double(const cbor_stream_t *stream, size_t offset, doubl
unsigned char *data = &stream->data[offset];
if (*data == CBOR_FLOAT64) {
*val = ntohd(*(uint64_t *)(data + 1));
CBOR_ENSURE_SIZE_READ(stream, offset + 9);
*val = ntohd(((cast_align_u8_t *)data)->u.u64);
return 9;
}
@ -568,6 +628,8 @@ size_t cbor_serialize_double(cbor_stream_t *s, double val)
size_t cbor_deserialize_byte_string(const cbor_stream_t *stream, size_t offset, char *val,
size_t length)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if (CBOR_TYPE(stream, offset) != CBOR_BYTES) {
return 0;
}
@ -575,6 +637,18 @@ size_t cbor_deserialize_byte_string(const cbor_stream_t *stream, size_t offset,
return decode_bytes(stream, offset, val, length);
}
size_t cbor_deserialize_byte_string_no_copy(const cbor_stream_t *stream, size_t offset, unsigned char **val,
size_t *length)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if (CBOR_TYPE(stream, offset) != CBOR_BYTES) {
return 0;
}
return decode_bytes_no_copy(stream, offset, val, length);
}
size_t cbor_serialize_byte_string(cbor_stream_t *stream, const char *val)
{
return encode_bytes(CBOR_BYTES, stream, val, strlen(val));
@ -588,6 +662,8 @@ size_t cbor_serialize_byte_stringl(cbor_stream_t *stream, const char *val, size_
size_t cbor_deserialize_unicode_string(const cbor_stream_t *stream, size_t offset, char *val,
size_t length)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if (CBOR_TYPE(stream, offset) != CBOR_TEXT) {
return 0;
}
@ -595,6 +671,18 @@ size_t cbor_deserialize_unicode_string(const cbor_stream_t *stream, size_t offse
return decode_bytes(stream, offset, val, length);
}
size_t cbor_deserialize_unicode_string_no_copy(const cbor_stream_t *stream, size_t offset, unsigned char **val,
size_t *length)
{
CBOR_ENSURE_SIZE_READ(stream, offset + 1);
if (CBOR_TYPE(stream, offset) != CBOR_TEXT) {
return 0;
}
return decode_bytes_no_copy(stream, offset, val, length);
}
size_t cbor_serialize_unicode_string(cbor_stream_t *stream, const char *val)
{
return encode_bytes(CBOR_TEXT, stream, val, strlen(val));

@ -391,6 +391,22 @@ size_t cbor_deserialize_byte_string(const cbor_stream_t *stream, size_t offset,
size_t cbor_serialize_unicode_string(cbor_stream_t *stream, const char *val);
/**
* @brief Deserialize bytes/unicode from @p stream to @p val (without copy)
*
* @param[in] stream The stream to deserialize
* @param[in] offset The offset within the stream where to start deserializing
* @param[out] val Pointer to a char *
* @param[out] length Pointer to a size_t to store the size of the string
*
* @return Number of bytes written into @p val
*/
size_t cbor_deserialize_byte_string_no_copy(const cbor_stream_t *stream, size_t offset,
unsigned char **val, size_t *length);
size_t cbor_deserialize_unicode_string_no_copy(const cbor_stream_t *stream, size_t offset,
unsigned char **val, size_t *length);
/**
* @brief Deserialize unicode string from @p stream to @p val
*

@ -279,6 +279,39 @@ static void test_byte_string(void)
}
}
static void test_byte_string_no_copy(void)
{
char buffer[128];
{
const char *input = "";
unsigned char data[] = {0x40};
unsigned char *out;
size_t size;
TEST_ASSERT(cbor_serialize_byte_string(&stream, input));
CBOR_CHECK_SERIALIZED(stream, data, sizeof(data));
TEST_ASSERT(cbor_deserialize_byte_string_no_copy(&stream, 0, &out, &size));
memcpy(buffer, out, size);
buffer[size] = '\0';
CBOR_CHECK_DESERIALIZED(input, buffer, EQUAL_STRING);
}
cbor_clear(&stream);
{
const char *input = "a";
unsigned char data[] = {0x41, 0x61};
unsigned char *out;
size_t size;
TEST_ASSERT(cbor_serialize_byte_string(&stream, input));
CBOR_CHECK_SERIALIZED(stream, data, sizeof(data));
TEST_ASSERT(cbor_deserialize_byte_string_no_copy(&stream, 0, &out, &size));
memcpy(buffer, out, size);
buffer[size] = '\0';
CBOR_CHECK_DESERIALIZED(input, buffer, EQUAL_STRING);
}
}
static void test_byte_string_invalid(void)
{
{
@ -318,6 +351,39 @@ static void test_unicode_string(void)
}
}
static void test_unicode_string_no_copy(void)
{
char buffer[128];
{
const char *input = "";
unsigned char data[] = {0x60};
unsigned char *out;
size_t size;
TEST_ASSERT(cbor_serialize_unicode_string(&stream, input));
CBOR_CHECK_SERIALIZED(stream, data, sizeof(data));
TEST_ASSERT(cbor_deserialize_unicode_string_no_copy(&stream, 0, &out, &size));
memcpy(buffer, out, size);
buffer[size] = '\0';
CBOR_CHECK_DESERIALIZED(input, buffer, EQUAL_STRING);
}
cbor_clear(&stream);
{
const char *input = "a";
unsigned char data[] = {0x61, 0x61};
unsigned char *out;
size_t size;
TEST_ASSERT(cbor_serialize_unicode_string(&stream, input));
CBOR_CHECK_SERIALIZED(stream, data, sizeof(data));
TEST_ASSERT(cbor_deserialize_unicode_string_no_copy(&stream, 0, &out, &size));
memcpy(buffer, out, size);
buffer[size] = '\0';
CBOR_CHECK_DESERIALIZED(input, buffer, EQUAL_STRING);
}
}
static void test_unicode_string_invalid(void)
{
{
@ -761,8 +827,10 @@ TestRef tests_cbor_all(void)
new_TestFixture(test_int64_t),
new_TestFixture(test_int64_t_invalid),
new_TestFixture(test_byte_string),
new_TestFixture(test_byte_string_no_copy),
new_TestFixture(test_byte_string_invalid),
new_TestFixture(test_unicode_string),
new_TestFixture(test_unicode_string_no_copy),
new_TestFixture(test_unicode_string_invalid),
new_TestFixture(test_array),
new_TestFixture(test_array_indefinite),

Loading…
Cancel
Save