Just a quick exception management tip for today.
I was debugging a weird cascade of exceptions in an application today — it started with an EOleException from a database connection issue, and rapidly degenerated into a series of EAccessViolation and EInvalidPointer exceptions: often a good sign of Use-After-Free or Double-Free scenarios. Problem was, I could not see any place where we could be using a object after freeing it, even in the error case.
Here’s a shortened version of the code:
procedure ConnectToDatabase; begin try CauseADatabaseException; // yeah... bear with me here. except on E: EDatabaseError do begin E.Message := 'Failed to connect to database; the '+ 'error message was: ' + E.Message; raise(E); end; end; end;
Can you spot the bug?
I must admit I read through the code quite a few times before I spotted it. It’s not an in-your-face-look-at-me type of bug! In fact, the line in question appears to be completely logical and plausible.
Okay, enough waffle. The bug is in the last line of the exception handler:
When we call raise(E) we are telling Delphi that here is a shiny brand new exception object that we want to raise. After Delphi raises the exception object, the original exception object is freed, and … wait a minute, that’s the exception object we were raising! One delicious serve of Use-After-Free or Double-Free coming right up!
We should be doing this instead:
raise; // don't reference E here!
Another thing to take away from this is: remember that you don’t control the lifetime of the exception object. Don’t store references to it and expect it to survive. If you want to maintain knowledge of an inner exception object, use Delphi’s own nested exception management, available since Delphi 2009.