diff --git a/tcpip.cc b/tcpip.cc index a4c3a729a..a2a43942f 100644 --- a/tcpip.cc +++ b/tcpip.cc @@ -1360,25 +1360,31 @@ static bool validateTCPhdr(u8 *tcpc, unsigned len) { tcpc += sizeof(struct tcp_hdr); optlen = hdrlen - sizeof(struct tcp_hdr); +#define OPTLEN_IS(expected) do { \ + if (optlen < (expected) || *++tcpc != (expected)) \ + return false; \ + optlen -= (expected); \ + tcpc += (expected) - 1; \ +} while(0); + while (optlen > 0) { switch (*tcpc) { + case 0: // EOL + /* Options processing is over. */ + return true; + case 1: // NOP + /* 1 byte, no length. All other options have a length. */ + optlen--; + tcpc++; + break; case 2: /* MSS */ - if (optlen < 4) - return false; - optlen -= 4; - tcpc += 4; + OPTLEN_IS(4); break; case 3: /* Window Scale */ - if (optlen < 3) - return false; - optlen -= 3; - tcpc += 3; + OPTLEN_IS(3); break; case 4: /* SACK Permitted */ - if (optlen < 2) - return false; - optlen -= 2; - tcpc += 2; + OPTLEN_IS(2); break; case 5: /* SACK */ if (optlen < *++tcpc) @@ -1389,23 +1395,19 @@ static bool validateTCPhdr(u8 *tcpc, unsigned len) { tcpc += (*tcpc - 1); break; case 8: /* Timestamp */ - if (optlen < 10) - return false; - optlen -= 10; - tcpc += 10; + OPTLEN_IS(10); break; case 14: /* Alternate checksum */ /* Sometimes used for hardware checksum offloading * ftp://ftp.ucsd.edu/pub/csl/fastnet/faq.txt */ - if (optlen < 3) - return false; - optlen -= 3; - tcpc += 3; + OPTLEN_IS(3); break; default: - optlen--; - tcpc++; + if (optlen < 2 || optlen < *++tcpc) + return false; + optlen -= *tcpc; + tcpc += (*tcpc - 1); break; } }