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