вторник, 1 января 2019 г.

Типовая ошибка при сравнении signed и unsigned int

#include <stdio.h>  
#include <stdint.h>

typedef struct {
    uint32_t iProtocol;
} TCN_BPDU;

#define TCN_BPDU_SIZE    sizeof(TCN_BPDU)
#define SIZE_LLC_HEADER  3

/* парсер TCN BPDU пакета */
static void _parseTcnBpdu(uint8_t *paPktData_, int nSize_)
{
    /*
     В качестве размера nSize_ приходит отрицательное число (-1), signed int.
     Под определением TCN_BPDU_SIZE скрывается результат работы функции sizeof(..),
     то есть 'size_t' - unsigned long int.
     При операции сравнения unsigned long int и signed int, 
     будет приведение signed значения к unsigned значению.
     То есть:
       nSize_ = -1 => nSize_ = (unsigned long int) -1 = 4294967295
     Поэтому, казалось бы:
       if ( nSize_ < TCN_BPDU_SIZE ) { return; }  => if ( -1 < 4 ) { return; }
     Но фактически:
       if ( nSize_ < TCN_BPDU_SIZE ) { return; } => if ( 4294967295 < 4 ) { return; }; 
     Результат: ЗАЩИТА НЕ ОТРАБОТАЕТ
    */
    if ( nSize_ < TCN_BPDU_SIZE ) {
        printf("THIS IS NOT TCN_BPDU\n");
        return;
    }
    printf("THIS IS TCN_BPDU\n");
}

/* обработчик приема пакета */
static void _receiveHandler(uint8_t *psBufRaw_, int nSize_)
{
    /*
     Мы знаем, что к нам приходит пакет с LLC header-ом,
     поэтому 'смело' смещаем указатель и уменьшаем размер данных
     на размер LLC header-а.
     И совершаем ошибку - так как размер пакета меньше размера LLC header-а,
     получаем отрицательную длину данных.
    */
    _parseTcnBpdu(psBufRaw + SIZE_LLC_HEADER, nSize - SIZE_LLC_HEADER);
}

int main(void)
{
    uint8_t aBuf[] = { 1, 2 };
    _receiveHandler(aBuf, sizeof(aBuf));
    return 0;
}