Date: Fri, 01 Nov 2002 07:23:35 -0500 From: Michael Sinz <msinz@wgate.com> To: Matt Dillon <dillon@earth.backplane.com>, freebsd-hackers@freebsd.org Subject: Socket so_linger setting Message-ID: <3DC27247.5040100@wgate.com>
next in thread | raw e-mail | index | archive | help
During some parameter limit checking work, I ran into what I believe to be an error in FreeBSD. (Albeit unlikely to be hit) A setsockopt of the SO_LINGER field will cause strange results if the value is set above 32767. This is due to the fact that in struct socket, the so_linger field is a signed short and the parameter passed to setsockopt for linger is a signed long. What happens is that any value between 32768 and 65535 will cause so_linger to be negative. And then getsockopt will return a sign extended negative value in the signed long field for linger. The "trivial" fix is to do the following: ------------------------------------------------------ --- uipc_socket.c Wed May 1 01:13:02 2002 +++ /tmp/uipc_socket.c Fri Nov 1 06:55:10 2002 @@ -1139,7 +1139,8 @@ if (error) goto bad; - so->so_linger = l.l_linger; + /* Limit the value to what fits in so_linger */ + so->so_linger = (l.l_linger > SHRT_MAX ? SHRT_MAX : l.linger); if (l.l_onoff) so->so_options |= SO_LINGER; else ------------------------------------------------------ What this does is limit the value to no more than 32767 (SHRT_MAX) However, I believe the more correct answer is that so_linger should not be a signed value to begin with. The reasoning is that what does a negative so_linger mean? To close the socket before the user does ;^)? It is somewhat obvious that so_linger does not need to be a long. It is not possible to change the API to make the input a short. Limiting the value to 32767 is reasonable (and that is a *vary* long linger time) However, given that negative linger values really don't exist (logically) it would be reasonable to not that field be signed. That would naturally limit the values to being within a valid range and prevent some strange results, specifically when looking at the tsleep() call where the so_linger field is just blindly multiplied by the hz of the system. (around line 312 of uipc_socket.c) A segative so_linger will get sign extended into a negative int (32-bit) (times hz) and then passed to tsleep which just checks for zero, passed on to timeout which then passes it to callout_reset. It turns out that callout_reset will take negative values and make them a single tick... (whew! lucky thing that was there :-) The question I have is: should put together a patch that changes so_linger (and xso_linger) to unsigned? (And make sure there are no bad side effects) or is the trivial fix above what is wanted? [ My personal feeling is that since so_linger has no valid negative value that the field should be unsigned. Not that it matters about improving the range as 32767 is over 9 hours. It is more a matter of "correctness" in the code/representation since the code assumes the value is not negative already. ] -- Michael Sinz -- Director, Systems Engineering -- Worldgate Communications A master's secrets are only as good as the master's ability to explain them to others. To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3DC27247.5040100>