Repository navigation
Check in checked builds that GetEventSize covers every debugger IPC event - #135462
Open
pcshrosbree wants to merge 1 commit into
Open
pcshrosbree wants to merge 1 commit into
pcshrosbree wants to merge 1 commit into
Conversation
DbgTransportSession::GetEventSize maps each DB_IPCE_* event type to its wire size. A type missing from its switch falls to the default case, which asserts in checked builds but in release builds sends only the event header, so the receiver reads stale bytes in place of the payload. This has happened at least three times (dotnet#97476, dotnet#110122, dotnet#134714), and nothing checked the switch against dbgipceventtypes.h. Add a checked-build check, CheckGetEventSizeCoverage, that expands IPC_EVENT_TYPE1 and IPC_EVENT_TYPE2 from dbgipceventtypes.h (TYPE0 entries are range markers and are skipped) and asserts, naming the type, on any declared type that GetEventSize does not size. So that the probe does not trip GetEventSize's own assert, the switch moves into a non-asserting helper, TryGetEventAdditionalSize, which reads only the event type (plus StepData.rangeCount, zero in the probe event). GetEventSize calls the helper and keeps its signature, its assert and its result. The check runs once, from the runtime side's DbgTransportSession::Init, which Debugger::Startup reaches whenever the debugger transport is enabled. It also compiles on the debugger side, where nothing calls it. Eight declared types have no case today. The check lists them as deliberately unsized, and that list is the only place a type can be exempted: - never sent, no sender anywhere in the tree: UNHANDLED_EXCEPTION, BREAKPOINT_REMOVE_RESULT, LIST_THREADS, SUSPEND_THREAD, RESUME_THREAD and CONTINUE_EXCEPTION; - never sent, receiver only: METADATA_UPDATE, whose only sender was removed in dotnet#103921; - DEBUGGER_INVALID, the debugger side's sentinel value, not an event. A comment in dbgipceventtypes.h, beside the rules for adding an event, points at GetEventSize and the check. The check catches a type that is missing from the switch. It does not catch a case that is present but sized wrongly. Fixes dotnet#134715
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @steveisok, @tommcdon, @dotnet/dotnet-diag |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem:
DbgTransportSession::GetEventSizemaps eachDB_IPCE_*event type to its wire size, and each new event is added to its switch by hand. A type missing from the switch falls todefault:. That asserts in checked builds, but in release builds it silently sends only the event header; for an event with a payload, the receiver then reads stale bytes in its place. This has happened at least three times: #97476 (DISABLE_OPTS, after #97461), #110122 (SET_ENABLE_CUSTOM_NOTIFICATION_RESULT) and #134714 (the fourRESOLVE_UPDATE_METADATA_*events, fixed by #134717). Nothing checked the switch againstdbgipceventtypes.h, and no test callsGetEventSize.Change: a
_DEBUG-only startup check that every declared event type has a case inGetEventSize, as proposed in #134715.Details
GetEventSizemoves unchanged into a private helper,TryGetEventAdditionalSize, which returns false for an unknown type.GetEventSizekeeps its signature, callers and behaviour: it calls the helper, and for an unknown type it still logs, asserts and returns the header size._DEBUG-onlyCheckGetEventSizeCoverageexpandsIPC_EVENT_TYPE1andIPC_EVENT_TYPE2fromdbgipceventtypes.h, in the style ofDoCompileTimeCheckOnDbgIpcEventTypes. TYPE0 entries are range markers and are skipped. It probes the helper with a zeroedDebuggerIPCEventof each type (the helper reads onlytype, plusStepData.rangeCountforSTEP/STEP_RESULT, which is 0). On a type with no case it asserts with a message that names the type and says what to do, for example:DB_IPCE_RESOLVE_UPDATE_METADATA_1 has no case in DbgTransportSession::TryGetEventAdditionalSize, the switch behind GetEventSize. Add its size there, or list it as never sent in DbgTransportSession::CheckGetEventSizeCoverage.DbgTransportSession::Init, whichDebugger::Startupreaches whenever the debugger transport is enabled. It also compiles on the debugger side, where nothing calls it.dbgipceventtypes.h, after the rules for adding an event, points at the switch and the check.No new tests are added: the check runs in every checked CoreCLR process on Unix that has the debugger transport enabled, so the existing checked test runs exercise it.
Classification
Eight declared types have no case in
GetEventSizetoday. The check lists them as deliberately unsized, and that list is the only place a type can be exempted; removing a type from the list makes the check cover it.UNHANDLED_EXCEPTION,BREAKPOINT_REMOVE_RESULT,LIST_THREADS,SUSPEND_THREAD,RESUME_THREAD,CONTINUE_EXCEPTIONMETADATA_UPDATEdebug/di/process.cppstill has a receiver, but the only sender was removed in #103921.DEBUGGER_INVALIDdebug/di/process.cpp).If you prefer, any of the seven never-sent types can instead be sized in
GetEventSize, or have its declaration removed, here or in a follow-up.DEBUGGER_INVALIDstays because it is a live sentinel.Validation
On linux-x64:
./build.sh clr+libs -c Release -rc Checked(which builds bothlibcoreclr.soandlibmscordbi.so), then./src/tests/build.sh Checked generatelayoutonly, then a small IL program assembled withCore_Root/ilasmand run underCore_Root/corerun.DOTNET_EnableDiagnostics_Debugger=0: runs to completion; the check is not reached (confirmed with a planted build as well).Each plant below was applied alone, followed by a CoreCLR rebuild and the same run; after restoring, the tree was rebuilt and runs clean again.
case DB_IPCE_RESOLVE_UPDATE_METADATA_1:DB_IPCE_RESOLVE_UPDATE_METADATA_1case DB_IPCE_RESOLVE_UPDATE_METADATA_1_RESULT:DB_IPCE_RESOLVE_UPDATE_METADATA_1_RESULTcase DB_IPCE_RESOLVE_UPDATE_METADATA_2:DB_IPCE_RESOLVE_UPDATE_METADATA_2case DB_IPCE_RESOLVE_UPDATE_METADATA_2_RESULT:DB_IPCE_RESOLVE_UPDATE_METADATA_2_RESULTDB_IPCE_METADATA_UPDATEfrom the never-sent listDB_IPCE_METADATA_UPDATENot run: Windows, macOS, arm64 and the Debug configuration;
DOTNET_EnableDiagnostics=0. On WindowsFEATURE_DBGIPCis off, so the transport source is not compiled there.Limits
GetEventSize. It does not catch a case that is present but sized wrongly.DOTNET_EnableDiagnostics=0orDOTNET_EnableDiagnostics_Debugger=0), and only in checked and debug builds.Fixes #134715
Note
This change and description were prepared with AI assistance (Anthropic Claude and OpenAI Codex), under my direction. I reviewed them before posting.