diff mbox series

nptl: Add smoke test for pthread_getcpuclockid failure

Message ID 20241121230047.2997929-1-siddhesh@sourceware.org
State New
Headers show
Series nptl: Add smoke test for pthread_getcpuclockid failure | expand

Commit Message

Siddhesh Poyarekar Nov. 21, 2024, 11 p.m. UTC
Exercise the invalid thread descriptor case to ensure full test coverage
for the function.

Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
---
 nptl/Makefile                            |  1 +
 nptl/tst-pthread-getcpuclockid-invalid.c | 47 ++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
 create mode 100644 nptl/tst-pthread-getcpuclockid-invalid.c

Comments

Florian Weimer Nov. 22, 2024, 7:42 a.m. UTC | #1
* Siddhesh Poyarekar:

> +int
> +do_test (void)
> +{
> +  pthread_t t = xpthread_create (NULL, thr, NULL);
> +  xpthread_join (t);
> +
> +  clockid_t c;
> +  TEST_COMPARE (pthread_getcpuclockid (t, &c), ESRCH);

This test is invalid because the lifetime of t has ended.  This
use-after-free bug becomes visible if you run the test with
GLIBC_TUNABLES=glibc.pthread.stack_cache_size=0.

Thanks,
Florian
Siddhesh Poyarekar Nov. 22, 2024, 11:39 a.m. UTC | #2
On 2024-11-22 02:42, Florian Weimer wrote:
>> +int
>> +do_test (void)
>> +{
>> +  pthread_t t = xpthread_create (NULL, thr, NULL);
>> +  xpthread_join (t);
>> +
>> +  clockid_t c;
>> +  TEST_COMPARE (pthread_getcpuclockid (t, &c), ESRCH);
> 
> This test is invalid because the lifetime of t has ended.  This
> use-after-free bug becomes visible if you run the test with
> GLIBC_TUNABLES=glibc.pthread.stack_cache_size=0.

Eek, of course, sorry I should have seen that :/

I can't see a way to intercept points within the lifetime of the thread 
id where tid is <=0, but I'll send an update if I do.

Thanks,
Sid
Florian Weimer Nov. 22, 2024, 11:43 a.m. UTC | #3
* Siddhesh Poyarekar:

> On 2024-11-22 02:42, Florian Weimer wrote:
>>> +int
>>> +do_test (void)
>>> +{
>>> +  pthread_t t = xpthread_create (NULL, thr, NULL);
>>> +  xpthread_join (t);
>>> +
>>> +  clockid_t c;
>>> +  TEST_COMPARE (pthread_getcpuclockid (t, &c), ESRCH);
>> This test is invalid because the lifetime of t has ended.  This
>> use-after-free bug becomes visible if you run the test with
>> GLIBC_TUNABLES=glibc.pthread.stack_cache_size=0.
>
> Eek, of course, sorry I should have seen that :/
>
> I can't see a way to intercept points within the lifetime of the
> thread id where tid is <=0, but I'll send an update if I do.

You could avoid joining the thread t and loop until
pthread_getcpuclockid fails, and then check that the error is ESRCH.

Thanks,
Florian
Siddhesh Poyarekar Nov. 22, 2024, 11:49 a.m. UTC | #4
On 2024-11-22 06:43, Florian Weimer wrote:
>> I can't see a way to intercept points within the lifetime of the
>> thread id where tid is <=0, but I'll send an update if I do.
> 
> You could avoid joining the thread t and loop until
> pthread_getcpuclockid fails, and then check that the error is ESRCH.

That would mean considering a timeout as success as well, wouldn't it? 
Or put in an upper limit to iterations.

Sid
Florian Weimer Nov. 22, 2024, 11:53 a.m. UTC | #5
* Siddhesh Poyarekar:

> On 2024-11-22 06:43, Florian Weimer wrote:
>>> I can't see a way to intercept points within the lifetime of the
>>> thread id where tid is <=0, but I'll send an update if I do.
>> You could avoid joining the thread t and loop until
>> pthread_getcpuclockid fails, and then check that the error is ESRCH.
>
> That would mean considering a timeout as success as well, wouldn't it?
> Or put in an upper limit to iterations.

I believe the kernel will reset the TID to zero once the thread exits.

Thanks,
Florian
Siddhesh Poyarekar Nov. 22, 2024, 12:02 p.m. UTC | #6
On 2024-11-22 06:53, Florian Weimer wrote:
> * Siddhesh Poyarekar:
> 
>> On 2024-11-22 06:43, Florian Weimer wrote:
>>>> I can't see a way to intercept points within the lifetime of the
>>>> thread id where tid is <=0, but I'll send an update if I do.
>>> You could avoid joining the thread t and loop until
>>> pthread_getcpuclockid fails, and then check that the error is ESRCH.
>>
>> That would mean considering a timeout as success as well, wouldn't it?
>> Or put in an upper limit to iterations.
> 
> I believe the kernel will reset the TID to zero once the thread exits.

Yes, but the main thread will need to win the race to see the TID before 
the thread descriptor is invalidated.

In fact, I wonder if we can even ensure that the main thread sees into 
only a valid (but not freed) thread descriptor.  If we can't ensure 
that, why go through the pain of the race anyway?  Wouldn't it be easier 
to put in a comment in this test saying that it's technically invalid, 
but we're using internal knowledge to test a path that cannot otherwise 
be reasonably tested?

thanks,
Sid
Florian Weimer Nov. 22, 2024, 12:18 p.m. UTC | #7
* Siddhesh Poyarekar:

> On 2024-11-22 06:53, Florian Weimer wrote:
>> * Siddhesh Poyarekar:
>> 
>>> On 2024-11-22 06:43, Florian Weimer wrote:
>>>>> I can't see a way to intercept points within the lifetime of the
>>>>> thread id where tid is <=0, but I'll send an update if I do.
>>>> You could avoid joining the thread t and loop until
>>>> pthread_getcpuclockid fails, and then check that the error is ESRCH.
>>>
>>> That would mean considering a timeout as success as well, wouldn't it?
>>> Or put in an upper limit to iterations.
>> I believe the kernel will reset the TID to zero once the thread
>> exits.
>
> Yes, but the main thread will need to win the race to see the TID
> before the thread descriptor is invalidated.

The thread descriptor is invalidated on join only, not on thread exit
(unless the thread is detached).  So I don't think this is a problem.

Thanks,
Florian
Siddhesh Poyarekar Nov. 22, 2024, 1:55 p.m. UTC | #8
On 2024-11-22 07:18, Florian Weimer wrote:
>> Yes, but the main thread will need to win the race to see the TID
>> before the thread descriptor is invalidated.
> 
> The thread descriptor is invalidated on join only, not on thread exit
> (unless the thread is detached).  So I don't think this is a problem.

OK, I've sent an updated test with the join call dropped.

Thanks,
Sid
diff mbox series

Patch

diff --git a/nptl/Makefile b/nptl/Makefile
index ceb91afafc..9faad1964c 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -317,6 +317,7 @@  tests = \
   tst-pthread-attr-affinity-fail \
   tst-pthread-attr-sigmask \
   tst-pthread-defaultattr-free \
+  tst-pthread-getcpuclockid-invalid \
   tst-pthread-gdb-attach \
   tst-pthread-gdb-attach-static \
   tst-pthread-key1-static \
diff --git a/nptl/tst-pthread-getcpuclockid-invalid.c b/nptl/tst-pthread-getcpuclockid-invalid.c
new file mode 100644
index 0000000000..f1720687a7
--- /dev/null
+++ b/nptl/tst-pthread-getcpuclockid-invalid.c
@@ -0,0 +1,47 @@ 
+/* Smoke test to verify that pthread_getcpuclockid fails with invalid thread
+   IDs.
+   Copyright the GNU Toolchain Authors.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <support/check.h>
+#include <support/test-driver.h>
+#include <support/xthread.h>
+
+void *
+thr (void *in)
+{
+  return in;
+}
+
+int
+do_test (void)
+{
+  pthread_t t = xpthread_create (NULL, thr, NULL);
+  xpthread_join (t);
+
+  clockid_t c;
+  TEST_COMPARE (pthread_getcpuclockid (t, &c), ESRCH);
+
+  return 0;
+}
+
+#include <support/test-driver.c>