dotnet / diagnostics

This repository contains the source code for various .NET Core runtime diagnostic tools and documents.
MIT License
1.18k stars 355 forks source link

_isVirtual has to be set as true in the context of DataTarget.LoadDump. #4796

Closed stjeong closed 2 months ago

stjeong commented 2 months ago

Description

At .\Microsoft.Diagnostics.Runtime\DataReaders\Core\CoreDumpReader.cs,

CreateModuleInfo always create PEModuleInfo instance with "virtualHint == false",

private ModuleInfo CreateModuleInfo(ElfLoadedImage image)
{
    using ElfFile? file = image.Open();

    // We suppress the warning because the function it wants us to use is not available on all ClrMD platforms

    // This substitution is for unloaded modules for which Linux appends " (deleted)" to the module name.
    string path = image.FileName.Replace(" (deleted)", "");
    if (file is not null)
    {
        long size = image.Size > long.MaxValue ? long.MaxValue : unchecked((long)image.Size);
        return new ElfModuleInfo(this, file, image.BaseAddress, size, path);
    }

    return new PEModuleInfo(this, image.BaseAddress, path, false);
}

When dll image in memory dump file has 'export data directory', this code lead to almost infinite loop because of invalid ImageExportDirectory instance given by TryGetExportSymbol method.

// .\Microsoft.Diagnostics.Runtime\Utilities\PEImage\PEImage.cs
public bool TryGetExportSymbol(string symbolName, out ulong offset)
{
    int nameIndex = 0;
    try
    {
        ImageDataDirectory exportTableDirectory = ExportDirectory;
        if (exportTableDirectory.VirtualAddress != 0 && exportTableDirectory.Size != 0)
        {
            if (TryRead(RvaToOffset(exportTableDirectory.VirtualAddress), out ImageExportDirectory exportDirectory))

     // ...omitted for brevity...

At the code of "RvaToOffset" method,

public int RvaToOffset(int virtualAddress)
{
    if (virtualAddress < 4096)
        return virtualAddress;

    if (_isVirtual)
        return virtualAddress;

    ImageSectionHeader[] sections = ReadSections();
    for (int i = 0; i < sections.Length; i++)
    {
        ref ImageSectionHeader section = ref sections[i];
        if (section.VirtualAddress <= virtualAddress && virtualAddress < section.VirtualAddress + section.VirtualSize)
            return (int)(section.PointerToRawData + ((uint)virtualAddress - section.VirtualAddress));
    }

    return -1;
}

Because _isVirtual is false, it doesn't run second if's "return virtualAddress", instead it calculates the value as RVA for PE offset, like loading from FILE directly.

Of course, the return value is incorrect address, the caller(TryGetExportSymbol) fills exportDirectory with garbage.

Other information

This problem is not a problem when loading memory dump for .NET 6 or later, because most .NET 6 DLL (for example: System.Private.CoreLib.dll) has no export data directory.

However, if the memory dump contains user DLL with export data directory, DataTarget.LoadDump runs a lot of loop (because loop count is filled from garbage). And for .NET 5 memory dump, you can meet the problem, because System.Private.CoreLib.dll has export data directory, whose size is 43.

mikem8361 commented 2 months ago

This should be in the CLRMD repo here: https://github.com/microsoft/clrmd/issues. I can't transfer it.

stjeong commented 2 months ago

I'm sorry... I visited some github repo, so I confused. I moved this issue to clrmd.