leak in NSSArena_Destroy()

RESOLVED FIXED in 3.12.1

Status

NSS
Libraries
RESOLVED FIXED
9 years ago
9 years ago

People

(Reporter: Boying Lu, Unassigned)

Tracking

unspecified
3.12.1

Firefox Tracking Flags

(Not tracked)

Details

Attachments

(1 attachment, 5 obsolete attachments)

4.25 KB, patch
Nelson Bolyard (seldom reads bugmail)
: review+
Details | Diff | Splinter Review
(Reporter)

Description

9 years ago
Using mdb+libumem.so provided in solaris 10, I found a leak in NSSArena_Destroy()

Re-produce steps:
Same with the steps in bug 431628

libumem.so reports a leak in NSSArena_Destroy() and following is the call stack
                 libumem.so.1`umem_cache_alloc_debug+0x14f
                 libumem.so.1`umem_cache_alloc+0x144
                 libumem.so.1`umem_alloc+0xc5
                 libumem.so.1`malloc+0x27
                 libumem.so.1`calloc+0x47
                 libnspr4.so`PR_Calloc+0x65
                 libnss3.so`error_get_my_stack+0xfc
                 libnss3.so`nss_ClearErrorStack+0x26
                 libnss3.so`NSSArena_Create+0x26
                 libnss3.so`NSSTrustDomain_Create+0x26
                 libnss3.so`STAN_LoadDefaultNSS3TrustDomain+0x79
                 libnss3.so`nss_Init+0x44d
                 libnss3.so`NSS_NoDB_Init+0x95
                 libpipnss.so`unsigned nsNSSComponent::InitializeNSS+0x1c9
                 libpipnss.so`unsigned nsNSSComponent::Init+0x331
                 libpipnss.so`unsigned nsNSSComponentConstructor+0xbf
                 libxpcom_core.so`unsigned nsGenericFactory::CreateInstance+0x46
                 
                 libxpcom_core.so`unsigned nsComponentManagerImpl::CreateInstanc
                 eByContractID+0xda   
                 ...

The root cause is in follow codes NSSArena_Destroy()
(
  NSSArena *arena
)
{
  nss_ClearErrorStack();
  ...
}

nss_ClearErrorStack() doesn't free the thread private data which is set in error_get_my_stack(). Though in error_once_function(), this data is 
set to be freed when thread terminates, it doesn't work for main thread

So the data (stack) which is created in main thread is leaked
(Reporter)

Comment 1

9 years ago
Created attachment 318969 [details] [diff] [review]
patch
Attachment #318969 - Flags: review?(wtc)

Comment 2

9 years ago
Boying, could you look at the full call stack and see if
it is the main thread that calls NSS_NoDB_Init?

If it is the main thread, then I mayhave to resolve this bug
WONTFIX.  The per-thread error stack is supposed to
be freed by the NSPR  thread private data destructor
when the thread terminates.  However, for the main
thread this doesn't happen unless the main thread
calls PR_Cleanup.

Because NSPR may be used in the destructors of
static C++ objects, many C++ applications such as
Firefox cannot call PR_Cleanup.

I tried to fix this problem by adding a shared library
"fini" routine to libnspr4.so (bug 362768), but I had
to remove the calls to NSPR  thread private data
destructors because they caused crashes
(bug 383977).  Strictly speaking those crashes are
application bugs, but NSPR has a backward
compatibility requirement, so in this case we have
to be "bug compatible".

An alternative approach to free the error stack of
the calling thread in NSS_Shutdown.  Would you
like to try that?
(Reporter)

Comment 3

