Index: lib/asan/asan_globals.cc =================================================================== --- lib/asan/asan_globals.cc +++ lib/asan/asan_globals.cc @@ -243,9 +243,25 @@ CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); if (CanPoisonMemory()) PoisonShadowForGlobal(g, 0); - // We unpoison the shadow memory for the global but we do not remove it from - // the list because that would require O(n^2) time with the current list - // implementation. It might not be worth doing anyway. + // We unpoison the shadow memory for the global and remove it from the list of + // globals to prevent GetGlobalsForAddress from accessing potentially unmapped + // memory. This implies O(nk) time for n registered list_of_all_globals items + // and k (<= n) unloaded symbols during dlclose. + ListOfGlobals *last_l = nullptr; + for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { + if (l->g == g) { + // Found the global, now unlink it from the list. + if (last_l) { + last_l->next = l->next; + } else { + list_of_all_globals = l->next; + } + // Allocator for 'l' does not have a delete operator, just leak it. + break; + } else { + last_l = l; + } + } // Release ODR indicator. if (UseODRIndicator(g)) { Index: test/asan/TestCases/Posix/dlclose-globals.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Posix/dlclose-globals.cc @@ -0,0 +1,38 @@ +// RUN: %clangxx_asan -O0 -DSHARED_LIB %s -fPIC -shared -o %t-so.so +// RUN: %clangxx_asan -O0 %s %libdl -o %t +// RUN: %env_asan_opts=handle_segv=0 %run %t %t-so.so 2>&1 | FileCheck %s + +#include +#include +#include + +#ifndef SHARED_LIB +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s test.so\n", argv[0]); + return 1; + } + const char *libname = argv[1]; + void *handle = dlopen(argv[1], RTLD_NOW | RTLD_LOCAL); + if (!handle) { + printf("dlopen failed: %s\n", dlerror()); + return 2; + } + char *ver = (char *)dlsym(handle, "VERSION"); + if (!ver) { + printf("dlsym(VERSION) failed: %s\n", dlerror()); + return 3; + } + + // CHECK: VERSION: dummy 1.0 + printf("VERSION: %s\n", ver); + fflush(stdout); + dlclose(handle); + + // CHECK: AddressSanitizer can not describe address in more detail (wild memory access suspected). + __asan_describe_address(0); + return 0; +} +#else // SHARED_LIB +extern "C" const char VERSION[] = "dummy 1.0"; +#endif // SHARED_LIB Index: test/asan/TestCases/Windows/freelibrary-globals.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Windows/freelibrary-globals.cc @@ -0,0 +1,35 @@ +// RUN: %clang_cl_asan -LD -O0 -DSHARED_LIB %s -Fe%t.dll +// RUN: %clang_cl_asan -O0 %s -Fe%te.exe +// RUN: %env_asan_opts=handle_segv=0 %run %te.exe %t.dll 2>&1 | FileCheck %s + +#include +#include +#include + +#ifndef SHARED_LIB +int main(int argc, char **argv) { + if (argc != 2) { + printf("Usage: %s test.dll\n", argv[0]); + return 1; + } + const char *libname = argv[1]; + HMODULE handle = LoadLibrary(libname); + if (!handle) + return 2; + + char *ver = (char *)GetProcAddress(handle, "VERSION"); + if (!ver) + return 3; + + // CHECK: VERSION: dummy 1.0 + printf("VERSION: %s\n", ver); + fflush(stdout); + FreeLibrary(handle); + + // CHECK: AddressSanitizer can not describe address in more detail (wild memory access suspected). + __asan_describe_address(0); + return 0; +} +#else // SHARED_LIB +extern "C" __declspec(dllexport) const char VERSION[] = "dummy 1.0"; +#endif // SHARED_LIB