Navigation Menu

Skip to content

Commit

Permalink
[tcp] Guard against malformed TCP options
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Jan 27, 2016
1 parent 6366fa7 commit fef8e34
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 13 deletions.
2 changes: 0 additions & 2 deletions src/include/ipxe/tcp.h
Expand Up @@ -140,8 +140,6 @@ struct tcp_timestamp_padded_option {

/** Parsed TCP options */
struct tcp_options {
/** MSS option, if present */
const struct tcp_mss_option *mssopt;
/** Window scale option, if present */
const struct tcp_window_scale_option *wsopt;
/** SACK permitted option, if present */
Expand Down
64 changes: 53 additions & 11 deletions src/net/tcp.c
Expand Up @@ -904,50 +904,86 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) {
/**
* Parse TCP received options
*
* @v tcp TCP connection
* @v data Raw options data
* @v len Raw options length
* @v tcp TCP connection (may be NULL)
* @v tcphdr TCP header
* @v hlen TCP header length
* @v options Options structure to fill in
* @ret rc Return status code
*/
static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
size_t len, struct tcp_options *options ) {
const void *end = ( data + len );
static int tcp_rx_opts ( struct tcp_connection *tcp,
const struct tcp_header *tcphdr, size_t hlen,
struct tcp_options *options ) {
const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) );
const void *end = ( ( ( void * ) tcphdr ) + hlen );
const struct tcp_option *option;
unsigned int kind;
size_t remaining;
size_t min;

/* Sanity check */
assert ( hlen >= sizeof ( *tcphdr ) );

/* Parse options */
memset ( options, 0, sizeof ( *options ) );
while ( data < end ) {
while ( ( remaining = ( end - data ) ) ) {

/* Extract option code */
option = data;
kind = option->kind;

/* Handle single-byte options */
if ( kind == TCP_OPTION_END )
return;
break;
if ( kind == TCP_OPTION_NOP ) {
data++;
continue;
}

/* Handle multi-byte options */
min = sizeof ( *option );
switch ( kind ) {
case TCP_OPTION_MSS:
options->mssopt = data;
/* Ignore received MSS */
break;
case TCP_OPTION_WS:
options->wsopt = data;
min = sizeof ( *options->wsopt );
break;
case TCP_OPTION_SACK_PERMITTED:
options->spopt = data;
min = sizeof ( *options->spopt );
break;
case TCP_OPTION_SACK:
/* Ignore received SACKs */
break;
case TCP_OPTION_TS:
options->tsopt = data;
min = sizeof ( *options->tsopt );
break;
default:
DBGC ( tcp, "TCP %p received unknown option %d\n",
tcp, kind );
break;
}
if ( remaining < min ) {
DBGC ( tcp, "TCP %p received truncated option %d\n",
tcp, kind );
return -EINVAL;
}
if ( option->length < min ) {
DBGC ( tcp, "TCP %p received underlength option %d\n",
tcp, kind );
return -EINVAL;
}
if ( option->length > remaining ) {
DBGC ( tcp, "TCP %p received overlength option %d\n",
tcp, kind );
return -EINVAL;
}
data += option->length;
}

return 0;
}

/**
Expand Down Expand Up @@ -1011,6 +1047,12 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
tcp->snd_win_scale = options->wsopt->scale;
tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
}
DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window "
"x%d, RX window x%d\n", tcp,
( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ),
( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ),
( 1 << tcp->snd_win_scale ),
( 1 << tcp->rcv_win_scale ) );
}

/* Ignore duplicate SYN */
Expand Down Expand Up @@ -1369,8 +1411,8 @@ static int tcp_rx ( struct io_buffer *iobuf,
ack = ntohl ( tcphdr->ack );
raw_win = ntohs ( tcphdr->win );
flags = tcphdr->flags;
tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
( hlen - sizeof ( *tcphdr ) ), &options );
if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 )
goto discard;
if ( tcp && options.tsopt )
tcp->ts_val = ntohl ( options.tsopt->tsval );
iob_pull ( iobuf, hlen );
Expand Down

0 comments on commit fef8e34

Please sign in to comment.