Public bug reported:

pthread_setattr_default_np race condition with pthread_create.

When creating a new thread with pthread_create and a NULL
pthread_attr_t* argument, the pthread_attr_t* argument must come from
the last call to pthread_setattr_default_np preceding the call to
pthread_create.

It seems that there is a race condition in glibc implementation such
that pthread_attr_t* argument comes from a pthread_setattr_default_np
call that succeeds a pthread_create call.

Example:

[max@supernova:~/src/test] $ g++ --version
g++ (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

[max@supernova:~/src/test] $ cat test.cc
#include <cstdio>
#include <thread>
#include <system_error>
#include <pthread.h>

void set_default_thread_stack_size(size_t size) {
    pthread_attr_t attr;
    if(int err = ::pthread_attr_init(&attr))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
    if(int err = ::pthread_attr_setstacksize(&attr, size))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_setstacksize");
    if(int err = ::pthread_setattr_default_np(&attr))
        throw std::system_error(err, std::system_category(), 
"pthread_setattr_default_np");
    if(int err = ::pthread_attr_destroy(&attr))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
}

void another_thread() {
    pthread_attr_t attr;
    if(int err = ::pthread_attr_init(&attr))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
    size_t stack_size;
    if(int err = ::pthread_attr_getstacksize(&attr, &stack_size))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_getstacksize");
    if(int err = ::pthread_attr_destroy(&attr))
        throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
    std::printf("thread stack size is %zu\n", stack_size);
}

int main() {
    another_thread();
    std::thread t0(another_thread);
    set_default_thread_stack_size(1024 * 1024);
    std::thread t1(another_thread);
    t0.join();
    t1.join();
}

[max@supernova:~/src/test] $ g++ -std=gnu++14 -W{all,extra,error} -O3
-march=native -o test test.cc -pthread

[max@supernova:~/src/test] $ ldd test
 linux-vdso.so.1 (0x00007ffd74d74000)
 libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f597ed00000)
 libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f597eae8000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f597e8c9000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f597e4d8000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f597e13a000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f597f28d000)

[max@supernova:~/src/test] $ /lib/x86_64-linux-gnu/libc.so.6
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.3.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.

[max@supernova:~/src/test] $ ./test
thread stack size is 8720384
thread stack size is 1048576
thread stack size is 1048576

The above output is incorrect. The expected output is:

thread stack size is 8720384
thread stack size is 8720384
thread stack size is 1048576

If `t0.join();` line is moved to right after `std::thread
t0(another_thread);` line then the code produces the correct output.

It appears that setting the default pthread_attr_t is not atomic/ordered
with respect to pthread_create calls.

** Affects: glibc (Ubuntu)
     Importance: Undecided
         Status: New


** Tags: glibc libc nptl pthreads threads

** Description changed:

  pthread_setattr_default_np race condition with pthread_create.
  
  When creating a new thread with pthread_create and a NULL
  pthread_attr_t* argument, the pthread_attr_t* argument must come from
  the last call to pthread_setattr_default_np preceding the call to
  pthread_create.
  
  It seems that there is a race condition in glibc implementation such
  that pthread_attr_t* argument comes from a pthread_setattr_default_np
  call that succeeds a pthread_create call.
  
  Example:
