How to explain access violation in RtlLeaveCriticalSection
Asked Answered
H

2

1

I have an application that requests data from a database triggered by a timer on a form. If there is an error (the connection to the database is lost), I sometimes I get the expected exception (EIBO_ISCError) and sometimes I get an access violation in RtlLeaveCriticalSection of ntdll.dll. Here is the corresponing Eurekalog stack:

------------------------------------------------------------------------------------------------------
|Adresse |Modul         |Unit               |Klasse             |Prozedur/Methode          |Zeile    |
------------------------------------------------------------------------------------------------------
|Laufender Thread: ID=1320; Priorität=0; Klasse=; [Haupt Thread]                                     |
|----------------------------------------------------------------------------------------------------|
|76FD2280|ntdll.dll     |                   |                   |RtlLeaveCriticalSection   |         |
|76FDE0ED|ntdll.dll     |                   |                   |RtlAllocateHeap           |         |
|76FE6CC5|ntdll.dll     |                   |                   |LdrUnlockLoaderLock       |         |
|7552EF19|KERNELBASE.dll|                   |                   |VirtualQueryEx            |         |
|7552EF02|KERNELBASE.dll|                   |                   |VirtualQueryEx            |         |
|7552EFE6|KERNELBASE.dll|                   |                   |VirtualQuery              |         |
|76FC012E|ntdll.dll     |                   |                   |KiUserExceptionDispatcher |         |
|0069D997|Program.exe   |IBODataset.pas     |TIBOInternalDataset|DoHandleError             |8407[23] |
|0063B3F7|Program.exe   |IB_Components.pas  |TIB_Session        |DoHandleError             |13181[2] |
|0068B36C|Program.exe   |IB_Session.pas     |TIB_SessionBase    |HandleException           |1442[58] |
|0068B03C|Program.exe   |IB_Session.pas     |TIB_SessionBase    |HandleException           |1384[0]  |
|0064EE74|Program.exe   |IB_Components.pas  |TIB_Statement      |API_Execute               |22927[14]|
|0064EE10|Program.exe   |IB_Components.pas  |TIB_Statement      |API_Execute               |22913[0] |
|00655D1D|Program.exe   |IB_Components.pas  |TIB_Dataset        |SysExecSelect             |26432[1] |
|0064DA60|Program.exe   |IB_Components.pas  |TIB_Statement      |SysExecStatement          |22259[9] |
|0064D7A1|Program.exe   |IB_Components.pas  |TIB_Statement      |SysExecute                |22173[12]|
|0064D708|Program.exe   |IB_Components.pas  |TIB_Statement      |SysExecute                |22161[0] |
|00655A9F|Program.exe   |IB_Components.pas  |TIB_Dataset        |SysExecute                |26373[7] |
|00655210|Program.exe   |IB_Components.pas  |TIB_Dataset        |SysOpen                   |26160[23]|
|006550F8|Program.exe   |IB_Components.pas  |TIB_Dataset        |SysOpen                   |26137[0] |
|006994E5|Program.exe   |IBODataset.pas     |TIBODataset        |DoBeforeOpen              |6312[17] |
|0061FBEA|Program.exe   |mvdb.pas           |TImvDatabase       |QueryRun                  |1393[10] |
...
|00B1D440|Program.exe   |StartDialogForm.pas|TFormStartDialog   |UpdateStartBar            |494[0]   |
|00B1D4C3|Program.exe   |StartDialogForm.pas|TFormStartDialog   |TimerExBarTimer           |521[6]   |
|76667BC5|USER32.dll    |                   |                   |DispatchMessageA          |         |
|76667BBB|USER32.dll    |                   |                   |DispatchMessageA          |         |
|00BF1178|Program.exe   |Program.dpr        |                   |                          |884[399] |
------------------------------------------------------------------------------------------------------

The code, which is executed, is nothing special. It boils down to:

qry := TIBOQuery.Create(nil);      //IBObjects
qry.SQL := 'SELECT COUNT(IDX) FROM TABLE';
qry.Prepare;

when creating the form and

qry.Open;       //<-- Exception
TotalCount := qry.Fields[0].AsVariant;
qry.Close;

in the OnTimer event of the MDI form.

The code line in IBObjects that is called in DoHandleError is

raise EIBO_ISCError.CreateISC( ... );

The underlaying exception is likely to be caused by a lost database connection in qry.Open. What I want to know is, which circumstances (read defects in my code) can lead to the behaviour, that sometimes this exception is handled as expected (EIBO_ISCError in Eurekalog) and sometimes the same exception leads to an access violation in RtlLeaveCriticalSection.

Hardily answered 25/3, 2014 at 9:57 Comment(5)
Without seeing any actual code, who knows?Introductory
The explanation is that your code has a defect.Evetta
@Introductory I added the code which triggers the error, but I think the real cause lies somewhere else.Hardily
@DavidHeffernan I am looking for the explanation what in my code might be defect, that leads to the access violation. I do not think the code which leads to the EIBO_ISCError-exception is defect. But I do not know enough about the error handling in Delphi to understand why RtlLeaveCriticalSection is called and how this can lead to an access violation.Hardily
The diagnostics say that an invalid pointer has reached RtlLeaveCriticalSection. There could be all sorts of reasons for that.Evetta
I
3

It looks like you have heap corruption. Somewhere, your program has written to memory that you're not supposed to write to.

That might mean you've written to a critical-section data structure belonging to the heap, but it might mean you've written somewhere else that caused the memory manager to think there's a critical-section object where there really isn't one.

The stack trace suggests you're still getting the usual exception you expected to get, but in attempting to handle that exception, something goes wrong.

You could try using the debugger to inspect other memory near where the invalid read occurs. See whether there are any strings or numbers you recognize from your program. They could indicate which section of code is writing where it shouldn't.

Induction answered 25/3, 2014 at 12:20 Comment(8)
Thank you, I think I found the cause. In Application.OnException I freed the Exception object trying to suppress the exception. But I think this is not legal. If I do not free the exception, the access violation is gone.Hardily
Yes, you should never free an exception, ever.Sissie
@NickHodges Thanks for clarifying - i summed it up in an answerHardily
@Nick You should never free an exception that you don't own. There are scenarios where exceptions should be freed. Granted not here, but your comment is too proscriptive.Evetta
@DavidHeffernan -- Interesting -- I'd love to see a scenario where you'd free an exception.Sissie
@NickHodges You might do it if you had called AcquireExceptionObject and then had no reason to re-raise the exception. The classic example is the one in TThread.Destroy.Evetta
@DavidHeffernan Interesting -- I wonder if you'd ever need to do that in "real life", and not just in the bowels of the RTL.Sissie
@NickHodges I call AcquireExceptionObject in my thread pool class. For exactly the same reason. I want to catch an exception on one thread and re-raise it on another thread.Evetta
H
0

From the comments, for reference:

The cause of this access violation was, that I freed an exception that occured while handling an other exception (like originally suggested here).

Citation from the comments:

you should never free an exception, ever

The exception was handled normally if during the handling of the original exception no other exception occured. If an other exception occured I got the access violation.

Hardily answered 26/3, 2014 at 17:23 Comment(1)
There are times when you need to explicitly free an exception. Not here though.Evetta

© 2022 - 2024 — McMap. All rights reserved.