libgit2sharp icon indicating copy to clipboard operation
libgit2sharp copied to clipboard

RepositoryStatus sometimes crashes with an AccessViolationException

Open jcansdale opened this issue 7 years ago • 13 comments

We've noticed that the GitHub Extension for Visual Studio has been crashing with an AccessViolationException. This often happens when a user previews a PR and the extension calls RetrieveStatus. This exception occurs randomly and is thrown by the git_status_list_new method.

This exception is most easily reproduced using 0.24 or 0.25.0-preview-0033, but it does also happen with 0.23.1 (but I haven't seen it using the repro below).

How to reproduce

  1. Clone https://github.com/github/VisualStudio.git to C:\source\github.com\github\VisualStudio\.
  2. Compile and run the following code in Release configuration using LibGit2Sharp v0.24:
        static void Main(string[] args)
        {
            var path = @"C:\source\github.com\github\VisualStudio";
            for (int count = 0; count < 1000; count++)
            {
                var status = new Repository(path).RetrieveStatus();
                Console.WriteLine(count + ": " + status.IsDirty);
            }
        }

I tried to create a standalone app, but wasn't able to trigger it with a very simple repo. I'm using the github\VisualStudio one because I'm most familiar it, but it does this with many others as well.

What to expect

The most common exception is as follows:

System.AccessViolationException was unhandled
  HResult=-2147467261
  Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
  Source=LibGit2Sharp
  StackTrace:
       at LibGit2Sharp.Core.NativeMethods.git_status_list_new(git_status_list*& git_status_list, git_repository* repo, GitStatusOptions options)
       at LibGit2Sharp.Core.Proxy.git_status_list_new(RepositoryHandle repo, GitStatusOptions options) in C:\projects\libgit2sharp\LibGit2Sharp\Core\Proxy.cs:line 2931
       at LibGit2Sharp.RepositoryStatus..ctor(Repository repo, StatusOptions options) in C:\projects\libgit2sharp\LibGit2Sharp\RepositoryStatus.cs:line 60
       at LibGit2Sharp.RepositoryExtensions.RetrieveStatus(IRepository repository) in C:\projects\libgit2sharp\LibGit2Sharp\RepositoryExtensions.cs:line 696
       at Libgit2Repro.Program.Main(String[] args) in c:\users\passp\onedrive\documents\visual studio 2015\Projects\Libgit2Repro\Libgit2Repro\Program.cs:line 20
       at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
       at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
       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.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()

But sometimes NotFoundExceptions are thrown as well.

image

Related issues / PRs

  • Find out why / when LibGit2Sharp is crashing: github/VisualStudio#1315
  • GitHub extension crashes when viewing pull requests: github/VisualStudio#1306
  • "could not find [file] to open" exception thrown when opening a PR: github/VisualStudio#1326
  • Move from LibGit2Sharp v0.24.0 to v0.23.1: github/VisualStudio#1316
  • Add using to avoid crash in Release mode: github/VisualStudio#1311

jcansdale avatar Nov 20 '17 12:11 jcansdale

@jcansdale How many times do you have to run the program to trigger the crash? What framework are you using when you experience the crash?

Using 0.25.0-preview-0033, I've run it several times with both net461 and netcoreapp2.0, and I haven't seen it crash yet.

However, I wasn't able to fully clone https://github.com/github/VisualStudio.git because it has a private submodule that I don't have access to:

fatal: clone of '[email protected]:github/VisualStudioBuildScripts' into submodule path 'C:/source/github.com/github/VisualStudio/script' failed

That might be why I'm not able to see it crash. Do you know of another repository that exhibits the same problem that I can fully clone?

bording avatar Nov 21 '17 06:11 bording

@bording ah yes, sorry that submodule is private - it contains our private keys etc. The build instructions cover it, you need to do a git submodule deinit script.

grokys avatar Nov 21 '17 11:11 grokys

Regarding reproing the crash, are you running in Release mode? The crash doesn't happen in Debug.

grokys avatar Nov 21 '17 11:11 grokys

It'd be interesting to see what the C stack trace looks like, but I certainly suspect the repository is being disposed and thus freed while we're in the middle of trying to run the status. It might need a sizeable repository in order for it to take long enough for GC to take effect.

carlosmn avatar Nov 21 '17 11:11 carlosmn

@bording,

How many times do you have to run the program to trigger the crash? What framework are you using when you experience the crash?

Often I would run it once and it would crash within 10 iterations. Just now I struggled a bit to get the crash. It seems to crash pretty soon or not at all. Maybe try restarting the app a few times?

It did finally crash:

image

I've got a crash .dmp file from this if that would be useful?

jcansdale avatar Nov 21 '17 11:11 jcansdale

I'm definitely running in release mode.

I went ahead and re-cloned the repo and removed the script submodule, butI have not been able to see a single crash. I've run the app many times now, and it's working fine.

@grokys @jcansdale I'm assuming you've got the script module actually checked out when you're running into the problem? Have you tried it with a clone of the repo without the script submodule?

If the presence of script submodule isn't relevant to causing the crash, then there must be some other variable that isn't yet accounted for.

What OS are you running on? What version of the .NET Framework do you have installed, and which one are you targeting? Are running the app as 32 or 64 bit?

I'm not sure any of that would make a difference, but I'm trying to think of what else it could be.

@jcansdale The dmp file might be helpful, if you've got a way to share it.

bording avatar Nov 21 '17 17:11 bording

Here's a stack trace of one of the times it crashed. The top points to https://github.com/libgit2/libgit2/blob/a5cf255b471ad7113247d552d5695db0cb720882/src/attrcache.c#L182 which is a call to the inlined https://github.com/libgit2/libgit2/blob/a5cf255b471ad7113247d552d5695db0cb720882/src/attrcache.c#L27-L36 which just looks up the entry in a dictionary.

It's still trying to look up address 0x14 which keeps looking like we cleaned up the repo, or at least the cache midway and we're trying to access the struct at NULL.

The crash is reproducing by opening the Avalonia PR page in GHfVS and clicking around a few different ones.

>    ntdll.dll!77e0b2e3()    Unknown
     [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll]    
     git2-a5cf255.dll!attr_cache_lookup(git_attr_file * * out_file, git_attr_file_entry * * out_entry, git_repository * repo, git_attr_session * attr_session, git_attr_file_source source, const char * base, const char * filename) Line 182    C
     git2-a5cf255.dll!git_attr_cache__get(git_attr_file * * out, git_repository * repo, git_attr_session * attr_session, git_attr_file_source source, const char * base, const char * filename, int(__stdcall*)(git_repository *, git_attr_file *, const char *) parser) Line 215    C
     git2-a5cf255.dll!git_ignore__push_dir(git_ignores * ign, const char * dir) Line 352    C
     git2-a5cf255.dll!filesystem_iterator_frame_push_ignores(filesystem_iterator * iter, filesystem_iterator_entry * frame_entry, filesystem_iterator_frame * new_frame) Line 1170    C
     git2-a5cf255.dll!filesystem_iterator_frame_push(filesystem_iterator * iter, filesystem_iterator_entry * frame_entry) Line 1353    C
     git2-a5cf255.dll!filesystem_iterator_advance_into(const git_index_entry * * out, git_iterator * i) Line 1576    C
     git2-a5cf255.dll!handle_unmatched_new_item(git_diff_generated * diff, diff_in_progress * info) Line 1055    C
     git2-a5cf255.dll!git_diff__from_iterators(git_diff * * out, git_repository * repo, git_iterator * old_iter, git_iterator * new_iter, const git_diff_options * opts) Line 1235    C
     git2-a5cf255.dll!git_diff_index_to_workdir(git_diff * * out, git_repository * repo, git_index * index, const git_diff_options * opts) Line 1377    C
     git2-a5cf255.dll!git_status_list_new(git_status_list * * out, git_repository * repo, const git_status_options * opts) Line 346    C

carlosmn avatar Nov 21 '17 17:11 carlosmn

I've been following the trail for all those libgit2sharp crashes I'm experiencing (I'm trying to use it from inside Unity, with random crashes all the time, most notably on domain reload but also in random situtations), which led me here.

Has there ever been a consensus on what causes this? Is the workaround (two years later) to still use 0.23? The same is still happening on 0.26.1 with libgit2 2.0.298, and also latest preview versions.

hybridherbst avatar Oct 20 '19 13:10 hybridherbst

Iirc, it was caused by objects being collected before native methods return. This was counter intuative as a developer used to managed code. The fix was to always use using blocks for objects that are disposable (most libgit2sharp types).

I hope that helps. It was a while ago we last faced this issue.

jcansdale avatar Oct 20 '19 20:10 jcansdale

I was able to pinpoint a couple more cases where that happened and have something that feels "kinda stable" right now. Same as you, I went to "using() everywhere" (I was caching the repository object before).

Additionally, the bug that took me the longest to find was that inside the using(), I was using LINQ queries and forgot about deferred execution - when execution was actually happening at some random other point in time, the repository object was long gone and I got those native crashes. Forcing execution inside the using() block with ToList() solved that part of the issue.

hybridherbst avatar Oct 20 '19 21:10 hybridherbst

I was caching the repository object before

Yup, I think we were caching repository objects as well. It gets particularly confusing when Debug builds work, but Release builds don't!

jcansdale avatar Oct 21 '19 08:10 jcansdale

And stepping through with debugger also always works - seems to prevent the repository object GC as well...

hybridherbst avatar Oct 21 '19 10:10 hybridherbst

I've the same problem with 0.26 and 0.27 preview:

System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
Repeat 2 times:
--------------------------------
   at LibGit2Sharp.Core.NativeMethods.git_status_list_new(LibGit2Sharp.Core.git_status_list* ByRef, LibGit2Sharp.Core.git_repository*, LibGit2Sharp.Core.GitStatusOptions)
--------------------------------
   at LibGit2Sharp.Core.Proxy.git_status_list_new(LibGit2Sharp.Core.Handles.RepositoryHandle, LibGit2Sharp.Core.GitStatusOptions)
   at LibGit2Sharp.RepositoryStatus..ctor(LibGit2Sharp.Repository, LibGit2Sharp.StatusOptions)
   at LibGit2Sharp.Repository.RetrieveStatus(LibGit2Sharp.StatusOptions

Just seen https://github.com/libgit2/libgit2sharp/issues/1886 ... my issue also started after an update of VS2019 to 16.10.1. (This update also contained the latest .NET 5 SDK 5.0.7)

DaveSenn avatar Jun 09 '21 17:06 DaveSenn