Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 27 Feb 2018 14:30:24 -0800 (PST)
From:      Chris Torek <torek@elf.torek.net>
To:        freebsd-hackers@freebsd.org
Subject:   Re: Marking select(2) as restrict
Message-ID:  <201802272230.w1RMUOmL079462@elf.torek.net>
In-Reply-To: <20180227210110.GA76452@stack.nl>

next in thread | previous in thread | raw e-mail | index | archive | help
If we can back up a bit, there are (at least) two mostly-separate
issues:

 * What types and qualfiiers to use for various parameters:
   These are dictated by standards, and we should just conform
   to whatever is in the standard unless there's some major
   reason to do otherwise.

   POSIX says that we *should* use "restrict" here:
   http://pubs.opengroup.org/onlinepubs/009696699/functions/pselect.html

   so if no one has a major reason to do otherwise (such as
   "no conforming program can tell that we didn't and all it will
   achieve is nothing" :-) ) I'd say add the restrict.

   (As kib and others have noted, it achieves a lot of nothing, so
   the priority for this particular conformance seems low.)

 * Select itself: it makes no sense *in the caller* to pass
   the same &fd_set argument to more than one parameter unless
   they are all input-only.  The kernel is going to write on them,
   and there is no guarantee that the kernel will write them in any
   particular order.  Hence if you write:

        ret = select(nfds, &a, &a, &a, timeout);

   you are nearly guaranteed to have a bug: this call can only
   be correct if the only value you intend to inspect is "ret".

   (That, in fact, is what devd was doing: if ret==0, daemonize,
   and in any case throw away the returned results in the by-pointer
   arguments.  Hence not a bug -- unless ... well, read on.)

   The remaining question is: can even that be correct?  In
   particular, should the kernel be required to use copy-in/copy-out
   semantics, or can it do the equivalent (minus error checking
   and a lot of other important details) of:

        int select(int nfds, fd_set *in, fd_set *out, fd_set *ex,
            struct timeval *tvp) {
                some_complicated_type v_in, v_out, v_ex;

                v1 = op(*in);
                *in = function(v1);
                v2 = op(*out);
                ...
        }

   ?  If the latter is *permitted*, then the call passing "&a"
   three times is *always* wrong, since *in might have been
   clobbered before *out is ever loaded.  If a program breaks
   because it was doing this, well, it was already broken, we were
   just lucky that it worked anyway.  (Was this good luck, or bad
   luck? :-) )

   Currently, the kernel does in fact copy them all in,
   then futz with them, then copy them all out (for good reasons).
   So the devd usage always works.  But will the kernel *always*
   do this, or is that an accident of the implementation?

   If the kernel *is* always going to do this, the documentation
   should say so, at which point the POSIX requirement for the
   restrict on the parameter is itself a bug (as far as BSD is
   concerned).  That would call for feature test macros around
   the declaration.

So, if we declare that select(nfds, &a, &a, &a, timeout) *is*
legal and meaningful in BSD, *don't* just put in the restrict:
use feature test macros as appropriate to handle any conformance
issues.  But please also update select(2) to call out when this
is OK (e.g., in devd's particular use case).

Chris



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