Skip to content

Commit 809edd8

Browse files
authored
Fix recursive expansion of null pointers in EE (#1550)
This PR addresses the issue of pointers to null being expandable. Added a check to see if something is a null pointer if its a pointer type and has the value of 0. Added a test. Addresses #698
1 parent a1034b0 commit 809edd8

File tree

4 files changed

+79
-3
lines changed

4 files changed

+79
-3
lines changed

src/MIDebugEngine/AD7.Impl/AD7Property.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public DEBUG_PROPERTY_INFO ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS
7878

7979
if ((dwFields & enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ATTRIB) != 0)
8080
{
81-
if (variable.CountChildren != 0)
81+
if (variable.CountChildren != 0 && !variable.IsNullPointer())
8282
{
8383
propertyInfo.dwAttrib |= enum_DBG_ATTRIB_FLAGS.DBG_ATTRIB_OBJ_IS_EXPANDABLE;
8484
}
@@ -127,10 +127,10 @@ public DEBUG_PROPERTY_INFO ConstructDebugPropertyInfo(enum_DEBUGPROP_INFO_FLAGS
127127
}
128128
}
129129

130-
// If the debugger has asked for the property, or the property has children (meaning it is a pointer in the sample)
130+
// If the debugger has asked for the property, or the property has non-null children,
131131
// then set the pProperty field so the debugger can call back when the children are enumerated.
132132
if (((dwFields & enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP) != 0) ||
133-
(variable.CountChildren != 0))
133+
(variable.CountChildren != 0 && !variable.IsNullPointer()))
134134
{
135135
propertyInfo.pProperty = (IDebugProperty2)this;
136136
propertyInfo.dwFields |= enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_PROP;

src/MIDebugEngine/Engine.Impl/Variables.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ internal interface IVariableInformation : IDisposable
3636
string EvalDependentExpression(string expr);
3737
bool IsVisualized { get; }
3838
bool IsReadOnly();
39+
bool IsNullPointer();
3940
enum_DEBUGPROP_INFO_FLAGS PropertyInfoFlags { get; set; }
4041
bool IsPreformatted { get; set; }
4142
string Address();
@@ -117,6 +118,30 @@ private static bool IsPointer(string typeName)
117118
return typeName.Trim().EndsWith("*", StringComparison.Ordinal);
118119
}
119120

121+
public bool IsNullPointer()
122+
{
123+
if (string.IsNullOrEmpty(TypeName) || !IsPointer(TypeName) || string.IsNullOrEmpty(Value))
124+
{
125+
return false;
126+
}
127+
128+
string trimmed = Value.Trim();
129+
130+
// GDB may represent null as "0x0", "0x00", "0x0000000000000000", etc.
131+
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
132+
{
133+
return long.TryParse(trimmed.Substring(2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out long hexVal) && hexVal == 0;
134+
}
135+
136+
// Decimal zero
137+
if (long.TryParse(trimmed, NumberStyles.Integer, CultureInfo.InvariantCulture, out long decVal) && decVal == 0)
138+
{
139+
return true;
140+
}
141+
142+
return false;
143+
}
144+
120145
public string FullName() // Full expression used to re-compute the value
121146
{
122147
if (_fullname == null)

src/MIDebugEngine/Natvis.Impl/Natvis.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public SimpleWrapper(string name, AD7Engine engine, IVariableInformation underly
4545
public virtual bool IsVisualized { get { return Parent.IsVisualized; } }
4646
public virtual enum_DEBUGPROP_INFO_FLAGS PropertyInfoFlags { get; set; }
4747
public virtual bool IsReadOnly() => Parent.IsReadOnly();
48+
public bool IsNullPointer() => Parent.IsNullPointer();
4849

4950
public VariableInformation FindChildByName(string name) => Parent.FindChildByName(name);
5051
public string EvalDependentExpression(string expr) => Parent.EvalDependentExpression(expr);

test/CppTests/Tests/ExpressionTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,56 @@ public void SetExpressionOnVariable(ITestSettings settings)
516516

517517
#endregion
518518

519+
[Theory]
520+
[DependsOnTest(nameof(CompileKitchenSinkForExpressionTests))]
521+
[RequiresTestSettings]
522+
public void NullPointerNotExpandable(ITestSettings settings)
523+
{
524+
this.TestPurpose("Verify that a null pointer is not expandable in the variables view.");
525+
this.WriteSettings(settings);
526+
527+
IDebuggee debuggee = SinkHelper.Open(this, settings.CompilerSettings, DebuggeeMonikers.KitchenSink.Expression);
528+
529+
using (IDebuggerRunner runner = CreateDebugAdapterRunner(settings))
530+
{
531+
this.Comment("Configure launch.");
532+
runner.Launch(settings.DebuggerSettings, debuggee, "-fExpression");
533+
534+
this.Comment("Set a breakpoint after pStu is set to nullptr.");
535+
runner.SetBreakpoints(debuggee.Breakpoints(SinkHelper.Expression, 53));
536+
537+
this.Comment("To start debugging and break.");
538+
runner.Expects.StoppedEvent(StoppedReason.Breakpoint).AfterConfigurationDone();
539+
540+
using (IThreadInspector threadInspector = runner.GetThreadInspector())
541+
{
542+
IFrameInspector currentFrame = threadInspector.Stack.First();
543+
544+
this.Comment("Verify pStu is null.");
545+
currentFrame.AssertEvaluateAsNull("pStu", EvaluateContext.Watch);
546+
547+
this.Comment("Verify null pointer is not expandable.");
548+
IVariableInspector pStuVar = currentFrame.GetVariable("pStu");
549+
Assert.True(
550+
pStuVar.VariablesReference == null || pStuVar.VariablesReference == 0,
551+
string.Format(CultureInfo.InvariantCulture,
552+
"Expected null pointer 'pStu' to not be expandable, but VariablesReference was {0}",
553+
pStuVar.VariablesReference));
554+
555+
this.Comment("Verify non-null pointer student (on stack) is still expandable.");
556+
IVariableInspector studentVar = currentFrame.GetVariable("student");
557+
Assert.True(
558+
studentVar.VariablesReference != null && studentVar.VariablesReference > 0,
559+
"Expected non-null struct 'student' to be expandable.");
560+
}
561+
562+
this.Comment("Run to completion.");
563+
runner.Expects.TerminatedEvent().AfterContinue();
564+
565+
runner.DisconnectAndVerify();
566+
}
567+
}
568+
519569
#region Private methods
520570

521571
private static StackFrame[] GenerateFramesList(IDebuggerSettings debugger)

0 commit comments

Comments
 (0)