Skip to content

Instantly share code, notes, and snippets.

@rprichard
Created May 9, 2018 21:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rprichard/d0fad2161a3e2b7474b561d040ec5b40 to your computer and use it in GitHub Desktop.
Save rprichard/d0fad2161a3e2b7474b561d040ec5b40 to your computer and use it in GitHub Desktop.
Demonstrate emutls + thread_local dtor bug
// Copyright 2018 Google LLC.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <array>
#include <thread>
struct TlsObject {
TlsObject();
~TlsObject();
// Use a large object to make it easier to trample on later.
std::array<int, 1024 * 1024> field;
};
TlsObject::TlsObject() {
printf("ctor %p\n", this);
}
TlsObject::~TlsObject() {
// Try to trample on freed heap memory. Ideally we'd use ASAN, or valgrind,
// or a free function that overwrites freed memory.
char* trample = new char[sizeof(TlsObject)];
memset(trample, 0xcd, sizeof(TlsObject));
delete [] trample;
// See whether this object's field is still correct.
printf("dtor %p, 0x%x\n", this, field[field.size() / 2]);
}
#if PTHREAD_HACK
int &hack_alloc_tls() {
static __thread int hack_tlsvar;
hack_tlsvar++;
return hack_tlsvar;
}
TlsObject &hack_reg_dtor() {
static thread_local TlsObject hack_tlsvar;
for (int& i : hack_tlsvar.field) {
i = 3;
}
return hack_tlsvar;
}
#endif
int main() {
#if PTHREAD_HACK
// This hack allocates the "call-cxx-dtors" key into a slot prior to the
// emutls_destroy key, avoiding the segfault.
pthread_key_t hack;
pthread_key_create(&hack, nullptr);
hack_alloc_tls(); // Register emutls key.
pthread_key_delete(hack); // Free up a slot before emutls' slot.
hack_reg_dtor(); // Register cxx dtor key.
#endif
std::thread([] {
static thread_local TlsObject mytlsvar;
for (int& i : mytlsvar.field) {
i = 7;
}
return nullptr;
}).join();
return 0;
}
@drikosev
Copy link

drikosev commented Feb 2, 2019

This hack seems to be functional if GNU GCC doesn't detect a native "__cxa_thread_atexit", as ie described below:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78968

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment