Marking select(2) as restrict

Jilles Tjoelker jilles at
Tue Feb 27 21:01:13 UTC 2018

On Mon, Feb 26, 2018 at 09:00:21PM +1100, Bruce Evans wrote:
> On Sun, 25 Feb 2018, Mark Millard wrote:
> > On 2018-Feb-25, at 7:45 PM, Bruce Evans <brde at> wrote:
> >> . . . restrict is not needed for the input-only
> >> args since const suffices.

> > Here you lost me.

> > With q and r having both the const and the restrict,
> > updates to p's "objects" can not change the
> > "object(s)" q and r validly can be used to access.
> > That can be important.

> > Without the "restrict" for q and r (but still
> > having the const for each) it is valid for updates
> > to p's "objects" to change what q and r then can
> > validly access.

> Yes, I forgot that all const on a pointer arg does is prevent modification
> through that pointer.  Modifications can still occur through other args.
> memmove() is a canonical example.  Its source arg is const, by the source
> is _always_ modified in the overlapping case that is the reason for
> existence of memmove().

In fact, it is even weaker. The C standard permits casting away const
and using the resulting pointer for reading. Unless the underlying
object is defined as const, is a string constant or the pointer is based
on a restrict pointer to const, the pointer can also be used for
writing. In FreeBSD code __DECONST should be used instead of a regular
cast, but the same things can be done with the resulting pointer.

> memcpy() is another canonical example.  Both of its args are declared with
> restrict, except in its man page.  Its source arg is still declared as
> const.  Without that, either arg could be modified.  With that and restrict
> on the other arg, I think it follows that the source arg cannot be modified.
> It is unclear if restrict on the source arg is needed too.

The restrict on the destination alone prevents it aliasing the source.
However, the restrict on the source prevents it aliasing globals and
thread-locals. This is not needed for memcpy() which does not write any,
but is often needed for more complicated functions.

> It is now clear(er) that POSIX's restricts for sigaction() are correct
> and not having them for nanosleep() is a bug.  nanosleep()'s args are
> const struct timespec * and struct timespec *.  Nothing prevents these
> being aliased, just like for memmove(), and unlike for memcpy(), the
> behaviour is not undefined when aliasing occurs.  Aliasing can only
> occur if the pointers are equal, since unlike for mem*() they don't point
> to arrays (but the prototype doesn't give this information.)  So
> nanosleep(&ts, &ts) must work, and working involves clobbering the input
> arg.  The implementation must be careful to not write the output before
> reading the input (if the pointers are not equal), and callers using the
> same timespec for input and output must not depend on the source being
> const.  This is just like for the non-restrict select() except it is
> easier to avoid problems (e.g., by copying) with a single small object
> than with a potentially large array for the input arg.

Not having restrict on nanosleep()'s args is not much of a burden since
its input must necessarily be read before the output can be written
(unlike sigaction()). Therefore, I don't think this is a bug.

A call to nanosleep() with two equal pointers also has the useful
property of being restartable after signals without needing fixups.

> My argument applies better to pointer args of different types.  Then const
> prevents modifications through some pointer args and the different types
> prevent aliasing of the non-const args to the const args.

Assuming there are no pointers to character type.

> For select() with restrict, I think the compiler cannot assert that
> the args don't overlap since (even without the detailed specification
> and Example 3), the compiler cannot know if select() modifies its args.
> For all that the compiler knows, select() might be a stub that never
> modifies or even reads anything.  I think doing no accesses satifies
> the constraints of restrict.  It might be valid for the compiler to
> assert that the (values pointed to by) the fdset args don't change for
> select(nfd, &fdset, &fdset, &fdset, &tv) (because any write access
> through an fdset type would give undefined behaviour on the other fdset
> args; tv can still change since it has a different type).  But this is
> precisely what is wanted for the original example where we only care
> about the return value -- then fdset is indeterminate after the call
> so we shouldn't use it; the compiler is unlikely to optimize the non-use
> of it and the worst that it can do is add a runtime assertion that the
> arg didn't change.

Agreed, with the addition that the arg may even change per Tijl
Cooseman's reply.

Jilles Tjoelker

More information about the freebsd-standards mailing list