Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 2 Dec 1995 21:12:06 GMT
From:      James Raynard <james@parody.tecc.co.uk>
To:        freebsd-hackers@freebsd.org
Subject:   Bug in TCP's urgent data mechanism (fwd)
Message-ID:  <199512022112.VAA00454@parody.tecc.co.uk>

next in thread | raw e-mail | index | archive | help
This was posted to comp.protocols.tcp-ip a few days ago and I thought I'd
forward it here as 2.1.0-RELEASE contains the (alleged) bug. Any comments?

James

------- start of forwarded message -------
Path: parody.tecc.co.uk!handbag.tecc.co.uk!plug.news.pipex.net!pipex!tube.news.pipex.net!pipex!tank.news.pipex.net!pipex!usenet.eel.ufl.edu!news-feed-1.peachnet.edu!pirates!cssun.mathcs.emory.edu!swrinde!newsfeed.internetmci.com!in1.uu.net!news.biu.ac.il!news.huji.ac.il!shum.cc.huji.ac.il!pretzel.cs.huji.ac.il!pita.cs.huji.ac.il!orenl
Newsgroups: comp.protocols.tcp-ip
Subject: Bug in TCP's urgent data mechanism
Message-ID: <49bpdr$d41@pretzel.cs.huji.ac.il>
From: orenl@pita.cs.huji.ac.il (Oren Laden)
Date: 27 Nov 1995 07:30:03 GMT
Distribution: world
Organization: The Hebrew U. of Jerusalem, Computer Science Dept.
NNTP-Posting-Host: pita.cs.huji.ac.il
Lines: 118

Description:
-----------
Under certain circumstances (see below) TCP's output code does not set
the URG flag on an outgoing packet holding urgent data. As a consequence
this data becomes available to the consumer process as part of the
regular stream.

Consider the following scenario (with two processes, A and B):
(assume at side A: snd_nxt == snd_una == snd_up == 573)
A issues a write() syscall for 28 bytes. Before TCP actually delivers
this data (OR the packet with the data got lost), A sends one OOB to B
(seq#601). Now TCP's output function (tcp_output()) sends this urgent
byte to the receiving side. At the other side, tcp_input() detects the
urgent data and pulls it out. It also sends ACK for 573 (because the
next packet was lost). tcp_output() (on A) retransmits 573-602 (the
28 bytes + the OOB). However, at the point where the code decides to
set (or unset) the URG flag on the outgoing packet, the test compares
snd_up to snd_nxt (which has advanced since !), resulting in a packet
holding urgent data, without the URG flag set !This byte propagates
later to process B's input stream, leading to unexpected behaviour.

For example, watch the following TCP trace (filtered output of tcpdump):

1.      A > b: P 557:573(16) ack 705 win 18980
2.      b > A: P 705:737(32) ack 573 win 18432
3.      A > b: P 601:602(1) ack 737 win 18980 urg 1
4.      b > A: . ack 573 win 18432
5.      A > b: . ack 737 win 18980
6.      b > A: P 737:745(8) ack 573 win 18432
7.      A > b: . ack 745 win 18980
8.      A > b: P 573:602(29) ack 745 win 18980
9.      b > A: . ack 602 win 18403
10.     b > A: P 745:769(24) ack 602 win 18431
11.     A > b: P 602:618(16) ack 769 win 18980

Note lines 3 (OOB delivering), 4 (ACK stays behind) and 8 (urgent data
without URG flag).

Release:
-------
BSD/OS 1.0, 1.1, 2.0, 2.0.1

as well as:

4.4BSD networking code (and derivatives).

Repeat-By:
---------
It isn't simple to make the bug show itself... especially at the user level.
Here is the proposed method (not deterministic, of course):
Creat process A and B on different machines. Use INET sockets (TCP) to
connect A and B, and then fork() A into two processes. Use the parent to
write data to B, and the child to write OOB data (one byte) from time to
time. If the network is congested enough, and you are lucky - process B
will receive one extra byte of data.

Fix:
---
The fix is very simple - a small patch to netinet/tcp_output.c :

Replace the lines surrounded by ifdef's (in tcp_output() subroutine...):

tcp_output(tp)
      register struct tcpcb *tp;
{
        ...
        ...
        ...
      /*
       * Calculate receive window.  Don't shrink window,
       * but avoid silly window syndrome.
       */
      if (win < (long)(so->so_rcv.sb_hiwat / 4) && win < (long)tp->t_maxseg)
              win = 0;
      if (win > (long)TCP_MAXWIN << tp->rcv_scale)
              win = (long)TCP_MAXWIN << tp->rcv_scale;
      if (win < (long)(tp->rcv_adv - tp->rcv_nxt))
              win = (long)(tp->rcv_adv - tp->rcv_nxt);
      ti->ti_win = htons((u_short) (win>>tp->rcv_scale));
#ifdef FIX_OOB_BUG

      if (SEQ_GT(tp->snd_up, tp->snd_una)) {
              ti->ti_urp = htons((u_short)(tp->snd_up - ntohl(ti->ti_seq)));

#else   /* THE CODE BELOW IS WRONG ! */

      if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
              ti->ti_urp = htons((u_short)(tp->snd_up - tp->snd_nxt));

#endif /* BUG SHOULD BE FIXED :-) */

              ti->ti_flags |= TH_URG;
      } else
              /*
               * If no urgent pointer to send, then we pull
               * the urgent pointer to the left edge of the send window
               * so that it doesn't drift into the send window on sequence
               * number wraparound.
               */

        ...
        ...
        ...
}

That's it folks.

Oren.
                                                  ___ 
**************************************** |\/\/\/||   \                     
** Oren Laden (orenl@cs.huji.ac.il) ** |      |  |___/   /\     ___     
** ---------------------------------- ** |    |  |   \  /  \   |   \ _____
** Distributed Operating Systems lab. ** | (0)(0)|___/ /----\  |___/   |  
**   Computer Science Institute     ** c      _)      /      \ |  \    |
** The Hebrew University of Jerusalem **| ,___|                |   \   |   
**    Jerusalem,  Israel 91904      **  |   /                          |
**************************************** /____\   S  I  M  P  S  O  N
   " Ouch ....   Quit It ! "          /      \       
------- end of forwarded message -------




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199512022112.VAA00454>