MIEngine crashes if the debuggee stops at an internal GDB breakpoint
gareth-rees opened this issue · comments
Background
GDB uses negative breakpoint numbers to represent "internal" breakpoints. See the "Setting Breakpoints" documentation which says:
GDB itself sometimes sets breakpoints in your program for special purposes, such as proper handling of
longjmp
(in C programs). These internal breakpoints are assigned negative numbers, starting with -1;info breakpoints
does not display them. You can see these breakpoints with the GDB maintenance commandmaint info breakpoints
"Internal" breakpoints can also be created using GDB's Python breakpoints interface, for example:
python gdb.Breakpoint("function_name", internal=True)
Steps to reproduce the bug
-
Put the following test program in
test.c
and compile it usinggcc -g -o test test.c
(or otherwise) to make an executabletest
with debug information.#include <stdio.h> int main(void) { printf("Hello, world!\n"); return 0; }
-
Launch Visual Studio Code.
-
Install the C/C++ extension if not already installed.
-
Select Run ⟶ Add Configuration... and add a "(gdb) Launch" configuration. Edit the
"program"
key to refer to the compiled test and set"stopAtEntry": true
. -
Select View ⟶ Run and launch the new configuration.
-
In the DEBUG CONSOLE window, create an internal breakpoint by typing
`python gdb.Breakpoint(line=7, internal=True)
-
Press the Continue button.
-
We expect execution to stop at the internal breakpoint just created, but in fact the debugging session ends. The DEBUG CONSOLE shows the following text (see screenshot below).
Breakpoint -14, main () at test.c:7 7 return 0; ERROR: Error while trying to enter break state. Debugging will now stop. Internal error in MIEngine. Exception of type 'System.OverflowException' was thrown. at System.Number.ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type) at System.Convert.ToUInt32(String value, IFormatProvider provider) at Microsoft.MIDebugEngine.ExceptionManager.TryGetExceptionBreakpoint(String bkptno, UInt64 address, TupleValue frame, String& exceptionName, String& exceptionDescription, Guid& exceptionCategoryGuid) at Microsoft.MIDebugEngine.DebuggedProcess.HandleBreakModeEvent(ResultEventArgs results, BreakRequest breakRequest) at Microsoft.MIDebugEngine.DebuggedProcess.<.ctor>b__63_6(Object o, EventArgs args) The program '/home/grees/core/release-x64/examples/test' has exited with code 42 (0x0000002a).
Analysis
GDB represents a breakpoint number as an int
. See the definition of struct breakpoint
in breakpoint.h, which includes:
/* Number assigned to distinguish breakpoints. */
int number = 0;
This means that when the debuggee stops at an internal GDB breakpoint, the MIEngine function TryGetExceptionBreakpoint()
is called with an argument containing the string representation of a negative number, for example, bkptno = "-28"
as shown in the screenshot below, which is from a debugger attached to the OpenDebugAD7 instance from a reproducer like the one described above:
This string is passed to Convert.ToUint32()
which raises System.OverflowException
. This is caught by an exception handler in the DebuggedProcess
constructor (see screenshot below) that handles it by calling Terminate()
which terminates MIEngine.
To prevent MIEngine from crashing when the debuggee stops at an internal GDB breakpoint, MIEngine must represent breakpoint numbers use a data type that's compatible with GDB. Since GDB uses int
, it should be safe for MIEngine to use long
.
Version numbers
C/C++ extension: 1.12.4
Version: 1.73.0
Commit: 8fa188b2b301d36553cbc9ce1b0a146ccb93351f
Date: 2022-11-01T15:44:09.336Z
Electron: 19.0.17
Chromium: 102.0.5005.167
Node.js: 16.14.2
V8: 10.2.154.15-electron.0
OS: Linux x64 5.4.0-125-generic
Sandboxed: No