9 years ago
(In reply to comment #2)
> Boying, could you look at the full call stack and see if
> it is the main thread that calls NSS_NoDB_Init?

Yes, the NSS_NoDB_Init() is called in main thread
> 
> If it is the main thread, then I mayhave to resolve this bug
> WONTFIX.  The per-thread error stack is supposed to
> be freed by the NSPR  thread private data destructor
> when the thread terminates.  However, for the main
> thread this doesn't happen unless the main thread
> calls PR_Cleanup.
> 
> Because NSPR may be used in the destructors of
> static C++ objects, many C++ applications such as
> Firefox cannot call PR_Cleanup.
> 
> I tried to fix this problem by adding a shared library
> "fini" routine to libnspr4.so (bug 362768), but I had
> to remove the calls to NSPR  thread private data
> destructors because they caused crashes
> (bug 383977).  Strictly speaking those crashes are
> application bugs, but NSPR has a backward
> compatibility requirement, so in this case we have
> to be "bug compatible".
> 
> An alternative approach to free the error stack of
> the calling thread in NSS_Shutdown.  Would you
> like to try that?
> 
ok, I'll give it a try tomorrow.

(Reporter)

Comment 4

9 years ago
Created attachment 319166 [details] [diff] [review]
new patch

Add nss_DestroyErrorStack() in NSS_Shutdown() and the leak is gone
(Reporter)

Updated

9 years ago
Attachment #319166 - Flags: review?(wtc)

Comment 5

9 years ago
Comment on attachment 319166 [details] [diff] [review]
new patch

r=wtc.

Sorry about the delay.  I was looking into what other
files need this fix because security/nss/lib/base is
used by two .so's -- libnss3.so and libnssckbi.so.
I suspect libnssckbi.so may also need to call
nss_DestroyErrorStack() in its shutdown/finalize
function.
Attachment #319166 - Flags: review?(wtc) → review+

Updated

9 years ago
Attachment #318969 - Attachment is obsolete: true
Attachment #318969 - Flags: review?(wtc)
Wan-Teh, will you commit the patch for Boying?

Comment 7

9 years ago
Created attachment 320313 [details] [diff] [review]
patch v3 (as checked in)

I checked in the patch on the NSS trunk (NSS 3.12.1).

In nss_DestroyErrorStack, I added a check "0 != error_stack_index"
to determine if error_stack_index is valid.  Strictly speaking this
check is incorrect because 0 is the first valid index for NSPR thread
private data.  Boying, please see if this breaks your memory leak fix.

Checking in base/base.h;
/cvsroot/mozilla/security/nss/lib/base/base.h,v  <--  base.h
new revision: 1.20; previous revision: 1.19
done
Checking in base/error.c;
/cvsroot/mozilla/security/nss/lib/base/error.c,v  <--  error.c
new revision: 1.8; previous revision: 1.7
done
Checking in nss/nssinit.c;
/cvsroot/mozilla/security/nss/lib/nss/nssinit.c,v  <--  nssinit.c
new revision: 1.95; previous revision: 1.94
done
Attachment #319166 - Attachment is obsolete: true

Comment 8

9 years ago
Created attachment 320324 [details] [diff] [review]
patch v4

Initialize the thread-private data index to -1 (an invalid
index), so that we can reliably detect whether the index is
valid.
Attachment #320313 - Attachment is obsolete: true
Attachment #320324 - Flags: review?(nelson)

Comment 9

9 years ago
Created attachment 320327 [details] [diff] [review]
patch v4.1

Improved the comment a bit.
Attachment #320324 - Attachment is obsolete: true
Attachment #320327 - Flags: review?(nelson)
Attachment #320324 - Flags: review?(nelson)
Comment on attachment 320327 [details] [diff] [review]
patch v4.1


>+ * Thread-private data indexes are in the range [0, 127], so
>+ * -1 is an invalid index.
>  */
> 
>-static PRUintn error_stack_index;
>+static PRIntn error_stack_index = -1;

Wan-Teh, what would you think about leaving error_stack_index 
as a PRUintn and using MAX_UINT instead of -1 ?

Comment 11

9 years ago
I can do that.  I use -1 because it is a simple constant
commonly used to indicate an error/invalid value.  If I
use MAX_UINT, I'll need to define a macro:

#define INVALID_TPD_INDEX MAX_UINT

and use INVALID_TPD_INDEX.  Is it the (PRUintn *) typecast
in my patch that bothers you?  I can avoid that typecast
like this:

    PRUintn index;
    PRStatus rv = PR_NewThreadPrivateIndex(&index, PR_Free);
    if (rv == PR_SUCCESS)
        error_stack_index = index;
    return rv;

Please let me which you prefer.  Thanks!
That cast is one issue, and your proposed alternative solution in 
comment 11 is also an acceptable solution to that issue.  

The other issue is this new statement in nss_DestroyErrorStack:

>+    PR_SetThreadPrivate(error_stack_index, NULL);

I think that will generate a warning, since the function expects unsigned
and the argument is signed. 

I suggest that nss_DestroyErrorStack should also set error_stack_index
back to the "invalid" value.

If you keep -1 as the invalid value, I'd suggest changing the "!= -1" tests 
to ">= 0" tests instead.  The thought of passing ANY negative numbers to that
function disturbs me.  I think I prefer the INVALID_TPD_INDEX approach.
Comment on attachment 320327 [details] [diff] [review]
patch v4.1

Please see preceding bug comments for review comments.
Attachment #320327 - Flags: review?(nelson) → review-

Comment 14

9 years ago
Created attachment 321349 [details] [diff] [review]
patch v5

I implemented the INVALID_TPD_INDEX approach.  Regrettably
NSPR doesn't define PR_UINTN_MAX, so I have to use UINT_MAX
from the Standard C library header <limits.h>

I don't set error_stack_index back to INVALID_TPD_INDEX in
nss_DestroyErrorStack for two reasons:

1. This function only destroys the error stack of the calling
thread.  It is not a shutdown function for the error stack
component.

2. Even if this function were the shutdown function for the
error stack component, it would not make sense to reset
error_stack_index because there is no way to unregister an
NSPR TPD index.  If we ever reinitialize NSS, we can just
use the error_stack_index that was allocated in the previous
NSS session.
Attachment #320327 - Attachment is obsolete: true
Attachment #321349 - Flags: review?(nelson)
Comment on attachment 321349 [details] [diff] [review]
patch v5

r=nelson.  Thanks.
Attachment #321349 - Flags: review?(nelson) → review+

Comment 16

9 years ago
Patch v5 checked in (as a supplment to patch v3) on
the NSS trunk (NSS 3.12.1.)

Checking in base/error.c;
/cvsroot/mozilla/security/nss/lib/base/error.c,v  <--  error.c
new revision: 1.9; previous revision: 1.8
done
Checking in nss/nssinit.c;
/cvsroot/mozilla/security/nss/lib/nss/nssinit.c,v  <--  nssinit.c
new revision: 1.96; previous revision: 1.95
done
Status: NEW → RESOLVED
Last Resolved: 9 years ago
OS: SunOS → All
Hardware: PC → All
Resolution: --- → FIXED
Target Milestone: --- → 3.12.1
You need to log in before you can comment on or make changes to this bug.