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 <[email protected]> --- 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..4206cf5a35 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 SO_RCVTIMEO_NEW 0x4040 +#define 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..1b7c5266f8 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 SO_RCVTIMEO_NEW 66 +#define 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..1d3f1186e1 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 SO_RCVTIMEO_NEW 0x0044 +#define 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
