[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