Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint - #124294

Merged
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue
Feb 12, 2026
Merged

Fix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uint#124294
tannergooding merged 2 commits into
mainfrom
copilot/fix-nindex-to-string-issue

Conversation

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Description

NIndex.ToStringFromEnd() and NRange.ToString() cast nint values to uint before formatting, truncating values exceeding uint.MaxValue on 64-bit platforms:

varni=NIndex.FromEnd(unchecked((nint)(1L+uint.MaxValue)));Console.WriteLine(ni.ToString());// Prints "^0" instead of "^4294967296"

Fix

  • NIndex.cs: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs: (uint)Start.Value(nuint)Start.Value and (uint)End.Value(nuint)End.Value in ToString()

Tests

  • Replaced [Fact] ToStringTest with [Theory] + [InlineData] for both NIndexTests and NRangeTests
  • Added [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))] tests exercising values above uint.MaxValue

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@github-actionsgithub-actionsBot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Feb 11, 2026
…int, add Theory tests
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix NIndex formatting for end index conversionFix NIndex/NRange ToString truncation on 64-bit: cast to nuint instead of uintFeb 11, 2026
CopilotAI requested a review from vcsjonesFebruary 11, 2026 20:43
@vcsjonesvcsjones added area-System.Numerics.Tensors and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Feb 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics-tensors
See info in area-owners.md if you want to be subscribed.

@vcsjones
vcsjones marked this pull request as ready for review February 11, 2026 20:51
CopilotAI review requested due to automatic review settings February 11, 2026 20:51

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes incorrect string formatting for NIndex/NRange on 64-bit platforms where values larger than uint.MaxValue were being truncated due to casts to uint during formatting.

Changes:

  • Update NIndex.ToStringFromEnd() to format using (nuint)Value rather than (uint)Value.
  • Update NRange.ToString() to format start/end using (nuint)Start.Value / (nuint)End.Value rather than uint.
  • Expand/adjust unit tests to cover 64-bit values above uint.MaxValue, converting prior [Fact] coverage into [Theory] + [InlineData] and adding 64-bit conditional cases.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

FileDescription
src/libraries/System.Numerics.Tensors/src/System/Buffers/NIndex.csFixes 64-bit truncation in ToStringFromEnd() by formatting via nuint.
src/libraries/System.Numerics.Tensors/src/System/Buffers/NRange.csFixes 64-bit truncation in ToString() by formatting start/end via nuint.
src/libraries/System.Numerics.Tensors/tests/NIndexTests.csAdds theory-based coverage and conditional 64-bit test cases for large values.
src/libraries/System.Numerics.Tensors/tests/NRangeTests.csAdds theory-based coverage and conditional 64-bit test cases for large range endpoints.

@vcsjones

vcsjones commented Feb 11, 2026

Copy link
Copy Markdown
Member

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please invoke the code-review skill and post your analysis/results as a comment on this PR.

🤖 Copilot Code Review

Holistic Assessment

Motivation: The bug is real and clearly reproducible — (uint) casts truncate nint values exceeding uint.MaxValue on 64-bit platforms, causing NIndex.FromEnd(1L + uint.MaxValue).ToString() to return ^0 instead of ^4294967296. The buffer comment on line 177 ("20 for longest possible nuint value") confirms the original intent was nuint.

Approach: Changing (uint)(nuint) is the minimal correct fix. The tests use [Theory]/[InlineData] and [ConditionalTheory] with PlatformDetection.Is64BitProcess appropriately, consistent with patterns used in NFloatTests.cs and elsewhere in the test project.

Summary: ✅ LGTM. The three cast fixes are correct, minimal, and well-tested. All findings below are positive confirmations or out-of-scope observations.


Detailed Findings

✅ Correctness — Cast fix is correct in all three locations

  • NIndex.cs:178: (uint)Value(nuint)Value in ToStringFromEnd()
  • NRange.cs:73: (uint)Start.Value(nuint)Start.Value in ToString()
  • NRange.cs:84: (uint)End.Value(nuint)End.Value in ToString()

The Value property returns nint (always non-negative for valid NIndex values), so the nuint cast is safe. The stackalloc buffer was already sized for nuint (21 chars = 1 for ^ + 20 digits for max nuint on 64-bit). The non-FromEnd path (NIndex.ToString() line 172) uses Value.ToString() which calls nint.ToString() directly — no truncation issue there.

✅ Test quality — Good coverage with appropriate conditional attributes

