| Message ID | 20240724131238.271095-1-william.tsai@sifive.com |
|---|---|
| State | Superseded |
| Headers |
Return-Path: <gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org> 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 E00693858402 for <patchwork@sourceware.org>; Wed, 24 Jul 2024 13:13:17 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) by sourceware.org (Postfix) with ESMTPS id 16EAE3858D29 for <gcc-patches@gcc.gnu.org>; Wed, 24 Jul 2024 13:12:44 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 16EAE3858D29 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=sifive.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=sifive.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 16EAE3858D29 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::42b ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721826765; cv=none; b=vD6mZ6Ggtm+LCYDeAp1e1aYsfp8foEyDY3L+pY9AUfqQuNEWbhtjlU2IG4gW6nolzzLmfhSJDVmRlV68rgbQC2YyCz+T1cr2hpGe67NQViNeDElpfoAI7SB8T3lwV7zswkqYaSkjFP16K+u6NsQI1vYQtbb43IlM+sPIQ26NjuU= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721826765; c=relaxed/simple; bh=o+rpZFXX3EVDa0e6YfE+QLQyqcsXCCuL4TSnGFuoj8Y=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=sdOkpJ00YR37LDeI0BIFR2rPG8Qb8ylgKyceMSxDG6CbUFlaRZSS9N+BTQrvYA2usLrBhgGZEZyJz1nhRjuAej1enYAAmB+o9RW6egvtCJ51HwY75w9abkPyYRt8DhKdyCrSd2mn6pbQvjhHBqSR9BFKfDmo3fsdp+bSxm29eHQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pf1-x42b.google.com with SMTP id d2e1a72fcca58-70d19d768c2so2662035b3a.3 for <gcc-patches@gcc.gnu.org>; Wed, 24 Jul 2024 06:12:44 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sifive.com; s=google; t=1721826763; x=1722431563; darn=gcc.gnu.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=cstHPV+G42xzZeUMZFZks7svo/71EX//PeGWu3iI8Qg=; b=MJqTEvCKvOY9y4E1oTWlhzOxux5baGaaukwR4YRron5A7AsC6QI/k1dVmqtTKbrwEb N0Kd5KDOaIOt8YPyfx8JWuczRpgvnqzFVTI83vtTRp1ZCYn6jlKEAuCRUEdB7i1U6z4K PbQhUcFiMfcZ2ArpuVTviP6s3ZNhlt7F5Of5zTk2W2Kwks0wu59Ypr0d4+bzZ8BJnZr6 3Ie8aXM0l2nRtbUD2eUR4f0U16WIHITr2bfJIusBtapr1WrMN4KZvk/I74G3t2o0PVtX 4XhPRJ7sDGLab4/sQGOKQ0uOAvsUeGmyJGg0WySh5j9p/9j+d07IssFz1lWO6Q6X+R3y Ojsg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721826763; x=1722431563; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=cstHPV+G42xzZeUMZFZks7svo/71EX//PeGWu3iI8Qg=; b=ARmLQsYNjOTmvseTZ7GesaGm89+KnNbDKRST9PMgirysOmMo1NwMYw0qeDgaZVI5Bj 5f39tILEoS01LNTXGomT05BEupEV74tTff1PQGR12y4YJqqHi/mbz77EAj0nHkRWjesZ 1YwvtvBRBQcJdEvquuhI9sSYoaAnsF12Zfii64CLCTeZ4hPL3loD54A0n/AsDYeRibfe 8TA3HBNbVxwQf8n8t5zj6IvY1Av0Y+rf85BU7ZNm9g4aYTBB9gqW8CKaeXiEw6R7kiCC 2z8i6YP8zD0QOZPSJA0Vrhzka7+X/eHnfEWfcZt+G2mr9QOGbK1Cdv+N1XiMoUAf5YHT dp7A== X-Forwarded-Encrypted: i=1; AJvYcCXz6UH04btapTsvNDl+Asf2UIBRx2L+kkLxr0J4RiRdt6YK/3CEyL9wDen6X2f7LFJMFjcWti5qfGd14WcECY/Nvv0vG5LpbQ== X-Gm-Message-State: AOJu0YzjWsWpNJsg/MUI+QV5WhnSjUsCa/9JwS9yTfE9sittPUXSzLat GS/f1xom/55JC/qN6PU2Iqt82u1AYtp2/8IMCwZKh1Zar/6XZr1qmNOM7cIDn2I= X-Google-Smtp-Source: AGHT+IEPDq+2r2t1e9gm7jiSZ/t9Y4ItDgkjvhh6RgDv1tksDEzvPxZERYqZyHyhhbGy79ARZLuPZg== X-Received: by 2002:a05:6a20:3d84:b0:1be:bfa2:5ac3 with SMTP id adf61e73a8af0-1c4286883bcmr11500116637.35.1721826762919; Wed, 24 Jul 2024 06:12:42 -0700 (PDT) Received: from sw07.internal.sifive.com ([4.53.31.132]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-70d21ab0cb4sm5492662b3a.130.2024.07.24.06.12.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 24 Jul 2024 06:12:42 -0700 (PDT) From: William Tsai <william.tsai@sifive.com> To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org, william.tsai@sifive.com Subject: [PATCH] libstdc++: Fix future.wait_until when given a negative time_point Date: Wed, 24 Jul 2024 06:12:38 -0700 Message-Id: <20240724131238.271095-1-william.tsai@sifive.com> X-Mailer: git-send-email 2.37.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-13.6 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.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> Errors-To: gcc-patches-bounces~patchwork=sourceware.org@gcc.gnu.org |
| Series |
libstdc++: Fix future.wait_until when given a negative time_point
|
|
Commit Message
William Tsai
July 24, 2024, 1:12 p.m. UTC
The template `future.wait_until` will expand to
`_M_load_and_test_until_impl` where it will call
`_M_load_and_test_until*` with given time_point casted into second and
nanosecond. The callee expects the caller to provide the values
correctly from caller while the caller did not make check with those
values. One possible error is that if `future.wait_until` is given with
a negative time_point, the underlying system call will raise an error as
the system call does not accept second < 0 and nanosecond < 1.
Following is a simple testcase:
```
#include <chrono>
#include <future>
#include <iostream>
using namespace std;
int main() {
promise<void> p;
future<void> f = p.get_future();
chrono::steady_clock::time_point tp(chrono::milliseconds{-10});
future_status status = f.wait_until(tp);
if (status == future_status::timeout) {
cout << "Timed out" << endl;
}
return 0;
}
```
libstdc++-v3/ChangeLog:
* include/bits/atomic_futex.h: Check if __s and __ns is valid before
calling before calling _M_load_and_test_until*
Signed-off-by: William Tsai <william.tsai@sifive.com>
---
libstdc++-v3/include/bits/atomic_futex.h | 4 ++++
1 file changed, 4 insertions(+)
Comments
On Wed, 24 Jul 2024 at 14:14, William Tsai <william.tsai@sifive.com> wrote: > > The template `future.wait_until` will expand to > `_M_load_and_test_until_impl` where it will call > `_M_load_and_test_until*` with given time_point casted into second and > nanosecond. The callee expects the caller to provide the values > correctly from caller while the caller did not make check with those > values. One possible error is that if `future.wait_until` is given with > a negative time_point, the underlying system call will raise an error as > the system call does not accept second < 0 and nanosecond < 1. Thanks for the patch, it looks correct. The futex syscall returns EINVAL in this case, which we don't handle, so the caller loops and keeps calling the syscall again, which fails again the same way. I think it would be good to mention EINVAL, e.g. "will raise an EINVAL error" instead of just "will raise an error". It would also be good to add a test to the testsuite for this. Do you have git write access, or do you need me to push it once it's approved? > > Following is a simple testcase: > ``` > #include <chrono> > #include <future> > #include <iostream> > > using namespace std; > > int main() { > promise<void> p; > future<void> f = p.get_future(); > chrono::steady_clock::time_point tp(chrono::milliseconds{-10}); > future_status status = f.wait_until(tp); > if (status == future_status::timeout) { > cout << "Timed out" << endl; > } > return 0; > } > ``` > > libstdc++-v3/ChangeLog: > > * include/bits/atomic_futex.h: Check if __s and __ns is valid before > calling before calling _M_load_and_test_until* > > Signed-off-by: William Tsai <william.tsai@sifive.com> > --- > libstdc++-v3/include/bits/atomic_futex.h | 4 ++++ > 1 file changed, 4 insertions(+) > > diff --git a/libstdc++-v3/include/bits/atomic_futex.h b/libstdc++-v3/include/bits/atomic_futex.h > index dd654174873..4c31946a97f 100644 > --- a/libstdc++-v3/include/bits/atomic_futex.h > +++ b/libstdc++-v3/include/bits/atomic_futex.h > @@ -173,6 +173,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > auto __s = chrono::time_point_cast<chrono::seconds>(__atime); > auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); > // XXX correct? > + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) > + return false; > return _M_load_and_test_until(__assumed, __operand, __equal, __mo, > true, __s.time_since_epoch(), __ns); > } > @@ -186,6 +188,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > auto __s = chrono::time_point_cast<chrono::seconds>(__atime); > auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); > // XXX correct? > + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) > + return false; > return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo, > true, __s.time_since_epoch(), __ns); > } > -- > 2.37.1 >
On Wed, 31 Jul 2024 at 22:39, Jonathan Wakely <jwakely@redhat.com> wrote: > > On Wed, 24 Jul 2024 at 14:14, William Tsai <william.tsai@sifive.com> wrote: > > > > The template `future.wait_until` will expand to > > `_M_load_and_test_until_impl` where it will call > > `_M_load_and_test_until*` with given time_point casted into second and > > nanosecond. The callee expects the caller to provide the values > > correctly from caller while the caller did not make check with those > > values. One possible error is that if `future.wait_until` is given with > > a negative time_point, the underlying system call will raise an error as > > the system call does not accept second < 0 and nanosecond < 1. > > Thanks for the patch, it looks correct. The futex syscall returns > EINVAL in this case, which we don't handle, so the caller loops and > keeps calling the syscall again, which fails again the same way. > > I think it would be good to mention EINVAL, e.g. "will raise an EINVAL > error" instead of just "will raise an error". I was finally getting around to merging this patch and realised the fix is actually much simpler. We do already check for negative timeouts, we just didn't handle values between -1s and 0s. We can fix it in the library, without changing the headers: --- a/libstdc++-v3/src/c++11/futex.cc +++ b/libstdc++-v3/src/c++11/futex.cc @@ -128,7 +128,7 @@ namespace if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed)) { // futex sets errno=EINVAL for absolute timeouts before the epoch. - if (__s.count() < 0) + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] return false; syscall_timespec rt; @@ -204,7 +204,7 @@ namespace if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed)) { // futex sets errno=EINVAL for absolute timeouts before the epoch. - if (__s.count() < 0) [[unlikely]] + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] return false; syscall_timespec rt; A timeout of 10ms before the epoch gets broken down into 0s and -10ms and so the __s.count() < 0 check is false, but the futex syscall still returns EINVAL. So I'll fix this that way instead. > > It would also be good to add a test to the testsuite for this. > > Do you have git write access, or do you need me to push it once it's approved? > > > > > > Following is a simple testcase: > > ``` > > #include <chrono> > > #include <future> > > #include <iostream> > > > > using namespace std; > > > > int main() { > > promise<void> p; > > future<void> f = p.get_future(); > > chrono::steady_clock::time_point tp(chrono::milliseconds{-10}); > > future_status status = f.wait_until(tp); > > if (status == future_status::timeout) { > > cout << "Timed out" << endl; > > } > > return 0; > > } > > ``` > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/atomic_futex.h: Check if __s and __ns is valid before > > calling before calling _M_load_and_test_until* > > > > Signed-off-by: William Tsai <william.tsai@sifive.com> > > --- > > libstdc++-v3/include/bits/atomic_futex.h | 4 ++++ > > 1 file changed, 4 insertions(+) > > > > diff --git a/libstdc++-v3/include/bits/atomic_futex.h b/libstdc++-v3/include/bits/atomic_futex.h > > index dd654174873..4c31946a97f 100644 > > --- a/libstdc++-v3/include/bits/atomic_futex.h > > +++ b/libstdc++-v3/include/bits/atomic_futex.h > > @@ -173,6 +173,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > auto __s = chrono::time_point_cast<chrono::seconds>(__atime); > > auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); > > // XXX correct? > > + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) > > + return false; > > return _M_load_and_test_until(__assumed, __operand, __equal, __mo, > > true, __s.time_since_epoch(), __ns); > > } > > @@ -186,6 +188,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > > auto __s = chrono::time_point_cast<chrono::seconds>(__atime); > > auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); > > // XXX correct? > > + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) > > + return false; > > return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo, > > true, __s.time_since_epoch(), __ns); > > } > > -- > > 2.37.1 > >
On Tue, 17 Dec 2024 at 19:38, Jonathan Wakely <jwakely@redhat.com> wrote: > > On Wed, 31 Jul 2024 at 22:39, Jonathan Wakely <jwakely@redhat.com> wrote: > > > > On Wed, 24 Jul 2024 at 14:14, William Tsai <william.tsai@sifive.com> wrote: > > > > > > The template `future.wait_until` will expand to > > > `_M_load_and_test_until_impl` where it will call > > > `_M_load_and_test_until*` with given time_point casted into second and > > > nanosecond. The callee expects the caller to provide the values > > > correctly from caller while the caller did not make check with those > > > values. One possible error is that if `future.wait_until` is given with > > > a negative time_point, the underlying system call will raise an error as > > > the system call does not accept second < 0 and nanosecond < 1. > > > > Thanks for the patch, it looks correct. The futex syscall returns > > EINVAL in this case, which we don't handle, so the caller loops and > > keeps calling the syscall again, which fails again the same way. > > > > I think it would be good to mention EINVAL, e.g. "will raise an EINVAL > > error" instead of just "will raise an error". > > I was finally getting around to merging this patch and realised the > fix is actually much simpler. We do already check for negative > timeouts, we just didn't handle values between -1s and 0s. We can fix > it in the library, without changing the headers: > > --- a/libstdc++-v3/src/c++11/futex.cc > +++ b/libstdc++-v3/src/c++11/futex.cc > @@ -128,7 +128,7 @@ namespace > if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed)) > { > // futex sets errno=EINVAL for absolute timeouts before the epoch. > - if (__s.count() < 0) > + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] > return false; > > syscall_timespec rt; > @@ -204,7 +204,7 @@ namespace > if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed)) > { > // futex sets errno=EINVAL for absolute timeouts before the epoch. > - if (__s.count() < 0) [[unlikely]] > + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] > return false; > > syscall_timespec rt; > > > A timeout of 10ms before the epoch gets broken down into 0s and -10ms > and so the __s.count() < 0 check is false, but the futex syscall still > returns EINVAL. > > So I'll fix this that way instead. Here's what I'm testing. commit a3f4ab3c7bfda80b5dd3ffe56b2accad15de3d71 Author: Jonathan Wakely <jwakely@redhat.com> AuthorDate: Tue Dec 17 21:32:19 2024 Commit: Jonathan Wakely <redi@gcc.gnu.org> CommitDate: Wed Dec 18 00:35:16 2024 libstdc++: Fix std::future::wait_until for subsecond negative times [PR118093] The current check for negative times (i.e. before the epoch) only checks for a negative number of seconds. For a time 1ms before the epoch the seconds part will be zero, but the futex syscall will still fail with an EINVAL error. Extend the check to handle this case. This change adds a redundant check in the headers too, so that we avoid even calling into the library for negative times. Both checks can be marked [[unlikely]]. libstdc++-v3/ChangeLog: PR libstdc++/118093 * include/bits/atomic_futex.h (_M_load_and_test_until_impl): Return false for times before the epoch. * src/c++11/futex.cc (_M_futex_wait_until): Extend check for negative times to check for subsecond times. Add unlikely attribute. (_M_futex_wait_until_steady): Likewise. * testsuite/30_threads/future/members/118093.cc: New test. diff --git a/libstdc++-v3/include/bits/atomic_futex.h b/libstdc++-v3/include/bits/atomic_futex.h index c7d90466418..fe13508462a 100644 --- a/libstdc++-v3/include/bits/atomic_futex.h +++ b/libstdc++-v3/include/bits/atomic_futex.h @@ -172,11 +172,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool __equal, memory_order __mo, const chrono::time_point<std::chrono::system_clock, _Dur>& __atime) { - auto __s = chrono::time_point_cast<chrono::seconds>(__atime); - auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); - // XXX correct? + auto __d = __atime.time_since_epoch(); + if (__d < __d.zero()) [[__unlikely__]] + return false; + auto __s = chrono::duration_cast<chrono::seconds>(__d); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s); return _M_load_and_test_until(__assumed, __operand, __equal, __mo, - true, __s.time_since_epoch(), __ns); + true, __s, __ns); } template<typename _Dur> @@ -185,11 +187,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool __equal, memory_order __mo, const chrono::time_point<std::chrono::steady_clock, _Dur>& __atime) { - auto __s = chrono::time_point_cast<chrono::seconds>(__atime); - auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); - // XXX correct? + auto __d = __atime.time_since_epoch(); + if (__d < __d.zero()) [[__unlikely__]] + return false; + auto __s = chrono::duration_cast<chrono::seconds>(__d); + auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s); return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo, - true, __s.time_since_epoch(), __ns); + true, __s, __ns); } public: diff --git a/libstdc++-v3/src/c++11/futex.cc b/libstdc++-v3/src/c++11/futex.cc index ada2fdf3c41..6139fc354c6 100644 --- a/libstdc++-v3/src/c++11/futex.cc +++ b/libstdc++-v3/src/c++11/futex.cc @@ -128,7 +128,7 @@ namespace if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed)) { // futex sets errno=EINVAL for absolute timeouts before the epoch. - if (__s.count() < 0) + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] return false; syscall_timespec rt; @@ -204,7 +204,7 @@ namespace if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed)) { // futex sets errno=EINVAL for absolute timeouts before the epoch. - if (__s.count() < 0) [[unlikely]] + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] return false; syscall_timespec rt; diff --git a/libstdc++-v3/testsuite/30_threads/future/members/118093.cc b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc new file mode 100644 index 00000000000..2bb1e1c6dad --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc @@ -0,0 +1,26 @@ +// { dg-do run { target c++11 } } + +#include <chrono> +#include <future> + +void +test_sys() +{ + std::promise<void> p; + std::chrono::system_clock::time_point tp(std::chrono::milliseconds{-10}); + (void) p.get_future().wait_until(tp); +} + +void +test_steady() +{ + std::promise<void> p; + std::chrono::steady_clock::time_point tp(std::chrono::milliseconds{-10}); + (void) p.get_future().wait_until(tp); +} + +int main() +{ + test_sys(); + test_steady(); +}
On Wed, 18 Dec 2024 at 00:35, Jonathan Wakely <jwakely@redhat.com> wrote: > > On Tue, 17 Dec 2024 at 19:38, Jonathan Wakely <jwakely@redhat.com> wrote: > > > > On Wed, 31 Jul 2024 at 22:39, Jonathan Wakely <jwakely@redhat.com> wrote: > > > > > > On Wed, 24 Jul 2024 at 14:14, William Tsai <william.tsai@sifive.com> wrote: > > > > > > > > The template `future.wait_until` will expand to > > > > `_M_load_and_test_until_impl` where it will call > > > > `_M_load_and_test_until*` with given time_point casted into second and > > > > nanosecond. The callee expects the caller to provide the values > > > > correctly from caller while the caller did not make check with those > > > > values. One possible error is that if `future.wait_until` is given with > > > > a negative time_point, the underlying system call will raise an error as > > > > the system call does not accept second < 0 and nanosecond < 1. > > > > > > Thanks for the patch, it looks correct. The futex syscall returns > > > EINVAL in this case, which we don't handle, so the caller loops and > > > keeps calling the syscall again, which fails again the same way. > > > > > > I think it would be good to mention EINVAL, e.g. "will raise an EINVAL > > > error" instead of just "will raise an error". > > > > I was finally getting around to merging this patch and realised the > > fix is actually much simpler. We do already check for negative > > timeouts, we just didn't handle values between -1s and 0s. We can fix > > it in the library, without changing the headers: > > > > --- a/libstdc++-v3/src/c++11/futex.cc > > +++ b/libstdc++-v3/src/c++11/futex.cc > > @@ -128,7 +128,7 @@ namespace > > if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed)) > > { > > // futex sets errno=EINVAL for absolute timeouts before the epoch. > > - if (__s.count() < 0) > > + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] > > return false; > > > > syscall_timespec rt; > > @@ -204,7 +204,7 @@ namespace > > if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed)) > > { > > // futex sets errno=EINVAL for absolute timeouts before the epoch. > > - if (__s.count() < 0) [[unlikely]] > > + if (__s.count() < 0 || __ns.count() < 0) [[unlikely]] > > return false; > > > > syscall_timespec rt; > > > > > > A timeout of 10ms before the epoch gets broken down into 0s and -10ms > > and so the __s.count() < 0 check is false, but the futex syscall still > > returns EINVAL. > > > > So I'll fix this that way instead. > > Here's what I'm testing. Pushed to trunk now.
diff --git a/libstdc++-v3/include/bits/atomic_futex.h b/libstdc++-v3/include/bits/atomic_futex.h index dd654174873..4c31946a97f 100644 --- a/libstdc++-v3/include/bits/atomic_futex.h +++ b/libstdc++-v3/include/bits/atomic_futex.h @@ -173,6 +173,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __s = chrono::time_point_cast<chrono::seconds>(__atime); auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); // XXX correct? + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) + return false; return _M_load_and_test_until(__assumed, __operand, __equal, __mo, true, __s.time_since_epoch(), __ns); } @@ -186,6 +188,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION auto __s = chrono::time_point_cast<chrono::seconds>(__atime); auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s); // XXX correct? + if ((__s.time_since_epoch().count() < 0) || (__ns.count() < 0)) + return false; return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo, true, __s.time_since_epoch(), __ns); }