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