The tests cover both fromStart and fromEnd paths, use boundary values (int.MaxValue, uint.MaxValue + 1, long.MaxValue), and correctly gate 64-bit-specific cases with [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.Is64BitProcess))]. PlatformDetection is already used in other tests in this project (TensorPrimitives.Generic.cs, TensorPrimitivesTests.cs).

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

NRange.cs:113 uses (uint)end > (uint)length || (uint)start > (uint)end — this truncates nint values on 64-bit, which could cause incorrect bounds validation for large collections. However, this is mirrored from Range.GetOffsetAndLength (which uses intuint, which is correct for that type) and is a separate issue from the ToString fix.

@tannergooding
tannergooding enabled auto-merge (squash) February 11, 2026 21:25
@tannergooding

Copy link
Copy Markdown
Member

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

CopilotAI commented Feb 11, 2026

Copy link
Copy Markdown
ContributorAuthor

💡 Out-of-scope observation — NRange.GetOffsetAndLength has a similar (uint) cast on nint

@copilot can you open a separate PR to address this.

I'm not able to open new PRs. Could you open an issue for tracking the (uint) truncation in NRange.GetOffsetAndLength (line 113) so it can be picked up separately? The fix there would be changing (uint) to (nuint) for the bounds checks on end, length, and start.

@vcsjones

Copy link
Copy Markdown
Member

I will open a separate PR for the offset / length issue.

@tannergooding
tannergooding merged commit de90299 into mainFeb 12, 2026
97 of 104 checks passed
@tannergooding

Copy link
Copy Markdown
Member

This one should be backported, yes?

@tannergooding

Copy link
Copy Markdown
Member

I'm fine with taking them all together, but I do think we should take them for bar check at least. -- CC. @jeffhandley

vcsjones added a commit that referenced this pull request Feb 24, 2026
…nt (#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…d of uint (dotnet#124294)
## Description
`NIndex.ToStringFromEnd()` and `NRange.ToString()` cast `nint` values to
`uint` before formatting, truncating values exceeding `uint.MaxValue` on
64-bit platforms:
```csharp
var ni = NIndex.FromEnd(unchecked((nint)(1L + uint.MaxValue)));
Console.WriteLine(ni.ToString()); // Prints "^0" instead of "^4294967296"
```
### Fix
- **NIndex.cs**: `(uint)Value` → `(nuint)Value` in `ToStringFromEnd()`
- **NRange.cs**: `(uint)Start.Value` → `(nuint)Start.Value` and
`(uint)End.Value` → `(nuint)End.Value` in `ToString()`
### Tests
- Replaced `[Fact] ToStringTest` with `[Theory]` + `[InlineData]` for
both `NIndexTests` and `NRangeTests`
- Added `[ConditionalTheory(typeof(PlatformDetection),
nameof(PlatformDetection.Is64BitProcess))]` tests exercising values
above `uint.MaxValue`
<!-- START COPILOT CODING AGENT TIPS -->
---
💬 We'd love your input! Share your thoughts on Copilot coding agent in
our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
iremyux pushed a commit to iremyux/dotnet-runtime that referenced this pull request Mar 2, 2026
…nt (dotnet#124300)
## Description
`NRange.GetOffsetAndLength` casts its `nint` parameters to `uint` for
range validation. On 64-bit platforms, this truncates values larger than
`uint.MaxValue`, allowing invalid ranges to pass validation silently.
```csharp
// Before: truncates upper 32 bits on 64-bit platforms
if ((uint)end > (uint)length || (uint)start > (uint)end)
// After: correct comparison for all nint values
if ((nuint)end > (nuint)length || (nuint)start > (nuint)end)
```
For example, `start = 0x1_0000_0001` and `end = 2` with `length =
0x1_0000_0003`: the `(uint)` cast truncates start to `1`, making `1 > 2`
false and incorrectly passing validation.
**Note:** Other `(uint)` conversion issues in `NRange` (e.g.,
`ToString`) are intentionally not addressed here — those are covered by
dotnet#124294.
### Tests
- Converted existing `GetOffsetAndLengthTest` `[Fact]` to `[Theory]`
with `[InlineData]`
- Split success and exception cases into separate theory methods
- Added `[ConditionalTheory(Is64BitProcess)]` tests exercising values
above `uint.MaxValue`, including a case that directly catches the
truncation bug
<!-- START COPILOT CODING AGENT TIPS -->
---
💡 You can make Copilot smarter by setting up custom instructions,
customizing its development environment and configuring Model Context
Protocol (MCP) servers. Learn more [Copilot coding agent
tips](https://gh.io/copilot-coding-agent-tips) in the docs.
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: vcsjones <361677+vcsjones@users.noreply.github.com>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Mar 15, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@vcsjones@tannergooding