-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix some Linux (and windows) Dac crashes when given reasonable inputs #124686
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
Changes from all commits
795b8a0
dff13a7
52544a8
aacec8e
d915be5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -120,7 +120,7 @@ BOOL DacValidateEEClass(PTR_EEClass pEEClass) | |||||||||||||||||
| // PREfix. | ||||||||||||||||||
| retval = FALSE; | ||||||||||||||||||
| } | ||||||||||||||||||
| else if (pEEClass != pMethodTable->GetClass()) | ||||||||||||||||||
| else if (pEEClass != pMethodTable->GetClassWithPossibleAV()) | ||||||||||||||||||
| { | ||||||||||||||||||
| retval = FALSE; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
@@ -698,7 +698,7 @@ ClrDataAccess::GetRegisterName(int regNum, unsigned int count, _Inout_updates_z_ | |||||||||||||||||
| return E_UNEXPECTED; | ||||||||||||||||||
|
|
||||||||||||||||||
| const WCHAR callerPrefix[] = W("caller."); | ||||||||||||||||||
| // Include null terminator in prefixLen/regLen because wcscpy_s will fail otherwise | ||||||||||||||||||
| // Include null terminator in prefixLen/regLen for the 'needed' count | ||||||||||||||||||
| unsigned int prefixLen = (unsigned int)ARRAY_SIZE(callerPrefix); | ||||||||||||||||||
| unsigned int regLen = (unsigned int)u16_strlen(regs[regNum]) + 1; | ||||||||||||||||||
| unsigned int needed = (callerFrame ? prefixLen - 1 : 0) + regLen; | ||||||||||||||||||
|
|
@@ -707,29 +707,31 @@ ClrDataAccess::GetRegisterName(int regNum, unsigned int count, _Inout_updates_z_ | |||||||||||||||||
|
|
||||||||||||||||||
| if (buffer) | ||||||||||||||||||
| { | ||||||||||||||||||
leculver marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
| if (count == 0) | ||||||||||||||||||
| return S_FALSE; | ||||||||||||||||||
|
|
||||||||||||||||||
| WCHAR* curr = buffer; | ||||||||||||||||||
| WCHAR* end = buffer + count; | ||||||||||||||||||
| unsigned int destSize = count; | ||||||||||||||||||
| if (curr < end && callerFrame) | ||||||||||||||||||
| { | ||||||||||||||||||
| unsigned int toCopy = prefixLen < destSize ? prefixLen : destSize; | ||||||||||||||||||
| wcscpy_s(curr, toCopy, callerPrefix); | ||||||||||||||||||
| // Point to null terminator | ||||||||||||||||||
| toCopy--; | ||||||||||||||||||
| unsigned int remaining = count; | ||||||||||||||||||
| if (callerFrame && remaining > 1) | ||||||||||||||||||
| { | ||||||||||||||||||
| unsigned int srcLen = prefixLen - 1; // exclude null | ||||||||||||||||||
| unsigned int toCopy = srcLen < (remaining - 1) ? srcLen : (remaining - 1); | ||||||||||||||||||
| memcpy(curr, callerPrefix, toCopy * sizeof(WCHAR)); | ||||||||||||||||||
| curr += toCopy; | ||||||||||||||||||
| destSize -= toCopy; | ||||||||||||||||||
| remaining -= toCopy; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| if (curr < end) | ||||||||||||||||||
| if (remaining > 1) | ||||||||||||||||||
| { | ||||||||||||||||||
| unsigned int toCopy = regLen < destSize ? regLen : destSize; | ||||||||||||||||||
| wcscpy_s(curr, toCopy, regs[regNum]); | ||||||||||||||||||
| // Point to null terminator | ||||||||||||||||||
| toCopy--; | ||||||||||||||||||
| unsigned int srcLen = regLen - 1; // exclude null | ||||||||||||||||||
| unsigned int toCopy = srcLen < (remaining - 1) ? srcLen : (remaining - 1); | ||||||||||||||||||
| memcpy(curr, regs[regNum], toCopy * sizeof(WCHAR)); | ||||||||||||||||||
| curr += toCopy; | ||||||||||||||||||
| destSize -= toCopy; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // Always null-terminate | ||||||||||||||||||
| *curr = W('\0'); | ||||||||||||||||||
|
|
||||||||||||||||||
| if (count < needed) | ||||||||||||||||||
| return S_FALSE; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
@@ -1707,7 +1709,7 @@ ClrDataAccess::GetMethodDescFromToken(CLRDATA_ADDRESS moduleAddr, mdToken token, | |||||||||||||||||
| HRESULT | ||||||||||||||||||
| ClrDataAccess::TraverseModuleMap(ModuleMapType mmt, CLRDATA_ADDRESS moduleAddr, MODULEMAPTRAVERSE pCallback, LPVOID token) | ||||||||||||||||||
| { | ||||||||||||||||||
| if (moduleAddr == 0) | ||||||||||||||||||
| if (moduleAddr == 0 || pCallback == NULL) | ||||||||||||||||||
| return E_INVALIDARG; | ||||||||||||||||||
|
|
||||||||||||||||||
| SOSDacEnter(); | ||||||||||||||||||
|
|
@@ -2057,13 +2059,18 @@ ClrDataAccess::GetMethodTableFieldData(CLRDATA_ADDRESS mt, struct DacpMethodTabl | |||||||||||||||||
| { | ||||||||||||||||||
| hr = E_INVALIDARG; | ||||||||||||||||||
| } | ||||||||||||||||||
| else if (pMT->GetClass() == NULL) | ||||||||||||||||||
| { | ||||||||||||||||||
| hr = E_INVALIDARG; | ||||||||||||||||||
| } | ||||||||||||||||||
|
Comment on lines
+2062
to
+2065
|
||||||||||||||||||
| else if (pMT->GetClass() == NULL) | |
| { | |
| hr = E_INVALIDARG; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the idea that every legacy DAC API that takes a MethodDesc pointer should validate it using DacValidateMD? If it is the case, there is a bunch more to fix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of special-casing -1 for this API only?
Copilot
AI
Feb 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The else block added in GetMethodsWithProfilerModifiedIL isn’t indented, which makes the control flow hard to follow and increases the risk of future edits accidentally changing which code is guarded by the pointer validation. Please reformat/indent the contents of the else block (and its closing brace) to match the file’s existing style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, I just discovered the same issue in my own testing. If you are fixing these shadowing bugs mind getting all of them at once? I believe there are three more in GetHandleEnumForTypes, GetHandleEnumForGC, and GetObjectStringData.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I missed those. Did you see them actually happen in practice, or just code review? I will add those to the PR, assuming this is one we will merge. I'll hold off on work until I know the ground rules.
Copilot
AI
Feb 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In GetObjectComWrappersData, now that the inner HRESULT hr shadowing was removed, the unconditional hr = S_FALSE; near the end of the method will force this API to always return S_FALSE even when rcw/mowList were successfully populated. Please remove that final overwrite (or gate it behind the “no data found” path) so callers can rely on S_OK when data is returned.
| hr = S_FALSE; |
Copilot
AI
Feb 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetThreadStaticBaseAddress dereferences pClass without checking for null. MethodTable::GetClassWithPossibleAV() can return NULL (e.g., if m_pEEClass is null), which would still crash here. Please add a null check and return an error HRESULT (or S_FALSE) instead of dereferencing.
| if (pClass->GetNumThreadStaticFields() == 0) | |
| { | |
| if (pClass == NULL) | |
| { | |
| hr = E_FAIL; | |
| } | |
| else if (pClass->GetNumThreadStaticFields() == 0) | |
| { |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -857,7 +857,17 @@ struct MSLAYOUT DacpGCInterestingInfoData | |
| ISOSDacInterface3 *psos3 = NULL; | ||
| if (SUCCEEDED(hr = sos->QueryInterface(__uuidof(ISOSDacInterface3), (void**) &psos3))) | ||
| { | ||
| hr = psos3->GetGCGlobalMechanisms(globalMechanisms); | ||
| // GetGCGlobalMechanisms writes MAX_GLOBAL_GC_MECHANISMS_COUNT entries (currently 6) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a better fix would be to enforce in the DAC that it never writes more than 6 entries. If MAX_GLOBAL_GC_MECHANISMS_COUNT is greater than 6 the API should return a failing HRESULT. If we ever need to support more than 6 we can make an API that properly handles dynamic sizing. Also heads up this is dead code in the runtime repo as far as I know. The header is used by some other libraries but I don't know of a code path that invokes this method. There is another copy of this header in the diagnostics repo where SOS is built and that one calls this code. |
||
| // into the provided buffer with no count parameter. Over-allocate so that a newer | ||
| // runtime writing more entries won't overflow our buffer. We only read back | ||
| // DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT elements into globalMechanisms. | ||
| size_t buffer[256] = {}; | ||
| hr = psos3->GetGCGlobalMechanisms(buffer); | ||
| if (SUCCEEDED(hr)) | ||
| { | ||
| for (int i = 0; i < DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++) | ||
| globalMechanisms[i] = buffer[i]; | ||
| } | ||
| psos3->Release(); | ||
| } | ||
| return hr; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no difference between GetClassWithPossibleAV and GetClass in DAC builds. This change is no-op
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I misunderstood that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted this part back but will wait for your reply on the bigger question below before continuing on this PR.