From dcdf4824bbeebbae8f96ee574bd9d94fb2441691 Mon Sep 17 00:00:00 2001 From: "rjesup%wgate.com" Date: Fri, 8 Aug 2003 13:06:40 +0000 Subject: [PATCH] Bug 193011: frame recursion limits re-added/docshell limit removed (backout of bug 98158 and follow-on patches). Also re-fixes bug 98158. r=darin, sr=dbaron --- content/base/src/nsFrameLoader.cpp | 170 ++++++++++++++++++++++------- 1 file changed, 131 insertions(+), 39 deletions(-) diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 99ac9cceda0..12ac1beae05 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -60,14 +60,28 @@ #include "nsIScriptSecurityManager.h" -#include "nsIURI.h" +#include "nsIURL.h" #include "nsNetUtil.h" #include "nsHTMLAtoms.h" #include "nsINameSpaceManager.h" -// Bug 98158: Limit to the number of total docShells in one page. -#define MAX_NUMBER_DOCSHELLS 100 +// Bug 136580: Limit to the number of nested content frames that can have the +// same URL. This is to stop content that is recursively loading +// itself. Note that "#foo" on the end of URL doesn't affect +// whether it's considered identical, but "?foo" or ";foo" are +// considered and compared. +#define MAX_SAME_URL_CONTENT_FRAMES 3 + +// Bug 8065: Limit content frame depth to some reasonable level. This +// does not count chrome frames when determining depth, nor does it +// prevent chrome recursion. Number is fairly arbitrary, but meant to +// keep number of shells to a reasonable number on accidental recursion with a +// small (but not 1) branching factor. With large branching factors the number +// of shells can rapidly become huge and run us out of memory. To solve that, +// we'd need to re-institute a fixed version of bug 98158. +#define MAX_DEPTH_CONTENT_FRAMES 10 + class nsFrameLoader : public nsIFrameLoader { @@ -84,8 +98,6 @@ public: NS_IMETHOD GetDocShell(nsIDocShell **aDocShell); NS_IMETHOD Destroy(); - PRInt32 GetDocShellChildCount(nsIDocShellTreeNode *aParentNode); - protected: nsresult GetPresContext(nsIPresContext **aPresContext); nsresult EnsureDocShell(); @@ -215,6 +227,98 @@ nsFrameLoader::LoadFrame() return rv; // We're not } + + // Bug 136580: Check for recursive frame loading + // pre-grab these for speed + nsCAutoString prepath; + nsCAutoString filepath; + nsCAutoString query; + nsCAutoString param; + rv = uri->GetPrePath(prepath); + NS_ENSURE_SUCCESS(rv,rv); + nsCOMPtr aURL(do_QueryInterface(uri, &rv)); // QI can fail + if (NS_SUCCEEDED(rv)) { + rv = aURL->GetFilePath(filepath); + NS_ENSURE_SUCCESS(rv,rv); + rv = aURL->GetQuery(query); + NS_ENSURE_SUCCESS(rv,rv); + rv = aURL->GetParam(param); + NS_ENSURE_SUCCESS(rv,rv); + } else { + // Not a URL, so punt and just take the whole path. Note that if you + // have a self-referential-via-refs non-URL (can't happen via nsSimpleURI, + // but could in theory with an external protocol handler) frameset it will + // recurse down to the depth limit before stopping, but it will stop. + rv = uri->GetPath(filepath); + NS_ENSURE_SUCCESS(rv,rv); + } + + PRInt32 matchCount = 0; + nsCOMPtr treeItem = do_QueryInterface(mDocShell); + nsCOMPtr parentAsItem; + treeItem->GetParent(getter_AddRefs(parentAsItem)); + while (parentAsItem) { + // Only interested in checking for recursion in content + PRInt32 parentType; + parentAsItem->GetItemType(&parentType); + if (parentType != nsIDocShellTreeItem::typeContent) { + break; // Not content + } + // Check the parent URI with the URI we're loading + nsCOMPtr parentAsNav(do_QueryInterface(parentAsItem)); + if (parentAsNav) { + // Does the URI match the one we're about to load? + nsCOMPtr parentURI; + parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); + if (parentURI) { + // Bug 98158/193011: We need to ignore data after the # + // Note that this code is back-stopped by the maximum-depth checks. + + // Check prepath (foo://blah@bar:port/) and filepath + // (/dir/dir/file.ext), but not # extensions. + // There's no easy way to get the URI without extension + // directly, so check prepath, filepath and query/param (if any) + + // Note that while in theory a CGI could return different data for + // the same query string, the spec states that it shouldn't, so + // we'll compare queries (and params). + nsCAutoString parentPrePath; + nsCAutoString parentFilePath; + nsCAutoString parentQuery; + nsCAutoString parentParam; + rv = parentURI->GetPrePath(parentPrePath); + NS_ENSURE_SUCCESS(rv,rv); + nsCOMPtr parentURL(do_QueryInterface(parentURI, &rv)); // QI can fail + if (NS_SUCCEEDED(rv)) { + rv = parentURL->GetFilePath(parentFilePath); + NS_ENSURE_SUCCESS(rv,rv); + rv = parentURL->GetQuery(parentQuery); + NS_ENSURE_SUCCESS(rv,rv); + rv = parentURL->GetParam(parentParam); + NS_ENSURE_SUCCESS(rv,rv); + } else { + rv = uri->GetPath(filepath); + NS_ENSURE_SUCCESS(rv,rv); + } + + // filepath will often not match; test it first + if (filepath.Equals(parentFilePath) && + query.Equals(parentQuery) && + prepath.Equals(parentPrePath) && + param.Equals(parentParam)) + { + matchCount++; + if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) { + NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up"); + return NS_ERROR_UNEXPECTED; + } + } + } + } + nsIDocShellTreeItem* temp = parentAsItem; + temp->GetParent(getter_AddRefs(parentAsItem)); + } + // Kick off the load... rv = mDocShell->LoadURI(uri, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_FALSE); @@ -266,28 +370,6 @@ nsFrameLoader::Destroy() return NS_OK; } -/** - * Count the total number of docshell children in each page. - */ -PRInt32 -nsFrameLoader::GetDocShellChildCount(nsIDocShellTreeNode* aParentNode) -{ - PRInt32 retval = 1; - - PRInt32 childCount; - PRInt32 i; - aParentNode->GetChildCount(&childCount); - for(i=0;i child; - aParentNode->GetChildAt(i,getter_AddRefs(child)); - nsCOMPtr childAsNode(do_QueryInterface(child)); - retval += GetDocShellChildCount(childAsNode); - } - - return retval; -} - nsresult nsFrameLoader::GetPresContext(nsIPresContext **aPresContext) { @@ -325,26 +407,36 @@ nsFrameLoader::EnsureDocShell() GetPresContext(getter_AddRefs(presContext)); NS_ENSURE_TRUE(presContext, NS_ERROR_UNEXPECTED); + // Bug 8065: Don't exceed some maximum depth in content frames + // (MAX_DEPTH_CONTENT_FRAMES) + PRInt32 depth = 0; nsCOMPtr parentAsSupports; presContext->GetContainer(getter_AddRefs(parentAsSupports)); - // bug98158:count the children under the root docshell. - // if the total number of children under the root docshell - // beyond the limit,return a error. if (parentAsSupports) { - nsCOMPtr parentAsItem = do_QueryInterface(parentAsSupports); + nsCOMPtr parentAsItem = + do_QueryInterface(parentAsSupports); - nsCOMPtr root; - parentAsItem->GetSameTypeRootTreeItem(getter_AddRefs(root)); + while (parentAsItem) { + ++depth; - nsCOMPtr rootNode(do_QueryInterface(root)); + if (depth >= MAX_DEPTH_CONTENT_FRAMES) { + NS_WARNING("Too many nested content frames so giving up"); - PRInt32 childrenCount; - childrenCount = GetDocShellChildCount(rootNode); + return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?) + } - if(childrenCount >= MAX_NUMBER_DOCSHELLS) { - NS_WARNING("Too many docshell (recursion?) so giving up"); - return NS_ERROR_FAILURE; + // Only count depth on content, not chrome. + // If we wanted to limit total depth, skip the following check: + PRInt32 parentType; + parentAsItem->GetItemType(&parentType); + + if (nsIDocShellTreeItem::typeContent == parentType) { + nsIDocShellTreeItem* temp = parentAsItem; + temp->GetParent(getter_AddRefs(parentAsItem)); + } else { + break; // we have exited content, stop counting, depth is OK! + } } }