Closed Bug 454298 Opened 16 years ago Closed 16 years ago

infinite recursion in GetIsKilled

Categories

(MailNews Core :: Database, defect)

defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: Bienvenu, Assigned: Bienvenu)

Details

Attachments

(2 files, 1 obsolete file)

If the threading structure of a message is messed up, GetIsKilled can recurse until the stack overflows. In particular, if a is the parent of b, and b is the parent of a, then we will recurse forever. In general, GetIsKilled doesn't deal with bogus parenting. I will fix the cause of the broken parenting, but we need to be tolerant of it, for existing db's. The test case in bug 367896 exhibits this problem. I'll attach a patch that basically just counts how deeply we've recursed and errors out if we've gone deeper than the number of children in the thread.
Attached patch proposed fix for crash (obsolete) — Splinter Review
What do you think of this approach, jcranmer?
Attachment #337543 - Flags: review?
Attachment #337543 - Flags: review? → review?(Pidgeot18)
Comment on attachment 337543 [details] [diff] [review]
proposed fix for crash

>diff --git a/mailnews/db/msgdb/src/nsMsgHdr.cpp b/mailnews/db/msgdb/src/nsMsgHdr.cpp
>+          PRUint32 numChildren;
>+          thread->GetNumChildren(&numChildren);
>+          if (numChildren < depth)

I think a comment explaining why the check is being made would be helpful here; it wasn't immediately obvious to me at first why numChildren and depth should be related (I read the patch before the comments on the bug).

>+NS_IMETHODIMP nsMsgHdr::GetIsKilled(PRBool *isKilled)
>+{
>+  NS_ENSURE_ARG_POINTER(isKilled);
>+  *isKilled = IsAncestorKilled(0);
>   return NS_OK;
> }

It shouldn't matter, but I think 1 is the proper parameter here, since the header being queried is in fact the parent of the current header. That should cause failure if the current thread has 1 child and the header thinks it has a parent. In any case, all that happens is but a single extra call, so it's no big deal.
Attachment #337543 - Flags: review?(Pidgeot18) → review+
carrying forward jcranmer's review (thx, jcranmer), requesting sr.
Attachment #337543 - Attachment is obsolete: true
Attachment #337566 - Flags: superreview?(neil)
Attachment #337566 - Flags: review+
OS: Windows XP → All
Hardware: PC → All
Attachment #337566 - Flags: superreview?(neil) → superreview+
Comment on attachment 337566 [details] [diff] [review]
fix addressing comments

>         (void) m_mdb->GetThreadContainingMsgHdr(this, getter_AddRefs(thread));
>         if (thread)
>         {
>           nsCOMPtr<nsIMsgDBHdr> claimant;
>           (void) thread->GetChild(threadParent, getter_AddRefs(claimant));
>           if (!claimant)
>           {
>             NS_ASSERTION(PR_FALSE, "Borked message header, please fix!");
>-            return NS_OK;
>+            return PR_FALSE;
>+          }
>+          PRUint32 numChildren;
>+          thread->GetNumChildren(&numChildren);
>+
>+          // we can't have as many ancestors as there are messages 
>+          // in the thread, so if that seems to be the case, error out.
>+          if (depth >= numChildren)
>+          {
>+            NS_ASSERTION(PR_FALSE, "Borked thread, please fix!");
NS_ERROR

Rather than getting the number of children every time, why not get it once in GetIsKilled and count down to zero? (If you do this I would also suggest checking the depth earlier.)
that's a good idea - I'll redo this.
I didn't move the check for ancestors because it's only an error when we're about to recurse - otherwise, I'd have had to pass in numChildren instead of numChildren - 1 as the number of ancestors to check.
Attachment #337881 - Flags: superreview?(neil)
Attachment #337881 - Flags: superreview?(neil) → superreview+
Comment on attachment 337881 [details] [diff] [review]
get thread count only once

>+  if (!isKilled)
>   {
>     nsMsgKey threadParent;
>     GetThreadParent(&threadParent);
My point is that we're already thinking of recursing here but it's up to you exactly where you'd like to put the check.

>+          NS_ASSERTION(PR_FALSE, "cycle in parent relationship, please fix!");
NS_ERROR
right, we're thinking of recursing, but the case where we don't recurse is ok, so in order to prevent false assertions, I need to put the assertion right before we'll actually recurse.

I've switched it to NS_ERROR

changeset:   305:c2932fcc8375
Status: NEW → RESOLVED
Closed: 16 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: