diff --git a/.gitignore b/.gitignore index 1ae86973..0bf99128 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,15 @@ -*.o -*.so -*.exe *.db +*.gcda +*.gcno +*.exe +*.o +*.old +*.pyc +*.so +*.vglog + c/*.test cpp/*.test -*.pyc -*.vglog build/ @@ -50,5 +54,9 @@ test/ssl/*.csr test/lib/c/*.test test/lib/cpp/*.test +test/unit/coverage.info +test/unit/mosq_test +test/unit/out + www/cache/ __pycache__ diff --git a/lib/packet_datatypes.c b/lib/packet_datatypes.c index 7acb7151..3065f124 100644 --- a/lib/packet_datatypes.c +++ b/lib/packet_datatypes.c @@ -174,24 +174,30 @@ void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word) } -int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, uint8_t *bytes) +int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes) { int i; - int remaining_mult = 1; uint8_t byte; - - *word = 0; - if(bytes) (*bytes) = 0; + int remaining_mult = 1; + int32_t lword = 0; + uint8_t lbytes = 0; for(i=0; i<4; i++){ if(packet->pos < packet->remaining_length){ - if(bytes) (*bytes)++; + lbytes++; byte = packet->payload[packet->pos]; - *word += (byte & 127) * remaining_mult; + lword += (byte & 127) * remaining_mult; remaining_mult *= 128; packet->pos++; if((byte & 128) == 0){ - return MOSQ_ERR_SUCCESS; + if(lbytes > 1 && byte == 0){ + /* Catch overlong encodings */ + return MOSQ_ERR_PROTOCOL; + }else{ + *word = lword; + if(bytes) (*bytes) = lbytes; + return MOSQ_ERR_SUCCESS; + } } }else{ return MOSQ_ERR_PROTOCOL; diff --git a/lib/packet_mosq.h b/lib/packet_mosq.h index f7bcdeb1..5b5b5c69 100644 --- a/lib/packet_mosq.h +++ b/lib/packet_mosq.h @@ -32,7 +32,7 @@ int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t c int packet__read_string(struct mosquitto__packet *packet, char **str, int *length); int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word); int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word); -int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, uint8_t *bytes); +int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes); void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte); void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count); diff --git a/test/Makefile b/test/Makefile index cf94ea73..e3f6cdf6 100644 --- a/test/Makefile +++ b/test/Makefile @@ -14,6 +14,7 @@ all : fake_user msgsps_pub msgsps_sub test : $(MAKE) -C broker test $(MAKE) -C lib test + $(MAKE) -C unit test ptest : $(MAKE) -C broker ptest diff --git a/test/unit/Makefile b/test/unit/Makefile new file mode 100644 index 00000000..4d8d7388 --- /dev/null +++ b/test/unit/Makefile @@ -0,0 +1,27 @@ +include ../../config.mk + +.PHONY: all test clean coverage + +CFLAGS=-I../.. -I../../lib -coverage +TEST_LDFLAGS=-lcunit -coverage + +all : test + +packet_datatypes.o : ../../lib/packet_datatypes.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +memory_mosq.o : ../../lib/memory_mosq.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +mosq_test : test.o datatypes.o memory_mosq.o packet_datatypes.o + $(CROSS_COMPILE)$(CC) -o $@ $^ ${TEST_LDFLAGS} + +test : mosq_test + ./mosq_test + +clean : + -rm -f test *.o + +coverage : + lcov --capture --directory . --output-file coverage.info + genhtml coverage.info --output-directory out diff --git a/test/unit/datatypes.c b/test/unit/datatypes.c new file mode 100644 index 00000000..60a0d215 --- /dev/null +++ b/test/unit/datatypes.c @@ -0,0 +1,259 @@ +#include +#include + +#include "packet_mosq.h" + +static void varint_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + int32_t value_expected, + int8_t bytes_expected) +{ + struct mosquitto__packet packet; + int32_t value = -1; + int8_t bytes = -1; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_varint(&packet, &value, &bytes); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(value, value_expected); + CU_ASSERT_EQUAL(bytes, bytes_expected); +} + + +/* This tests reading a Variable Byte Integer from an incoming packet. + * + * It tests: + * * Empty packet + */ +static void TEST_varint_read_empty(void) +{ + struct mosquitto__packet packet; + int rc; + + /* Empty packet */ + varint_read_helper(NULL, 0, MOSQ_ERR_PROTOCOL, -1, -1); +} + + +/* This tests reading a Variable Byte Integer from an incoming packet. + * + * It tests: + * * Truncated packets (varint encoding is longer than data) + */ +static void TEST_varint_read_truncated(void) +{ + struct mosquitto__packet packet; + uint8_t payload[20]; + int rc; + + /* Varint bigger than packet */ + memset(payload, 0, sizeof(payload)); + payload[0] = 0x80; + varint_read_helper(payload, 1, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Varint bigger than packet */ + memset(payload, 1, sizeof(payload)); + payload[0] = 0x80; + payload[1] = 0x80; + varint_read_helper(payload, 2, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Varint bigger than packet */ + memset(payload, 0, sizeof(payload)); + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x80; + varint_read_helper(payload, 3, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Varint bigger than packet */ + memset(payload, 0, sizeof(payload)); + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x80; + payload[3] = 0x80; + varint_read_helper(payload, 4, MOSQ_ERR_PROTOCOL, -1, -1); +} + + +/* This tests reading a Variable Byte Integer from an incoming packet. + * + * It tests: + * * Correct values on boundaries of 1, 2, 3, 4 byte encodings + */ +static void TEST_varint_read_boundaries(void) +{ + struct mosquitto__packet packet; + uint8_t payload[20]; + int rc; + + /* Value = 0 */ + memset(payload, 0, sizeof(payload)); + payload[0] = 0x00; + varint_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 0, 1); + + /* Value = 127 (just beneath the crossover to two bytes */ + memset(payload, 0, sizeof(payload)); + payload[0] = 0x7F; + varint_read_helper(payload, 1, MOSQ_ERR_SUCCESS, 127, 1); + + /* Value = 128 (just after the crossover to two bytes */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x01; + varint_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 128, 2); + + /* Value = 16383 (just before the crossover to three bytes */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0x7F; + varint_read_helper(payload, 2, MOSQ_ERR_SUCCESS, 16383, 2); + + /* Value = 16384 (just after the crossover to three bytes */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x01; + varint_read_helper(payload, 3, MOSQ_ERR_SUCCESS, 16384, 3); + + /* Value = 2,097,151 (just before the crossover to four bytes */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0xFF; + payload[2] = 0x7F; + varint_read_helper(payload, 3, MOSQ_ERR_SUCCESS, 2097151, 3); + + /* Value = 2,097,152 (just after the crossover to four bytes */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x80; + payload[3] = 0x01; + varint_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 2097152, 4); + + /* Value = 268,435,455 (highest value allowed) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0xFF; + payload[2] = 0xFF; + payload[3] = 0x7F; + varint_read_helper(payload, 4, MOSQ_ERR_SUCCESS, 268435455, 4); +} + +/* This tests reading a Variable Byte Integer from an incoming packet. + * + * It tests: + * * Too long encoding (5 bytes) + */ +static void TEST_varint_read_5_bytes(void) +{ + struct mosquitto__packet packet; + uint8_t payload[20]; + int rc; + + /* Value = 268,435,456 (one higher than highest value allowed) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x80; + payload[3] = 0x80; + payload[4] = 0x01; + varint_read_helper(payload, 5, MOSQ_ERR_PROTOCOL, -1, -1); +} + + +/* This tests reading a Variable Byte Integer from an incoming packet. + * + * It tests: + * * Overlong encodings (e.g. 2 bytes to encode "1") + */ +static void TEST_varint_read_overlong_encoding(void) +{ + struct mosquitto__packet packet; + uint8_t payload[20]; + int rc; + + /* Overlong encoding of 0 (1 byte value encoded as 2 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x00; + varint_read_helper(payload, 2, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Overlong encoding of 127 (1 byte value encoded as 2 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0x00; + varint_read_helper(payload, 2, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Overlong encoding of 128 (2 byte value encoded as 3 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x81; + payload[2] = 0x00; + varint_read_helper(payload, 3, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Overlong encoding of 16,383 (2 byte value encoded as 3 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0xFF; + payload[2] = 0x00; + varint_read_helper(payload, 3, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Overlong encoding of 16,384 (3 byte value encoded as 4 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0x80; + payload[1] = 0x80; + payload[2] = 0x81; + payload[3] = 0x00; + varint_read_helper(payload, 4, MOSQ_ERR_PROTOCOL, -1, -1); + + /* Overlong encoding of 2,097,151 (3 byte value encoded as 4 bytes) */ + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + payload[0] = 0xFF; + payload[1] = 0xFF; + payload[2] = 0xFF; + payload[3] = 0x00; + varint_read_helper(payload, 4, MOSQ_ERR_PROTOCOL, -1, -1); +} + + +int init_datatype_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("datatypes", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Variable Byte Integer read (empty packet)", TEST_varint_read_empty) + || !CU_add_test(test_suite, "Variable Byte Integer read (truncated packets)", TEST_varint_read_truncated) + || !CU_add_test(test_suite, "Variable Byte Integer read (encoding boundaries)", TEST_varint_read_boundaries) + || !CU_add_test(test_suite, "Variable Byte Integer read (five byte encoding)", TEST_varint_read_5_bytes) + || !CU_add_test(test_suite, "Variable Byte Integer read (overlong encodings)", TEST_varint_read_overlong_encoding) + ){ + + printf("Error adding datatypes CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/test.c b/test/unit/test.c new file mode 100644 index 00000000..3071fead --- /dev/null +++ b/test/unit/test.c @@ -0,0 +1,27 @@ +#include + +#include +#include + +int init_datatype_tests(void); + +int main(int argc, char *argv[]) +{ + + if(CU_initialize_registry() != CUE_SUCCESS){ + printf("Error initializing CUnit registry.\n"); + return 1; + } + + if(init_datatype_tests()){ + CU_cleanup_registry(); + return 1; + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + + return 0; +} +