From patchwork Tue Sep 8 14:57:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 40379 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 6FA7B3952DBB; Tue, 8 Sep 2020 14:58:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 6FA7B3952DBB DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1599577086; bh=de6p5TdJYmEXV9hD8taCyUz4DnNGv6mCD1HLGTH3jfc=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To: From; b=Lo0kGyyAwBeFPph4YI4QPJxQy/OFOXjSRERy4YDTBsa5FqjwS65yVL3sLtva+LncZ jecD9Y9aCjCxPDwKXs2e+hVrJz0lEd3nvpRsRknpy1hniVjiAxJUsGIbpD3uZQEDxA Nv8owrssD2eXdHuT85XnTE4Sq52AOaw+wrCrYJj8= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-qk1-x741.google.com (mail-qk1-x741.google.com [IPv6:2607:f8b0:4864:20::741]) by sourceware.org (Postfix) with ESMTPS id 50D3D39518B4 for ; Tue, 8 Sep 2020 14:58:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org 50D3D39518B4 Received: by mail-qk1-x741.google.com with SMTP id w186so15579976qkd.1 for ; Tue, 08 Sep 2020 07:58:04 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=de6p5TdJYmEXV9hD8taCyUz4DnNGv6mCD1HLGTH3jfc=; b=MXTr9yjQRO3pIxcdS/nGdEYdalPdU/d4hFsl2tKkC5lN41k3t5YwvRdYSom6XAsL9h /Lc9PWkJP6Sf/8dfZP+iM2iikrb2v8Kx8YJYbZCI2saUnHiYfImJ1BFbHUaAV+wNKCQe ihUE9m7Ke9kYg7Ci2CUO72t/UR65bCF6+BLRrqRhw7bEXOaUP38Vl3lJpX97SPKKF3LS EiOmDicIbOLVJZRCYwPuTe9LnP/LTN42wCNXMoQnp5IF2wmOUkae64glC3CNAUtz7kOm k9hynmaIZQulEqEwQKplWjlg5IpLMbOsZVjg0GceKIovHDF84NbcIrwoOwcmN/VpcIyf xZtg== X-Gm-Message-State: AOAM533OifzuBV2XxQHOPpYT1FEzq1A9s3P2YDp6cuwOTYbJwthwG6zp xFu7+J9IFGSvCiqsX3SqRDTN/SenZY+oEg== X-Google-Smtp-Source: ABdhPJxjAgeYVexRE7LZyTdaJaapv+j0ktmVnXaoME1SqTcrGxd01UQIhXWcUQhp4/VZ1bhCNM/B4A== X-Received: by 2002:a37:7182:: with SMTP id m124mr417968qkc.37.1599577083246; Tue, 08 Sep 2020 07:58:03 -0700 (PDT) Received: from localhost.localdomain ([177.194.48.209]) by smtp.googlemail.com with ESMTPSA id e188sm10800748qkd.55.2020.09.08.07.58.02 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Sep 2020 07:58:02 -0700 (PDT) To: libc-alpha@sourceware.org Subject: [RFC 13/14] linux: Add fallback for 64-bit time_t SO_TIMESTAMP{NS} Date: Tue, 8 Sep 2020 11:57:37 -0300 Message-Id: <20200908145738.640039-13-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200908145738.640039-1-adhemerval.zanella@linaro.org> References: <20200908145738.640039-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-13.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Adhemerval Zanella via Libc-alpha From: Adhemerval Zanella Reply-To: Adhemerval Zanella Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" The constant value will be changed for __TIMESIZE=64, so binaries built with 64-bit time support might fail to work properly on old kernels. Both {get,set}sockopt will retry the syscall with the old constant values and the timeout value adjusted when kernel returns ENOTPROTOPT. The recvmsg handling is more complicated because it requires check the returned kernel control message and make some convertions. For !__ASSUME_TIME64_SYSCALLS it converts the first 32-bit time SO_TIMESTAMP or SO_TIMESTAMPNS and appends it to the control buffer if has extra space or returns MSG_CTRUNC otherwise. The 32-bit time field is kept as-is. Calls with __TIMESIZE=32 will see the converted 64-bit time control messages as spurious control message of unknown type. Calls with __TIMESIZE=64 running on pre-time64 kernels will see the original message as a spurious control ones of unknown typ while running on kernel with native 64-bit time support will only see the time64 version of the control message. Checked on x86_64-linux-gnu and i686-linux-gnu (on 5.4 and on 4.15 kernel). --- include/sys/socket.h | 9 +++ sysdeps/unix/sysv/linux/getsockopt.c | 12 ++++ sysdeps/unix/sysv/linux/recvmsg.c | 93 ++++++++++++++++++++++++++-- sysdeps/unix/sysv/linux/setsockopt.c | 12 ++++ 4 files changed, 121 insertions(+), 5 deletions(-) diff --git a/include/sys/socket.h b/include/sys/socket.h index 0e39dd2a3a..c551c8fa87 100644 --- a/include/sys/socket.h +++ b/include/sys/socket.h @@ -162,6 +162,15 @@ libc_hidden_proto (__libc_sa_len) # define SA_LEN(_x) __libc_sa_len((_x)->sa_family) #endif +/* Used on y2038 emulation on 64-bit time_t binaries running on older + kernel without 64-bit time_t support. */ +#define SCM_TIMESTAMP_OLD SO_TIMESTAMP_OLD +#define SCM_TIMESTAMPNS_OLD SO_TIMESTAMPNS_OLD +#define SCM_TIMESTAMPING_OLD SO_TIMESTAMPING_OLD +#define SCM_TIMESTAMP_NEW SO_TIMESTAMP_NEW +#define SCM_TIMESTAMPNS_NEW SO_TIMESTAMPNS_NEW +#define SCM_TIMESTAMPING_NEW SO_TIMESTAMPING_NEW + libc_hidden_proto (__cmsg_nxthdr) #endif diff --git a/sysdeps/unix/sysv/linux/getsockopt.c b/sysdeps/unix/sysv/linux/getsockopt.c index 5dce0e29ee..0089a177b6 100644 --- a/sysdeps/unix/sysv/linux/getsockopt.c +++ b/sysdeps/unix/sysv/linux/getsockopt.c @@ -72,6 +72,18 @@ getsockopt32 (int fd, int level, int optname, void *optval, *tv64 = valid_timeval32_to_timeval64 (tv32); *len = sizeof (*tv64); } + break; + + case SO_TIMESTAMP_NEW: + case SO_TIMESTAMPNS_NEW: + { + if (optname == SO_TIMESTAMP_NEW) + optname = SO_TIMESTAMP_OLD; + if (optname == SO_TIMESTAMPNS_NEW) + optname = SO_TIMESTAMPNS_OLD; + r = getsockopt_syscall (fd, level, optname, optval, len); + } + break; } return r; diff --git a/sysdeps/unix/sysv/linux/recvmsg.c b/sysdeps/unix/sysv/linux/recvmsg.c index a86d502922..c6d1d10b05 100644 --- a/sysdeps/unix/sysv/linux/recvmsg.c +++ b/sysdeps/unix/sysv/linux/recvmsg.c @@ -21,14 +21,97 @@ #include #include +#ifndef __ASSUME_TIME64_SYSCALLS +/* It converts the first SO_TIMESTAMP or SO_TIMESTAMPNS with 32-bit time and + appends it to the control buffer. The 32-bit time field is kept as-is. + + Calls with __TIMESIZE=32 will see the converted 64-bit time control + messages as spurious control message of unknown type. + + Calls with __TIMESIZE=64 running on pre-time64 kernels will see the + original message as a spurious control ones of unknown typ while running + on kernel with native 64-bit time support will only see the time64 version + of the control message. */ +static void +convert_scm_timestamps (struct msghdr *msg, socklen_t msgsize) +{ + if (msg->msg_control == NULL || msg->msg_controllen == 0) + return; + + /* The returnted control message format for SO_TIMESTAMP_NEW is a + 'struct __kernel_sock_timeval' while for SO_TIMESTAMPNS_NEW is a + 'struct __kernel_timespec'. In both case it is essentially two + uint64_t members. */ + uint64_t tvts[2]; + + struct cmsghdr *cmsg, *last = NULL; + int type = 0; + + for (cmsg = CMSG_FIRSTHDR (msg); + cmsg != NULL; + cmsg = CMSG_NXTHDR (msg, cmsg)) + { + if (cmsg->cmsg_level != SOL_SOCKET) + continue; + + switch (cmsg->cmsg_type) + { + case SCM_TIMESTAMP_OLD: + if (type != 0) + break; + type = SCM_TIMESTAMP_NEW; + goto common; + + case SCM_TIMESTAMPNS_OLD: + type = SCM_TIMESTAMPNS_NEW; + + /* fallthrough */ + common: + memcpy (tvts, CMSG_DATA (cmsg), sizeof (tvts)); + break; + } + + last = cmsg; + } + + if (last == NULL || type == 0) + return; + + if (CMSG_SPACE (sizeof tvts) > msgsize - msg->msg_controllen) + { + msg->msg_flags |= MSG_CTRUNC; + return; + } + + msg->msg_controllen += CMSG_SPACE (sizeof tvts); + cmsg = CMSG_NXTHDR(msg, last); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = type; + cmsg->cmsg_len = CMSG_LEN (sizeof tvts); + memcpy (CMSG_DATA (cmsg), tvts, sizeof tvts); +} +#endif + ssize_t __libc_recvmsg (int fd, struct msghdr *msg, int flags) { -# ifdef __ASSUME_RECVMSG_SYSCALL - return SYSCALL_CANCEL (recvmsg, fd, msg, flags); -# else - return SOCKETCALL_CANCEL (recvmsg, fd, msg, flags); -# endif + ssize_t r; +#ifndef __ASSUME_TIME64_SYSCALLS + socklen_t orig_controllen = msg->msg_controllen; +#endif + +#ifdef __ASSUME_RECVMSG_SYSCALL + r = SYSCALL_CANCEL (recvmsg, fd, msg, flags); +#else + r = SOCKETCALL_CANCEL (recvmsg, fd, msg, flags); +#endif + +#ifndef __ASSUME_TIME64_SYSCALLS + if (r >= 0) + convert_scm_timestamps (msg, orig_controllen); +#endif + + return r; } weak_alias (__libc_recvmsg, recvmsg) weak_alias (__libc_recvmsg, __recvmsg) diff --git a/sysdeps/unix/sysv/linux/setsockopt.c b/sysdeps/unix/sysv/linux/setsockopt.c index ebc32d788c..6aabe8bbde 100644 --- a/sysdeps/unix/sysv/linux/setsockopt.c +++ b/sysdeps/unix/sysv/linux/setsockopt.c @@ -74,6 +74,18 @@ setsockopt32 (int fd, int level, int optname, const void *optval, r = setsockopt_syscall (fd, level, optname, &tv32, sizeof (tv32)); } + break; + + case SO_TIMESTAMP_NEW: + case SO_TIMESTAMPNS_NEW: + { + if (optname == SO_TIMESTAMP_NEW) + optname = SO_TIMESTAMP_OLD; + if (optname == SO_TIMESTAMPNS_NEW) + optname = SO_TIMESTAMPNS_OLD; + r = setsockopt_syscall (fd, level, optname, NULL, 0); + } + break; } return r;