NetBSD Problem Report #59559
From kre@munnari.OZ.AU Sun Jul 27 13:42:41 2025
Return-Path: <kre@munnari.OZ.AU>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id B4C5B1A923C
for <gnats-bugs@gnats.NetBSD.org>; Sun, 27 Jul 2025 13:42:41 +0000 (UTC)
Message-Id: <202507271342.56RDgVdM010506@jacaranda.noi.kre.to>
Date: Sun, 27 Jul 2025 20:42:31 +0700 (+07)
From: kre@munnari.OZ.AU
To: gnats-bugs@NetBSD.org
Subject: libc needs namespace protection for RENAME'd symbols
X-Send-Pr-Version: 3.95
>Number: 59559
>Category: lib
>Synopsis: libc needs namespace protection for RENAME'd symbols
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: lib-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Sun Jul 27 13:45:00 +0000 2025
>Last-Modified: Mon Jul 28 15:25:00 +0000 2025
>Originator: Robert Elz
>Release: All of them
>Organization:
>Environment:
Irrelevant
>Description:
Note this is an offshoot of PR toolchain/59549 so you can look
at that one to get the history - just ignore everything about
both gdb and ctype(3) (the two subjects of that PR) as none of
that is relevant here, and concentrate only on the reason that
gdb was needed in the first place for that PR.
Also see src/lib/libc/README for information on how the libc
namespace & __RENAME() functionality is intended to work.
And last of the preamble here, note that a lot of this is just
my impressions, and might all be wrong, so if you know better,
please explain the errors.
What that README doesn't mention (and should) is that almost
every libc global symbol which is referenced (as distinct from
just defined) inside libc everywhere, needs to undergo the
namespace dance, as described there for "NetBSD-specific
nonstandard extensions" (even when they aren't) as otherwise
a call in libc to what should be a standard function can be
overridden by an application which doesn't use that symbol,
doesn't include any header which defines it, and hence is
permitted to redefine the symbol however it likes.
But that's not the point of this PR, just a side comment.
It is the third type of libc function (in the README, in the
section "Old versions of library routines") which is the
issue here.
I will briefly summarise the example given there so I can
expand on it more:
There once was a function (well there still is)
time_t time(time_t *);
but once upon a time, time_t was int (aka int32_t)
making that function effectively:
int32_t time(int32_t *);
but the definition of time_t was changed, so now
we actually need
int64_t time(int64_t *);
In olden timed we had
_time() (from namespace.h) for the code
time (as a weak alias in libc).
With that, any really old code that called time() from
libc actually ended up calling _time() - the 32 bit
version of that interface - yet if an application doesn't
care about that interface, doesn't include <time.h> nor
any other header which defines it, it can define its own
global "time" (as a global variable, or a function),
which it can use, while anything inside libc calls _time()
and all is good - this is how things are supposed to work.
(And note, that up to that point, the signature of time()
had never [within the meaning of "never" here] changed,
so according to the README, the _time() variant from
namespace wouldn't be needed - but it is, and was, and
was all done correctly, it is just the README that is
lacking here.)
Then we changed time_t, and as README says, libc now
provides
_time global (implementing the old signature)
time weak alias for _time
__time50 global (implementing the new signature)
which all looks good, both _time and __time50 (for the "50"
see the README, that's not especially relevant here) are
_* symbols, which applications are not supposed to use,
those are reserved to the implementation, so no application
should be defining a global symbol __time50.
Or you'd think.
But the application still calls "time(...)", and
compiler/linker magic make new calls turn into calls to
__time50() (that's OK, that's the implementation doing
that, so use of the _* symbol is permitted). That's
what makes all of this work for your normal application.
But the application can also redefine time to be something
else - except this now turns into a bad example to use,
as time() is a standard function, defined in <time.h> and
if the application has included <time.h> it is not supposed
to redefine any symbols that header makes available.
So let's switch to the other case "NetBSD-specific nonstandard
extensions" - those things can be subject to signature changes
as much as anything else, and for this use the example from the
original PR (what caused all the problems) gmtime_r -- this is
a POSIX standard function, but not a C standard function (which
kind of makes it a weird special case, but still).
That one got internally renamed (to __gmtime_r50) at the same
time as time() was renamed above.
But as gmtime_r isn't a standard C function, an application
which wants to use it might very easily want to define a version
of it, just in case the implementation does not provide that
function (which as it happens, some do not, I believe, despite
it being required by POSIX).
As the function is intended to implement the (POSIX) standard
gmtime_r function, it is reasonable to get its declaration from
<time.h> which is where POSIX says it should be (though that is
probably fairly stupid, as if the declaration is there, and
visible, the implementation should be available as well).
Anyway, if the application does define a gmtime_r() function,
the renaming magic that accompanies the declaration of that
function (as the original gmtime_r is the 32 bit version, and
no new code wants that any more) applies to the definition of
the function (just as it does - as the README explains - to the
implementation inside libc).
That is, by providing a gmtime_r function definition, what the
application has actually done (with implementation assistance)
is to define a function called __gmtime_r50(). That would be
OK, except internally libc calls gmtime_r (it is used to implement
gmtime) so now we have the libc internals calling the application's
function - from which good results should not be expected (and
indeed, they were not - the implementation has gmtime() calling
gmtime_r() - the application has gmtime_r() calling gmtime(), using
the libc version of that, which is of course, simply infinite
recursion, hence the application crashed).
We could avoid that by treating all RENAME'd symbols (which always
rename into implementation namespace of course) as if they were
global symbols in the application namespace, and doing the namespace
dance for them as well -- meaning that in this case, __gmtime_r50
would be ___gmtime_r50, and that's what would be called inside libc,
and for external callers, there'd be a weak alias, which would map
__gmtime_r50 into ___gmtime_r50 for any application which does not
define its own __gmtime_r50 (ie: gmtime_r).
That is, we could, but should we? The __RENAME() is only visible
in the header along with the declaration of the function being
renamed, and if that is there, and the application has made it
visible, do the application should not be redefining the function.
So we could just leave things alone, and punt any issues caused
by this back as application bugs (which really they are). If the
application in this case had simply defined gmtime_r without including
<time.h> if that were possible (in this case, I believe not), or or
explicitly requested strict Cxx (doesn't matter which version)
then it would not have encountered the __RENAME() and it all would
have just worked.
[Note: for the application in question doing all this was probably
insane anyway, as its gmtime_r() implementation was decidedly inferior
to the libc version, and in particular, was not thread safe, in an
application which, I believe, uses threads.]
>How-To-Repeat:
See PR toolchain/59549 But don't bother trying to make it happen.
>Fix:
We could add a bunch more to src/lib/libc/include/namespace.h
but should we?
>Release-Note:
>Audit-Trail:
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59559: libc needs namespace protection for RENAME'd symbols
Date: Mon, 28 Jul 2025 05:02:00 +0300
The narration switches between gmtime* and mktime*. Is that a typo?
Please, can you edit-pr if it is. TIA.
-uwe
From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59559: libc needs namespace protection for RENAME'd symbols
Date: Mon, 28 Jul 2025 22:21:10 +0700
Date: Mon, 28 Jul 2025 02:10:02 +0000 (UTC)
From: "Valery Ushakov via gnats" <gnats-admin@NetBSD.org>
Message-ID: <20250728021002.18DBC1A923E@mollari.NetBSD.org>
| The narration switches between gmtime* and mktime*. Is that a typo?
typo, no, brain fade, yes ...
| Please, can you edit-pr if it is. TIA.
Fixed, thanks for pointing it out.
kre
>Unformatted:
(Contact us)
$NetBSD: query-full-pr,v 1.47 2022/09/11 19:34:41 kim Exp $
$NetBSD: gnats_config.sh,v 1.9 2014/08/02 14:16:04 spz Exp $
Copyright © 1994-2025
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.