Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 17 Jun 2018 14:50:35 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Jilles Tjoelker <jilles@stack.nl>
Cc:        Bruce Evans <brde@optusnet.com.au>, Eitan Adler <eadler@freebsd.org>,  src-committers@freebsd.org, svn-src-all@freebsd.org,  svn-src-head@freebsd.org
Subject:   Re: svn commit: r335041 - head/lib/libc/stdlib
Message-ID:  <20180617134656.I1009@besplex.bde.org>
In-Reply-To: <20180613211443.GA57326@stack.nl>
References:  <201806130852.w5D8qH9a093758@repo.freebsd.org> <20180613194008.W2003@besplex.bde.org> <20180613211443.GA57326@stack.nl>

next in thread | previous in thread | raw e-mail | index | archive | help
On Wed, 13 Jun 2018, Jilles Tjoelker wrote:

> On Wed, Jun 13, 2018 at 08:03:13PM +1000, Bruce Evans wrote:
>> On Wed, 13 Jun 2018, Eitan Adler wrote:
>
>>> Log:
>>>  libc: remove explicit cast NULL in atoi
>
>>>  There isn't any reason to cast NULL so just remove it. Noticed when
>>>  cleaning up top.
>
>> There are many reasons to cast NULL for all members of the ato*() family:
>> - it is required if no prototype is in scope
>> - C99 specifies ato*() in terms of strtol*() and uses the cast to NULL,
>> ...
>> - POSIX specifies ato*() in terms of strtol*() and uses the cast to NULL,
>> ...
>
> These reasons can be summarized to a single reason: the cast is required
> if no prototype is in scope.
>
> I think it is unwise to call any function without a prototype in scope,
> since this runs a risk of undefined behaviour if you get the types
> wrong.

That is a style matter, so it must not be enforced by specifications or man
pages, and specifications and man pages should not have details to advocate
it or to say when a prototype is needed for every single function.

> For the code in libc, we ensure a prototype is in scope and no cast is
> required. For the code in the man page, I doubt we should allow for
> programmers that play tricks by declaring system functions manually
> using K&R-style declarations or (even worse) call functions without
> declaring them at all.

Programmers who understand prototypes have no difficulty with not using
them.  libc isn't even controlled by us.  Its API is specified by the
standard selected by the user.  Even C99 doesn't require prototypes.

> Note that NULL may still need a cast when passed to a function with
> variable number of parameters. Ideally these types are also checked
> using attributes.

A cast is almost needed for this NULL too.  strtol()'s endptr arg
should have type const char * so that strtol() can handle const char * 
string args without needing to cast away const in the caller.  But then
strtol() couldn't handle plain char * string args without needing worse
casting away of const for the variable pointing to endptr.  This variable
would have type char * so its address would have type char **.  C's type
system is too weak for safe conversion of this to const char **, so the
prototype is not allowed to do it without a diagnostic and an explicit
cast must not be used.  This cast is also dangerous, so the problem is
reduced by using plain char ** for endptr.

Since NULL is either an integer constant with value 0 or such a constant
cast to unqualified void *, it can be converted without a diagnostic to
either char ** or const char **.  This is not obvious, and the explicit
cast makes it clearer that conversion to plain char ** is really intended.

>> FreeBSD used to do the same here, and should do the same here and
>> elsewhere by copying better wording from POSIX whenever possible.
>
> For some reason, the FreeBSD text does not have the exception about
> error handling. This exception permits an implementation like musl's
> which calculates using int and hard-codes base 10, even if the compiler
> documents a cast from long to int as truncating bits. I don't think we
> should take advantage of this, though, since making atoi() faster than
> strtol() may encourage people to use atoi().

Encouraging use of atoi() would almost be correct.  All it needs to
be a good API is defined error handling for unrepresentable values
and garbage input.  Too bad if the programmer doesn't check for errors.
Error checking for strtol() is very rarely complete or correct.
Unfortunately, atoi() isn't allowed to handle garbage input correctly.
It is only allowed to do the right thing for unrepresentable values.

I would clamp atoi() to INT_MAX/MIN and return ERANGE for unrepresentable
values instead of blindly truncating long values.  This already happens
accidentally with 32-bit longs.  FreeBSD's man page even documents this
by giving the implementation detail for the undefined behaviour.

Bruce



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