Bug in tcp_output?
Chris Harrer
cjharrer at comcast.net
Mon Mar 22 12:27:22 UTC 2010
Bruce and Rui,
I owe you an apology, sorry. As I was reading Bruce's response, I was
saying to myself that something isn't adding up and sure enough I went back
and looked and noticed that I was running with a locally modified tcp_var.h
(which changed the snd_cwnd container).
The "clean" version does indeed work as expected.
Again, sorry for the "noise".
Thanks.
Chris
-----Original Message-----
From: Rui Paulo [mailto:rpaulo at gmail.com] On Behalf Of Rui Paulo
Sent: Sunday, March 21, 2010 9:15 AM
To: Bruce Evans
Cc: Chris Harrer; freebsd-net at freebsd.org
Subject: Re: Bug in tcp_output?
On 21 Mar 2010, at 07:17, Bruce Evans wrote:
> On Sat, 20 Mar 2010, Rui Paulo wrote:
>
>> On 18 Mar 2010, at 20:19, Chris Harrer wrote:
>>>
>>> In the following block of code, running on a x86_64 platform, I believe
that
>>> cwin should be declared as an int:
>>> ...
>>> else {
>>>
>>> long cwin; ß-- Should be an int
>>> ...
>>> if (len > 0) {
>>>
>>> cwin = tp->snd_cwnd -
>>>
>>> (tp->snd_nxt - tp->sack_newdata) -
>>>
>>> sack_bytes_rxmt;
>>>
>>> if (cwin < 0)
>>>
>>> cwin = 0;
>>>
>>> len = lmin(len, cwin);
>>>
>>> }
>>>
>>> }
>>>
>>> }
>>>
>>>
>>>
>>> Consider the case where:
>>>
>>> sack_rxmit = 0
>>>
>>> sack_bytes_rxmt = 0x2238
>>>
>>> off = 0
>>>
>>> len =0xa19c
>>>
>>> tp->snd_cwnd = 0x2238
>>>
>>> tp->snd_nxt = 0xdd6d7974
>>>
>>> tp->sack_newdata = 0xdd6d6858
>>>
>>> In this case cwin evaluates to 0x00000000ffffe37c, which is not <0, but
>>> instead huge. This causes the remaining data on the sockets so->so_snd
>>> buffer to be sent to the network causing more problems at the receiver
which
>>> is already dropping frames.
>>
>> I see. This is most likely a bug. Can you send-pr so this doesn't get
lost?
>
> What bug do you see? This is most likely not a bug. I only see the
> following bugs
> - the suggestion to increase the fragility of the code by changing cwin to
> int
> - lots of whitespace lossage
> - the style bug in the declaration of cwin (nested declaration)
> - lots fragile but working code. It depends on the machine being a normal
> 2's complement one. It would fail on normal 1's complement machines and
> on abnormal 2's complement ones, but so would many other things in the
> kernel.
> - type and arithmetic errors that are not made at runtime resulting in a
> value that wouldn't work, though the runtime value would.
>
> Relevant code quoted again, with the whitespace fixed:
>
>>> cwin = tp->snd_cwnd -
>>> (tp->snd_nxt - tp->sack_newdata) -
>>> sack_bytes_rxmt;
>
> On 64-bit machines, with the above values, this is:
>
> rhs = (u_long)0x2238UL -
> ((tcp_seq)0xdd6d7974 -
> (tcp_seq)0xdd6d6858) -
> (int)0x2238;
> = (u_long)0x2238UL -
> ((uint32_t)0xdd6d7974 -
> (uint32_t)0xdd6d6858) -
> (int)0x2238;
> = (u_long)0x2238UL -
> (u_int)0x111c -
> (int)0x2238;
> = (u_long)0x111c -
> (int)0x2238;
> = (u_long)0x111c -
> (u_long)0x2238;
> = (u_long)0xffffffffffffeee4;
> cwin = (long)rhs;
> = -(long)0x111c;
>
> I might have made arithmetic errors too, but I'm sure that I got the
> conversions essentially correct. On machines with 64-bit u_longs,
> almost everything is evaluated modulo 2^64. This gives a large positive
> value, but not one with the top bits set up to only the 31st as would
> happen on machines with 32-bit u_longs. Then the final conversion to
> long gives a negative value.
Right. I made some bad calculations.
>
> This is fragile, but it is a standard 2's complement hack. It would
> fail mainly on normal ones complement machines when the rhs is
> (u_long)0xFF...FF. Then the lhs is probably negative 0, which is
> not less than 0.
>
> The fragility is essentially the same on machines with 32-bit u_longs.
> Almost everything is evaluated modulo 2^32...
>
> Using 64-bit u_longs for tp->snd_cwnd (and thus for almost the entire
> calculation) is exessive but doesn't cause any problems.
>
> Using a signed type for sack_bytes_rxmt asks for sign extension bugs but
> doesn't get them. Here it is promoted to a u_long so there are no
> sign extension bugs for it here.
>
> Using a signed type for cwin is essential for the comparison of cwin
> with 0 to work.
Right.
> This signed type should have the same size as the rhs
> to avoid even more fragility (if it were int, then you would have to
> worry about the value being changed to a non-working value by the
> implementation-defined conversion of the rhs to cwin not just for
> values larger than LONG_MAX but also for ones larger than INT_MAX.
> `int' should work in practice. This and other things depend on the
> difference of the tcp_seq's not being anywhere near as large as
> 0x7fffffff).
I assumed that Chris saw a problem with this code after being hit by some
TCP/IP interop issue. Was this the case?
--
Rui Paulo
More information about the freebsd-net
mailing list