[PATCH v2] linux-user: add y2038 safe socket timeout options

Łukasz Stelmach posted 1 patch 1 day, 20 hours ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20250904152922.2949232-1-l.stelmach@samsung.com
Maintainers: Laurent Vivier <laurent@vivier.eu>
linux-user/alpha/sockbits.h   |  8 ++++--
linux-user/generic/sockbits.h |  8 ++++--
linux-user/hppa/sockbits.h    |  8 ++++--
linux-user/mips/sockbits.h    |  8 ++++--
linux-user/ppc/sockbits.h     |  8 +++---
linux-user/sparc/sockbits.h   |  8 ++++--
linux-user/strace.c           |  6 +++--
linux-user/syscall.c          | 47 +++++++++++++++++++++--------------
8 files changed, 67 insertions(+), 34 deletions(-)
[PATCH v2] linux-user: add y2038 safe socket timeout options
Posted by Łukasz Stelmach 1 day, 20 hours ago
Linux kernel has redefined some socket options values and
introduces y2038 safe code paths on 32-bit architectures.
See include/uapi/asm-generic/socket.h in the kernel sources.

The argument for SO_RCVTIMEO_NEW (66) SO_SNDTIMEO_NEW (67) is
struct __kernel_sock_timeval which always comprises two 64-bit
fields regardless of the architecture.

Unlike the kernel QEMU needs to support both old and new values
so SO_SNDTIME and SO_RCVTIMEO have been renamed to *_OLD,
but neither old nor new values have been marked as "default".

Signed-off-by: Łukasz Stelmach <l.stelmach@samsung.com>
---
v2:
 - added missing TARGET_ prefix in header files

 linux-user/alpha/sockbits.h   |  8 ++++--
 linux-user/generic/sockbits.h |  8 ++++--
 linux-user/hppa/sockbits.h    |  8 ++++--
 linux-user/mips/sockbits.h    |  8 ++++--
 linux-user/ppc/sockbits.h     |  8 +++---
 linux-user/sparc/sockbits.h   |  8 ++++--
 linux-user/strace.c           |  6 +++--
 linux-user/syscall.c          | 47 +++++++++++++++++++++--------------
 8 files changed, 67 insertions(+), 34 deletions(-)

diff --git a/linux-user/alpha/sockbits.h b/linux-user/alpha/sockbits.h
index d54dc98c09..114e808693 100644
--- a/linux-user/alpha/sockbits.h
+++ b/linux-user/alpha/sockbits.h
@@ -28,8 +28,8 @@
 #define TARGET_SO_RCVBUFFORCE   0x100b
 #define TARGET_SO_RCVLOWAT  0x1010
 #define TARGET_SO_SNDLOWAT  0x1011
-#define TARGET_SO_RCVTIMEO  0x1012
-#define TARGET_SO_SNDTIMEO  0x1013
+#define TARGET_SO_RCVTIMEO_OLD  0x1012
+#define TARGET_SO_SNDTIMEO_OLD  0x1013
 #define TARGET_SO_ACCEPTCONN    0x1014
 #define TARGET_SO_PROTOCOL  0x1028
 #define TARGET_SO_DOMAIN    0x1029
@@ -75,6 +75,10 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define TARGET_SO_NOFCS     43
 
