dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.98k stars 4.66k forks source link

Validating uint with Range causes OverflowException #47278

Open TjeuKayim opened 3 years ago

TjeuKayim commented 3 years ago

Describe the bug

The validation for a uint query parameter with RangeAttribute can cause System.OverflowException instead of responding with status code 400 Bad Request.

To Reproduce

Create a new ASP.NET 5 Web API project, add this method to the controller:

[HttpGet("/echo")]
public uint Echo([FromQuery, Range(0, 7)] uint a)
{
    return a;
}
dotnet run
curl https://localhost:5001/echo?a=4000000000

Exceptions

fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
      An unhandled exception has occurred while executing the request.
      System.OverflowException: Value was either too large or too small for an Int32.
         at System.Convert.ThrowInt32OverflowException()
         at System.UInt32.System.IConvertible.ToInt32(IFormatProvider provider)
         at System.Convert.ToInt32(Object value, IFormatProvider provider)
         at System.ComponentModel.DataAnnotations.RangeAttribute.<>c.<SetupConversion>b__29_0(Object v)
         at System.ComponentModel.DataAnnotations.RangeAttribute.IsValid(Object value)
         at System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object value, ValidationContext validationContext)
         at System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object value, ValidationContext validationContext)
         at Microsoft.AspNetCore.Mvc.DataAnnotations.DataAnnotationsModelValidator.Validate(ModelValidationContext validationContext)
         at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.ValidateNode()
         at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitSimpleType()
         at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(ModelMetadata& metadata, String& key, Object model)
         at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model)
         at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Validate(ModelMetadata metadata, String key, Object model, Boolean alwaysValidateAtTopLevel, Object container)
         at Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator.Validate(ActionContext actionContext, ValidationStateDictionary validationState, String prefix, Object model, ModelMetadata metadata, Object container)
         at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.EnforceBindRequiredAndValidate(ObjectModelValidator baseObjectValidator, ActionContext actionContext, ParameterDescriptor parameter, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult, Object container)
         at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
         at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()

Further technical details

dotnet --info
.NET SDK (reflecting any global.json):
 Version:   5.0.200-preview.20601.7
 Commit:    b3b934bbf2

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18363
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.200-preview.20601.7\

Host (useful for support):
  Version: 5.0.2
  Commit:  cb5f173b96
ghost commented 3 years ago

Tagging subscribers to this area: @ajcvickers See info in area-owners.md if you want to be subscribed.

Issue Details
### Describe the bug The validation for a `uint` query parameter with `RangeAttribute` can cause `System.OverflowException` instead of responding with status code 400 Bad Request. ### To Reproduce Create a new ASP.NET 5 Web API project, add this method to the controller: ```cs [HttpGet("/echo")] public uint Echo([FromQuery, Range(0, 7)] uint a) { return a; } ``` ``` dotnet run curl https://localhost:5001/echo?a=4000000000 ``` ### Exceptions ```log fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1] An unhandled exception has occurred while executing the request. System.OverflowException: Value was either too large or too small for an Int32. at System.Convert.ThrowInt32OverflowException() at System.UInt32.System.IConvertible.ToInt32(IFormatProvider provider) at System.Convert.ToInt32(Object value, IFormatProvider provider) at System.ComponentModel.DataAnnotations.RangeAttribute.<>c.b__29_0(Object v) at System.ComponentModel.DataAnnotations.RangeAttribute.IsValid(Object value) at System.ComponentModel.DataAnnotations.ValidationAttribute.IsValid(Object value, ValidationContext validationContext) at System.ComponentModel.DataAnnotations.ValidationAttribute.GetValidationResult(Object value, ValidationContext validationContext) at Microsoft.AspNetCore.Mvc.DataAnnotations.DataAnnotationsModelValidator.Validate(ModelValidationContext validationContext) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.ValidateNode() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitSimpleType() at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.VisitImplementation(ModelMetadata& metadata, String& key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Visit(ModelMetadata metadata, String key, Object model) at Microsoft.AspNetCore.Mvc.ModelBinding.Validation.ValidationVisitor.Validate(ModelMetadata metadata, String key, Object model, Boolean alwaysValidateAtTopLevel, Object container) at Microsoft.AspNetCore.Mvc.ModelBinding.ObjectModelValidator.Validate(ActionContext actionContext, ValidationStateDictionary validationState, String prefix, Object model, ModelMetadata metadata, Object container) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.EnforceBindRequiredAndValidate(ObjectModelValidator baseObjectValidator, ActionContext actionContext, ParameterDescriptor parameter, ModelMetadata metadata, ModelBindingContext modelBindingContext, ModelBindingResult modelBindingResult, Object container) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container) at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<g__Bind|0>d.MoveNext() ``` ### Further technical details - ASP.NET version 5 - Output of `dotnet --info` ```txt dotnet --info .NET SDK (reflecting any global.json): Version: 5.0.200-preview.20601.7 Commit: b3b934bbf2 Runtime Environment: OS Name: Windows OS Version: 10.0.18363 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\5.0.200-preview.20601.7\ Host (useful for support): Version: 5.0.2 Commit: cb5f173b96 ```
Author: TjeuKayim
Assignees: -
Labels: `area-System.ComponentModel.DataAnnotations`, `untriaged`
Milestone: -
stephentoub commented 3 years ago

This happens because Range(0, 7) is using the Range(int, int) constructor. As such, it's trying to convert the supplied uint value of 4,000,000,000 to an int, which results in an overflow. I don't know whether that's the desired behavior for RangeValidator.IsValid, but presumably it should just have another catch for OverflowException at: https://github.com/dotnet/runtime/blob/5761dd49339911241209c1ffd61105a94edb6df1/src/libraries/System.ComponentModel.Annotations/src/System/ComponentModel/DataAnnotations/RangeAttribute.cs#L124-L135

Regardless, as a workaround you should be able to use Range(typeof(uint), "0", "7") instead.

TjeuKayim commented 3 years ago

Thanks for the explanation!