kern/110249: [kern] setsockopt() error regression in FreeBSD 6.x

Eric P. Scott eps+pbug0703 at
Tue Mar 13 06:20:02 UTC 2007

>Number:         110249
>Category:       kern
>Synopsis:       [kern] setsockopt() error regression in FreeBSD 6.x
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Tue Mar 13 06:20:01 GMT 2007
>Originator:     Eric P. Scott
>Release:        FreeBSD 6.2-RELEASE-p2 i386
ana-systems, Inc.
System: FreeBSD sixofone 6.2-RELEASE-p2 FreeBSD 6.2-RELEASE-p2 #0: Tue Feb 27 22:41:06 UTC 2007 root at i386
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]

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.
#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) {
	if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const void *)&v,
		(socklen_t)sizeof v)<0) {
[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 = sotounpcb(so);

More information about the freebsd-bugs mailing list