obex_iterator: rewrite to avoid out-of-bounds reads, add fuzzer

This commit is contained in:
Matthias Ringwald 2021-02-25 14:50:31 +01:00
parent 3cd982f433
commit d1769b9f48
3 changed files with 79 additions and 58 deletions

View File

@ -67,49 +67,77 @@ static int obex_packet_header_offset_for_opcode(uint8_t opcode){
}
}
static void obex_iterator_init(obex_iterator_t *context, int header_offset, const uint8_t * packet_data, uint16_t packet_len){
static void obex_iterator_reset(obex_iterator_t *context){
memset(context, 0, sizeof(obex_iterator_t));
context->valid = false;
}
// check if num bytes are available, and if not, invalidate iterator
static bool obex_iterator_check(obex_iterator_t *context, uint32_t num_bytes){
if ((context->offset + num_bytes) >= context->length){
context->valid = false;
}
return context->valid;
}
static void obex_iterator_prepare(obex_iterator_t *context){
if (obex_iterator_check(context, 1) == false) return;
const uint8_t * data = context->data + context->offset;
uint8_t encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
if (obex_iterator_check(context, 3) == false) return;
context->data_size = big_endian_read_16(data, 1);
context->header_size = 3;
break;
case 2:
// 8-bit value
context->data_size = 1;
context->header_size = 1;
break;
case 3:
// 32-bit value
context->data_size = 4;
context->header_size = 1;
break;
default:
// avoid compiler warning about unused cases (encoding in [0..3])
break;
}
(void) obex_iterator_check(context, context->header_size + context->data_size);
}
static void obex_iterator_init(obex_iterator_t *context, int header_offset, const uint8_t * packet_data, uint16_t packet_len){
if (header_offset >= packet_len) return;
context->data = packet_data + header_offset;
context->length = packet_len - header_offset;
context->valid = true;
obex_iterator_prepare(context);
}
void obex_iterator_init_with_request_packet(obex_iterator_t *context, const uint8_t * packet_data, uint16_t packet_len){
obex_iterator_reset(context);
if (packet_len == 0) return;
int header_offset = obex_packet_header_offset_for_opcode(packet_data[0]);
obex_iterator_init(context, header_offset, packet_data, packet_len);
}
void obex_iterator_init_with_response_packet(obex_iterator_t *context, uint8_t request_opcode, const uint8_t * packet_data, uint16_t packet_len){
obex_iterator_reset(context);
int header_offset = (request_opcode == OBEX_OPCODE_CONNECT) ? 7 : 3;
obex_iterator_init(context, header_offset, packet_data, packet_len);
}
int obex_iterator_has_more(const obex_iterator_t * context){
return context->offset < context->length;
return context->valid;
}
void obex_iterator_next(obex_iterator_t * context){
int len = 0;
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
len = big_endian_read_16(data, 1);
break;
case 2:
// 8-bit value
len = 2;
break;
case 3:
// 32-bit value
len = 5;
break;
// avoid compiler warning about unused cases (by unclever compilers)
default:
break;
}
context->offset += len;
if (context->valid == false) return;
context->offset += context->header_size + context->data_size;
obex_iterator_prepare(context);
}
// OBEX packet header access functions
@ -125,38 +153,11 @@ uint32_t obex_iterator_get_data_32(const obex_iterator_t * context){
return big_endian_read_32(context->data, context->offset + 1);
}
uint32_t obex_iterator_get_data_len(const obex_iterator_t * context){
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
return big_endian_read_16(data, 1) - 3;
case 2:
// 8-bit value
return 1;
case 3:
// 32-bit value
return 4;
// avoid compiler warning about unused cases (by unclever compilers)
default:
return 0;
}
return context->data_size;
}
const uint8_t * obex_iterator_get_data(const obex_iterator_t * context){
const uint8_t * data = context->data + context->offset;
int encoding = data[0] >> 6;
switch (encoding){
case 0:
case 1:
// 16-bit length info prefixed
return &data[3];
default:
// 8-bit value
// 32-bit value
return &data[1];
}
return &context->data[context->offset + context->header_size];
}
#ifdef ENABLE_OBEX_DUMP

View File

@ -43,13 +43,17 @@ extern "C" {
#endif
#include <stdint.h>
#include "btstack_bool.h"
/* API_START */
/* API_START */
typedef struct obex_iterator {
const uint8_t * data;
uint16_t offset;
uint16_t length;
uint16_t header_size;
uint16_t data_size;
bool valid;
} obex_iterator_t;
// OBEX packet header iterator

View File

@ -0,0 +1,16 @@
#include <stdint.h>
#include <stddef.h>
#include "classic/obex_iterator.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
obex_iterator_t it;
for (obex_iterator_init_with_request_packet(&it, data, size); obex_iterator_has_more(&it) ; obex_iterator_next(&it)){
uint32_t len = obex_iterator_get_data_len(&it);
const uint8_t * item = obex_iterator_get_data(&it);
// access first and last byte
(void) data[0];
(void) data[len-1];
}
return 0;
}