svn commit: r193941 - head/sys/netinet
John Baldwin
jhb at freebsd.org
Thu Jun 11 14:22:30 UTC 2009
On Wednesday 10 June 2009 11:53:16 pm Bruce Evans wrote:
> On Wed, 10 Jun 2009, John Baldwin wrote:
>
> > Log:
> > Change a few members of tcpcb that store cached copies of ticks to be ints
> > instead of unsigned longs. This fixes a few overflow edge cases on 64-bit
> > platforms. Specifically, if an idle connection receives a packet shortly
>
> I think the variables should still be unsigned (ints now). Otherwise there
> is at best benign undefined behaviour when the variables overflow at
> INT_MAX, while the code is apparently designed to work with unsigned
> values (it casts to int to look at differences between the unsigned values).
I wanted to match 'ticks' and figured changing 'ticks' was a far larger
headache.
> > Modified: head/sys/netinet/tcp_input.c
> > ==============================================================================
> > --- head/sys/netinet/tcp_input.c Wed Jun 10 18:26:02 2009 (r193940)
> > +++ head/sys/netinet/tcp_input.c Wed Jun 10 18:27:15 2009 (r193941)
> > @@ -1778,7 +1778,7 @@ tcp_do_segment(struct mbuf *m, struct tc
> > TSTMP_LT(to.to_tsval, tp->ts_recent)) {
> >
> > /* Check to see if ts_recent is over 24 days old. */
> > - if ((int)(ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
> > + if ((ticks - tp->ts_recent_age) > TCP_PAWS_IDLE) {
>
> The variables are now ints, and there is now also overflow in the
> subtraction. E.g., INT_MAX - INT_MIN now overflows. On 2's complement
> machines it normally overflows to -2, which is probably the wrong value
> (the large unsigned value 0xFFF...FEU is probably correct), but it is
> the same value as is given by casting the difference of unsigned values
> to int ((int)0xFFF...FEU = -2). This is because, when representing times
> mod UINT_MAX+1, all time differences up to UINT_MAX can be represented,
> but only if we keep track of which operand is older (here we probably do
> know that ts_recent_age is older); if we don't keep track then we normally
> assume that (unsigned) differences smaller than INT_MAX mean that the
> difference is nonnegative while differences larger than INT_MAX mean
> that the difference is negative. The signed interpreatation works well
> if we know that the nonnegative differences never exceed INT_MAX.
I believe in these cases that 1) the differences should never exceed INT_MAX,
and 2) we do know which value should be older.
> Style bug: without the cast, the above has excessive parentheses.
Ok.
> > Modified: head/sys/netinet/tcp_usrreq.c
> > ==============================================================================
> > --- head/sys/netinet/tcp_usrreq.c Wed Jun 10 18:26:02 2009 (r193940)
> > +++ head/sys/netinet/tcp_usrreq.c Wed Jun 10 18:27:15 2009 (r193941)
> > @@ -1823,7 +1823,7 @@ db_print_tcpcb(struct tcpcb *tp, const c
> > tp->snd_recover);
> >
> > db_print_indent(indent);
> > - db_printf("t_maxopd: %u t_rcvtime: %lu t_startime: %lu\n",
> > + db_printf("t_maxopd: %u t_rcvtime: %u t_startime: %u\n",
> > tp->t_maxopd, tp->t_rcvtime, tp->t_starttime);
> >
> > db_print_indent(indent);
>
> You still print all the times as unsigned. Since the variables are now
> signed, this is now a printf format error, which gcc still doesn't detect
> but I usually do.
Ok.
> > Modified: head/sys/netinet/tcp_var.h
> > ==============================================================================
> > --- head/sys/netinet/tcp_var.h Wed Jun 10 18:26:02 2009 (r193940)
> > +++ head/sys/netinet/tcp_var.h Wed Jun 10 18:27:15 2009 (r193941)
> > @@ -139,8 +139,8 @@ struct tcpcb {
> >
> > u_int t_maxopd; /* mss plus options */
> >
> > - u_long t_rcvtime; /* inactivity time */
> > - u_long t_starttime; /* time connection was established */
> > + int t_rcvtime; /* inactivity time */
> > + int t_starttime; /* time connection was established */
> > int t_rtttime; /* round trip time */
> > tcp_seq t_rtseq; /* sequence number being timed */
> >
> > @@ -167,7 +167,7 @@ struct tcpcb {
> > u_char rcv_scale; /* window scaling for recv window */
> > u_char request_r_scale; /* pending window scaling */
> > u_int32_t ts_recent; /* timestamp echo data */
> > - u_long ts_recent_age; /* when last updated */
> > + int ts_recent_age; /* when last updated */
> > u_int32_t ts_offset; /* our timestamp offset */
> >
> > tcp_seq last_ack_sent;
> > @@ -175,7 +175,7 @@ struct tcpcb {
> > u_long snd_cwnd_prev; /* cwnd prior to retransmit */
> > u_long snd_ssthresh_prev; /* ssthresh prior to retransmit */
> > tcp_seq snd_recover_prev; /* snd_recover prior to retransmit */
> > - u_long t_badrxtwin; /* window for retransmit recovery */
> > + int t_badrxtwin; /* window for retransmit recovery */
> > u_char snd_limited; /* segments limited transmitted */
> > /* SACK related state */
> > int snd_numholes; /* number of holes seen by sender */
> >
>
> Should all be changed to u_int?
>
> There still seem to be some very nice sign extension/overflow bugs.
> E.g., `ticks' is (bogusly) signed. After changing the above variables
> to int to be bug for bug compatible with `ticks', the semantics of
> expressions not directly touched in this commit is changed too. E.g.,
> there is the expression `ticks < tp->t_badrxtwin'. Changing the signedness
> of the type of tp_t_badrxtwin stops promotion of `ticks' to unsigned in
> this expression. However, this expression seems to have been just broken
> before, and the change only moves the bug slightly (from times near where
> `ticks' wraps around at to to times near where `ticks' overflows at
> INT_MAX). To handle wraparound and avoid overflow, such expressions should
> be written as (int)(ticks - tp->t_badrxtwin) < 0 where the variables have
> unsigned type. This seems to have already been done for all the other
> variables changed in this commit, except the cast to int is missing in
> some cases.
Yes, I noticed the t_badrxtwin breakage but wasn't sure how best to fix it
(or at least thought that it should be a separate followup since it was
already broken with wraparound). I considered making it work more like
the other variables such as t_rcvtime that merely store a cached value of
'ticks' and then use 'ticks - foo' and compare it with the given interval.
This would entail renaming 't_badrxtwin' to 't_badrxttime' or some such.
That seems clearer to me than '(int)(ticks - t_badrxtwin) < 0'.
--
John Baldwin
More information about the svn-src-all
mailing list