Fixed up bug with encode_application_double function.

Added new bacapp_decode_application_data_safe function and other *_safe functions that aim to prevent buffer overruns if encoded application message is malformed.
Note: bacstr unit tests currently have one failure.
This commit is contained in:
minack
2009-08-10 00:06:56 +00:00
parent d088e19ed2
commit a944510441
6 changed files with 449 additions and 18 deletions
+5
View File
@@ -129,6 +129,11 @@ extern "C" {
unsigned max_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value);
bool bacapp_decode_application_data_safe(
uint8_t * new_apdu,
uint32_t new_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value);
int bacapp_encode_application_data(
uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value);
+21 -2
View File
@@ -70,6 +70,11 @@ extern "C" {
uint8_t * apdu,
uint8_t * tag_number,
uint32_t * value);
int decode_tag_number_and_value_safe(
uint8_t * apdu,
uint8_t apdu_len_remaining,
uint8_t * tag_number,
uint32_t * value);
/* returns true if the tag is context specific */
bool decode_is_context_specific(
uint8_t * apdu);
@@ -171,6 +176,12 @@ extern "C" {
uint16_t * object_type,
uint32_t * instance);
int decode_object_id_safe(
uint8_t * apdu,
uint32_t len_value,
uint16_t * object_type,
uint32_t * instance);
int decode_context_object_id(
uint8_t * apdu,
uint8_t tag_number,
@@ -316,7 +327,11 @@ extern "C" {
int decode_bacnet_time(
uint8_t * apdu,
BACNET_TIME * btime);
int encode_context_time(
int decode_bacnet_time_safe(
uint8_t * apdu,
uint32_t len_value,
BACNET_TIME * btime);
int encode_context_time(
uint8_t * apdu,
uint8_t tag_number,
BACNET_TIME * btime);
@@ -351,7 +366,11 @@ extern "C" {
int decode_date(
uint8_t * apdu,
BACNET_DATE * bdate);
int decode_application_date(
int decode_date_safe(
uint8_t * apdu,
uint32_t len_value,
BACNET_DATE * bdate);
int decode_application_date(
uint8_t * apdu,
BACNET_DATE * bdate);
int decode_context_date(
+11 -1
View File
@@ -42,6 +42,11 @@
extern "C" {
#endif /* __cplusplus */
int decode_real_safe(
uint8_t * apdu,
uint32_t len_value,
float *real_value);
int decode_real(
uint8_t * apdu,
float *real_value);
@@ -56,7 +61,12 @@ extern "C" {
int decode_double(
uint8_t * apdu,
double *real_value);
int encode_bacnet_double(
int decode_double_safe(
uint8_t * apdu,
uint32_t len_value,
double *double_value);
int encode_bacnet_double(
double value,
uint8_t * apdu);
+236 -8
View File
@@ -157,7 +157,6 @@ int bacapp_decode_data(
{
int len = 0;
if (apdu && value) {
switch (tag_data_type) {
#if defined (BACAPP_NULL)
@@ -186,12 +185,12 @@ int bacapp_decode_data(
#endif
#if defined (BACAPP_REAL)
case BACNET_APPLICATION_TAG_REAL:
len = decode_real(&apdu[0], &(value->type.Real));
len = decode_real_safe(&apdu[0], len_value_type, &(value->type.Real));
break;
#endif
#if defined (BACAPP_DOUBLE)
case BACNET_APPLICATION_TAG_DOUBLE:
len = decode_double(&apdu[0], &(value->type.Double));
len = decode_double_safe(&apdu[0], len_value_type, &(value->type.Double));
break;
#endif
#if defined (BACAPP_OCTET_STRING)
@@ -224,12 +223,12 @@ int bacapp_decode_data(
#endif
#if defined (BACAPP_DATE)
case BACNET_APPLICATION_TAG_DATE:
len = decode_date(&apdu[0], &value->type.Date);
len = decode_date_safe(&apdu[0], len_value_type, &value->type.Date);
break;
#endif
#if defined (BACAPP_TIME)
case BACNET_APPLICATION_TAG_TIME:
len = decode_bacnet_time(&apdu[0], &value->type.Time);
len = decode_bacnet_time_safe(&apdu[0], len_value_type, &value->type.Time);
break;
#endif
#if defined (BACAPP_OBJECT_ID)
@@ -237,7 +236,7 @@ int bacapp_decode_data(
{
uint16_t object_type = 0;
uint32_t instance = 0;
len = decode_object_id(&apdu[0], &object_type, &instance);
len = decode_object_id_safe(&apdu[0], len_value_type, &object_type, &instance);
value->type.Object_Id.type = object_type;
value->type.Object_Id.instance = instance;
}
@@ -248,6 +247,10 @@ int bacapp_decode_data(
}
}
if ( len == 0 && tag_data_type != BACNET_APPLICATION_TAG_NULL && tag_data_type != BACNET_APPLICATION_TAG_BOOLEAN )
{
value->tag = MAX_BACNET_APPLICATION_TAG;
}
return len;
}
@@ -281,6 +284,73 @@ int bacapp_decode_application_data(
return len;
}
/*
** Usage: Similar to strtok. Call function the first time with new_apdu and new_adu_len set to apdu buffer
** to be processed. Subsequent calls should pass in NULL.
**
** Returns true if a application message is correctly parsed.
** Returns false if no more application messages are available.
**
** This function is NOT thread safe.
**
** Notes: The _safe suffix is there because the function should be relatively safe against buffer overruns.
**
*/
bool bacapp_decode_application_data_safe(
uint8_t * new_apdu,
uint32_t new_apdu_len,
BACNET_APPLICATION_DATA_VALUE * value)
{
/* The static variables that store the apdu buffer between function calls */
static uint8_t * apdu = NULL;
static int32_t apdu_len_remaining = 0;
static uint32_t apdu_len = 0;
int len = 0;
int tag_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
bool ret = false;
if ( new_apdu != NULL )
{
apdu = new_apdu;
apdu_len_remaining = new_apdu_len;
apdu_len = 0;
}
if (value && apdu_len_remaining > 0 && !decode_is_context_specific(&apdu[apdu_len]) ) {
value->context_specific = false;
tag_len =
decode_tag_number_and_value_safe(&apdu[apdu_len], apdu_len_remaining, &tag_number,
&len_value_type);
/* If tag_len is zero, then the tag information is truncated */
if (tag_len) {
apdu_len += tag_len;
apdu_len_remaining -= tag_len;
/* The tag is boolean then len_value_type is interpreted as value, not length, so dont bother
** checking with apdu_len_remaining */
if ( tag_number == BACNET_APPLICATION_TAG_BOOLEAN || len_value_type <= apdu_len_remaining )
{
value->tag = tag_number;
len =
bacapp_decode_data(&apdu[apdu_len], tag_number, len_value_type,
value);
apdu_len += len;
apdu_len_remaining -= len;
ret = true;
}
}
value->next = NULL;
}
return ret;
}
int bacapp_encode_context_data_value(
uint8_t * apdu,
uint8_t context_tag_number,
@@ -984,6 +1054,9 @@ bool bacapp_parse_application_data(
#include <string.h>
#include "ctest.h"
#include <assert.h>
/* generic - can be used by other unit tests
returns true if matching or same, false if different */
bool bacapp_same_value(
@@ -1081,15 +1154,166 @@ bool bacapp_same_value(
#endif
#if defined (BACAPP_BIT_STRING)
case BACNET_APPLICATION_TAG_BIT_STRING:
status =
bitstring_same(&value->type.Bit_String,
&test_value->type.Bit_String);
break;
#endif
default:
status = false;
break;
}
}
#endif
return status;
}
void testBACnetApplicationData_Safe(
Test * pTest)
{
int i;
uint8_t apdu[MAX_APDU];
int len = 0;
int apdu_len;
BACNET_APPLICATION_DATA_VALUE input_value[13];
uint32_t len_segment[13];
uint32_t single_length_segment;
BACNET_APPLICATION_DATA_VALUE value;
for ( i = 0;i < 13; i++)
{
input_value[i].tag = (BACNET_APPLICATION_TAG)i;
input_value[i].context_specific = 0;
input_value[i].context_tag = 0;
input_value[i].next = NULL;
switch(input_value[i].tag)
{
case BACNET_APPLICATION_TAG_NULL:
/* NULL: no data */
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
input_value[i].type.Boolean = true;
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
input_value[i].type.Unsigned_Int = 0xDEADBEEF;
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
input_value[i].type.Signed_Int = 0x00C0FFEE;
break;
case BACNET_APPLICATION_TAG_REAL:
input_value[i].type.Real = 3.141592654f;
break;
case BACNET_APPLICATION_TAG_DOUBLE:
input_value[i].type.Double = 2.32323232323;
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
octetstring_init(&input_value[i].type.Octet_String, "This is a o-string", strlen("This is a o-string"));
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
characterstring_init_ansi(&input_value[i].type.Character_String, "Hello There!");
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
bitstring_init(&input_value[i].type.Bit_String);
bitstring_set_bit(&input_value[i].type.Bit_String, 0, true);
bitstring_set_bit(&input_value[i].type.Bit_String, 1, false);
bitstring_set_bit(&input_value[i].type.Bit_String, 2, false);
bitstring_set_bit(&input_value[i].type.Bit_String, 3, true);
bitstring_set_bit(&input_value[i].type.Bit_String, 4, false);
bitstring_set_bit(&input_value[i].type.Bit_String, 5, true);
bitstring_set_bit(&input_value[i].type.Bit_String, 6, true);
break;
case BACNET_APPLICATION_TAG_ENUMERATED:
input_value[i].type.Enumerated = 0x0BADF00D;
break;
case BACNET_APPLICATION_TAG_DATE:
input_value[i].type.Date.day = 10;
input_value[i].type.Date.month = 9;
input_value[i].type.Date.wday = 3;
input_value[i].type.Date.year = 1998;
break;
case BACNET_APPLICATION_TAG_TIME:
input_value[i].type.Time.hour = 12;
input_value[i].type.Time.hundredths = 56;
input_value[i].type.Time.min = 20;
input_value[i].type.Time.sec = 41;
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
input_value[i].type.Object_Id.instance = 1234;
input_value[i].type.Object_Id.type = 12;
break;
default:
break;
}
single_length_segment = bacapp_encode_data(&apdu[len], &input_value[i]);;
assert(single_length_segment > 0);
/* len_segment is accumulated length */
if ( i == 0 )
{
len_segment[i] = single_length_segment;
}
else
{
len_segment[i] = single_length_segment + len_segment[i-1];
}
len = len_segment[i];
}
/*
** Start processing packets at processivly truncated lengths
*/
for ( apdu_len = len; apdu_len >=0; apdu_len--)
{
bool status;
bool expected_status;
for ( i = 0;i < 14; i++)
{
if ( i == 13 )
{
expected_status = false;
}
else{
if ( apdu_len < len_segment[i] )
{
expected_status = false;
}
else
{
expected_status = true;
}
}
status = bacapp_decode_application_data_safe(i == 0?apdu:NULL, apdu_len, &value);
ct_test(pTest, status == expected_status);
if ( status )
{
ct_test(pTest, value.tag == i);
ct_test(pTest,
bacapp_same_value(&input_value[i], &value));
ct_test(pTest, !value.context_specific);
ct_test(pTest, value.next == NULL);
}
else
{
break;
}
}
}
}
void testBACnetApplicationDataLength(
Test * pTest)
{
@@ -1453,6 +1677,7 @@ void testBACnetApplicationData(
return;
}
#ifdef TEST_BACNET_APPLICATION_DATA
int main(
void)
@@ -1466,7 +1691,10 @@ int main(
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetApplicationDataLength);
assert(rc);
ct_setStream(pTest, stdout);
rc = ct_addTestFunction(pTest, testBACnetApplicationData_Safe);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
+144 -7
View File
@@ -344,6 +344,33 @@ int decode_tag_number(
return len;
}
/* Same as function above, but will safely fail is packet has been truncated */
int decode_tag_number_safe(
uint8_t * apdu,
uint16_t apdu_len_remaining,
uint8_t * tag_number)
{
int len = 0; /* return value */
/* decode the tag number first */
if ( apdu_len_remaining >= 1 ) {
if (decode_is_extended_tag_number(&apdu[0]) && apdu_len_remaining >= 2) {
/* extended tag */
if (tag_number) {
*tag_number = apdu[1];
}
len = 2;
} else {
if (tag_number) {
*tag_number = (uint8_t) (apdu[0] >> 4);
}
len = 1;
}
}
return len;
}
bool decode_is_opening_tag(
uint8_t * apdu)
{
@@ -407,6 +434,62 @@ int decode_tag_number_and_value(
return len;
}
/* Same as function above, but will safely fail is packet has been truncated */
int decode_tag_number_and_value_safe(
uint8_t * apdu,
uint8_t apdu_len_remaining,
uint8_t * tag_number,
uint32_t * value)
{
int len = 0;
len = decode_tag_number_safe(&apdu[0], apdu_len_remaining, tag_number);
if ( len > 0 ) {
apdu_len_remaining -= len;
if (decode_is_extended_value(&apdu[0])) {
/* tagged as uint32_t */
if (apdu[len] == 255 && apdu_len_remaining >= 5) {
uint32_t value32;
len++;
len += decode_unsigned32(&apdu[len], &value32);
if (value) {
*value = value32;
}
}
/* tagged as uint16_t */
else if (apdu[len] == 254 && apdu_len_remaining >= 3) {
uint16_t value16;
len++;
len += decode_unsigned16(&apdu[len], &value16);
if (value) {
*value = value16;
}
}
/* no tag - must be uint8_t */
else if (apdu[len] < 254 && apdu_len_remaining >= 1) {
if (value) {
*value = apdu[len];
}
len++;
}
else {
/* packet is truncated */
len = 0;
}
} else if (decode_is_opening_tag(&apdu[0]) && value) {
*value = 0;
} else if (decode_is_closing_tag(&apdu[0]) && value) {
/* closing tag */
*value = 0;
} else if (value) {
/* small value */
*value = apdu[0] & 0x07;
}
}
return len;
}
/* from clause 20.2.1.3.2 Constructed Data */
/* returns true if the tag is context specific and matches */
bool decode_is_context_tag(
@@ -728,6 +811,20 @@ int decode_object_id(
return len;
}
int decode_object_id_safe(
uint8_t * apdu,
uint32_t len_value,
uint16_t * object_type,
uint32_t * instance)
{
if ( len_value != 4 ) {
return 0;
}
else {
return decode_object_id(apdu, object_type, instance);
}
}
int decode_context_object_id(
uint8_t * apdu,
uint8_t tag_number,
@@ -1396,8 +1493,9 @@ int encode_application_double(
{
int len = 0;
/* assumes that the tag only consumes 1 octet */
len = encode_bacnet_double(value, &apdu[1]);
/* assumes that the tag only consumes 2 octet */
len = encode_bacnet_double(value, &apdu[2]);
len +=
encode_tag(&apdu[0], BACNET_APPLICATION_TAG_DOUBLE, false,
(uint32_t) len);
@@ -1491,6 +1589,25 @@ int decode_bacnet_time(
return 4;
}
int decode_bacnet_time_safe(
uint8_t * apdu,
uint32_t len_value,
BACNET_TIME * btime)
{
if ( len_value != 4 )
{
btime->hour = 0;
btime->hundredths = 0;
btime->min = 0;
btime->sec = 0;
return len_value;
}
else
{
return decode_bacnet_time(apdu, btime);
}
}
int decode_application_time(
uint8_t * apdu,
BACNET_TIME * btime)
@@ -1613,6 +1730,24 @@ int decode_date(
return 4;
}
int decode_date_safe(
uint8_t * apdu,
uint32_t len_value,
BACNET_DATE * bdate)
{
if ( len_value != 4 ) {
bdate->day = 0;
bdate->month = 0;
bdate->wday = 0;
bdate->year = 0;
return len_value;
}
else {
return decode_date(apdu, bdate);
}
}
int decode_application_date(
uint8_t * apdu,
BACNET_DATE * bdate)
@@ -1870,7 +2005,7 @@ void testBACDCodeReal(
return;
}
void testBACDCodeDouble(
static void testBACDCodeDouble(
Test * pTest)
{
uint8_t double_array[8] = { 0 };
@@ -1891,12 +2026,12 @@ void testBACDCodeDouble(
/* a real will take up 4 octects plus a one octet tag */
apdu_len = encode_application_double(&apdu[0], value);
ct_test(pTest, apdu_len == 9);
ct_test(pTest, apdu_len == 10);
/* len tells us how many octets were used for encoding the value */
len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_DOUBLE);
ct_test(pTest, decode_is_context_specific(&apdu[0]) == false);
ct_test(pTest, len == 1);
ct_test(pTest, len == 2);
ct_test(pTest, long_value == 8);
decode_double(&apdu[len], &decoded_value);
ct_test(pTest, decoded_value == value);
@@ -2472,7 +2607,7 @@ void testFloatContextDecodes(
ct_test(pTest, in == out);
}
void testObjectIDContextDecodes(
static void testObjectIDContextDecodes(
Test * pTest)
{
uint8_t apdu[MAX_APDU];
@@ -2501,7 +2636,7 @@ void testObjectIDContextDecodes(
}
void testCharacterStringContextDecodes(
static void testCharacterStringContextDecodes(
Test * pTest)
{
uint8_t apdu[MAX_APDU];
@@ -2701,6 +2836,8 @@ int main(
rc = ct_addTestFunction(pTest, testOctetStringContextDecodes);
assert(rc);
rc = ct_addTestFunction(pTest, testBACDCodeDouble);
assert(rc);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
+32
View File
@@ -76,6 +76,22 @@ int decode_real(
return 4;
}
int decode_real_safe(
uint8_t * apdu,
uint32_t len_value,
float *real_value)
{
if ( len_value != 4 )
{
*real_value = 0.0f;
return len_value;
}
else
{
return decode_real(apdu, real_value);
}
}
int decode_context_real(
uint8_t * apdu,
uint8_t tag_number,
@@ -159,6 +175,22 @@ int decode_double(
return 8;
}
int decode_double_safe(
uint8_t * apdu,
uint32_t len_value,
double *double_value)
{
if ( len_value != 8 )
{
*double_value = 0.0;
return len_value;
}
else
{
return decode_double(apdu, double_value);
}
}
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
/* returns the number of apdu bytes consumed */
int encode_bacnet_double(