Bug #40005 NullReferenceException in MySqlCommand.TimeOut causing process to crash
Submitted: 13 Oct 2008 16:54 Modified: 8 Jul 2009 14:52
Reporter: Dennis Gronewold
Status: Closed
Category:Connector/Net Severity:S1 (Critical)
Version:5.1.5,5.2.3 OS:Microsoft Windows
Assigned to: Reggie Burnett Target Version:
Tags: timeout, NullReferenceException
Triage: D2 (Serious)

[13 Oct 2008 16:54] Dennis Gronewold
Description:
This exception has occured a few times on our application server, causing the process to
terminate:

System.NullReferenceException: Object reference not set to an instance of an object.
   bei MySql.Data.MySqlClient.MySqlCommand.TimeoutExpired(Object commandObject)
   bei System.Threading._TimerCallback.TimerCallback_Context(Object state)
   bei System.Threading.ExecutionContext.runTryCode(Object userData)
   bei
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode
code, CleanupCode backoutCode, Object userData)
   bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,
ContextCallback callback, Object state)
   bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext,
ContextCallback callback, Object state)
   bei System.Threading._TimerCallback.PerformTimerCallback(Object state)

This is the code from MySqlCommand.TimeoutExpired:

MySqlCommand cmd = (commandObject as MySqlCommand);

cmd.timedOut = true;
try
{
    cmd.Cancel();
}
catch (Exception ex)
{
   // if something goes wrong, we log it and eat it.  There's really nothing
   // else we can do.
   if (connection.Settings.Logging)
       Logger.LogException(ex);
}

It seems that the cast somehow fails or null is getting passed in. Both is inexplicable
to me, as the MySqlCommand clearly is passed as state to the TimerCallback:

timer = new Timer(timerDelegate, this, this.CommandTimeout * 1000, Timeout.Infinite);

How to repeat:
Not quite sure yet, will try to produce a test case.

Suggested fix:
Check if cast was successful:

MySqlCommand cmd = (commandObject as MySqlCommand);

if (cmd != null)
{
   cmd.timedOut = true;
   try
   {
      cmd.Cancel();
   }
   catch (Exception ex)
   {
      // if something goes wrong, we log it and eat it.  There's really nothing
      // else we can do.
      if (connection.Settings.Logging)
         Logger.LogException(ex);
   }
}
[14 Oct 2008 8:38] Tonci Grgin
Hi Dennis and thanks for your report.

Please do try to produce a test case for this so I can check.
[20 Oct 2008 11:58] Dennis Gronewold
Hi Tonci,

unfortunately I'm not able to provide a test case locally. Judging from our server log,
this seems to have happened under high load, which I can't reproduce on my machine. But I
think that my suggested fix makes sense anyway, because the cast is unsafe and could fail
for other reasons.
[15 Jan 2009 20:25] Tonci Grgin
Dennis, I must agree with proposed check thus verified even though I've never seen it...
Let's see what Reggie says.
[7 Jul 2009 21:39] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/78163
[7 Jul 2009 21:40] Reggie Burnett
fixed in 5.1.8, 5.2.7, and 6.0.5.  Was never able to reproduce this but added check that
user suggested.
[8 Jul 2009 14:52] Tony Bedford
An entry was added to the 5.1.8, 5.2.7 and 6.0.5 changelogs:

MySQL Connector/NET generated the following exception:

System.NullReferenceException: Object reference not set to an instance of an object.
   bei MySql.Data.MySqlClient.MySqlCommand.TimeoutExpired(Object commandObject)
   bei System.Threading._TimerCallback.TimerCallback_Context(Object state)
   bei System.Threading.ExecutionContext.runTryCode(Object userData)
   bei
System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode
code, CleanupCode backoutCode, Object userData)
   bei System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext,
ContextCallback callback, Object state)
   bei System.Threading.ExecutionContext.Run(ExecutionContext executionContext,
ContextCallback callback, Object state)
   bei System.Threading._TimerCallback.PerformTimerCallback(Object state)