Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unhandled Exception: System.AccessViolationException in PeNet.Header.Resource.StringTable.GetValue #350

Open
VaronisContributor opened this issue Feb 4, 2025 · 4 comments
Labels

Comments

@VaronisContributor
Copy link

Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

   at System.Text.UnicodeEncoding.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, DecoderNLS baseDecoder)
   at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
   at PeNet.NET48_Helpers.GetString(Encoding encoding, Span`1 span)
   at PeNet.FileParser.BufferFile.ReadUnicodeString(Int64 offset)
   at PeNet.Header.Resource.StringTable.<>c__DisplayClass39_0.<GetValue>b__0(TString s)
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at PeNet.Header.Resource.StringTable.GetValue(String value)
   at my code I can't disclose...

The call is like this:

PeFile peFile;
if (PeFile.TryParse(assemblyPath, out peFile))
{
                    var stringTable = peFile?.Resources?.VsVersionInfo?.StringFileInfo?.StringTable;
                    if (stringTable != null && stringTable.Length > 0)
                    {
                        var stringTable0 = stringTable[0];
                        string result = stringTable0.LegalCopyright;
                        return result ?? string.Empty;
                    }
}

try-catch block doesn't work since it is a corrupted process state indicating exception.

The version is from nuget.org - https://www.nuget.org/packages/PeNet/4.1.1 + dependencies.

The code executed in mstest and called from an assembly compiled for 4.7.2

@secana
Copy link
Owner

secana commented Feb 5, 2025

Hi @VaronisContributor! Thanks for reporting the issue. Do you have a minimal example how to repoduce the bug?

@secana secana added the bug label Feb 5, 2025
@VaronisContributor
Copy link
Author

VaronisContributor commented Feb 6, 2025

Hi @secana, thank you for quick response.

We don't have a reproduction scenario, since it is uncertain how can this happen. Here is the most detailed description I can provide.
It reproduces on random files during long executions once per approximately 200k files. The files are different each time. I can't provide our private files, but it did reproduce at least once on publicly available binary (vcruntime140_1.dll):

2/6/2025 2:06:07 AM:Debug: Failed on vcruntime140_1.dll with exception.
	System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
	   at System.Text.ASCIIEncoding.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, DecoderNLS decoder)
	   at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
	   at PeNet.NET48_Helpers.GetString(Encoding encoding, Span`1 span)
	   at PeNet.FileParser.BufferFile.ReadAsciiString(Int64 offset)
	   at PeNet.HeaderParser.Pe.ImportedFunctionsParser.ParseTarget()
	   at PeNet.HeaderParser.SafeParser`1.GetParserTarget()

It also fails on different functions in the stack, here is another one from logs:

	System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
	   at System.Text.UnicodeEncoding.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, DecoderNLS baseDecoder)
	   at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
	   at PeNet.NET48_Helpers.GetString(Encoding encoding, Span`1 span)
	   at PeNet.FileParser.BufferFile.ReadUnicodeString(Int64 offset)
	   at PeNet.Header.Resource.VsVersionInfo.IsStringFileInfoFirstChild()
	   at PeNet.Header.Resource.VsVersionInfo.get_VarFileInfo()
	   at PeNet.Header.Resource.VsVersionInfo.get_StringFileInfo()

All our access to a single instance of PeFile is under lock on the same object.

        private PeFile PeFile { get; }
        private object PeMutex = new object();

In order to prevent lazy load of the properties, we added the following code in the Constructor:

            try
            {
                if (PeFile?.Resources?.VsVersionInfo?.StringFileInfo?.StringTable?.FirstOrDefault()?.LegalCopyright == null)
                {
                    Log.Trace($"No LegalCopyright for {FileName}");
                }
                if (PeFile?.Sha1 == null)
                {
                    Log.Trace($"No SHA1 for {FileName}");
                }
                if (PeFile?.Resources?.VsVersionInfo?.VsFixedFileInfo == null)
                {
                    Log.Trace($"No VsFixedFileInfo for {FileName}");
                }
                if (PeFile?.ImageNtHeaders?.FileHeader != null)
                {
                    Log.Trace($"No FileHeader for {FileName}");
                }
                if (PeFile?.ImageNtHeaders?.OptionalHeader?.DataDirectory != null)
                {
                    Log.Trace($"No DataDirectory for {FileName}");
                }
            }
            catch (Exception ex)
            {
                Log.Debug($"Unexpected exception for {FileName}", ex);
                throw;
            }

The above are all the properties we access in the member functions, and all the functions access it under lock like this:

            lock (PeMutex)
            {
                        //...
            }

There is a processing of multiple files in parallel in many tasks. The only concurrency possible (not intended, but still possible) is concurrent work with files. For instance, there is a non-zero possibility (not that I can tell of a specific scenario) that the file can be deleted at the same time we explore it with PeNetby a different process. But in such case I would obviously expect a regular exception instead of corrupted process state indicating exception.

Hope it will help.

@secana
Copy link
Owner

secana commented Feb 6, 2025

Thanks for the additional insights. I guess that will be hard one to debug. What kind of .NET version are you using on which OS? It seemt that the NET48_Helper is called, which should not be the case for any of the "new" .NET variants, e.g. .NET 6/7/8/9

@VaronisContributor
Copy link
Author

VaronisContributor commented Feb 9, 2025

Agent:

OS = Windows_NT
OSArchitecture = X64 
OSVersion = 10.0.17763

Project:
<TargetFramework>net472</TargetFramework>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants