Skip to content

Commit

Permalink
vsock: Enable y2038 safe timeval for timeout
Browse files Browse the repository at this point in the history
Reuse the timeval compat code from core/sock to handle 32-bit and
64-bit timeval structures. Also introduce a new socket option define
to allow using y2038 safe timeval under 32-bit.

The existing behavior of sock_set_timeout and vsock's timeout setter
differ when the time value is out of bounds. vsocks current behavior
is retained at the expense of not being able to share the full
implementation.

This allows the LTP test vsock01 to pass under 32-bit compat mode.

Fixes: fe0c72f ("socket: move compat timeout handling into sock.c")
Signed-off-by: Richard Palethorpe <[email protected]>
Cc: Richard Palethorpe <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
  • Loading branch information
Richard Palethorpe authored and davem330 committed Oct 8, 2021
1 parent 685c3f2 commit 4c1e34c
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 22 deletions.
4 changes: 4 additions & 0 deletions include/net/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -2836,4 +2836,8 @@ void sock_set_sndtimeo(struct sock *sk, s64 secs);

int sock_bind_add(struct sock *sk, struct sockaddr *addr, int addr_len);

int sock_get_timeout(long timeo, void *optval, bool old_timeval);
int sock_copy_user_timeval(struct __kernel_sock_timeval *tv,
sockptr_t optval, int optlen, bool old_timeval);

#endif /* _SOCK_H */
13 changes: 12 additions & 1 deletion include/uapi/linux/vm_sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
* timeout for a STREAM socket.
*/

#define SO_VM_SOCKETS_CONNECT_TIMEOUT 6
#define SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD 6

/* Option name for using non-blocking send/receive. Use as the option name
* for setsockopt(3) or getsockopt(3) to set or get the non-blocking
Expand All @@ -81,6 +81,17 @@

#define SO_VM_SOCKETS_NONBLOCK_TXRX 7

#define SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW 8

#if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
#define SO_VM_SOCKETS_CONNECT_TIMEOUT SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD
#else
#define SO_VM_SOCKETS_CONNECT_TIMEOUT \
(sizeof(time_t) == sizeof(__kernel_long_t) ? SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD : SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW)
#endif
#endif

/* The vSocket equivalent of INADDR_ANY. This works for the svm_cid field of
* sockaddr_vm and indicates the context ID of the current endpoint.
*/
Expand Down
35 changes: 24 additions & 11 deletions net/core/sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ void sk_error_report(struct sock *sk)
}
EXPORT_SYMBOL(sk_error_report);

static int sock_get_timeout(long timeo, void *optval, bool old_timeval)
int sock_get_timeout(long timeo, void *optval, bool old_timeval)
{
struct __kernel_sock_timeval tv;

Expand Down Expand Up @@ -379,12 +379,11 @@ static int sock_get_timeout(long timeo, void *optval, bool old_timeval)
*(struct __kernel_sock_timeval *)optval = tv;
return sizeof(tv);
}
EXPORT_SYMBOL(sock_get_timeout);

static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen,
bool old_timeval)
int sock_copy_user_timeval(struct __kernel_sock_timeval *tv,
sockptr_t optval, int optlen, bool old_timeval)
{
struct __kernel_sock_timeval tv;

if (old_timeval && in_compat_syscall() && !COMPAT_USE_64BIT_TIME) {
struct old_timeval32 tv32;

Expand All @@ -393,23 +392,37 @@ static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen,

if (copy_from_sockptr(&tv32, optval, sizeof(tv32)))
return -EFAULT;
tv.tv_sec = tv32.tv_sec;
tv.tv_usec = tv32.tv_usec;
tv->tv_sec = tv32.tv_sec;
tv->tv_usec = tv32.tv_usec;
} else if (old_timeval) {
struct __kernel_old_timeval old_tv;

if (optlen < sizeof(old_tv))
return -EINVAL;
if (copy_from_sockptr(&old_tv, optval, sizeof(old_tv)))
return -EFAULT;
tv.tv_sec = old_tv.tv_sec;
tv.tv_usec = old_tv.tv_usec;
tv->tv_sec = old_tv.tv_sec;
tv->tv_usec = old_tv.tv_usec;
} else {
if (optlen < sizeof(tv))
if (optlen < sizeof(*tv))
return -EINVAL;
if (copy_from_sockptr(&tv, optval, sizeof(tv)))
if (copy_from_sockptr(tv, optval, sizeof(*tv)))
return -EFAULT;
}

return 0;
}
EXPORT_SYMBOL(sock_copy_user_timeval);

static int sock_set_timeout(long *timeo_p, sockptr_t optval, int optlen,
bool old_timeval)
{
struct __kernel_sock_timeval tv;
int err = sock_copy_user_timeval(&tv, optval, optlen, old_timeval);

if (err)
return err;

if (tv.tv_usec < 0 || tv.tv_usec >= USEC_PER_SEC)
return -EDOM;

Expand Down
25 changes: 15 additions & 10 deletions net/vmw_vsock/af_vsock.c
Original file line number Diff line number Diff line change
Expand Up @@ -1614,13 +1614,18 @@ static int vsock_connectible_setsockopt(struct socket *sock,
vsock_update_buffer_size(vsk, transport, vsk->buffer_size);
break;

case SO_VM_SOCKETS_CONNECT_TIMEOUT: {
struct __kernel_old_timeval tv;
COPY_IN(tv);
case SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW:
case SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD: {
struct __kernel_sock_timeval tv;

err = sock_copy_user_timeval(&tv, optval, optlen,
optname == SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD);
if (err)
break;
if (tv.tv_sec >= 0 && tv.tv_usec < USEC_PER_SEC &&
tv.tv_sec < (MAX_SCHEDULE_TIMEOUT / HZ - 1)) {
vsk->connect_timeout = tv.tv_sec * HZ +
DIV_ROUND_UP(tv.tv_usec, (1000000 / HZ));
DIV_ROUND_UP((unsigned long)tv.tv_usec, (USEC_PER_SEC / HZ));
if (vsk->connect_timeout == 0)
vsk->connect_timeout =
VSOCK_DEFAULT_CONNECT_TIMEOUT;
Expand Down Expand Up @@ -1653,7 +1658,9 @@ static int vsock_connectible_getsockopt(struct socket *sock,

union {
u64 val64;
struct old_timeval32 tm32;
struct __kernel_old_timeval tm;
struct __kernel_sock_timeval stm;
} v;

int lv = sizeof(v.val64);
Expand All @@ -1680,12 +1687,10 @@ static int vsock_connectible_getsockopt(struct socket *sock,
v.val64 = vsk->buffer_min_size;
break;

case SO_VM_SOCKETS_CONNECT_TIMEOUT:
lv = sizeof(v.tm);
v.tm.tv_sec = vsk->connect_timeout / HZ;
v.tm.tv_usec =
(vsk->connect_timeout -
v.tm.tv_sec * HZ) * (1000000 / HZ);
case SO_VM_SOCKETS_CONNECT_TIMEOUT_NEW:
case SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD:
lv = sock_get_timeout(vsk->connect_timeout, &v,
optname == SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD);
break;

default:
Expand Down

0 comments on commit 4c1e34c

Please sign in to comment.