+/* New socket timeout options that are y2038 safe. */
+#define TARGET_SO_RCVTIMEO_NEW 66
+#define TARGET_SO_SNDTIMEO_NEW 67
+
 /* TARGET_O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
diff --git a/linux-user/generic/sockbits.h b/linux-user/generic/sockbits.h
index b3b4a8e44c..7b78b9dcaa 100644
--- a/linux-user/generic/sockbits.h
+++ b/linux-user/generic/sockbits.h
@@ -34,8 +34,8 @@
 #define TARGET_SO_PEERCRED     17
 #define TARGET_SO_RCVLOWAT     18
 #define TARGET_SO_SNDLOWAT     19
-#define TARGET_SO_RCVTIMEO     20
-#define TARGET_SO_SNDTIMEO     21
+#define TARGET_SO_RCVTIMEO_OLD 20
+#define TARGET_SO_SNDTIMEO_OLD 21
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define TARGET_SO_SECURITY_AUTHENTICATION              22
@@ -58,4 +58,8 @@
 
 #define TARGET_SO_PROTOCOL             38
 #define TARGET_SO_DOMAIN               39
+
+/* New socket timeout options that are y2038 safe. */
+#define TARGET_SO_RCVTIMEO_NEW 66
+#define TARGET_SO_SNDTIMEO_NEW 67
 #endif
diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h
index 23f69a3293..bb98a8c8be 100644
--- a/linux-user/hppa/sockbits.h
+++ b/linux-user/hppa/sockbits.h
@@ -17,8 +17,8 @@
 #define TARGET_SO_RCVBUFFORCE  0x100b
 #define TARGET_SO_SNDLOWAT     0x1003
 #define TARGET_SO_RCVLOWAT     0x1004
-#define TARGET_SO_SNDTIMEO     0x1005
-#define TARGET_SO_RCVTIMEO     0x1006
+#define TARGET_SO_SNDTIMEO_OLD 0x1005
+#define TARGET_SO_RCVTIMEO_OLD 0x1006
 #define TARGET_SO_ERROR        0x1007
 #define TARGET_SO_TYPE         0x1008
 #define TARGET_SO_PROTOCOL     0x1028
@@ -67,6 +67,10 @@
 
 #define TARGET_SO_CNX_ADVICE           0x402E
 
+/* New socket timeout options that are y2038 safe. */
+#define TARGET_SO_RCVTIMEO_NEW 0x4040
+#define TARGET_SO_SNDTIMEO_NEW 0x4041
+
 /* TARGET_O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
  */
diff --git a/linux-user/mips/sockbits.h b/linux-user/mips/sockbits.h
index 562cad88e2..11d25a5066 100644
--- a/linux-user/mips/sockbits.h
+++ b/linux-user/mips/sockbits.h
@@ -37,8 +37,8 @@
 #define TARGET_SO_RCVBUF       0x1002  /* Receive buffer. */
 #define TARGET_SO_SNDLOWAT     0x1003  /* send low-water mark */
 #define TARGET_SO_RCVLOWAT     0x1004  /* receive low-water mark */
-#define TARGET_SO_SNDTIMEO     0x1005  /* send timeout */
-#define TARGET_SO_RCVTIMEO     0x1006  /* receive timeout */
+#define TARGET_SO_SNDTIMEO_OLD 0x1005  /* send timeout */
+#define TARGET_SO_RCVTIMEO_OLD 0x1006  /* receive timeout */
 #define TARGET_SO_ACCEPTCONN   0x1009
 #define TARGET_SO_PROTOCOL     0x1028  /* protocol type */
 #define TARGET_SO_DOMAIN       0x1029  /* domain/socket family */
@@ -71,6 +71,10 @@
 #define TARGET_SO_RCVBUFFORCE          33
 #define TARGET_SO_PASSSEC              34
 
