Date: Mon, 12 Mar 2007 23:07:39 -0700 (PDT) From: "Eric P. Scott" <eps+pbug0703@ana.com> To: FreeBSD-gnats-submit@FreeBSD.org Subject: kern/110249: [kern] setsockopt() error regression in FreeBSD 6.x Message-ID: <200703130607.l2D67dhW022284@anna.ana.com> Resent-Message-ID: <200703130620.l2D6K2nU050123@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 110249 >Category: kern >Synopsis: [kern] setsockopt() error regression in FreeBSD 6.x >Confidential: no >Severity: serious >Priority: medium >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Tue Mar 13 06:20:01 GMT 2007 >Closed-Date: >Last-Modified: >Originator: Eric P. Scott >Release: FreeBSD 6.2-RELEASE-p2 i386 >Organization: ana-systems, Inc. >Environment: System: FreeBSD sixofone 6.2-RELEASE-p2 FreeBSD 6.2-RELEASE-p2 #0: Tue Feb 27 22:41:06 UTC 2007 root@i386-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC i386 >Description: This started out as "why is net/linux-nx-client broken for FreeBSD 6.x," but turns out to have far deeper implications. It should come as no surprise there's a lot of code out there that invokes system calls that might fail, and makes certain assumptions about how such failures will be reported. Sources intended to be cross-platform sometimes need to make allowances for differing implementations; e.g. some "System V- derived" environments may not behave identically to "BSD-derived" ones. However, once the characteristics of a particular platform are established, they're expected to remain essentially immutable. On those rare occasions where it's deemed necessary to modify an ABI, extreme care must be taken to do so as non- destructively as possible. Mitigation strategies may take many forms, such as creating additional sysent[] vectors, or providing compatibility knobs. This PR is about what happens when an executable attempts to set TCP_NODELAY on a UNIX protocol socket. This is, of course, a "stupid" thing for an program to attempt. It can't possibly succeed, and software developers Should Know Better. However, it happens in The Real World, and we need to respect that. On FreeBSD 4.x/5.x and Linux 2.4.x/2.6.x, the specific error reported in this case is [EOPNOTSUPP]. Mac OS X 10.4.x and DragonFly are also in this camp. OpenBSD, Solaris 8/9, and Mac OS X 10.3.x return [ENOPROTOOPT] instead. The descriptions of both error codes in the intro(2) manual page would tend to suggest either one is plausible in this situation, and Real World code that's intended to be "reasonably portable" typically treats either one identically. The lone dissenter had been NetBSD, which, beginning with their 1.4 release, picked [EINVAL]. In The Early Days of UNIX (when there just weren't that many error codes defined), that might have been a logical choice. I claim it's not a wise one today. Shortly before FreeBSD 6.0 was released, some well-meaning, but reckless, soul made the terrible mistake of inflicting NetBSD's braindamage upon uipc_ctloutput(), and this appears to be the result of src/kern/uipc_usrreq.c CVS Revision 1.153: "Check sopt_level in uipc_ctloutput() and return early if it is non-zero. This prevents unintended consequnces[sic] when an application calls things like setsockopt(x, SOL_SOCKET, SO_REUSEADDR, ...) on a Unix domain socket." I understand why it was put in there, and what it was intended to accomplish, but it's too heavy-handed--especially considering its adverse impact on anything using compat4x, compat5x, or the Linuxulator. It's also inconsistent with what's documented on the setsockopt(2) and unix(4) man pages for 6.2-RELEASE. The documentation is fine; it's the kernel that needs to conform. I'm proposing simply modifying the return value to be [EOPNOTSUPP]. I've tested this change using a patched kernel, and it unbroke all of the legacy FreeBSD executables and Linux binaries I tried. >How-To-Repeat: #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/tcp.h> main(int argc, char *argv[]) { register int s; int v; if ((s=socket(PF_UNIX, SOCK_STREAM, 0))<0) { perror("socket"); exit(1); } v=1; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *)&v, (socklen_t)sizeof v)<0) { perror("TCP_NODELAY"); (void)fflush(stderr); } (void)close(s); exit(0); } >Fix: [This is for 6.2-RELEASE; -CURRENT is similar, except there's no UNP_LOCK()] --- src/kern/uipc_usrreq.c.orig Thu Jul 13 06:42:37 2006 +++ src/kern/uipc_usrreq.c @@ -630,7 +630,7 @@ int error, optval; if (sopt->sopt_level != 0) - return (EINVAL); + return (EOPNOTSUPP); UNP_LOCK(); unp = sotounpcb(so); >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200703130607.l2D67dhW022284>