[Ace-users] [ace-bugs] ACE_Service_Config TSS usage: Improper TSS
usage/memory management
Bennett, Patrick
Patrick.Bennett at inin.com
Fri Jun 22 14:55:02 CDT 2007
ACE VERSION: 5.5.7
HOST MACHINE and OPERATING SYSTEM: Windows XP/Vista
COMPILER NAME AND VERSION (AND PATCHLEVEL): VC++ 7.1 SP1
THE $ACE_ROOT/ace/config.h FILE:
#define ACE_DEFINES_DEFAULT_WIN32_SECURITY_ATTRIBUTES
#define ACE_DEFAULT_THREAD_KEYS 128
#include "ace/config-win32.h"
AREA/CLASS/EXAMPLE AFFECTED: ACE_Service_Config and its use of TSS for
pointers to itself (which ACE_TSS can then destroy erroneously)
DOES THE PROBLEM AFFECT:
COMPILATION? No
LINKING? No
EXECUTION? Yes
SYNOPSIS: When ACE is used within a DLL dynamically loaded into another
process that doesn't use ACE, the use of TLS in ACE_Service_Config
causes
the heap to be corrupted the heap during thread-detach cleanup.
DESCRIPTION: ACE_Service_Config uses ACE_TSS to store pointers to itself
yet ACE_TSS is designed to own all objects assigned to it, deleting them
as threads are destroyed (via the ACE_OS::cleanup_tss (0); call in
DllMain's DLL_THREAD_DETACH in OS_NS_stdio.cpp).
Since ACE_Service_Config was actually created as an aggregate member
within ACE_Singleton ('new' was called on
ACE_Singleton<ACE_Service_Config> not ACE_Service_Config!), having the
cleanup method in ACE_TSS call delete on the pointer assigned to it
causes
it to try to delete the ACE_Service_Config instance pointer even though
it
wasn't what was allocated from the heap! With VC++ at least, this means
the heap is corrupted as a pointer incorrectly offset by 4 bytes is
passed
to delete (it's passing the pointer to the aggregated member instead of
the containing class that was actually allocated off the heap).
ACE_Service_Config has a hack in its destructor to set the
tss_.ts_object(0) value to 0 so that ACE_TSS won't clean it up, but this
assumes that the ACE_Service_Config created via a static variable at
startup was actually created on the main thread!
Worse yet, it assumes the destructor (object manager cleanup) is called
on the same thread that called the constructor. But then again, that
all
pales compared to deleting an aggregated instance anyway...
For normal console applications, the destructor will be called
before the thread it was created within actually goes away so the
destructor hack resetting the TSS object will work. However,
if ACE is used by a DLL loaded dynamically by another process, the
ACE_Service_Config global isn't necessarily destroyed before the thread
it
was created on goes away, so the ts_object isn't reset and
ACE_TSS::cleanup ends up deleting the 'this' pointer that
ACE_Service_Config's constructor set to itself.
Unfortunately, this took way too long to track down. :(
REPEAT BY:
This was 100% repeatable with a test program, but its not something that
can easily be posted. It was basically an Internet Explorer add-in
loaded
dynamically by IE, and the crash would come as IE terminated the thread
ACE was loaded on.
SAMPLE FIX/WORKAROUND:
The TSS usage within ACE_Service_Config is completely broken.
Not only does it assume that the thread which created the
ACE_Service_Config is the same one that will later access it (if not,
the tss info will be 0), it places pointers to its *aggregated* self
within a class that assumes ownership and will delete them!
Perhaps a wrapper class should be allocated/stored in ACE_TSS instead
that
simply contains a pointer to an ACE_Service_Gestalt and whose destructor
does nothing. The current() method in ACE_Service_Config should
also be changed to return the 'global' singleton instance if the
tss_ object is 0.
Thanks,
Patrick Bennett
More information about the Ace-users
mailing list