+/* New socket timeout options that are y2038 safe. */
+#define TARGET_SO_RCVTIMEO_NEW         66
+#define TARGET_SO_SNDTIMEO_NEW         67
+
 /** sock_type - Socket types
  *
  * Please notice that for binary compat reasons MIPS has to
diff --git a/linux-user/ppc/sockbits.h b/linux-user/ppc/sockbits.h
index ee453347a3..25455a948e 100644
--- a/linux-user/ppc/sockbits.h
+++ b/linux-user/ppc/sockbits.h
@@ -14,10 +14,10 @@
 #define TARGET_SO_RCVLOWAT     16
 #undef  TARGET_SO_SNDLOWAT
 #define TARGET_SO_SNDLOWAT     17
-#undef  TARGET_SO_RCVTIMEO
-#define TARGET_SO_RCVTIMEO     18
-#undef  TARGET_SO_SNDTIMEO
-#define TARGET_SO_SNDTIMEO     19
+#undef  TARGET_SO_RCVTIMEO_OLD
+#define TARGET_SO_RCVTIMEO_OLD 18
+#undef  TARGET_SO_SNDTIMEO_OLD
+#define TARGET_SO_SNDTIMEO_OLD 19
 #undef  TARGET_SO_PASSCRED
 #define TARGET_SO_PASSCRED     20
 #undef  TARGET_SO_PEERCRED
diff --git a/linux-user/sparc/sockbits.h b/linux-user/sparc/sockbits.h
index 0a822e3e1f..8fce0e5373 100644
--- a/linux-user/sparc/sockbits.h
+++ b/linux-user/sparc/sockbits.h
@@ -24,8 +24,8 @@
 #define TARGET_SO_BSDCOMPAT    0x0400
 #define TARGET_SO_RCVLOWAT     0x0800
 #define TARGET_SO_SNDLOWAT     0x1000
-#define TARGET_SO_RCVTIMEO     0x2000
-#define TARGET_SO_SNDTIMEO     0x4000
+#define TARGET_SO_RCVTIMEO_OLD 0x2000
+#define TARGET_SO_SNDTIMEO_OLD 0x4000
 #define TARGET_SO_ACCEPTCONN   0x8000
 
 #define TARGET_SO_SNDBUF       0x1001
@@ -104,6 +104,10 @@
 
 #define TARGET_SO_ZEROCOPY             0x003e
 
+/* New socket timeout options that are y2038 safe. */
+#define TARGET_SO_RCVTIMEO_NEW         0x0044
+#define TARGET_SO_SNDTIMEO_NEW         0x0045
+
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define TARGET_SO_SECURITY_AUTHENTICATION              0x5001
 #define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT        0x5002
diff --git a/linux-user/strace.c b/linux-user/strace.c
index 1233ebceb0..9639d47d70 100644
--- a/linux-user/strace.c
+++ b/linux-user/strace.c
@@ -2970,11 +2970,13 @@ print_optint:
         case TARGET_SO_RCVLOWAT:
             qemu_log("SO_RCVLOWAT,");
             goto print_optint;
-        case TARGET_SO_RCVTIMEO:
+        case TARGET_SO_RCVTIMEO_OLD:
+        case TARGET_SO_RCVTIMEO_NEW:
             qemu_log("SO_RCVTIMEO,");
             print_timeval(optval, 0);
             break;
-        case TARGET_SO_SNDTIMEO:
+        case TARGET_SO_SNDTIMEO_OLD:
+        case TARGET_SO_SNDTIMEO_NEW:
             qemu_log("SO_SNDTIMEO,");
             print_timeval(optval, 0);
             break;
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 91360a072c..fb37b516db 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -1132,7 +1132,8 @@ static inline abi_long copy_to_user_timeval(abi_ulong target_tv_addr,
     return 0;
 }
 