- 
- [max@supernova:~/src/test] $ /lib/x86_64-linux-gnu/libc.so.6
- GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
- Copyright (C) 2018 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions.
- There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
- PARTICULAR PURPOSE.
- Compiled by GNU CC version 7.3.0.
- libc ABIs: UNIQUE IFUNC
- For bug reporting instructions, please see:
- <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
  
  [max@supernova:~/src/test] $ g++ --version
  g++ (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
  Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  
  [max@supernova:~/src/test] $ cat test.cc
  #include <cstdio>
  #include <thread>
  #include <system_error>
  #include <pthread.h>
  
  void set_default_thread_stack_size(size_t size) {
-     pthread_attr_t attr;
-     if(int err = ::pthread_attr_init(&attr))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
-     if(int err = ::pthread_attr_setstacksize(&attr, size))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_setstacksize");
-     if(int err = ::pthread_setattr_default_np(&attr))
-         throw std::system_error(err, std::system_category(), 
"pthread_setattr_default_np");
-     if(int err = ::pthread_attr_destroy(&attr))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
+     pthread_attr_t attr;
+     if(int err = ::pthread_attr_init(&attr))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
+     if(int err = ::pthread_attr_setstacksize(&attr, size))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_setstacksize");
+     if(int err = ::pthread_setattr_default_np(&attr))
+         throw std::system_error(err, std::system_category(), 
"pthread_setattr_default_np");
+     if(int err = ::pthread_attr_destroy(&attr))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
  }
  
  void another_thread() {
-     pthread_attr_t attr;
-     if(int err = ::pthread_attr_init(&attr))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
-     size_t stack_size;
-     if(int err = ::pthread_attr_getstacksize(&attr, &stack_size))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_getstacksize");
-     if(int err = ::pthread_attr_destroy(&attr))
-         throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
-     std::printf("thread stack size is %zu\n", stack_size);
+     pthread_attr_t attr;
+     if(int err = ::pthread_attr_init(&attr))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
+     size_t stack_size;
+     if(int err = ::pthread_attr_getstacksize(&attr, &stack_size))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_getstacksize");
+     if(int err = ::pthread_attr_destroy(&attr))
+         throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
+     std::printf("thread stack size is %zu\n", stack_size);
  }
  
  int main() {
-     another_thread();
-     std::thread t0(another_thread);
-     set_default_thread_stack_size(1024 * 1024);
-     std::thread t1(another_thread);
-     t0.join();
-     t1.join();
+     another_thread();
+     std::thread t0(another_thread);
+     set_default_thread_stack_size(1024 * 1024);
+     std::thread t1(another_thread);
+     t0.join();
+     t1.join();
  }
  
  [max@supernova:~/src/test] $ g++ -std=gnu++14 -W{all,extra,error} -O3
  -march=native -o test test.cc -pthread
  
- [max@supernova:~/src/test] $ ./test 
+ [max@supernova:~/src/test] $ ldd test
+       linux-vdso.so.1 (0x00007ffd74d74000)
+       libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
(0x00007f597ed00000)
+       libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 
(0x00007f597eae8000)
+       libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 
(0x00007f597e8c9000)
+       libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f597e4d8000)
+       libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f597e13a000)
+       /lib64/ld-linux-x86-64.so.2 (0x00007f597f28d000)
+ 
+ [max@supernova:~/src/test] $ /lib/x86_64-linux-gnu/libc.so.6
+ GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+ This is free software; see the source for copying conditions.
+ There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE.
+ Compiled by GNU CC version 7.3.0.
+ libc ABIs: UNIQUE IFUNC
+ For bug reporting instructions, please see:
+ <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
+ 
+ [max@supernova:~/src/test] $ ./test
  thread stack size is 8720384
  thread stack size is 1048576
  thread stack size is 1048576
  
  The above output is incorrect. The expected output is:
  
  thread stack size is 8720384
  thread stack size is 8720384
  thread stack size is 1048576
  
  If `t0.join();` line is moved to right after `std::thread
  t0(another_thread);` line then the code produces the correct output.
  
  It looks that setting the default pthread_attr_t is not atomic with
  respect to pthread_create calls.

