svn commit: r193941 - head/sys/netinet

Bruce Evans brde at optusnet.com.au
Sat Jun 13 00:57:01 UTC 2009


On Fri, 12 Jun 2009, John Baldwin wrote:

> On Thursday 11 June 2009 11:12:58 pm Bruce Evans wrote:
>> On Thu, 11 Jun 2009, John Baldwin wrote:
>>
>>> On Wednesday 10 June 2009 11:53:16 pm Bruce Evans wrote:
>>>> On Wed, 10 Jun 2009, John Baldwin wrote:
>>>
>>> I wanted to match 'ticks' and figured changing 'ticks' was a far larger
>>> headache.
>>
>> By changing the signedness you get undefined behaviour for the other types
>> too, and risk new and/or different sign extension bugs.
>
> FWIW, the variables were signed before they were changed to unsigned and are now
> back as signed again.  It just seems really odd to have the types not match
> 'ticks' especially since many of the values are just cached copies of 'ticks'.

No, they were originally u_long and stayed that way until you changed them
(except possibly for newer variables).  The type of `ticks' is just wrong,
and fixing that would take more work, but there is no reason to propagate
this bug to new variables.  The original version is:

% Index: tcp_var.h
% ===================================================================
% RCS file: /home/ncvs/src/sys/netinet/tcp_var.h,v
% retrieving revision 1.51
% retrieving revision 1.52
% diff -u -2 -r1.51 -r1.52
% --- tcp_var.h	28 Aug 1999 00:49:33 -0000	1.51
% +++ tcp_var.h	30 Aug 1999 21:17:07 -0000	1.52
% ...
% @@ -99,10 +103,10 @@
%  	u_int	t_maxopd;		/* mss plus options */
% 
% -	u_int	t_idle;			/* inactivity time */

This even had a reasonable type.

% -	u_long	t_duration;		/* connection duration */

Maybe copying this type is responsible for the sizing bug.

% -	int	t_rtt;			/* round trip time */

This was a short in FreeBSD-1 and presumably in Net/2.  Since it is unrelated
to `ticks' and doesn't need to exceed INT_MAX (or maybe even SHRT_MAX), a
signed type is best for it.

% +	u_long	t_rcvtime;		/* inactivity time */
% +	u_long	t_starttime;		/* time connection was established */

New variables.

% +	int	t_rtttime;		/* round trip time */

This was t_rtt in the above.  This commit just regressed its name (to a
more verbose one with a bogus extra "t" ("ttime" = "time time").  There is
now also a t_bw_rtttime with the same bugs.  Other rtt variables are
still spelled correctly, but their types are all over the map: from Mar
30 sources:

| icmp6.h:struct	rttimer;
| sctp_structs.h:	uint32_t prev_rtt;
| sctp_structs.h:	uint8_t do_rtt;
| sctp_uio.h:	uint32_t spinfo_srtt;
| sctp_uio.h:	uint32_t rtt;
| sctp_uio.h:	uint32_t sctps_fastretransinrtt;	/* number of multiple FR in a
| tcp.h:	u_int32_t	tcpi_rtt;		/* Smoothed RTT in usecs. */
| tcp.h:	u_int32_t	tcpi_rttvar;		/* RTT variance in usecs. */
| tcp.h:	u_int32_t	__tcpi_rcv_rtt;
| tcp_hostcache.h:	u_long	rmx_rtt;	/* estimated round trip time */
| tcp_hostcache.h:	u_long	rmx_rttvar;	/* estimated rtt variance */
| tcp_timer.h: * up in the srtt because the timestamp is often calculated at
| tcp_var.h:	u_long	t_starttime;		/* time connection was established */
| tcp_var.h:	int	t_rtttime;		/* round trip time */
| tcp_var.h:	int	t_bw_rtttime;		/* used for bandwidth calculation */
| tcp_var.h:	int	t_srtt;			/* smoothed round-trip time */
| tcp_var.h:	int	t_rttvar;		/* variance in round-trip time */

This was short in FreeeBSD-1.

| tcp_var.h:	u_int	t_rttmin;		/* minimum rtt allowed */

This was u_short in FreeeBSD-1.

| tcp_var.h:	u_int	t_rttbest;		/* best rtt we've seen */
| tcp_var.h:	u_long	t_rttupdated;		/* number of times rtt sampled */
| tcp_var.h:	int	t_rttlow;		/* smallest observerved RTT */
| tcp_var.h:	u_long	rmx_rtt;	/* estimated round trip time */
| tcp_var.h:	u_long	rmx_rttvar;	/* estimated rtt variance */
| tcp_var.h:	u_long		t_starttime;
| tcp_var.h: * With these scales, srtt has 3 bits to the right of the binary point,
| tcp_var.h: * and thus an "ALPHA" of 0.875.  rttvar has 2 bits to the right of the
| tcp_var.h:#define	TCP_RTT_SCALE		32	/* multiplier for srtt; 3 bits frac. */
| tcp_var.h:#define	TCP_RTT_SHIFT		5	/* shift for srtt; 3 bits frac. */
| tcp_var.h:#define	TCP_RTTVAR_SCALE	16	/* multiplier for rttvar; 2 bits */
| tcp_var.h:#define	TCP_RTTVAR_SHIFT	4	/* shift for rttvar; 2 bits */
| tcp_var.h: * The initial retransmission should happen at rtt + 4 * rttvar.
| tcp_var.h: * Because of the way we do the smoothing, srtt and rttvar
| tcp_var.h:	max((tp)->t_rttmin, (((tp)->t_srtt >> (TCP_RTT_SHIFT - TCP_DELTA_SHIFT))  \
| tcp_var.h:	  + (tp)->t_rttvar) >> TCP_DELTA_SHIFT)
| tcp_var.h:	u_long	tcps_segstimed;		/* segs where we tried to get rtt */
| tcp_var.h:	u_long	tcps_rttupdated;	/* times we succeeded */
| tcp_var.h:	u_long	tcps_cachedrtt;		/* times cached RTT in route updated */
| tcp_var.h:	u_long	tcps_cachedrttvar;	/* times cached rttvar updated */
| tcp_var.h:	u_long	tcps_usedrtt;		/* times RTT initialized from route */
| tcp_var.h:	u_long	tcps_usedrttvar;	/* times RTTVAR initialized from rt */
| tcp_var.h:	{ "rttdflt", CTLTYPE_INT }, \
| vinet.h:	int	_tcp_inflight_rttthresh;
| vinet.h:#define	V_tcp_inflight_rttthresh VNET_INET(tcp_inflight_rttthresh)

Back to the old diff:

%  	tcp_seq	t_rtseq;		/* sequence number being timed */
% 
% -	int	t_rxtcur;		/* current retransmit value */
% +	int	t_rxtcur;		/* current retransmit value (ticks) */

This was short in FreeBSD-1.  short would probably have remained enough
with HZ=100.  Less probably with HZ=1000.

%  	u_int	t_maxseg;		/* maximum segment size */
%  	int	t_srtt;			/* smoothed round-trip time */
% @@ -132,4 +136,8 @@
%  	tcp_cc	cc_send;		/* send connection count */
%  	tcp_cc	cc_recv;		/* receive connection count */
% +/* experimental */
% +	u_long	snd_cwnd_prev;		/* cwnd prior to retransmit */
% +	u_long	snd_ssthresh_prev;	/* ssthresh prior to retransmit */
% +	u_long	t_badrxtwin;		/* window for retransmit recovery */
%  };

