Переглянути джерело

[tcp] Guard against malformed TCP options

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 8 роки тому
джерело
коміт
fef8e34b6f
2 змінених файлів з 53 додано та 13 видалено
  1. 0
    2
      src/include/ipxe/tcp.h
  2. 53
    11
      src/net/tcp.c

+ 0
- 2
src/include/ipxe/tcp.h Переглянути файл

@@ -140,8 +140,6 @@ struct tcp_timestamp_padded_option {
140 140
 
141 141
 /** Parsed TCP options */
142 142
 struct tcp_options {
143
-	/** MSS option, if present */
144
-	const struct tcp_mss_option *mssopt;
145 143
 	/** Window scale option, if present */
146 144
 	const struct tcp_window_scale_option *wsopt;
147 145
 	/** SACK permitted option, if present */

+ 53
- 11
src/net/tcp.c Переглянути файл

@@ -904,50 +904,86 @@ static struct tcp_connection * tcp_demux ( unsigned int local_port ) {
904 904
 /**
905 905
  * Parse TCP received options
906 906
  *
907
- * @v tcp		TCP connection
908
- * @v data		Raw options data
909
- * @v len		Raw options length
907
+ * @v tcp		TCP connection (may be NULL)
908
+ * @v tcphdr		TCP header
909
+ * @v hlen		TCP header length
910 910
  * @v options		Options structure to fill in
911
+ * @ret rc		Return status code
911 912
  */
912
-static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data,
913
-			  size_t len, struct tcp_options *options ) {
914
-	const void *end = ( data + len );
913
+static int tcp_rx_opts ( struct tcp_connection *tcp,
914
+			 const struct tcp_header *tcphdr, size_t hlen,
915
+			 struct tcp_options *options ) {
916
+	const void *data = ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) );
917
+	const void *end = ( ( ( void * ) tcphdr ) + hlen );
915 918
 	const struct tcp_option *option;
916 919
 	unsigned int kind;
920
+	size_t remaining;
921
+	size_t min;
917 922
 
923
+	/* Sanity check */
924
+	assert ( hlen >= sizeof ( *tcphdr ) );
925
+
926
+	/* Parse options */
918 927
 	memset ( options, 0, sizeof ( *options ) );
919
-	while ( data < end ) {
928
+	while ( ( remaining = ( end - data ) ) ) {
929
+
930
+		/* Extract option code */
920 931
 		option = data;
921 932
 		kind = option->kind;
933
+
934
+		/* Handle single-byte options */
922 935
 		if ( kind == TCP_OPTION_END )
923
-			return;
936
+			break;
924 937
 		if ( kind == TCP_OPTION_NOP ) {
925 938
 			data++;
926 939
 			continue;
927 940
 		}
941
+
942
+		/* Handle multi-byte options */
943
+		min = sizeof ( *option );
928 944
 		switch ( kind ) {
929 945
 		case TCP_OPTION_MSS:
930
-			options->mssopt = data;
946
+			/* Ignore received MSS */
931 947
 			break;
932 948
 		case TCP_OPTION_WS:
933 949
 			options->wsopt = data;
950
+			min = sizeof ( *options->wsopt );
934 951
 			break;
935 952
 		case TCP_OPTION_SACK_PERMITTED:
936 953
 			options->spopt = data;
954
+			min = sizeof ( *options->spopt );
937 955
 			break;
938 956
 		case TCP_OPTION_SACK:
939 957
 			/* Ignore received SACKs */
940 958
 			break;
941 959
 		case TCP_OPTION_TS:
942 960
 			options->tsopt = data;
961
+			min = sizeof ( *options->tsopt );
943 962
 			break;
944 963
 		default:
945 964
 			DBGC ( tcp, "TCP %p received unknown option %d\n",
946 965
 			       tcp, kind );
947 966
 			break;
948 967
 		}
968
+		if ( remaining < min ) {
969
+			DBGC ( tcp, "TCP %p received truncated option %d\n",
970
+			       tcp, kind );
971
+			return -EINVAL;
972
+		}
973
+		if ( option->length < min ) {
974
+			DBGC ( tcp, "TCP %p received underlength option %d\n",
975
+			       tcp, kind );
976
+			return -EINVAL;
977
+		}
978
+		if ( option->length > remaining ) {
979
+			DBGC ( tcp, "TCP %p received overlength option %d\n",
980
+			       tcp, kind );
981
+			return -EINVAL;
982
+		}
949 983
 		data += option->length;
950 984
 	}
985
+
986
+	return 0;
951 987
 }
952 988
 
953 989
 /**
@@ -1011,6 +1047,12 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq,
1011 1047
 			tcp->snd_win_scale = options->wsopt->scale;
1012 1048
 			tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE;
1013 1049
 		}
1050
+		DBGC ( tcp, "TCP %p using %stimestamps, %sSACK, TX window "
1051
+		       "x%d, RX window x%d\n", tcp,
1052
+		       ( ( tcp->flags & TCP_TS_ENABLED ) ? "" : "no " ),
1053
+		       ( ( tcp->flags & TCP_SACK_ENABLED ) ? "" : "no " ),
1054
+		       ( 1 << tcp->snd_win_scale ),
1055
+		       ( 1 << tcp->rcv_win_scale ) );
1014 1056
 	}
1015 1057
 
1016 1058
 	/* Ignore duplicate SYN */
@@ -1369,8 +1411,8 @@ static int tcp_rx ( struct io_buffer *iobuf,
1369 1411
 	ack = ntohl ( tcphdr->ack );
1370 1412
 	raw_win = ntohs ( tcphdr->win );
1371 1413
 	flags = tcphdr->flags;
1372
-	tcp_rx_opts ( tcp, ( ( ( void * ) tcphdr ) + sizeof ( *tcphdr ) ),
1373
-		      ( hlen - sizeof ( *tcphdr ) ), &options );
1414
+	if ( ( rc = tcp_rx_opts ( tcp, tcphdr, hlen, &options ) ) != 0 )
1415
+		goto discard;
1374 1416
 	if ( tcp && options.tsopt )
1375 1417
 		tcp->ts_val = ntohl ( options.tsopt->tsval );
1376 1418
 	iob_pull ( iobuf, hlen );

Завантаження…
Відмінити
Зберегти