Bug #113360 ODBC driver 8.2 causes crash when setting prefetch option
Submitted: 7 Dec 2023 11:17 Modified: 8 Dec 2023 14:47
Reporter: Adina Berg Email Updates:
Status: Can't repeat Impact on me:
None 
Category:Connector / ODBC Severity:S2 (Serious)
Version:8.2 OS:Windows
Assigned to: CPU Architecture:Any

[7 Dec 2023 11:17] Adina Berg
Description:
We are facing some issues when using the MySQL ODBC 8.2 driver that causes the entire application to crash. Downgrading to the 8.0 driver solves the issue.

Versions used to reproduce:
.NET Core 6.0
System.Data.Odbc 6.0.0
MySQL Community Server (GPL) 5.7.42
MySQL ODBC 8.2 Unicode Driver

How to repeat:
See below a small console application that runs a query with 64 columns, using a connection with PREFETCH=1. The issue occurs most of the time whenever the query selects a "larger" number or columns, while selecting e.g. 8 columns usually does not cause the issue. I have not been able to pin point an exact number of columns/length of query for which the error starts occurring, since it happens intermittently. The problem was originally reported by our customer and they use a smaller query selecting 24 columns so I do not believe the number of columns is really relevant, however this was the way I could reproduce the problem.

I tried with different values for PREFETCH (10, 100, 10000) and saw the same problem. If I set PREFETCH=0 or omit the parameter completely, the issue does not occur. 

-----------------------------------------------------------------------------------
namespace MySQLTestProgram;

using System.Data.Odbc;

