Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20261102165837.24591-8-kaloz@kernel.org>
Date: Mon,  2 Nov 2026 17:58:34 +0100
From: Imre Kaloz <kaloz@...nel.org>
To: musl@...ts.openwall.com
Subject: [PATCH 07/10] convert kernel timevals where suseconds_t is 32-bit

sparc64's kernel has a 32-bit suseconds_t, so its struct timeval is
{long, int}. These syscalls passed musl's struct timeval through
unchanged when time_t and long have the same width, which on
big-endian sparc64 puts tv_usec in the wrong half. SIOCGSTAMP returns
the same layout. SO_TIMESTAMP needs Linux 5.1 or later, since
recvmsg's conversion of the old timestamp assumes two longs.
---
 src/internal/syscall.h    |  5 +++++
 src/linux/clock_adjtime.c | 18 ++++++++++++++++++
 src/linux/wait4.c         | 10 ++++++++++
 src/misc/getrusage.c      | 10 ++++++++++
 src/misc/ioctl.c          | 12 ++++++++++++
 src/network/getsockopt.c  | 13 +++++++++++++
 src/network/setsockopt.c  |  6 ++++++
 src/select/select.c       |  5 +++++
 src/signal/getitimer.c    | 12 ++++++++++++
 src/signal/setitimer.c    | 18 ++++++++++++++++++
 10 files changed, 109 insertions(+)

diff --git a/src/internal/syscall.h b/src/internal/syscall.h
index 236fb68d..32ef6a05 100644
--- a/src/internal/syscall.h
+++ b/src/internal/syscall.h
@@ -352,6 +352,11 @@ static inline long __alt_socketcall(int sys, int sock, int cp, syscall_arg_t a,
 #define SYS_accept SYS_accept4
 #endif
 
+#ifdef KERNEL_SUSECONDS_IS_INT
+/* struct __kernel_old_timeval, whose tv_usec is an int here */
+struct k_timeval { long tv_sec; int tv_usec; };
+#endif
+
 #ifndef SO_RCVTIMEO_OLD
 #define SO_RCVTIMEO_OLD  20
 #endif
diff --git a/src/linux/clock_adjtime.c b/src/linux/clock_adjtime.c
index d4d03d24..b2c4393c 100644
--- a/src/linux/clock_adjtime.c
+++ b/src/linux/clock_adjtime.c
@@ -1,6 +1,7 @@
 #include <sys/timex.h>
 #include <time.h>
 #include <errno.h>
+#include <string.h>
 #include "syscall.h"
 
 #define IS32BIT(x) !((x)+0x80000000ULL>>32)
@@ -144,8 +145,25 @@ int clock_adjtime (clockid_t clock_id, struct timex *utx)
 		}
 		return __syscall_ret(r);
 	}
+#ifdef KERNEL_SUSECONDS_IS_INT
+	struct timex ktx = *utx;
+	struct k_timeval ktv = { utx->time.tv_sec, utx->time.tv_usec };
+	memcpy(&ktx.time, &ktv, sizeof ktv);
+#ifdef SYS_adjtimex
+	if (clock_id==CLOCK_REALTIME) r = __syscall(SYS_adjtimex, &ktx);
+	else
+#endif
+	r = __syscall(SYS_clock_adjtime, clock_id, &ktx);
+	if (r>=0) {
+		memcpy(&ktv, &ktx.time, sizeof ktv);
+		*utx = ktx;
+		utx->time = (struct timeval){ ktv.tv_sec, ktv.tv_usec };
+	}
+	return __syscall_ret(r);
+#else
 #ifdef SYS_adjtimex
 	if (clock_id==CLOCK_REALTIME) return syscall(SYS_adjtimex, utx);
 #endif
 	return syscall(SYS_clock_adjtime, clock_id, utx);
+#endif
 }
diff --git a/src/linux/wait4.c b/src/linux/wait4.c
index fb08c0d0..611158fc 100644
--- a/src/linux/wait4.c
+++ b/src/linux/wait4.c
@@ -27,6 +27,16 @@ pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru)
 #endif
 	char *dest = ru ? (char *)&ru->ru_maxrss - 4*sizeof(long) : 0;
 	r = __sys_wait4(pid, status, options, dest);
