dotnet / dotnet/runtime

.NET debugger passes invalid value for method parameter of specific value types during expression evaluation

Open
#123,165 3 comments 0 reactions 0 assignees View on GitHub
area-Diagnostics-coreclr
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Description

Let the value type `TestStruct` has 3 `byte` fields:

```C#
public record struct TestStruct(byte A, byte B, byte C)
{
public string Print(TestStruct other) => $"{this}/{other}";
public override string ToString() => $"{A}/{B}/{C}";
}
```

When you pass parameter to method `TestStruct.Print(TestStruct other)` while evaluating expressions using debugging features like Watch (Visual Studio), the debugger actually passes an invalid value to the method.

This bug depends on the layout of the value type;

Reproducible with:
- Value type with 5 `byte` fields
- Value type with 3 `short` fields
- Value type with 2 `short` fields and 1 enum field that have `short` as underlying type
- Value type with 1 `short` field and 3 `byte` fields

Not reproducible with:
- Value type with 4 `byte` fields
- Value type with 5 `short` fields
- Value type with 3 `int` fields
- Value type with 1 `int` field and 1 `byte` field

These bugs are reproducible on almost every debugging features, so I suspect this bug is from CLR debugging interface somewhere around `ICorDebugEval2`.

### Reproduction Steps

1. Create new C# Console Application
2. Paste the following code:

```C#
TestStruct v = default;
Console.WriteLine(v.Print(v));

public record struct TestStruct(byte A, byte B, byte C)
{
public string Print(TestStruct other) => $"{this}/{other}";
public override string ToString() => $"{A}/{B}/{C}";
}
```

3. Set breakpoint on line 2 (`Console.WriteLine(v.Print(v))`)
4. Run in Debug mode, Any CPU
5. Evaluate `v.Print(v)` using Watch (Visual Studio), Immediate Window (Visual Studio), or Debug Console (Visual Studio Code)
6. Confirm evaluation result is not `0/0/0/0/0/0`

### Expected behavior

Debugger should pass proper value.
`v.Print(v)` should return string `0/0/0/0/0/0`.

### Actual behavior

Debugger is not passing proper value.
`v.Print(v)` returns strings like `0/0/0/152/75/113`.

The return value varies between executions, but is consistent in a single breakpoint hit.

### Regression?

This issue is reproducible on almost every debugging features:
- Watch (Visual Studio)
- Immediate Window (Visual Studio)
- Debug Console (Visual Studio Code)
- Watch (dnSpy)
- Threads & Variables (Rider)

This issue is also reproducible on almost every .NET runtimes:
- .NET Framework 4.8
- .NET Core 6.0, 8.0, 9.0, 10.0

### Known Workarounds

1. Changing value type to reference type avoids the issue.
2. Changing layout of value type by adding extra fields to the value type may avoids the issue.

### Configuration

- Reproducible on almost every .NET runtime version
- OS: Windows 11 Pro 24H2, 26100.7462
- CPU: Intel(R) Core(TM) Ultra 7 265K

### Other information

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.