class Program
{
    static void Main()
    {
        var connectionString =
            $"Driver={MySQL ODBC 8.2 Unicode Driver};DATABASE=TPCH_SMALL;UID=user;PWD=password;SERVER=localhost;PORT=3307;PREFETCH=1";
        
        const string query1 =
            "SELECT `CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS`,`CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS` FROM `CUSTOMER`";

        const string query2 =
            "SELECT `CUSTOMER`.`C_CUSTKEY` AS `COLUMN1_ALIAS`,`CUSTOMER`.`C_NAME` AS `COLUMN2_ALIAS`,`CUSTOMER`.`C_ADDRESS` AS `COLUMN3_ALIAS`,`CUSTOMER`.`C_NATIONKEY` AS `COLUMN4_ALIAS`,`CUSTOMER`.`C_PHONE` AS `COLUMN5_ALIAS`,`CUSTOMER`.`C_ACCTBAL` AS `COLUMN6_ALIAS`,`CUSTOMER`.`C_MKTSEGMENT` AS `COLUMN7_ALIAS`,`CUSTOMER`.`C_COMMENT` AS `COLUMN8_ALIAS` FROM `CUSTOMER`";
        
        var query = query1;
        try
        {
            using (var connection = new OdbcConnection(connectionString))
            {
                connection.Open();

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = query;
                    using (var reader = command.ExecuteReader())
                    {
                        Console.WriteLine("Reader is ready");
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught exception:\n{ex}");
        }

        Console.WriteLine("Program ran to completion");
    }
}
-----------------------------------------------------------------------------------

Run the above test program with query1 several times. About half of the times, one of three things happens:

1. No output or error indication. Application just crashes.

2. Crash with below error:
Fatal error. System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
   at Interop+Odbc.SQLExecDirectW(System.Data.Odbc.OdbcStatementHandle, System.String, Int32)
--------------------------------
   at System.Data.Odbc.OdbcStatementHandle.ExecuteDirect(System.String)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(System.Data.CommandBehavior, System.String, Boolean, System.Object[], SQL_API)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(System.Data.CommandBehavior, System.String, Boolean)
   at System.Data.Odbc.OdbcCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.Odbc.OdbcCommand.ExecuteReader()
   at MySQLTestProgram.ProgramSmall.Main()

3. Crash with below error:
Process terminated. Infinite recursion during resource lookup within System.Private.CoreLib.  This may be a bug in System.Private.CoreLib, or potentially in certain extensibility points such as assembly resolve events or CultureInfo names.  Resource name: Arg_AccessViolationException
   at System.Environment.FailFast(System.String)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.AccessViolationException..ctor()
   at System.Resources.RuntimeResourceSet..ctor(System.IO.Stream, Boolean)
   at System.Resources.ManifestBasedResourceGroveler.CreateResourceSet(System.IO.Stream, System.Reflection.Assembly)
   at System.Resources.ManifestBasedResourceGroveler.GrovelForResourceSet(System.Globalization.CultureInfo, System.Collections.Generic.Dictionary`2<System.String,System.Resources.ResourceSet>, Boolean, Boolean)
   at System.Resources.ResourceManager.InternalGetResourceSet(System.Globalization.CultureInfo, Boolean, Boolean)
   at System.Resources.ResourceManager.GetString(System.String, System.Globalization.CultureInfo)
   at System.SR.InternalGetResourceString(System.String)
   at System.SR.GetResourceString(System.String)
   at System.AccessViolationException..ctor()
   at Interop+Odbc.SQLExecDirectW(System.Data.Odbc.OdbcStatementHandle, System.String, Int32)
   at Interop+Odbc.SQLExecDirectW(System.Data.Odbc.OdbcStatementHandle, System.String, Int32)
   at System.Data.Odbc.OdbcStatementHandle.ExecuteDirect(System.String)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(System.Data.CommandBehavior, System.String, Boolean, System.Object[], SQL_API)
   at System.Data.Odbc.OdbcCommand.ExecuteReaderObject(System.Data.CommandBehavior, System.String, Boolean)
   at System.Data.Odbc.OdbcCommand.ExecuteReader(System.Data.CommandBehavior)
   at System.Data.Odbc.OdbcCommand.ExecuteReader()
   at MySQLTestProgram.Program.Main()

Also attaching ODBC trace logs from test runs executing query1 and query2, setting prefetch=0 and prefetch=1 respectively.

Suggested fix:
I expect to be able to set the PREFETCH option without getting a crash.

Using the older 8.0 river as a workaround only works temporarily.
[7 Dec 2023 12:39] MySQL Verification Team
Hello Adina Berg,

Thank you for the bug report.
Could you please provide table structure for table "CUSTOMER"(please make it as private if you prefer)?

Regards,
Ashwini Patil
[7 Dec 2023 12:56] Adina Berg
Hi Ashwini Patil,

Below is the table description.
MySQL  localhost:3307 ssl  SQL > describe TPCH_SMALL.CUSTOMER;
+--------------+---------------+------+-----+---------+-------+
| Field        | Type          | Null | Key | Default | Extra |
+--------------+---------------+------+-----+---------+-------+
| C_CUSTKEY    | int           | NO   | PRI | NULL    |       |
| C_NAME       | varchar(25)   | NO   |     | NULL    |       |
| C_ADDRESS    | varchar(40)   | NO   |     | NULL    |       |
| C_NATIONKEY  | int           | NO   | MUL | NULL    |       |
| C_PHONE      | char(15)      | NO   |     | NULL    |       |
| C_ACCTBAL    | decimal(12,2) | NO   |     | NULL    |       |
| C_MKTSEGMENT | char(10)      | NO   |     | NULL    |       |
| C_COMMENT    | varchar(117)  | NO   |     | NULL    |       |
+--------------+---------------+------+-----+---------+-------+

However, I noticed the same behavior if I modify the query to select from a table that does not exist in the current database. Something like
"SELECT [many column names with aliases...] FROM DUMMY_TABLE_NAME" would also cause a crash.
[8 Dec 2023 12:04] MySQL Verification Team
Hello Adina Berg,

Thank you for the details.
I tried to reproduce your issue on windows 11 with Connector/ODBC 8.2 and MySQL Server 5.7 using the test case and table given but I am not seeing any issues at my end. Please see the screenshot below.

Regards,
Ashwini Patil
[8 Dec 2023 12:05] MySQL Verification Team
Connector/ODBC 8.2 test results

Attachment: 113360_test_results.png (image/png, text), 37.03 KiB.

[8 Dec 2023 12:09] Adina Berg
Hi Ashwini Patil,

Thank you for looking into the issue. On my side the error appears intermittently, about every other or third time I run the test program. Did you try a few times in a row?

/ Adina
[8 Dec 2023 12:15] MySQL Verification Team
I tried executing the same query multiple times, also tried prefetch =10 and prefetch =100 but not seeing any issues at my end. Thanks.
[8 Dec 2023 14:47] Adina Berg
We reproduced the issue against several different servers and on different clients. I notice that sometimes the test program finishes without any issues many times in a row, and other times the test program fails many times in a row. Although I do not see why it would make a difference, on my side I see the error more frequently when running MySQLTestProgram.exe directly in windows command line, instead of running with F5 in Visual Studio.

What is very clear on my side is that whenever I use the 8.0 driver the issue never occurs.

I believe that the fix https://bugs.mysql.com/bug.php?id=111036 could be related since this was changed between the 8.0.33 and 8.02.00 versions of the driver.
[19 Dec 2023 21:42] Markus Kollind
Hi Ashwini Patil, 

after some more investigation, we see this for all .NET applications (.NET 6 as well as .NET Framework 4.8). 

After some testing, I can reproduce the issue in PowerShell (using some .NET calls) accessing both a MySQL 5.7 or a MySQL 8.2 server (although I have actually used the 8.1 driver; the issues seems to be for all drivers after 8.0.x). After two or three queries my PowerShell window is terminated with the message "[process exited with code 3221226356 (0xc0000374)]" or "[process exited with code 3221226356 (0xc0000374)]". 

These are my steps to reproduce: 

1. I started a docker container with MySQL: 
   docker run -d -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

2. I created a PowerShell function: 
   function Get-ODBC-Query{
       param(
           [string]$query=$(throw 'query is required'),
           [string]$connstring=$(throw 'connection string is required')
       )
       $conn = New-Object System.Data.Odbc.OdbcConnection
       $conn.ConnectionString = "$connstring"
       $conn.open()
       $cmd = New-object System.Data.Odbc.OdbcCommand($query,$conn)
       $ds = New-Object system.Data.DataSet
       (New-Object system.Data.odbc.odbcDataAdapter($cmd)).fill($ds) | out-null
       $conn.close()
       $ds.Tables[0]
   }  

3. I ran the following command, to have some data to test it on (note that the table is empty): 
   Get-ODBC-Query -connstring "driver=MySQL ODBC 8.1 ANSI Driver;host=localhost;port=3306;prefetch=1000;NO_CACHE=1;NO_SCHEMA=1;FORWARD_CURSOR=1;DATABASE=mysql;uid=root" -query "create table mupp (mupp varchar(512))"

4. I executed a long dummy-query a couple of times: 
   Get-ODBC-Query -connstring "driver=MySQL ODBC 8.1 ANSI Driver;host=localhost;port=3306;prefetch=1000;NO_CACHE=1;NO_SCHEMA=1;FORWARD_CURSOR=1;DATABASE=mysql;uid=root" -query "select mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp,mupp from mupp"

Usually the second time, PowerShell dies with an error (one of the two above, as far as I have been able to see). In a few occasions I have not been able to make it crash. But starting a new PowerShell instance and redoing the select query usually end up in the crash after 2 or 3 tries.
[21 Dec 2023 13:38] Markus Kollind
Is there anyway to get this reopened, or do we need to create a new bug report? 

When just running this example in PowerShell, we see ACCESS_VIOLATION (C0000005) or a STATUS_HEAP_CORRUPTION (c0000374) after just a few runs.