aboutsummaryrefslogtreecommitdiff
path: root/lib/libc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc')
-rw-r--r--lib/libc/aarch64/string/timingsafe_memcmp.S2
-rw-r--r--lib/libc/gdtoa/_hdtoa.c3
-rw-r--r--lib/libc/gdtoa/_hldtoa.c3
-rw-r--r--lib/libc/gen/Symbol.map2
-rw-r--r--lib/libc/gen/fts-compat.c2
-rw-r--r--lib/libc/gen/fts-compat11.c2
-rw-r--r--lib/libc/gen/gen-compat.h48
-rw-r--r--lib/libc/gen/getgrouplist.329
-rw-r--r--lib/libc/gen/getgrouplist.c6
-rw-r--r--lib/libc/gen/initgroups.3101
-rw-r--r--lib/libc/gen/initgroups.c55
-rw-r--r--lib/libc/gen/psignal.39
-rw-r--r--lib/libc/gen/sysconf.c2
-rw-r--r--lib/libc/include/compat.h1
-rw-r--r--lib/libc/rpc/rpc_generic.c4
-rw-r--r--lib/libc/stdtime/Makefile.inc3
-rw-r--r--lib/libc/stdtime/ctime.339
-rw-r--r--lib/libc/stdtime/tzset.357
-rw-r--r--lib/libc/tests/stdio/printfloat_test.c13
-rw-r--r--lib/libc/tests/stdtime/Makefile1
-rw-r--r--lib/libc/tests/stdtime/detect_tz_changes_test.c113
-rw-r--r--lib/libc/tests/string/memcmp_test.c12
-rw-r--r--lib/libc/tests/tls/dso/Makefile1
-rw-r--r--lib/libc/tests/tls_dso/Makefile1
24 files changed, 385 insertions, 124 deletions
diff --git a/lib/libc/aarch64/string/timingsafe_memcmp.S b/lib/libc/aarch64/string/timingsafe_memcmp.S
index 28fdd911a387..4cc10a152aa8 100644
--- a/lib/libc/aarch64/string/timingsafe_memcmp.S
+++ b/lib/libc/aarch64/string/timingsafe_memcmp.S
@@ -114,4 +114,4 @@ ENTRY(timingsafe_memcmp)
csetm w0, lo
csinc w0, w0, wzr, ls
ret
-END(timingsafe_bcmp)
+END(timingsafe_memcmp)
diff --git a/lib/libc/gdtoa/_hdtoa.c b/lib/libc/gdtoa/_hdtoa.c
index 8ae739acf0db..9c42630cd918 100644
--- a/lib/libc/gdtoa/_hdtoa.c
+++ b/lib/libc/gdtoa/_hdtoa.c
@@ -40,6 +40,7 @@
#define DBL_ADJ (DBL_MAX_EXP - 2)
#define SIGFIGS ((DBL_MANT_DIG + 3) / 4 + 1)
+#define MAX_HEX_DIGITS ((DBL_MANT_DIG + 3 - 1) / 4 + 1)
static const float one[] = { 1.0f, -1.0f };
@@ -111,7 +112,7 @@ __hdtoa(double d, const char *xdigs, int ndigits, int *decpt, int *sign,
s0 = rv_alloc(bufsize);
/* Round to the desired number of digits. */
- if (SIGFIGS > ndigits && ndigits > 0) {
+ if (MAX_HEX_DIGITS > ndigits && ndigits > 0) {
float redux = one[u.bits.sign];
int offset = 4 * ndigits + DBL_MAX_EXP - 4 - DBL_MANT_DIG;
u.bits.exp = offset;
diff --git a/lib/libc/gdtoa/_hldtoa.c b/lib/libc/gdtoa/_hldtoa.c
index 965d2349d103..5f10d12c5c09 100644
--- a/lib/libc/gdtoa/_hldtoa.c
+++ b/lib/libc/gdtoa/_hldtoa.c
@@ -65,6 +65,7 @@ typedef uint32_t manl_t;
#define LDBL_ADJ (LDBL_MAX_EXP - 2)
#define SIGFIGS ((LDBL_MANT_DIG + 3) / 4 + 1)
+#define MAX_HEX_DIGITS ((LDBL_MANT_DIG + 3 - 1) / 4 + 1)
static const float one[] = { 1.0f, -1.0f };
@@ -125,7 +126,7 @@ __hldtoa(long double e, const char *xdigs, int ndigits, int *decpt, int *sign,
s0 = rv_alloc(bufsize);
/* Round to the desired number of digits. */
- if (SIGFIGS > ndigits && ndigits > 0) {
+ if (MAX_HEX_DIGITS > ndigits && ndigits > 0) {
float redux = one[u.bits.sign];
int offset = 4 * ndigits + LDBL_MAX_EXP - 4 - LDBL_MANT_DIG;
#ifdef __i386__
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index 26f638568efc..494b65bc5cc1 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -193,7 +193,6 @@ FBSD_1.0 {
__isinff;
__isinfl;
isatty;
- initgroups;
jrand48;
lcong48;
ldexp;
@@ -462,6 +461,7 @@ FBSD_1.8 {
fdscandir_b;
fts_open_b;
glob_b;
+ initgroups;
inotify_add_watch;
inotify_init;
inotify_init1;
diff --git a/lib/libc/gen/fts-compat.c b/lib/libc/gen/fts-compat.c
index f87cabf085f7..62a1e0a81f62 100644
--- a/lib/libc/gen/fts-compat.c
+++ b/lib/libc/gen/fts-compat.c
@@ -44,9 +44,9 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "gen-compat.h"
#include "fts-compat.h"
#include "un-namespace.h"
+#include "gen-compat.h"
#include "gen-private.h"
diff --git a/lib/libc/gen/fts-compat11.c b/lib/libc/gen/fts-compat11.c
index 0351ce5ac690..5abb378f5f08 100644
--- a/lib/libc/gen/fts-compat11.c
+++ b/lib/libc/gen/fts-compat11.c
@@ -43,9 +43,9 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "gen-compat.h"
#include "fts-compat11.h"
#include "un-namespace.h"
+#include "gen-compat.h"
#include "gen-private.h"
diff --git a/lib/libc/gen/gen-compat.h b/lib/libc/gen/gen-compat.h
index 08e80ede6b6e..19b9addb4321 100644
--- a/lib/libc/gen/gen-compat.h
+++ b/lib/libc/gen/gen-compat.h
@@ -40,16 +40,50 @@ struct freebsd11_statfs;
struct freebsd11_dirent *freebsd11_readdir(DIR *);
int freebsd11_readdir_r(DIR *, struct freebsd11_dirent *,
struct freebsd11_dirent **);
-int freebsd11_stat(const char *, struct freebsd11_stat *);
-int freebsd11_lstat(const char *, struct freebsd11_stat *);
-int freebsd11_fstat(int, struct freebsd11_stat *);
-int freebsd11_fstatat(int, const char *, struct freebsd11_stat *, int);
-int freebsd11_statfs(const char *, struct freebsd11_statfs *);
-int freebsd11_getfsstat(struct freebsd11_statfs *, long, int);
int freebsd11_getmntinfo(struct freebsd11_statfs **, int);
char *freebsd11_devname(__uint32_t dev, __mode_t type);
-char *freebsd11_devname_r(__uint32_t dev, __mode_t type, char *buf, int len);
+char *freebsd11_devname_r(__uint32_t dev, __mode_t type, char *buf,
+ int len);
+
+/*
+ * We want freebsd11_fstat in C source to result in resolution to
+ * - fstat@FBSD_1.0 for libc.so (but we do not need the _definition_
+ * of this fstat, it is provided by libsys.so which we want to use).
+ * - freebsd11_fstat for libc.a (since if we make it fstat@FBSD_1.0
+ * for libc.a, then final linkage into static object ignores version
+ * and would reference fstat, which is the current syscall, not the
+ * compat syscall). libc.a provides the freebsd11_fstat implementation.
+ * Note that freebsd11_fstat from libc.a is not used for anything, but
+ * we make it correct nonetheless, just in case it would.
+ * This is arranged by COMPAT_SYSCALL, and libc can just use freebsd11_fstat.
+ */
+#ifdef PIC
+#define COMPAT_SYSCALL(rtype, fun, args, sym, ver) \
+ rtype fun args; __sym_compat(sym, fun, ver);
+#else
+#define COMPAT_SYSCALL(rtype, fun, args, sym, ver) \
+ rtype fun args;
+#endif
+
+COMPAT_SYSCALL(int, freebsd11_stat, (const char *, struct freebsd11_stat *),
+ stat, FBSD_1.0);
+COMPAT_SYSCALL(int, freebsd11_lstat, (const char *, struct freebsd11_stat *),
+ lstat, FBSD_1.0);
+COMPAT_SYSCALL(int, freebsd11_fstat, (int, struct freebsd11_stat *),
+ fstat, FBSD_1.0);
+COMPAT_SYSCALL(int, freebsd11_fstatat, (int, const char *,
+ struct freebsd11_stat *, int), fstatat, FBSD_1.1);
+
+COMPAT_SYSCALL(int, freebsd11_statfs, (const char *,
+ struct freebsd11_statfs *), statfs, FBSD_1.0);
+COMPAT_SYSCALL(int, freebsd11_getfsstat, (struct freebsd11_statfs *, long,
+ int), getfsstat, FBSD_1.0);
+
+COMPAT_SYSCALL(int, freebsd14_setgroups, (int gidsize, const __gid_t *gidset),
+ setgroups, FBSD_1.0);
+
+#undef COMPAT_SYSCALL
#endif /* _GEN_COMPAT_H_ */
diff --git a/lib/libc/gen/getgrouplist.3 b/lib/libc/gen/getgrouplist.3
index e9a980f99751..e3939fc2481a 100644
--- a/lib/libc/gen/getgrouplist.3
+++ b/lib/libc/gen/getgrouplist.3
@@ -1,5 +1,13 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-3-Clause
+.\"
.\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 2025 The FreeBSD Foundation
+.\"
+.\" Portions of this documentation were written by Olivier Certner
+.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+.\" Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -25,12 +33,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 26, 2014
+.Dd August 29, 2025
.Dt GETGROUPLIST 3
.Os
.Sh NAME
.Nm getgrouplist
-.Nd calculate group access list
+.Nd produce a user's effective group list
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -40,16 +48,16 @@
.Sh DESCRIPTION
The
.Fn getgrouplist
-function reads through the group file and calculates
-the group access list for the user specified in
-.Fa name .
-The
+function reads through the group database to retrieve the supplementary groups
+for the user specified in
+.Fa name ,
+and returns the effective group list, whose first group is the value of
+.Fa basegid
+and the others are the retrieved supplementary groups.
.Fa basegid
-is automatically included in the groups list.
-Typically this value is given as
-the group number from the password file.
+typically is the user's group number from the password database.
.Pp
-The resulting group list is returned in the array pointed to by
+The effective group list is returned in the array pointed to by
.Fa groups .
The caller specifies the size of the
.Fa groups
@@ -70,6 +78,7 @@ Here, the group array will be filled with as many groups as will fit.
group membership list
.El
.Sh SEE ALSO
+.Xr setcred 2 ,
.Xr setgroups 2 ,
.Xr initgroups 3
.Sh HISTORY
diff --git a/lib/libc/gen/getgrouplist.c b/lib/libc/gen/getgrouplist.c
index 5bd06bc5121f..9c57b7031336 100644
--- a/lib/libc/gen/getgrouplist.c
+++ b/lib/libc/gen/getgrouplist.c
@@ -29,13 +29,8 @@
* SUCH DAMAGE.
*/
-/*
- * get credential
- */
#include <sys/types.h>
-#include <grp.h>
-#include <string.h>
#include <unistd.h>
#include <ssp/ssp.h>
@@ -46,4 +41,3 @@ __ssp_real(getgrouplist)(const char *uname, gid_t agroup, gid_t *groups, int *gr
{
return __getgroupmembership(uname, agroup, groups, *grpcnt, grpcnt);
}
-
diff --git a/lib/libc/gen/initgroups.3 b/lib/libc/gen/initgroups.3
index 03bd07494fc9..4f538fb180ec 100644
--- a/lib/libc/gen/initgroups.3
+++ b/lib/libc/gen/initgroups.3
@@ -1,5 +1,13 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-3-Clause
+.\"
.\" Copyright (c) 1983, 1991, 1993
.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 2025 The FreeBSD Foundation
+.\"
+.\" Portions of this documentation were written by Olivier Certner
+.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+.\" Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -25,12 +33,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 26, 2014
+.Dd September 17, 2025
.Dt INITGROUPS 3
.Os
.Sh NAME
.Nm initgroups
-.Nd initialize group access list
+.Nd initialize supplementary groups as per the group database
.Sh LIBRARY
.Lb libc
.Sh SYNOPSIS
@@ -40,19 +48,18 @@
.Sh DESCRIPTION
The
.Fn initgroups
-function
-uses the
-.Xr getgrouplist 3
-function to calculate the group access list for the user
-specified in
+function initializes the current process' supplementary groups as prescribed by
+its arguments and the system's group database.
+.Pp
+It first uses the
+.Fn getgrouplist
+function to compute a list of groups containing the passed
+.Fa basegid ,
+which typically is the user's initial numerical group ID from the password
+database, and the supplementary groups in the group database for the user named
.Fa name .
-This group list is then setup for the current process using
-.Xr setgroups 2 .
-The
-.Fa basegid
-is automatically included in the groups list.
-Typically this value is given as
-the group number from the password file.
+It then installs this list as the current process' supplementary groups using
+.Fn setgroups .
.Sh RETURN VALUES
.Rv -std initgroups
.Sh ERRORS
@@ -60,7 +67,7 @@ The
.Fn initgroups
function may fail and set
.Va errno
-for any of the errors specified for the library function
+to any of the errors specified for the library function
.Xr setgroups 2 .
It may also return:
.Bl -tag -width Er
@@ -77,3 +84,67 @@ The
.Fn initgroups
function appeared in
.Bx 4.2 .
+.Pp
+The
+.Fn initgroups
+function changed semantics in
+.Fx 15 ,
+following that of
+.Xr setgroups 2
+in the same release.
+Before that, it would also set the effective group ID to
+.Fa basegid ,
+and would not include the latter in the supplementary groups except before
+.Fx 8 .
+Its current behavior in these respects is known to be compatible with that of
+the following systems up to the specified versions that are current at time of
+this writing:
+.Bl -dash -width "-" -compact
+.It
+Linux (up to 6.6) with the GNU libc (up to 2.42)
+.It
+.Nx 1.1 and greater (up to 10)
+.It
+.Ox (up to 7.7)
+.It
+Systems based on illumos (up to August 2025 sources)
+.El
+.Sh SECURITY CONSIDERATIONS
+As
+.Fa basegid
+is typically the user's initial numerical group ID, to which the current
+process' effective group ID is generally initialized, processes using functions
+to change their effective group ID
+.Pq via Xr setgid 2 or similar
+or that are spawned from executables with the set-group-ID mode bit set will not
+be able to relinquish the access rights deriving from being a member of
+.Fa basegid ,
+as these functions do not change the supplementary groups.
+.Pp
+This behavior is generally desirable in order to paper over the difference of
+treatment between the effective group and supplementary ones in this situation,
+as they are all in the end indiscriminately used in traditional UNIX
+discretionary access checks.
+It blends well with the practice of allocating each user its own private group,
+as processes launched from a set-group-ID executable keep the same user and
+consistently stay also in the same user's group.
+Finally, it was also chosen for compatibility with other systems
+.Po
+see the
+.Sx HISTORY
+section
+.Pc .
+.Pp
+This convention of including
+.Fa basegid
+in the supplementary groups is however only enforced by the
+.Fn initgroups
+function, and not by the
+.Xr setgroups 2
+system call, so applications expressly wanting to include in the supplementary
+groups only those specified by the group database can themselves call
+.Fn getgrouplist
+and then
+.Fn setgroups
+on the result with the first element skipped
+.Pq see Xr getgrouplist 3 .
diff --git a/lib/libc/gen/initgroups.c b/lib/libc/gen/initgroups.c
index b6697dd7ed8f..a1a7d92250e2 100644
--- a/lib/libc/gen/initgroups.c
+++ b/lib/libc/gen/initgroups.c
@@ -3,6 +3,11 @@
*
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2025 The FreeBSD Foundation
+ *
+ * Portions of this software were developed by Olivier Certner
+ * <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+ * Foundation.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -29,34 +34,52 @@
* SUCH DAMAGE.
*/
-#include <sys/param.h>
+/* For __sym_compat(). */
+#include <sys/cdefs.h>
-#include "namespace.h"
-#include <err.h>
-#include "un-namespace.h"
#include <errno.h>
-#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-int
-initgroups(const char *uname, gid_t agroup)
+/* For freebsd14_setgroups(). */
+#include "gen-compat.h"
+
+static int
+initgroups_impl(const char *uname, gid_t agroup,
+ int (*setgroups)(int, const gid_t *))
{
- int ngroups, ret;
- long ngroups_max;
gid_t *groups;
+ long ngroups_max;
+ int ngroups, ret;
/*
- * Provide space for one group more than possible to allow
- * setgroups to fail and set errno.
+ * Provide space for one group more than possible to allow setgroups()
+ * to fail and set 'errno' in case we get back more than {NGROUPS_MAX} +
+ * 1 groups.
*/
ngroups_max = sysconf(_SC_NGROUPS_MAX) + 2;
- if ((groups = malloc(sizeof(*groups) * ngroups_max)) == NULL)
- return (ENOMEM);
+ groups = malloc(sizeof(*groups) * ngroups_max);
+ if (groups == NULL)
+ return (-1); /* malloc() set 'errno'. */
ngroups = (int)ngroups_max;
- getgrouplist(uname, agroup, groups, &ngroups);
- ret = setgroups(ngroups, groups);
+ (void)getgrouplist(uname, agroup, groups, &ngroups);
+ ret = (*setgroups)(ngroups, groups);
+
free(groups);
- return (ret);
+ return (ret); /* setgroups() set 'errno'. */
}
+
+int
+initgroups(const char *uname, gid_t agroup)
+{
+ return (initgroups_impl(uname, agroup, setgroups));
+}
+
+int
+freebsd14_initgroups(const char *uname, gid_t agroup)
+{
+ return (initgroups_impl(uname, agroup, freebsd14_setgroups));
+}
+
+__sym_compat(initgroups, freebsd14_initgroups, FBSD_1.0);
diff --git a/lib/libc/gen/psignal.3 b/lib/libc/gen/psignal.3
index 098b7b02a9b9..bf6a99b4b113 100644
--- a/lib/libc/gen/psignal.3
+++ b/lib/libc/gen/psignal.3
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 10, 2025
+.Dd September 23, 2025
.Dt PSIGNAL 3
.Os
.Sh NAME
@@ -141,6 +141,13 @@ The name in
can be either the name of the signal, with or without the
.Dq SIG
prefix, or a decimal number.
+.Sh RETURN VALUES
+The
+.Fn sig2str
+and
+.Fn str2sig
+return 0 on success and -1 on translation failure.
+In the latter case the memory to store the translation result is left intact.
.Sh SEE ALSO
.Xr sigaction 2 ,
.Xr perror 3 ,
diff --git a/lib/libc/gen/sysconf.c b/lib/libc/gen/sysconf.c
index 66562d0e29f0..b5b732eed05d 100644
--- a/lib/libc/gen/sysconf.c
+++ b/lib/libc/gen/sysconf.c
@@ -51,7 +51,7 @@
#include "un-namespace.h"
#include "../stdlib/atexit.h"
-#include "tzdir.h" /* from ../../../contrib/tzcode/stdtime */
+#include "tzdir.h" /* from ../../../contrib/tzcode */
#include "libc_private.h"
#define _PATH_ZONEINFO TZDIR /* from tzfile.h */
diff --git a/lib/libc/include/compat.h b/lib/libc/include/compat.h
index 97f22607ddd7..630ffe7daae3 100644
--- a/lib/libc/include/compat.h
+++ b/lib/libc/include/compat.h
@@ -80,4 +80,3 @@ __sym_compat(setgroups, freebsd14_setgroups, FBSD_1.0);
#undef __weak_reference
#endif /* __LIBC_COMPAT_H__ */
-
diff --git a/lib/libc/rpc/rpc_generic.c b/lib/libc/rpc/rpc_generic.c
index 0e563f6a5996..8019f2d8f236 100644
--- a/lib/libc/rpc/rpc_generic.c
+++ b/lib/libc/rpc/rpc_generic.c
@@ -610,6 +610,10 @@ __rpc_taddr2uaddr_af(int af, const struct netbuf *nbuf)
return NULL;
break;
#endif
+ case AF_NETLINK:
+ if (asprintf(&ret, "%s", (char *)nbuf->buf) < 0)
+ return NULL;
+ break;
case AF_LOCAL:
sun = nbuf->buf;
if (asprintf(&ret, "%.*s", (int)(sun->sun_len -
diff --git a/lib/libc/stdtime/Makefile.inc b/lib/libc/stdtime/Makefile.inc
index 647cbe6f40ba..1baa39a6c0a6 100644
--- a/lib/libc/stdtime/Makefile.inc
+++ b/lib/libc/stdtime/Makefile.inc
@@ -32,4 +32,5 @@ MLINKS+=strftime.3 strftime_l.3
MLINKS+=strptime.3 strptime_l.3
MLINKS+=time2posix.3 posix2time.3
MLINKS+=tzset.3 daylight.3 \
- tzset.3 timezone.3
+ tzset.3 timezone.3 \
+ tzset.3 tzname.3
diff --git a/lib/libc/stdtime/ctime.3 b/lib/libc/stdtime/ctime.3
index 96b7f775535a..6384e8bd959b 100644
--- a/lib/libc/stdtime/ctime.3
+++ b/lib/libc/stdtime/ctime.3
@@ -27,7 +27,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd March 26, 2024
+.Dd September 23, 2025
.Dt CTIME 3
.Os
.Sh NAME
@@ -41,6 +41,8 @@
.Nm localtime ,
.Nm localtime_r ,
.Nm mktime ,
+.Nm offtime ,
+.Nm offtime_r ,
.Nm timegm
.Nd transform binary date and time values
.Sh LIBRARY
@@ -68,14 +70,19 @@
.Fn localtime_r "const time_t *clock" "struct tm *result"
.Ft time_t
.Fn mktime "struct tm *tm"
+.Ft struct tm *
+.Fn offtime "const time_t *clock" "long offset"
+.Ft struct tm *
+.Fn offtime_r "const time_t *clock" "long offset" "struct tm *result"
.Ft time_t
.Fn timegm "struct tm *tm"
.Sh DESCRIPTION
The
.Fn ctime ,
.Fn gmtime ,
+.Fn localtime ,
and
-.Fn localtime
+.Fn offtime
functions all take as argument a pointer to a time value representing
the time in seconds since the Epoch (00:00:00 UTC on January 1, 1970;
see
@@ -123,6 +130,18 @@ adjustment, and returns a pointer to a
.Vt struct tm .
.Pp
The
+.Fn offtime
+function similarly converts the time value with a time zone adjustment
+corresponding to the provided
+.Fa offset ,
+which is expressed in seconds, with positive values indicating a time
+zone ahead of UTC (east of the Prime Meridian).
+It does not call
+.Xr tzset 3
+or modify
+.Va tzname .
+.Pp
+The
.Fn ctime
function
adjusts the time value for the current time zone in the same manner as
@@ -155,13 +174,15 @@ except the caller must provide the output buffer
.Fa buf ,
which must be at least 26 characters long, to store the result in.
The
-.Fn localtime_r
+.Fn localtime_r ,
+.Fn gmtime_r ,
and
-.Fn gmtime_r
+.Fn offtime_r
functions provide the same functionality as
-.Fn localtime
+.Fn localtime ,
+.Fn gmtime ,
and
-.Fn gmtime
+.Fn offtime
respectively, except the caller must provide the output buffer
.Fa result .
.Pp
@@ -368,6 +389,12 @@ and
.Fn localtime_r
functions have been available since
.Fx 8.0 .
+The
+.Fn offtime
+and
+.Fn offtime_r
+functions were added in
+.Fx 15.0 .
.Sh BUGS
Except for
.Fn difftime ,
diff --git a/lib/libc/stdtime/tzset.3 b/lib/libc/stdtime/tzset.3
index 94ccbec9aba7..33e6a556306d 100644
--- a/lib/libc/stdtime/tzset.3
+++ b/lib/libc/stdtime/tzset.3
@@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd December 25, 2023
+.Dd September 30, 2025
.Dt TZSET 3
.Os
.Sh NAME
@@ -58,8 +58,8 @@ specifies how this is done.
.Pp
If
.Ev TZ
-does not appear in the environment, the best available approximation to
-local wall clock time, as specified by the
+does not appear in the environment, the best available approximation
+to local wall clock time, as specified by the
.Xr tzfile 5 Ns -format
file
.Pa /etc/localtime
@@ -68,9 +68,7 @@ is used.
If
.Ev TZ
appears in the environment but its value is a null string, Coordinated
-Universal Time
-.Pq Tn UTC
-is used (without leap second correction).
+Universal Time (UTC) is used (without leap second correction).
.Pp
If
.Ev TZ
@@ -81,13 +79,12 @@ the rest of its value is used as a pathname of a
file from which to read the time conversion information.
If the first character of the pathname is a slash
.Pq Ql /
-it is used as
-an absolute pathname; otherwise, it is used as a pathname relative to
-the system time conversion information directory.
+it is used as an absolute pathname; otherwise, it is used as a
+pathname relative to the system time conversion information directory.
.Pp
-If its value does not begin with a colon, it is first used as the pathname
-of a file (as described above) from which to read the time conversion
-information.
+If its value does not begin with a colon, it is first used as the
+pathname of a file (as described above) from which to read the time
+conversion information.
If that file cannot be read, the value is then interpreted as a direct
specification (the format is described below) of the time conversion
information.
@@ -96,24 +93,23 @@ If the
.Ev TZ
environment variable does not specify a
.Xr tzfile 5 Ns -format
-file and cannot be interpreted as a direct specification,
-.Tn UTC
-is used.
+file and cannot be interpreted as a direct specification, UTC is used.
.Pp
After the first call to
.Nm tzset ,
the
.Vt timezone
-variable is set to the difference, in seconds, between Coordinated Universal
-Time (UTC) and the local time.
+variable is set to the difference, in seconds, between Coordinated
+Universal Time (UTC) and the local time.
The
.Vt daylight
variable is set to 0 if the local timezone is observing standard time.
-It is set to 1 if the local timezone ever observes an alternate time, such as summer time.
+It is set to 1 if the local timezone ever observes an alternate time,
+such as summer time.
The first element of the
.Vt tzname
-array is the timezone name of standard time, while the second element is the
-name of the altnerative time zone.
+array is the timezone name of standard time, while the second element
+is the name of the altnerative time zone.
.Sh SPECIFICATION FORMAT
When
.Ev TZ
@@ -151,8 +147,7 @@ minus
.Pq Ql \- ,
plus
.Pq Ql + ,
-and
-.Tn ASCII
+and ASCII
.Dv NUL
are allowed.
.It Em offset
@@ -294,10 +289,8 @@ by the
file
.Em posixrules
in the system time conversion information directory are used, with the
-standard and summer time offsets from
-.Tn UTC
-replaced by those specified by
-the
+standard and summer time offsets from UTC replaced by those specified
+by the
.Em offset
values in
.Ev TZ .
@@ -315,20 +308,14 @@ local time zone file
.It Pa /usr/share/zoneinfo
time zone directory
.It Pa /usr/share/zoneinfo/posixrules
-rules for
-.Tn POSIX Ns -style
-.Tn TZ Ns 's
+rules for POSIX-style TZs
.It Pa /usr/share/zoneinfo/Etc/GMT
-for
-.Tn UTC
-leap seconds
+for UTC leap seconds
.El
.Pp
If the file
.Pa /usr/share/zoneinfo/UTC
-does not exist,
-.Tn UTC
-leap seconds are loaded from
+does not exist, UTC leap seconds are loaded from
.Pa /usr/share/zoneinfo/posixrules .
.Sh SEE ALSO
.Xr date 1 ,
diff --git a/lib/libc/tests/stdio/printfloat_test.c b/lib/libc/tests/stdio/printfloat_test.c
index 031859124163..4493fe1c15d3 100644
--- a/lib/libc/tests/stdio/printfloat_test.c
+++ b/lib/libc/tests/stdio/printfloat_test.c
@@ -398,6 +398,18 @@ ATF_TC_BODY(subnormal_float, tc)
testfmt("-0X1P-149", "%A", negative);
}
+ATF_TC_WITHOUT_HEAD(hexadecimal_rounding_fullprec);
+ATF_TC_BODY(hexadecimal_rounding_fullprec, tc)
+{
+ /* Double: %.13a with binary64 mantissa=53 */
+ testfmt("0x1.1234567890bbbp+0", "%.13a", 0x1.1234567890bbbp+0);
+
+#if defined(__aarch64__)
+ /* On arm64, long double is IEEE binary128 (mantissa=113) */
+ testfmt("0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0", "%.28La", 0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0L);
+#endif
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -414,6 +426,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, hexadecimal_rounding);
ATF_TP_ADD_TC(tp, subnormal_double);
ATF_TP_ADD_TC(tp, subnormal_float);
+ ATF_TP_ADD_TC(tp, hexadecimal_rounding_fullprec);
return (atf_no_error());
}
diff --git a/lib/libc/tests/stdtime/Makefile b/lib/libc/tests/stdtime/Makefile
index 6b9068e1641b..590dea22da31 100644
--- a/lib/libc/tests/stdtime/Makefile
+++ b/lib/libc/tests/stdtime/Makefile
@@ -3,6 +3,7 @@
ATF_TESTS_C+= strptime_test
ATF_TESTS_C+= detect_tz_changes_test
+CFLAGS.detect_tz_changes_test+= -I${SRCTOP}/contrib/tzcode
.if ${MK_DETECT_TZ_CHANGES} != "no"
CFLAGS.detect_tz_changes_test+= -DDETECT_TZ_CHANGES
.endif
diff --git a/lib/libc/tests/stdtime/detect_tz_changes_test.c b/lib/libc/tests/stdtime/detect_tz_changes_test.c
index 6648d8498cc5..06c31c9fbc3d 100644
--- a/lib/libc/tests/stdtime/detect_tz_changes_test.c
+++ b/lib/libc/tests/stdtime/detect_tz_changes_test.c
@@ -20,12 +20,16 @@
#include <time.h>
#include <unistd.h>
+#include "tzdir.h"
+
#include <atf-c.h>
-static const struct tzcase {
+struct tzcase {
const char *tzfn;
const char *expect;
-} tzcases[] = {
+};
+
+static const struct tzcase tzcases[] = {
/*
* A handful of time zones and the expected result of
* strftime("%z (%Z)", tm) when that time zone is active
@@ -41,7 +45,8 @@ static const struct tzcase {
{ "UTC", "+0000 (UTC)" },
{ 0 },
};
-
+static const struct tzcase utc = { "UTC", "+0000 (UTC)" };
+static const struct tzcase invalid = { "invalid", "+0000 (-00)" };
static const time_t then = 1751328000; /* 2025-07-01 00:00:00 UTC */
static bool debugging;
@@ -62,9 +67,9 @@ debug(const char *fmt, ...)
static void
change_tz(const char *tzn)
{
- static const char *zfn = "/usr/share/zoneinfo";
- static const char *tfn = "root/etc/.localtime";
- static const char *dfn = "root/etc/localtime";
+ static const char *zfn = TZDIR;
+ static const char *tfn = "root" TZDEFAULT ".tmp";
+ static const char *dfn = "root" TZDEFAULT;
ssize_t clen;
int zfd, sfd, dfd;
@@ -96,6 +101,50 @@ test_tz(const char *expect)
ATF_CHECK_STREQ(expect, buf);
}
+ATF_TC(tz_default);
+ATF_TC_HEAD(tz_default, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test default zone");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_default, tc)
+{
+ /* prepare chroot with no /etc/localtime */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz("+0000 (UTC)");
+}
+
+ATF_TC(tz_invalid_file);
+ATF_TC_HEAD(tz_invalid_file, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test invalid zone file");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_invalid_file, tc)
+{
+ static const char *dfn = "root/etc/localtime";
+ int fd;
+
+ /* prepare chroot with bogus /etc/localtime */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ ATF_REQUIRE((fd = open(dfn, O_RDWR | O_CREAT, 0644)) >= 0);
+ ATF_REQUIRE_EQ(8, write(fd, "invalid\n", 8));
+ ATF_REQUIRE_EQ(0, close(fd));
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz(invalid.expect);
+}
+
ATF_TC(thin_jail);
ATF_TC_HEAD(thin_jail, tc)
{
@@ -320,6 +369,30 @@ test_tz_env(const char *tzval, const char *expect)
test_tz(expect);
}
+static void
+tz_env_common(void)
+{
+ char path[MAXPATHLEN];
+ const struct tzcase *tzcase = tzcases;
+ int len;
+
+ /* relative path */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++)
+ test_tz_env(tzcase->tzfn, tzcase->expect);
+ /* absolute path */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) {
+ len = snprintf(path, sizeof(path), "%s/%s", TZDIR, tzcase->tzfn);
+ ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path));
+ test_tz_env(path, tzcase->expect);
+ }
+ /* absolute path with additional slashes */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) {
+ len = snprintf(path, sizeof(path), "%s/////%s", TZDIR, tzcase->tzfn);
+ ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path));
+ test_tz_env(path, tzcase->expect);
+ }
+}
+
ATF_TC(tz_env);
ATF_TC_HEAD(tz_env, tc)
{
@@ -327,10 +400,22 @@ ATF_TC_HEAD(tz_env, tc)
}
ATF_TC_BODY(tz_env, tc)
{
- const struct tzcase *tzcase;
+ tz_env_common();
+ /* escape from TZDIR is permitted when not setugid */
+ test_tz_env("../zoneinfo/UTC", utc.expect);
+}
- for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++)
- test_tz_env(tzcase->tzfn, tzcase->expect);
+
+ATF_TC(tz_invalid_env);
+ATF_TC_HEAD(tz_invalid_env, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test invalid TZ value");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_invalid_env, tc)
+{
+ test_tz_env("invalid", invalid.expect);
+ test_tz_env(":invalid", invalid.expect);
}
ATF_TC(setugid);
@@ -367,23 +452,25 @@ ATF_TC_HEAD(tz_env_setugid, tc)
}
ATF_TC_BODY(tz_env_setugid, tc)
{
- const struct tzcase *tzcase = tzcases;
-
ATF_REQUIRE_EQ(0, seteuid(UID_NOBODY));
ATF_REQUIRE(issetugid());
- for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++)
- test_tz_env(tzcase->tzfn, tzcase->expect);
+ tz_env_common();
+ /* escape from TZDIR is not permitted when setugid */
+ test_tz_env("../zoneinfo/UTC", invalid.expect);
}
ATF_TP_ADD_TCS(tp)
{
debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
isatty(STDERR_FILENO);
+ ATF_TP_ADD_TC(tp, tz_default);
+ ATF_TP_ADD_TC(tp, tz_invalid_file);
ATF_TP_ADD_TC(tp, thin_jail);
#ifdef DETECT_TZ_CHANGES
ATF_TP_ADD_TC(tp, detect_tz_changes);
#endif /* DETECT_TZ_CHANGES */
ATF_TP_ADD_TC(tp, tz_env);
+ ATF_TP_ADD_TC(tp, tz_invalid_env);
ATF_TP_ADD_TC(tp, setugid);
ATF_TP_ADD_TC(tp, tz_env_setugid);
return (atf_no_error());
diff --git a/lib/libc/tests/string/memcmp_test.c b/lib/libc/tests/string/memcmp_test.c
index 5286a0b994f3..fa2f498ccfaf 100644
--- a/lib/libc/tests/string/memcmp_test.c
+++ b/lib/libc/tests/string/memcmp_test.c
@@ -41,14 +41,14 @@
#endif
/*
- * On FreeBSD we demand that memcmp returns the difference between the
- * characters at the first site of mismatch. However, ISO/IEC 9899:1990
- * only specifies that a number greater than, equal to, or less than
- * zero shall be returned. If a unit test for this less strict
- * behaviour is desired, define RES(x) to be (((x) > 0) - ((x) < 0)).
+ * On FreeBSD we previously demanded that memcmp returns the difference
+ * between the characters at the first site of mismatch. However,
+ * ISO/IEC 9899:1990 only specifies that a number greater than, equal
+ * to, or less than zero shall be returned. If a unit test for the
+ * more strict behaviour is desired, define RES(x) to be (x).
*/
#ifndef RES
-#define RES(x) (x)
+#define RES(x) (((x) > 0) - ((x) < 0))
#endif
static int (*memcmp_fn)(const void *, const void *, size_t);
diff --git a/lib/libc/tests/tls/dso/Makefile b/lib/libc/tests/tls/dso/Makefile
index 5efd8b29a6bd..783534ff7aae 100644
--- a/lib/libc/tests/tls/dso/Makefile
+++ b/lib/libc/tests/tls/dso/Makefile
@@ -6,6 +6,7 @@ SRCS= h_tls_dlopen.c
MAN=
PACKAGE= tests
+NO_DEV_PACKAGE=
LIBDIR= ${TESTSBASE}/lib/libc/tls
SHLIB_MAJOR= 1
diff --git a/lib/libc/tests/tls_dso/Makefile b/lib/libc/tests/tls_dso/Makefile
index 89296c643695..7cb8f98b431e 100644
--- a/lib/libc/tests/tls_dso/Makefile
+++ b/lib/libc/tests/tls_dso/Makefile
@@ -7,6 +7,7 @@ LIBDIR= ${TESTSBASE}/lib/libc/tls
SHLIBDIR= ${TESTSBASE}/lib/libc/tls
SHLIB_MAJOR= 1
PACKAGE= tests
+NO_DEV_PACKAGE=
WITHOUT_STATIC=
WITHOUT_PROFILE=