Probably an error for these to be u_long.  However, in old times people
worried more about int being 16 bits and used long too much.  Using fixed-
width types would be worse.

% 
% ...

I checked more closely for uses of these variables and found:
- most uses of t_rcvtime are of the form:

 		if ((ticks - tp->t_rcvtime) >= tcp_keepidle + tcp_maxidle)

   This is safe with int variables modulo its undefined behaviour being
   benign.  The expression "ticks - tp->t_rcvtime" is almost always
   parenthesized, as would be needed if it were cast, but this is
   especially bogus when it is compared with an unparenthesized expression
   as above.

- t_starttime is also a struct member in struct tcptw.  (struct tcptw has
   many related style bugs.  Only 5 of its members have a name beginning
   with tw_; 5 have no apparent prefix at all, 1 has an apparent ts_
   prefix that is not a prefix; t_recent has the same confusing prefix as
   t_starttime).

- struct tcpcb has many bugs of the same type :-(.  The recently changed
   ts_recent_age is one (ts_ only appears to be a prefix), and near it
   is the unprefixed request_r_scale...

- Apart from the above style bugs, the ofuscated t_starttime in struct
   tcptw gives another instance of the bug that you just fixed.  It still
   has type u_long (you didn't touch it), and is used in the problematic
   way: from tcp_timewait.c:

% 	tw->t_starttime = tp->t_starttime;
% ...
% int
% tcp_twrecycleable(struct tcptw *tw)
% {
% 	tcp_seq new_iss = tw->iss;
% 	tcp_seq new_irs = tw->irs;
% 
% 	INP_INFO_WLOCK_ASSERT(&tcbinfo);
% 	new_iss += (ticks - tw->t_starttime) * (ISN_BYTES_PER_SECOND / hz);
% 	new_irs += (ticks - tw->t_starttime) * (MS_ISN_BYTES_PER_SECOND / hz);

This gives the usual problem when tw_starttime was set from `ticks' before
`ticks' overflowed (ticks < INT_MAX), but `ticks' has now overflowed to be
negative.

% 
% 	if (SEQ_GT(new_iss, tw->snd_nxt) && SEQ_GT(new_irs, tw->rcv_nxt))
% 		return (1);
% 	else
% 		return (0);
% }

- there are 2 broken comparisons involving t_badrxtwin altogether: from
   tcp_input.c:

% 				if (tp->t_rxtshift == 1 &&
% 				    ticks < tp->t_badrxtwin) {
% ...
% 		if (tp->t_rxtshift == 1 && ticks < tp->t_badrxtwin) {

Bruce


More information about the svn-src-head mailing list