-#if defined(TARGET_NR_clock_adjtime64) && defined(CONFIG_CLOCK_ADJTIME)
+#if defined(TARGET_NR_clock_adjtime64) && defined(CONFIG_CLOCK_ADJTIME) || \
+    defined(TARGET_NR_getsockopt) || defined(TARGET_NR_setsockopt)
 static inline abi_long copy_from_user_timeval64(struct timeval *tv,
                                                 abi_ulong target_tv_addr)
 {
@@ -2358,21 +2359,28 @@ static abi_long do_setsockopt(int sockfd, int level, int optname,
 #endif
     case TARGET_SOL_SOCKET:
         switch (optname) {
-        case TARGET_SO_RCVTIMEO:
-        case TARGET_SO_SNDTIMEO:
+        case TARGET_SO_RCVTIMEO_OLD:
+        case TARGET_SO_SNDTIMEO_OLD:
+        case TARGET_SO_RCVTIMEO_NEW:
+        case TARGET_SO_SNDTIMEO_NEW:
         {
                 struct timeval tv;
+                bool old_timeval = (optname == TARGET_SO_RCVTIMEO_OLD ||
+                                    optname == TARGET_SO_SNDTIMEO_OLD);
 
-                if (optlen != sizeof(struct target_timeval)) {
+                if (optlen != (old_timeval ? sizeof(struct target_timeval) : \
+                               sizeof(struct target__kernel_sock_timeval))) {
                     return -TARGET_EINVAL;
                 }
 
-                if (copy_from_user_timeval(&tv, optval_addr)) {
+                if ((old_timeval && copy_from_user_timeval(&tv, optval_addr)) ||
+                    (!old_timeval && copy_from_user_timeval64(&tv, optval_addr))) {
                     return -TARGET_EFAULT;
                 }
 
                 ret = get_errno(setsockopt(sockfd, SOL_SOCKET,
-                                optname == TARGET_SO_RCVTIMEO ?
+                                optname == TARGET_SO_RCVTIMEO_OLD || \
+                                optname == TARGET_SO_RCVTIMEO_NEW ?
                                     SO_RCVTIMEO : SO_SNDTIMEO,
                                 &tv, sizeof(tv)));
                 return ret;
@@ -2590,13 +2598,16 @@ static abi_long do_getsockopt(int sockfd, int level, int optname,
         /* These don't just return a single integer */
         case TARGET_SO_PEERNAME:
             goto unimplemented;
-        case TARGET_SO_RCVTIMEO: {
+        case TARGET_SO_RCVTIMEO_OLD:
+        case TARGET_SO_RCVTIMEO_NEW:
+        case TARGET_SO_SNDTIMEO_OLD:
+        case TARGET_SO_SNDTIMEO_NEW:
+        {
             struct timeval tv;
             socklen_t tvlen;
+            bool old_timeval = (optname == TARGET_SO_RCVTIMEO_OLD ||
+                                optname == TARGET_SO_SNDTIMEO_OLD);
 
-            optname = SO_RCVTIMEO;
-
-get_timeout:
             if (get_user_u32(len, optlen)) {
                 return -TARGET_EFAULT;
             }
@@ -2605,15 +2616,18 @@ get_timeout:
             }
 
             tvlen = sizeof(tv);
-            ret = get_errno(getsockopt(sockfd, level, optname,
+            ret = get_errno(getsockopt(sockfd, level,
+                                       optname == TARGET_SO_RCVTIMEO_OLD || \
+                                       optname == TARGET_SO_RCVTIMEO_NEW ?
+                                       SO_RCVTIMEO : SO_SNDTIMEO,
                                        &tv, &tvlen));
             if (ret < 0) {
                 return ret;
             }
-            if (len > sizeof(struct target_timeval)) {
-                len = sizeof(struct target_timeval);
-            }
-            if (copy_to_user_timeval(optval_addr, &tv)) {
+            len = MIN(len, (old_timeval ? sizeof(struct target_timeval) : \
+                            sizeof(struct target__kernel_sock_timeval)));
+            if ((old_timeval && copy_to_user_timeval(optval_addr, &tv)) ||
+                (!old_timeval && copy_to_user_timeval64(optval_addr, &tv))) {
                 return -TARGET_EFAULT;
             }
             if (put_user_u32(len, optlen)) {
@@ -2621,9 +2635,6 @@ get_timeout:
             }
             break;
         }
-        case TARGET_SO_SNDTIMEO:
-            optname = SO_SNDTIMEO;
-            goto get_timeout;
         case TARGET_SO_PEERCRED: {
             struct ucred cr;
             socklen_t crlen;
-- 
2.39.5