** Description changed:

  pthread_setattr_default_np race condition with pthread_create.
  
  When creating a new thread with pthread_create and a NULL
  pthread_attr_t* argument, the pthread_attr_t* argument must come from
  the last call to pthread_setattr_default_np preceding the call to
  pthread_create.
  
  It seems that there is a race condition in glibc implementation such
  that pthread_attr_t* argument comes from a pthread_setattr_default_np
  call that succeeds a pthread_create call.
  
  Example:
  
  [max@supernova:~/src/test] $ g++ --version
  g++ (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
  Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  
  [max@supernova:~/src/test] $ cat test.cc
  #include <cstdio>
  #include <thread>
  #include <system_error>
  #include <pthread.h>
  
  void set_default_thread_stack_size(size_t size) {
      pthread_attr_t attr;
      if(int err = ::pthread_attr_init(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
      if(int err = ::pthread_attr_setstacksize(&attr, size))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_setstacksize");
      if(int err = ::pthread_setattr_default_np(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_setattr_default_np");
      if(int err = ::pthread_attr_destroy(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
  }
  
  void another_thread() {
      pthread_attr_t attr;
      if(int err = ::pthread_attr_init(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
      size_t stack_size;
      if(int err = ::pthread_attr_getstacksize(&attr, &stack_size))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_getstacksize");
      if(int err = ::pthread_attr_destroy(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
      std::printf("thread stack size is %zu\n", stack_size);
  }
  
  int main() {
      another_thread();
      std::thread t0(another_thread);
      set_default_thread_stack_size(1024 * 1024);
      std::thread t1(another_thread);
      t0.join();
      t1.join();
  }
  
  [max@supernova:~/src/test] $ g++ -std=gnu++14 -W{all,extra,error} -O3
  -march=native -o test test.cc -pthread
  
  [max@supernova:~/src/test] $ ldd test
-       linux-vdso.so.1 (0x00007ffd74d74000)
-       libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
(0x00007f597ed00000)
-       libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 
(0x00007f597eae8000)
-       libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 
(0x00007f597e8c9000)
-       libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f597e4d8000)
-       libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f597e13a000)
-       /lib64/ld-linux-x86-64.so.2 (0x00007f597f28d000)
+  linux-vdso.so.1 (0x00007ffd74d74000)
+  libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
(0x00007f597ed00000)
+  libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f597eae8000)
+  libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f597e8c9000)
+  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f597e4d8000)
+  libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f597e13a000)
+  /lib64/ld-linux-x86-64.so.2 (0x00007f597f28d000)
  
  [max@supernova:~/src/test] $ /lib/x86_64-linux-gnu/libc.so.6
  GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
  Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.
  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
  PARTICULAR PURPOSE.
  Compiled by GNU CC version 7.3.0.
  libc ABIs: UNIQUE IFUNC
  For bug reporting instructions, please see:
  <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
  
  [max@supernova:~/src/test] $ ./test
  thread stack size is 8720384
  thread stack size is 1048576
  thread stack size is 1048576
  
  The above output is incorrect. The expected output is:
  
  thread stack size is 8720384
  thread stack size is 8720384
  thread stack size is 1048576
  
  If `t0.join();` line is moved to right after `std::thread
  t0(another_thread);` line then the code produces the correct output.
  
- It looks that setting the default pthread_attr_t is not atomic with
+ It appears that setting the default pthread_attr_t is not atomic with
  respect to pthread_create calls.

** Description changed:

  pthread_setattr_default_np race condition with pthread_create.
  
  When creating a new thread with pthread_create and a NULL
  pthread_attr_t* argument, the pthread_attr_t* argument must come from
  the last call to pthread_setattr_default_np preceding the call to
  pthread_create.
  
  It seems that there is a race condition in glibc implementation such
  that pthread_attr_t* argument comes from a pthread_setattr_default_np
  call that succeeds a pthread_create call.
  
  Example:
  
  [max@supernova:~/src/test] $ g++ --version
  g++ (Ubuntu 8.3.0-6ubuntu1~18.04.1) 8.3.0
  Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.  There is NO
  warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  
  [max@supernova:~/src/test] $ cat test.cc
  #include <cstdio>
  #include <thread>
  #include <system_error>
  #include <pthread.h>
  
  void set_default_thread_stack_size(size_t size) {
      pthread_attr_t attr;
      if(int err = ::pthread_attr_init(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
      if(int err = ::pthread_attr_setstacksize(&attr, size))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_setstacksize");
      if(int err = ::pthread_setattr_default_np(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_setattr_default_np");
      if(int err = ::pthread_attr_destroy(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
  }
  
  void another_thread() {
      pthread_attr_t attr;
      if(int err = ::pthread_attr_init(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_init");
      size_t stack_size;
      if(int err = ::pthread_attr_getstacksize(&attr, &stack_size))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_getstacksize");
      if(int err = ::pthread_attr_destroy(&attr))
          throw std::system_error(err, std::system_category(), 
"pthread_attr_destroy");
      std::printf("thread stack size is %zu\n", stack_size);
  }
  
  int main() {
      another_thread();
      std::thread t0(another_thread);
      set_default_thread_stack_size(1024 * 1024);
      std::thread t1(another_thread);
      t0.join();
      t1.join();
  }
  
  [max@supernova:~/src/test] $ g++ -std=gnu++14 -W{all,extra,error} -O3
  -march=native -o test test.cc -pthread
  
  [max@supernova:~/src/test] $ ldd test
   linux-vdso.so.1 (0x00007ffd74d74000)
   libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 
(0x00007f597ed00000)
   libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f597eae8000)
   libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f597e8c9000)
   libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f597e4d8000)
   libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f597e13a000)
   /lib64/ld-linux-x86-64.so.2 (0x00007f597f28d000)
  
  [max@supernova:~/src/test] $ /lib/x86_64-linux-gnu/libc.so.6
  GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1) stable release version 2.27.
  Copyright (C) 2018 Free Software Foundation, Inc.
  This is free software; see the source for copying conditions.
  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
  PARTICULAR PURPOSE.
  Compiled by GNU CC version 7.3.0.
  libc ABIs: UNIQUE IFUNC
  For bug reporting instructions, please see:
  <https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
  
  [max@supernova:~/src/test] $ ./test
  thread stack size is 8720384
  thread stack size is 1048576
  thread stack size is 1048576
  
  The above output is incorrect. The expected output is:
  
  thread stack size is 8720384
  thread stack size is 8720384
  thread stack size is 1048576
  
  If `t0.join();` line is moved to right after `std::thread
  t0(another_thread);` line then the code produces the correct output.
  
- It appears that setting the default pthread_attr_t is not atomic with
- respect to pthread_create calls.
+ It appears that setting the default pthread_attr_t is not atomic/ordered
+ with respect to pthread_create calls.

-- 
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/1844022

Title:
  pthread_setattr_default_np race condition with pthread_create.

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/glibc/+bug/1844022/+subscriptions

-- 
ubuntu-bugs mailing list
ubuntu-bugs@lists.ubuntu.com
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to