+#ifdef KERNEL_SUSECONDS_IS_INT
+	if (r>0 && ru) {
+		struct k_timeval kt[2];
+		memcpy(kt, dest, sizeof kt);
+		ru->ru_utime = (struct timeval)
+			{ .tv_sec = kt[0].tv_sec, .tv_usec = kt[0].tv_usec };
+		ru->ru_stime = (struct timeval)
+			{ .tv_sec = kt[1].tv_sec, .tv_usec = kt[1].tv_usec };
+	}
+#endif
 	if (r>0 && ru && sizeof(time_t) > sizeof(long)) {
 		long kru[4];
 		memcpy(kru, dest, 4*sizeof(long));
diff --git a/src/misc/getrusage.c b/src/misc/getrusage.c
index 8e03e2e3..c7c44246 100644
--- a/src/misc/getrusage.c
+++ b/src/misc/getrusage.c
@@ -23,6 +23,16 @@ int getrusage(int who, struct rusage *ru)
 #endif
 	char *dest = (char *)&ru->ru_maxrss - 4*sizeof(long);
 	r = __syscall(SYS_getrusage, who, dest);
+#ifdef KERNEL_SUSECONDS_IS_INT
+	if (!r) {
+		struct k_timeval kt[2];
+		memcpy(kt, dest, sizeof kt);
+		ru->ru_utime = (struct timeval)
+			{ .tv_sec = kt[0].tv_sec, .tv_usec = kt[0].tv_usec };
+		ru->ru_stime = (struct timeval)
+			{ .tv_sec = kt[1].tv_sec, .tv_usec = kt[1].tv_usec };
+	}
+#endif
 	if (!r && sizeof(time_t) > sizeof(long)) {
 		long kru[4];
 		memcpy(kru, dest, 4*sizeof(long));
diff --git a/src/misc/ioctl.c b/src/misc/ioctl.c
index 35804f02..553a699d 100644
--- a/src/misc/ioctl.c
+++ b/src/misc/ioctl.c
@@ -132,6 +132,18 @@ int ioctl(int fd, int req, ...)
 	va_start(ap, req);
 	arg = va_arg(ap, void *);
 	va_end(ap);
+#ifdef KERNEL_SUSECONDS_IS_INT
+	if (req == SIOCGSTAMP) {
+		struct k_timeval kt;
+		int r = __syscall(SYS_ioctl, fd, req, &kt);
+		if (!r) {
+			struct timeval *tv = arg;
+			tv->tv_sec = kt.tv_sec;
+			tv->tv_usec = kt.tv_usec;
+		}
+		return __syscall_ret(r);
+	}
+#endif
 	int r = __syscall(SYS_ioctl, fd, req, arg);
 	if (SIOCGSTAMP != SIOCGSTAMP_OLD && req && r==-ENOTTY) {
 		for (int i=0; i<sizeof compat_map/sizeof *compat_map; i++) {
diff --git a/src/network/getsockopt.c b/src/network/getsockopt.c
index d3640d9c..bd152983 100644
--- a/src/network/getsockopt.c
+++ b/src/network/getsockopt.c
@@ -5,7 +5,11 @@
 
 int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t *restrict optlen)
 {
+#ifdef KERNEL_SUSECONDS_IS_INT
+	struct k_timeval ktv;
+#else
 	long tv32[2];
+#endif
 	struct timeval *tv;
 
 	int r = __socketcall(getsockopt, fd, level, optname, optval, optlen, 0);
@@ -19,12 +23,21 @@ int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t
 			if (*optlen < sizeof *tv) return __syscall_ret(-EINVAL);
 			if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD;
 			if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD;
+#ifdef KERNEL_SUSECONDS_IS_INT
+			r = __socketcall(getsockopt, fd, level, optname,
+				&ktv, (socklen_t[]){sizeof ktv}, 0);
+			if (r<0) break;
+			tv = optval;
+			tv->tv_sec = ktv.tv_sec;
+			tv->tv_usec = ktv.tv_usec;
+#else
 			r = __socketcall(getsockopt, fd, level, optname,
 				tv32, (socklen_t[]){sizeof tv32}, 0);
 			if (r<0) break;
 			tv = optval;
 			tv->tv_sec = tv32[0];
 			tv->tv_usec = tv32[1];
+#endif
 			*optlen = sizeof *tv;
 			break;
 		case SO_TIMESTAMP:
diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c
index 612a1947..ad949d1e 100644
--- a/src/network/setsockopt.c
+++ b/src/network/setsockopt.c
@@ -29,8 +29,14 @@ int setsockopt(int fd, int level, int optname, const void *optval, socklen_t opt
 			if (optname==SO_RCVTIMEO) optname=SO_RCVTIMEO_OLD;
 			if (optname==SO_SNDTIMEO) optname=SO_SNDTIMEO_OLD;
 
+#ifdef KERNEL_SUSECONDS_IS_INT
+			r = __socketcall(setsockopt, fd, level, optname,
+				((struct k_timeval[]){{s, CLAMP(us)}}),
+				sizeof(struct k_timeval), 0);
+#else
 			r = __socketcall(setsockopt, fd, level, optname,
 				((long[]){s, CLAMP(us)}), 2*sizeof(long), 0);
+#endif
 			break;
 		case SO_TIMESTAMP:
 		case SO_TIMESTAMPNS:
diff --git a/src/select/select.c b/src/select/select.c
index f1d72863..248a140e 100644
--- a/src/select/select.c
+++ b/src/select/select.c
@@ -36,8 +36,13 @@ int select(int n, fd_set *restrict rfds, fd_set *restrict wfds, fd_set *restrict
 	s = CLAMP(s);
 #endif
 #ifdef SYS_select
+#ifdef KERNEL_SUSECONDS_IS_INT
+	struct k_timeval ktv = { s, us };
+	return syscall_cp(SYS_select, n, rfds, wfds, efds, tv ? &ktv : 0);
+#else
 	return syscall_cp(SYS_select, n, rfds, wfds, efds,
 		tv ? ((long[]){s, us}) : 0);
+#endif
 #else
 	return syscall_cp(SYS_pselect6, n, rfds, wfds, efds,
 		tv ? ((long[]){s, ns}) : 0, ((syscall_arg_t[]){ 0, _NSIG/8 }));
diff --git a/src/signal/getitimer.c b/src/signal/getitimer.c
index 36d1eb9d..30c538fc 100644
--- a/src/signal/getitimer.c
+++ b/src/signal/getitimer.c
@@ -14,5 +14,17 @@ int getitimer(int which, struct itimerval *old)
 		}
 		return __syscall_ret(r);
 	}
+#ifdef KERNEL_SUSECONDS_IS_INT
+	struct k_timeval kold[2];
+	int r = __syscall(SYS_getitimer, which, kold);
+	if (!r) {
+		old->it_interval = (struct timeval)
+			{ kold[0].tv_sec, kold[0].tv_usec };
+		old->it_value = (struct timeval)
+			{ kold[1].tv_sec, kold[1].tv_usec };
+	}
+	return __syscall_ret(r);
+#else
 	return syscall(SYS_getitimer, which, old);
+#endif
 }
diff --git a/src/signal/setitimer.c b/src/signal/setitimer.c
index 0dfbeb4d..b38fe266 100644
--- a/src/signal/setitimer.c
+++ b/src/signal/setitimer.c
@@ -22,5 +22,23 @@ int setitimer(int which, const struct itimerval *restrict new, struct itimerval
 		}
 		return __syscall_ret(r);
 	}
+#ifdef KERNEL_SUSECONDS_IS_INT
+	struct k_timeval knew[2], kold[2];
+	if (new) {
+		knew[0] = (struct k_timeval)
+			{ new->it_interval.tv_sec, new->it_interval.tv_usec };
+		knew[1] = (struct k_timeval)
+			{ new->it_value.tv_sec, new->it_value.tv_usec };
+	}
+	int r = __syscall(SYS_setitimer, which, new ? knew : 0, old ? kold : 0);
+	if (!r && old) {
+		old->it_interval = (struct timeval)
+			{ kold[0].tv_sec, kold[0].tv_usec };
+		old->it_value = (struct timeval)
+			{ kold[1].tv_sec, kold[1].tv_usec };
+	}
+	return __syscall_ret(r);
+#else
 	return syscall(SYS_setitimer, which, new, old);
+#endif
 }
-- 
2.47.3

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.