Skip to content

Clean up DeadlineMiddleware/Interceptor edge cases#4547

Merged
pepone merged 7 commits intoicerpc:mainfrom
pepone:fix-deadline-minvalue-sentinel
Apr 30, 2026
Merged

Clean up DeadlineMiddleware/Interceptor edge cases#4547
pepone merged 7 commits intoicerpc:mainfrom
pepone:fix-deadline-minvalue-sentinel

Conversation

@pepone
Copy link
Copy Markdown
Member

@pepone pepone commented Apr 23, 2026

Two related edge-case fixes on the deadline middleware/interceptor. Kept in one PR because they touch the same files and share the same theme.

Commit 1 — DateTime.MinValue sentinel (closes #4419)

DeadlineMiddleware used the decoded DateTime.MinValue as its "field absent" sentinel. A peer encoding `ticks=0` decodes to exactly that value, so an explicit MinValue deadline was indistinguishable from an absent field and bypassed deadline enforcement entirely.

Switched to explicit `TryGetValue` on the fields dictionary, then decode + enforce. Added a regression test encoding `new DateTime(0, DateTimeKind.Utc)`.

Commit 2 — extreme future deadline clamp (closes #4420)

`CancellationTokenSource.CancelAfter(TimeSpan)` rejects values greater than `int.MaxValue` ms (~24.8 days). A peer-encoded deadline thousands of years in the future (or a caller-supplied `IDeadlineFeature` near `DateTime.MaxValue`) produced a TimeSpan that `CancelAfter` rejected with `ArgumentOutOfRangeException`, leaking as a generic `InternalError` response.

  • DeadlineMiddleware: clamp the computed timeout before PerformDispatchAsync.
  • DeadlineInterceptor: clamp the timeout in PerformInvokeAsync; reject configured defaultTimeout beyond the CancelAfter max at construction.

At the clamp bound (~24.8 days) the deadline is effectively infinite for RPC purposes; matches the precedent set by HttpClient.Timeout.

Closes #4419
Closes #4420

Test plan

  • dotnet build src/IceRpc.Deadline/IceRpc.Deadline.csproj (clean)
  • dotnet test tests/IceRpc.Deadline.Tests/IceRpc.Deadline.Tests.csproj — 17/17 passed
  • All four new tests fail when the production fixes are stashed (verified before/after)

DeadlineMiddleware used the decoded DateTime.MinValue as its "field
absent" sentinel. A peer encoding ticks=0 decodes to exactly that value,
so an explicitly encoded MinValue deadline was indistinguishable from an
absent field and bypassed deadline enforcement entirely.

Check field presence explicitly via TryGetValue, then decode and
enforce. Add a regression test encoding ticks=0 (DateTime(0, Utc) so
ToUniversalTime doesn't shift it) and assert the middleware returns
DeadlineExceeded and does not call the next dispatcher.

Closes icerpc#4419
Copilot AI review requested due to automatic review settings April 23, 2026 15:24
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes deadline enforcement in IceRpc.Deadline by ensuring an explicitly encoded ticks=0 (which decodes to DateTime.MinValue) is treated as an expired deadline instead of being mistaken for an absent field.

Changes:

  • Switched DeadlineMiddleware from a decoded-value sentinel check (DateTime.MinValue) to an explicit TryGetValue presence check before decoding/enforcing the deadline.
  • Added a regression test covering an explicitly encoded ticks=0 deadline and asserting it returns DeadlineExceeded without invoking the next dispatcher.

Reviewed changes

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

File Description
tests/IceRpc.Deadline.Tests/DeadlineMiddlewareTests.cs Adds a regression test for an explicitly encoded DateTime.MinValue (ticks=0) deadline to ensure it is enforced as expired.
src/IceRpc.Deadline/DeadlineMiddleware.cs Fixes the sentinel collision by checking field presence with TryGetValue and decoding/enforcing only when present.

CancellationTokenSource.CancelAfter(TimeSpan) rejects values greater
than int.MaxValue milliseconds (~24.8 days). A peer-encoded deadline
thousands of years in the future (or a caller-supplied IDeadlineFeature
near DateTime.MaxValue) produced a TimeSpan that CancelAfter rejected
with ArgumentOutOfRangeException, leaking as a generic InternalError
response on the server side.

- DeadlineMiddleware: clamp the computed timeout before PerformDispatchAsync.
- DeadlineInterceptor: clamp the timeout in PerformInvokeAsync for the
  peer-deadline-via-feature path, and reject a configured defaultTimeout
  greater than the CancelAfter maximum at construction time.

At the clamp bound (~24.8 days) the deadline is effectively infinite
for RPC purposes; matches the precedent set by HttpClient.Timeout.

Closes icerpc#4420
@pepone pepone changed the title Stop treating DateTime.MinValue as the absent deadline sentinel Clean up DeadlineMiddleware/Interceptor edge cases Apr 23, 2026
pepone added 2 commits April 23, 2026 18:06
Add the same CancelAfter-maximum cap to DeadlineFeature.FromTimeout so
that the failure surfaces at the call site instead of inside the
interceptor or as a later DateTime overflow. Also reject non-positive
timeouts here, matching the DeadlineInterceptor constructor's
validation of its defaultTimeout.

Document the cap on:
- DeadlineFeature.FromTimeout (param docs + ArgumentException).
- DeadlineInterceptor constructor's defaultTimeout param (+ class-level
  remark about silent clamping for extreme IDeadlineFeature values).
- DeadlineMiddleware class remarks (silent clamping of peer deadlines).
Copy link
Copy Markdown
Member

@externl externl left a comment

Choose a reason for hiding this comment

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

Looks good!

- Split the InfiniteTimeSpan exception out of the constraint clause in
  DeadlineInterceptor's defaultTimeout doc so it reads behavior →
  constraint → explicit exception.
- Assert the next dispatcher is invoked in the middleware extreme-future
  clamp test so a regression that swallowed the request would be caught.
Copy link
Copy Markdown
Member

@InsertCreativityHere InsertCreativityHere left a comment

Choose a reason for hiding this comment

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

Looks good to me!
I think the added comments could have some fluff pruned out of them though. : v)

Comment thread src/IceRpc.Deadline/DeadlineMiddleware.cs Outdated
Comment thread src/IceRpc.Deadline/DeadlineMiddleware.cs Outdated
Comment thread src/IceRpc.Deadline/DeadlineInterceptor.cs Outdated
Comment thread src/IceRpc.Deadline/DeadlineInterceptor.cs Outdated
Comment thread src/IceRpc.Deadline/DeadlineInterceptor.cs Outdated
pepone and others added 2 commits April 30, 2026 10:58
Co-authored-by: Austin Henriksen <austin.r.henriksen79@gmail.com>
@pepone pepone merged commit 2069157 into icerpc:main Apr 30, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Audit-Medium] extreme future deadlines and timeouts can leak raw Arg… [Audit-Low] DateTime.MinValue is both the "field missing" sentinel an…

4 participants