System.AccessViolationException on encrypted database
Closed this issue ยท 14 comments
Hi,
I'm using ASP.NET Core 2.0 and SQLite with following sqlite related packages:
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.5" />
<PackageReference Include="SQLitePCLRaw.bundle_sqlcipher" Version="1.1.9" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.7" ExcludeAssets="All" />
As long as I have an unencrypted database, I can write as many entries as I want in my db. But when I encrypt my database, after approximatelly 20-30 saved entries, I get following error:
System.AccessViolationException: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
This happens always on this line:
await _dbContext.SaveChangesAsync();
On Stackoverflow there are many theories about this error: Switching to another target platform might help or closing the sql connection. Switching from "Any CPU" target to "x64" doesn't help. And switching to "x86" leads immediatelly to a crash.
With respect to "closing the connection" I guessed that the Entity framework is doing this automatically. Here a snippet of my C# code in the Startup
class.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<QlcContext>(opt => opt.UseSqlite(GetConnection()));
services.AddTransient<DbContext, QlcContext>();
services.AddMvc();
}
private DbConnection GetConnection()
{
string connectionString = Configuration.GetConnectionString("QlcConnection");
SqliteConnection connection = new SqliteConnection(connectionString);
connection.Open();
// For my unencrypted test, the next lines get commented out,
// except the return statement
string password = "MyPass";
var command = connection.CreateCommand();
command.CommandText = "SELECT quote($password);";
command.Parameters.AddWithValue("$password", password);
var quotedPassword = (string)command.ExecuteScalar();
command.CommandText = "PRAGMA key = " + quotedPassword;
command.Parameters.Clear();
command.ExecuteNonQuery();
return connection;
}
Am I doing anything wrong?
Thanks in advance!
There is mismatch of version. Since you are using EF Core 2.0.1, remove reference to Microsoft.EntityFrameworkCore.Sqlite.Design. That package does not exist for 2.0 release.
Also 2.0 depends on version 1.1.7 of SQLitePCLRaw so you should use that for bundle_sqlcipher. If you need to use 1.1.9 only then upgrade package of bundle_green too.
Hi @smitpatel ,
thx for the answer! This is my current SQLite related package setup:
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="SQLitePCLRaw.bundle_sqlcipher" Version="1.1.9" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="1.1.9" ExcludeAssets="All" />
I still get the error.
The documentation for the UseSqlite()
method states for the DbConnection
argument:
An existing to be used to connect to the database. If the connection is in the open state then EF will not open or close the connection. If the connection is in the closed state then EF will open and close the connection as needed.
In my case, the connection is already opened which means, it won't be closed. Maybe this is causing the problem. Still: It is strange, that this doesn't cause any problems, if I remove the encryption code part and give an already opened connection as parameter.
My problem is, that I don't know how to close the connection when I don't need it anymore. Is there any option/flag which I can pass which tells the DbContext
instance to always close the connection?
Update:
In my QlcContext
class which extends DbContext
I have a SqliteConnection
variable which is initialized in OnConfiguring()
. I also override Dispose()
which closes the connection again. Nevertheless: I still get the System.AccessViolationException
although much later.
public class QlcContext : DbContext
{
// ..
private SqliteConnection connection;
public override void Dispose()
{
base.Dispose();
connection.Close();
}
protected override void OnConfiguring(DbContextOptionsBuilder contextOptionsBuilder)
{
connection = new SqliteConnection("Data Source=qlc.db");
connection.Open();
string password = "MyPass";
var command = connection.CreateCommand();
command.CommandText = "SELECT quote($password);";
command.Parameters.AddWithValue("$password", password);
var quotedPassword = (string)command.ExecuteScalar();
command.CommandText = "PRAGMA key = " + quotedPassword;
command.Parameters.Clear();
command.ExecuteNonQuery();
contextOptionsBuilder.UseSqlite(connection);
}
}
Do you have the full stack trace of the AccessViolationException
? (value of ToString()
)
I'm was having the same issue. Stack trace:
at SQLitePCL.SQLite3Provider_sqlcipher.NativeMethods.sqlite3_step(IntPtr stmt)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)
at Microsoft.Data.Sqlite.SqliteCommand.ExecuteNonQuery()
at Microsoft.Data.Sqlite.SqliteTransaction.Commit()
at Acme.Stuff.Queue.StoreItem(List`1 items) in C:\Acme Stuff\Queue.cs:line 123
at System.Threading.Tasks.Task.Execute()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot)
at System.Threading.Tasks.Task.ExecuteEntry(Boolean bPreventDoubleExecution)
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Downgrading SQLitePCLRaw.bundle_sqlcipher from 1.1.9 to 1.1.7 fixed it for me.
@bricelam It seems like this is the 2nd or 3rd time someone has said that switching from 1.1.9 to 1.1.7 made a problem go away. I'm concerned that a regression slipped in somewhere. Do you have any more specific information?
All we do on SqliteTransaction.Commit()
is send an END
statement. My best guess is that a callback/user object is getting mangled from compiling against SQLitePCLRaw.core 1.1.7 and running with SQLitePCLRaw.bundle_sqlcipher 1.1.9.
Why is it possible to run incompatible nuget packages? This is the second time that this happens to me! :-(
@cocowalla Could you try version 1.1.10-pre20180223200113? I can't reproduce the error anymore. But it was hard anyway to reproduce it since I close manually the connection in my Dispose()
method in my class.
@ericsink Did you fix any bug in the latest pre version?
@StefanoD Yes, I'll try it tonight if I remember. It should be easy to reproduce if the issue is still there, as it happened on every run for me
@StefanoD asked: "Did you fix any bug in the latest pre version?"
Yes. I put in some code to protect against double free/close, related to the CLR finalizers I added in 1.1.8.
@cocowalla Can you still reproduce the bug?
I have a strange crash which I can only reproduce in release mode. No log gets written. It just crashes.
The issue was caused by mismatched SQLitePCLRaw.* package versions.