-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
gcc thread_local destructor cause use after free #2519
Comments
CC @lhmouse |
Yes I knew this bug a couple of years ago... but who the heck has been caring about this problem? The emutls thing is utterly broken. I happened to report a similar issue a few hours ago (if you So here comes the rule of thumb: Don't use |
Well.. pretty sad. I might post a patch to winpthread to workaround this specific issue. |
…this allows CodeLite better control of the order of execution by the priority provided by the ServiceProvider classes. Don't use thread_local :( this is due to MinGW compiler bug on Windows. See this report for example: msys2/MINGW-packages#2519 Make FileExtManager thread safe by adding a critical section
My understanding is slightly different:
So, the core issue looks like there are two separate mechanisms for calling something at thread exit: |
Moreover, it looks like on Linux (at the very least) #include <stdio.h>
#include <pthread.h>
#include <cxxabi.h>
int a, b, c, d, e;
extern int __dso_handle;
void mydestr_atexit(void *arg) {
printf("mydestr_atexit(%p)\n", arg);
}
void mydestr_pthread_key(void *arg) {
printf("mydestr_pthread_key(%p)\n", arg);
}
void* thread(void *arg) {
printf("thread %p\n", arg);
abi::__cxa_thread_atexit(mydestr_atexit, &b, &__dso_handle);
{
pthread_key_t key;
pthread_key_create(&key, mydestr_pthread_key);
pthread_setspecific(key, &c);
}
abi::__cxa_thread_atexit(mydestr_atexit, &d, &__dso_handle);
{
pthread_key_t key;
pthread_key_create(&key, mydestr_pthread_key);
pthread_setspecific(key, &e);
}
return nullptr;
}
int main() {
printf("a=%p\nb=%p\nc=%p\nd=%p\ne=%p\n", &a, &b, &c, &d, &e);
pthread_t th;
pthread_create(&th, nullptr, thread, &a);
pthread_join(th, nullptr);
} Moreover, it looks like destructors by |
I "discovered" this and tried to make some progress, but there's still a case that results in use-after-free: |
Will it help if I believe something similar was suggested in this message, possibly for another issue: https://sourceforge.net/p/mingw-w64/mailman/message/37140300/
Such behavior would be a little more consistent with what happens on Linux: "user-defined" Should help with this specific issue. Not sure how it plays with dynamic linking, configuring, etc. The patch would be quite small, though. Also, I assume it's impossible for crt to be detached from a thread before winpthreads, as the latter depends(?) on the former. |
This particular mingw-w64 "crt" code is present in each module, ie always statically linked. So each module has its own copy, and winpthreads calling it would call its copy only. Unless I'm missing something |
Would probably be better to discuss such details on mingw-w64-public list though. |
Can we turn the mingw's implementation of _Thread_local to be consistence with msvc? |
Please read this series of articles first: http://www.nynaeve.net/?p=183 |
Yeap, I've read it before, so I post it here |
Then you should have known that there is no such support in GCC. It is consequently impossible at least for now. |
I think it's time that we give the posix thread model up. See #13259. |
@lhmouse, sorry for the ping. Is this the same bug? Are there any plans to fix it? How does it affect the program?
|
@oltolm Someone expressed negative thoughts in mingw-w64 so I am afraid there is currently no plan on it. A proper fix for it requires hacking the CRT to invoke thread-local destructors before calling Footnotes |
gcc14 win32 thread model seems to support c++11 thread. gcc14 will be released soon, so maybe consider use win32 thread model instead. |
I don't think it will solve the issue: Destructors of static objects are called by the VCRT/UCRT And in addition, switching the thread model is an ABI break. |
How does Clang solve this problem? |
It has nothing to do with Clang; the magic lies in UCRT. We have https://github.com/mingw-w64/mingw-w64/blob/dbfdf802580c0d6b90b91995dab019f2a7787a8e/mingw-w64-crt/crt/tls_atexit.c#L108-L119 which causes UCRT to invoke destructors of thread-local objects before static ones. For MSVCRT we have an emulated (non-conforming) one in 'mingw-w64-crt/misc/register_tls_atexit.c'. |
So the advantage of MCF over MSVCRT is correctness and performance and the advantage of MCF over UCRT is performance? Is that correct? Why not create a minimal environment/subsystem for MCF with development tools only? It is probably source compatible, so building packages would be easy. Sadly I can not use it without pacman. I would contribute to the MCF subsystem. |
Yes.
See https://gcc-mcf.lhmouse.com/ which is largely repackaged MSYS2 environments. |
I know about it, it's great, but as I said I can't use it without pacman. pacman is too convenient. |
How about this? https://github.com/lhmouse/MINGW-packages One reason that I do not upload pacman packages is that they are all 'drop-in' replacements for MSYS2 ones, and I have to configure my local pacman.conf to ignore them; otherwise they might be upgraded by pacman to MSYS2 ones. I can upload a snapshot of my local packages. Once you have these packages you can bootstrap them yourself. |
@oltolm pacman packages can be found here: https://files.lhmouse.com/pkg-snapshots/ . Source scripts are in https://github.com/lhmouse/MINGW-packages . Installing these packages locally with |
Hello,
I posted this issue on the gcc bug tracker, but I am not entirely sure if this is supposed to be a GCC bug or a mingw bug.
I see it has also been reported here, however at the moment I can not create a sourceforge account to comment on that. I apologize if this is not the proper place.
The issue here, to my best understanding, is the following.
At the very first access of a thread-local object in a new program,
emutls_init
is called via__emutls_get_address
. This function allocates some memory for the storage usingmalloc
(seeemutls_alloc
), and registersemutls_destroy
to be executed at thread exit, using__gthread_key_create
.Immediately after, the
TLS init function
(generated by GCC) is called. This calls the C++ constructor on the storage provided at the previous point. It also uses__gthread_key_create
to register the C++ destructor for execution at thread exit.When a thread terminates,
_pthread_cleanup_dest
in winpthread is called. This function is responsible for calling all the destructors registered withpthread_key_create
. Unfortunately, the execution order of such destructors is unspecified. In this case, it happens thatemutls_destroy
is called before the C++ destructor, which then executes on deallocated memory.A quick workaround would be to reverse the loop in winpthread/src/thread.c:842. This will cause the destructors to be called in reverse order, and it will probably work correctly in most of the cases.
The only other solution would be to merge emutls+TLS init on GCC side, in such a way to have a single destructor callback to both invoke the C++ destructor and cleanup the memory.
The text was updated successfully, but these errors were encountered: