Closed Bug 297277 Opened 20 years ago Closed 20 years ago

potential OOM crash/mlk [@ nsPrintOptions::_CreatePrintSettings]

Categories

(Core :: Printing: Output, defect)

x86
Windows XP
defect
Not set
critical

Tracking

()

RESOLVED FIXED

People

(Reporter: timeless, Assigned: bastiaan)

References

()

Details

(Keywords: crash, memory-leak)

Crash Data

Attachments

(2 files, 17 obsolete files)

77.88 KB, patch
dmosedale
: superreview+
benjamin
: approval1.8b4+
Details | Diff | Splinter Review
72.15 KB, patch
Details | Diff | Splinter Review
this is an audit of sorts, it's based on a dreftool audit http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/gfx/src/nsPrintOptionsImpl.cpp&rev=1.67&mark=265#260 Deref-error: "sDefaultFont" http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/gfx/src/nsPrintOptionsImpl.cpp&rev=1.67&mark=887#882 Deref-error: "printSettings" and this is really a followup to bug 235643 ... bonus points for replacing SetLength(0) with Truncate() partial credit for fixing indentation to not have 3 space (code seems to be a mix of 1space, 2space, 3space, 4space, plus combinations)
> sDefaultFont = new nsFont(aFont); > aFont = *sDefaultFont; I don't see how these are dereferencing errors. Could you elaborate?
nothing makes sure the new doesn't fail. new is an alloc and can fail, presume we don't throw (because if we throw, the world ends, we try to ask vc6 and gcc not to throw [we incorrectly encourage vc7 to throw, but that's very much our fault and a regression]).
Attached patch cleanup patch (diff -u) (obsolete) — Splinter Review
This patch fixes the dereferencing errors, and is a general cleanup of nsPrintOptionsImpl.*. It does in particular: * use the new string classes; * check for pointers/null correctly; * fix whitespacing and indentation; * more appropriate code patterns, return from errors immediately; * use macros for repetitive code; * null out _retvals; * ensure that parameter names (at least vaguely) reflect their purpose; * replaces SetLength(0) with Truncate().
Assignee: printing → baafie
Status: NEW → ASSIGNED
Attached patch cleanup patch (diff -w) (obsolete) — Splinter Review
Comment on attachment 186174 [details] [diff] [review] cleanup patch (diff -u) I did a call for volunteers for reviewing this patch (since printing doesn't seem covered by any module owners/peers in particular), but there were none. So, I'm setting asking the same reviewers as I did for the last printing patch. I Hope that's okay.
Attachment #186174 - Flags: superreview?(bzbarsky)
Attachment #186174 - Flags: review?(timeless)
Comment on attachment 186174 [details] [diff] [review] cleanup patch (diff -u) i gave feedback to baafie on irc.
Attachment #186174 - Flags: superreview?(bzbarsky)
Attachment #186174 - Flags: review?(timeless)
Attachment #186174 - Flags: review-
Comment on attachment 186174 [details] [diff] [review] cleanup patch (diff -u) + NS_ERROR("Operator new() returned nsnull; out of memory?"); (maybe timeless already mentioned this, but) you can't use NS_ERROR for OOM. it's an error you need to handle, you can't assert allocs succeed.
Summary of timeless' comments: <timeless> if [you have something in the constructor that] you need to init something that can fail, it really should be moved to an nsresult Init(){} method <timeless> NS_ENSURE_ARG_POINTER(sDefaultFont); -> NS_ENSURE_STATE(sDefaultFont); <timeless> NS_ENSURE_ARG_POINTER(ioParamBlock); -> NS_ENSURE_TRUE(.., OOM) <timeless> [whitespace alignment] <timeless> + NS_ADDREF(*aPrinterEnumerator = NS_STATIC_CAST(nsISimpleEnumerator*, printerListEnum)); <timeless> is that cast needed? <timeless> NS_IMETHODIMP nsPrintOptions::CreatePrintSettings(nsIPrintSettings **_retval) <timeless> { <timeless> + NS_ENSURE_ARG_POINTER(_retval); <timeless> i think we're not ensuring pointers for outs <timeless> NS_ENSURE_TRUE -> NS_ENSURE_SUCCESS in some places <timeless> sprintf(str, "%6.2f", aVal); -> snprintf All of timeless's comments were addressed; most notably I've added an Init() method, and made its existence known amongst class factories that inherent the class, as well as the other consumers.
Attachment #186174 - Attachment is obsolete: true
Attachment #186364 - Flags: review?(timeless)
Attachment #186175 - Attachment is obsolete: true
Attachment #186364 - Flags: review?(timeless)
Let's do it right this time..
Attachment #186364 - Attachment is obsolete: true
Attachment #186376 - Flags: review?(timeless)
Attachment #186365 - Attachment is obsolete: true
This patch addresses further comments from timeless, most notably the following: * sDefaultFont is no longer declared static, so permanent storage is no longer required for it. However, the caller of GetDefaultFont() deletes the retrieved font after using it, without setting the pointer to nsnull. Then, our class would attempt to delete it a second time upon destruction, which would cause a crash. The patch therefore includes a small change to layout/generic/nsSimplePageSequence.cpp, which makes the caller nulls out the pointer to our default font. Arguably the caller should not be deleting it at all, but that's a subject for another bug. * My usage of NS_ENSURE_ARG was replaced by NS_ENSURE_ARG_POINTER, because it is more appropriate. * Init() was declared virtual. * ShowPrintSetupDialog() now has better error checking, an unused nsCOMPtr and an unneeded assertion were removed.
Attachment #186376 - Attachment is obsolete: true
Attachment #186425 - Flags: review?(timeless)
Attachment #186376 - Flags: review?(timeless)
Attachment #186377 - Attachment is obsolete: true
This patch adds trivial changes such as slightly better error handling and some more whitespace cleanups.
Attachment #186425 - Attachment is obsolete: true
Attachment #186866 - Flags: review?(timeless)
Attachment #186426 - Attachment is obsolete: true
Attachment #186425 - Flags: review?(timeless)
Comment on attachment 186867 [details] [diff] [review] cleanup patch, revision 2.1 (diff -w) >Index: gfx/src/nsPrintOptionsImpl.cpp >@@ -340,854 +335,931 @@ const char* nsPrintOptions::GetPrefName( > * This will read in the generic prefs (not specific to a printer) > * or it will it can read the prefs in using the printer name to qualify "or it will it can" ?! >+nsPrintOptions::AvailablePrinters(nsISimpleEnumerator **aPrinterEnumerator) >+ nsCOMPtr<nsPrinterListEnumerator> printerListEnum = >+ new nsPrinterListEnumerator(); nsCOMPtr was not designed for non interface creatures, i believe you want nsRefPtr. >+nsPrintOptions::WriteInchesFromTwipsPref(const char * aPrefId, nscoord aTwips) > { > nsAutoString inchesStr; > char * str = ToNewCString(inchesStr); > if (str) { > mPrefBranch->SetCharPref(aPrefId, str); doesn't this leak str? :) >Index: gfx/src/nsPrintOptionsImpl.h >+class NS_GFX nsPrintOptions : public nsIPrintOptions, public >+ nsIPrintSettingsService >+ * method Init >+ * Initializes member variables. Every consumer that does manual >+ * addref'ing needs to call this method! -indicate when and how often- i think you mean manual creation instead of do_CreateInstance. >+ * @return nsresult error code normally we'd list out params as @returns >+ virtual nsresult Init(); >+ * method WritePrefString >+ * Writes PRUnichar* to Prefs and deletes the contents of aString >+ nsresult WritePrefString(const char * aPrefId, const nsAString& aString);
Address comments.
Attachment #186866 - Attachment is obsolete: true
Attachment #186913 - Flags: review?(timeless)
Attachment #186866 - Flags: review?(timeless)
Attachment #186867 - Attachment is obsolete: true
Attachment #186913 - Flags: superreview?(rbs)
Attachment #186913 - Flags: review?(timeless)
Attachment #186913 - Flags: review+
Comment on attachment 186913 [details] [diff] [review] cleanup patch, revision 2.2 (diff -u) I am not interested in super-reviewing this, I don't see much value in such an invasive cleanup. It messes the bonsai records for little gain.
Attachment #186913 - Flags: superreview?(rbs)
Comment on attachment 186913 [details] [diff] [review] cleanup patch, revision 2.2 (diff -u) I'll ask bz, in that case.. I <think> he would be willing to do it when he has time.
Attachment #186913 - Flags: superreview?(bzbarsky)
Comment on attachment 186914 [details] [diff] [review] cleanup patch, revision 2.2 (diff -w) nsAutoString inchesStr; inchesStr.AppendFloat(inches); + mPrefBranch->SetCharPref(aPrefId, NS_ConvertUTF16toUTF8(inchesStr).get()); OK, why does this use an nsAutoString instead of an nsCAutoString at all? nsSharedPageData::~nsSharedPageData() + mHeadFootFont = nsnull; why bother? (note that the other Free calls in this function do not set the pointer to null)
(In reply to comment #21) > OK, why does this use an nsAutoString instead of an nsCAutoString at all? No particular reason. (I'll change it to nsCAutoString) > nsSharedPageData::~nsSharedPageData() > + mHeadFootFont = nsnull; > > why bother? (note that the other Free calls in this function do not set the > pointer to null) See comment #12.
Attachment #186913 - Flags: superreview?(bzbarsky)
In this revision, I've addressed biesi's comment about ns[C]AutoString. In addition, I've removed my change to nsSimplePageSequence.cpp, seeing as leaving it out doesn't crash my build anymore. The change probably had no effect anyway, because |delete| is null safe.. At this point I don't understand why the crash was happening in the first place, but since I can't reproduce it anymore, I'm letting go of this hunk.
Attachment #186914 - Attachment is obsolete: true
Attachment #186913 - Attachment is obsolete: true
Attachment #188243 - Flags: superreview?(bzbarsky)
Attachment #188243 - Flags: review-
Attachment #188243 - Flags: review-
It'll take me some time to get to this -- I'm still swamped with things that collected over the last month. You may want to look for someone else to superreview...
Attachment #188243 - Flags: superreview?(bzbarsky) → superreview?(dmose)
Comment on attachment 188243 [details] [diff] [review] cleanup patch, revision 2.3 (diff -u) In general it looks good; most of my comments are nit-picking, so addressing them should be straightforward. >Index: gfx/idl/nsIPrintOptions.idl >=================================================================== >RCS file: /cvsroot/mozilla/gfx/idl/nsIPrintOptions.idl,v >retrieving revision 1.37 >diff -p -8 -u -r1.37 nsIPrintOptions.idl >--- gfx/idl/nsIPrintOptions.idl 17 Apr 2004 21:52:27 -0000 1.37 >+++ gfx/idl/nsIPrintOptions.idl 4 Jul 2005 12:07:31 -0000 >@@ -87,31 +87,32 @@ interface nsIPrintOptions : nsISupports > >- [noscript] void SetFontNamePointSize(in nsNativeStringRef aName, in PRInt32 aPointSize); >+ [noscript] void SetFontNamePointSize(in AString aName, in PRInt32 aPointSize); Since the native reference is gone, how about dropping the [noscript]? > >-[scriptable, uuid(a6cf9128-15b3-11d2-932e-00805f8add32)] >+[scriptable, uuid(d9948a4d-f49c-4456-938a-acda2c8d7741)] >+ I'm confused. Why bump the UUID on the interface after the one that has actually changed? >Index: gfx/src/nsPrintOptionsImpl.cpp >=================================================================== >RCS file: /cvsroot/mozilla/gfx/src/nsPrintOptionsImpl.cpp,v >retrieving revision 1.73 >diff -p -8 -u -r1.73 nsPrintOptionsImpl.cpp >--- gfx/src/nsPrintOptionsImpl.cpp 10 Jun 2005 22:18:16 -0000 1.73 >+++ gfx/src/nsPrintOptionsImpl.cpp 4 Jul 2005 12:07:32 -0000 > // Additional Prefs >-static const char kPrintPaperSize[] = "print_paper_size"; // this has been deprecated >+// this has been deprecated >+static const char kPrintPaperSize[] = "print_paper_size"; This makes it look a bit like the comment could apply to everything below it. Maybe just change the comment to "// deprecated" at the end of the same line? > nsPrintOptions::~nsPrintOptions() > { >- if (sDefaultFont != nsnull) { >- delete sDefaultFont; >- sDefaultFont = nsnull; >+ if (mDefaultFont) { >+ delete mDefaultFont; >+ mDefaultFont = nsnull; > } > } What's the point of setting mDefaultFont to nsnull after deleting it? You're in the destructor, so mDefaultFont is about to go away completely. >>+NS_IMETHODIMP > nsPrintOptions::ShowPrintSetupDialog(nsIPrintSettings *aPS) >+{ >+ NS_ENSURE_ARG_POINTER(aPS); >+ nsresult rv; > >- return rv; >- >+ // create a nsISupportsArray of the parameters >+ // being passed to the window >+ nsCOMPtr<nsISupportsArray> array; >+ rv = NS_NewISupportsArray(getter_AddRefs(array)); >+ NS_ENSURE_SUCCESS(rv, rv); >+ >+ nsCOMPtr<nsISupports> psSupports = do_QueryInterface(aPS); >+ NS_ASSERTION(psSupports, "PrintSettings must be a supports"); >+ >+ if (psSupports) >+ array->AppendElement(psSupports); What's the point of putting this inside an if? There's already an assertion checking to make sure that you can QI to nsISupports, and if you can't that's a bug that needs to be fixed, not something that should be checked for at runtime. >+ >+ nsCOMPtr<nsIDialogParamBlock> ioParamBlock = >+ do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID, &rv); >+ NS_ENSURE_SUCCESS(rv, rv); >+ >+ ioParamBlock->SetInt(0, 0); >+ >+ nsCOMPtr<nsISupports> blkSupps = do_QueryInterface(ioParamBlock); >+ NS_ASSERTION(blkSupps, "IOBlk must be a supports"); >+ if (blkSupps) >+ array->AppendElement(blkSupps); Same question. >+NS_IMETHODIMP >+nsPrintOptions::AvailablePrinters(nsISimpleEnumerator **aPrinterEnumerator) > { >- NS_ENSURE_ARG_POINTER(aPrinterEnumerator); >- *aPrinterEnumerator = nsnull; >+ *aPrinterEnumerator = nsnull; > >- nsCOMPtr<nsPrinterListEnumerator> printerListEnum = new nsPrinterListEnumerator(); >- NS_ENSURE_TRUE(printerListEnum.get(), NS_ERROR_OUT_OF_MEMORY); >+ nsRefPtr<nsPrinterListEnumerator> printerListEnum = >+ new nsPrinterListEnumerator(); >+ NS_ENSURE_TRUE(printerListEnum, NS_ERROR_OUT_OF_MEMORY); > >- nsresult rv = printerListEnum->Init(); >- if (NS_SUCCEEDED(rv)) { >- *aPrinterEnumerator = NS_STATIC_CAST(nsISimpleEnumerator*, printerListEnum); >- NS_ADDREF(*aPrinterEnumerator); >- } >- return rv; >+ NS_ADDREF(*aPrinterEnumerator = printerListEnum); >+ >+ nsresult rv = printerListEnum->Init(); >+ if (NS_FAILED(rv)) >+ NS_RELEASE(*aPrinterEnumerator); >+ >+ return rv; Instead of calling Init() after the AddRef, call it before. Furthermore, you can avoid unnecessary pointer dereferences by doing the assignment to the out param at the very end using the form: NS_ADDREF(*aPrinterEnumerator = printerListEnum.get()) > >-NS_IMETHODIMP nsPrintOptions::DisplayJobProperties( const PRUnichar *aPrinter, nsIPrintSettings* aPrintSettings, PRBool *aDisplayed) >+NS_IMETHODIMP >+nsPrintOptions::DisplayJobProperties(const PRUnichar *aPrinter, >+ nsIPrintSettings* aPrintSettings, >+ PRBool *aDisplayed) > { >- NS_ENSURE_ARG(aPrinter); >- *aDisplayed = PR_FALSE; >+ NS_ENSURE_ARG_POINTER(aPrinter); >+ *aDisplayed = PR_FALSE; >+ >+ nsresult rv; >+ nsCOMPtr<nsIPrinterEnumerator> propDlg; > >- nsresult rv; >- nsCOMPtr<nsIPrinterEnumerator> propDlg; >+ propDlg = do_CreateInstance(kCPrinterEnumerator, &rv); >+ NS_ENSURE_SUCCESS(rv, rv); > >- propDlg = do_CreateInstance(kCPrinterEnumerator, &rv); >- if (NS_FAILED(rv)) >- return rv; >+ NS_ENSURE_ARG_POINTER(aPrintSettings); >+ rv = propDlg->DisplayPropertiesDlg((PRUnichar*)aPrinter, aPrintSettings); Prefer C++ style casts to C-style casts, since they express intent more precisely and thus allow the compiler to catch more bugs at build time. In this case, NS_CONST_CAST is what you want. >-/* [noscript] voidPtr GetNativeData (in short aDataType); */ > NS_IMETHODIMP nsPrintOptions::GetNativeData(PRInt16 aDataType, void * *_retval) > { >- NS_ENSURE_ARG_POINTER(_retval); > *_retval = nsnull; >- > return NS_ERROR_NOT_IMPLEMENTED; > } Might as well get rid of the |*retval = nsnull| line here; you're already returning an error, so you don't need to guarantee that *retval is set to anything in particular. > nsresult nsPrintOptions::_CreatePrintSettings(nsIPrintSettings **_retval) > { > nsresult rv = NS_OK; >+ *_retval = nsnull; The above line shouldn't be necessary; see previous comment. There seem to be more instances of this pattern throughout the patch. None should be necessary and just waste cycles. >- nsPrintSettings * printSettings = new nsPrintSettings(); // does not initially ref count >+ // does not initially ref count >+ nsPrintSettings * printSettings = new nsPrintSettings(); > NS_ENSURE_TRUE(printSettings, NS_ERROR_OUT_OF_MEMORY); > > NS_ADDREF(*_retval = printSettings); // ref count >- InitPrintSettingsFromPrefs(*_retval, PR_FALSE, nsIPrintSettings::kInitSaveAll); // ignore return value >+ InitPrintSettingsFromPrefs(*_retval, PR_FALSE, >+ nsIPrintSettings::kInitSaveAll); >+ // ignore return value A good way to indicate that a return value is intentionally ignore is simply to cast the call itself to void. > return rv; > } > >-/* nsIPrintSettings CreatePrintSettings (); */ > NS_IMETHODIMP nsPrintOptions::CreatePrintSettings(nsIPrintSettings **_retval) > { >+ *_retval = nsnull; > return _CreatePrintSettings(_retval); > } What's the point of having this split into two functions? Why not just move all the code in _CreatePrintSettings here? >+nsresult >+nsPrintOptions::ReadPrefString(const char * aPrefId, nsAString& aString) > { > NS_ENSURE_STATE(mPrefBranch); >+ NS_ENSURE_ARG_POINTER(aPrefId); >+ > char * str = nsnull; > nsresult rv = mPrefBranch->GetCharPref(aPrefId, &str); >- if (NS_SUCCEEDED(rv) && str) { >- CopyUTF8toUTF16(str, aString); >- nsMemory::Free(str); >+ if (NS_SUCCEEDED(rv)) { >+ // By encapsulating str, we save copying and freeing it >+ aString = nsDependentString((NS_ConvertUTF8toUTF16(str))); > } > return rv; > } The above code will leak, because str itself is never freed. Since NS_ConvertUTF8toUTF16 is itself implicitly copying the string into a stack variable, there's no real savings there either. Additionally, this code is attempting to return a string dependant on a stack variable which is about to go out of scope. Either this is going to force a second copy, or else you'll have a bug; I'm not sure which. I'd vote with leaving this function that way it was before. >+void >+nsPrintOptions::WriteInchesFromTwipsPref(const char * aPrefId, nscoord aTwips) > { >- if (!mPrefBranch) { >+ if (!mPrefBranch) > return; >- } Why get rid of the braces here? It's an invitation to a future error if someone decides to add some other piece of code to the else clause but forgets to add them back.
Attachment #188243 - Flags: superreview?(dmose) → superreview-
>+ aString = nsDependentString((NS_ConvertUTF8toUTF16(str))); yeah, that should absolutely stay with CopyUTF8toUTF16. I'm surprised that you can construct an nsDependentString like that... maybe that function should also use nsXPIDLCString.
This patch addresses dmose's comments.
Attachment #188243 - Attachment is obsolete: true
Attachment #189056 - Flags: superreview?(dmose)
Attachment #189056 - Flags: superreview?(dmose)
The right patch this time.
Attachment #189056 - Attachment is obsolete: true
Attachment #188241 - Attachment is obsolete: true
Attachment #189059 - Flags: superreview?(dmose)
timeless mentioned that when removing [noscript], I should switch from InitialCaps to interCaps.
Attachment #189059 - Attachment is obsolete: true
Attachment #189307 - Flags: superreview?(dmose)
Attachment #189059 - Flags: superreview?(dmose)
Attachment #189060 - Attachment is obsolete: true
Comment on attachment 189307 [details] [diff] [review] cleanup patch, revision 2.5.1 (diff -u) sr=dmose
Attachment #189307 - Flags: superreview?(dmose) → superreview+
Attachment #189307 - Flags: approval1.8b4?
Attachment #189307 - Flags: approval1.8b4? → approval1.8b4+
Checked in by timeless (2005-07-19 14:55). Thanks guys!
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
This caused a bunch of issues on Mac; see bug 306370.
Crash Signature: [@ nsPrintOptions::_CreatePrintSettings]
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: