Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix OverflowException in JsonSerializer.Serialize #1308

Merged
merged 8 commits into from
Feb 20, 2020

Conversation

felipepessoto
Copy link
Contributor

Fix #609

@felipepessoto felipepessoto changed the title [WIP] - Fix OverflowException [WIP] - Fix OverflowException in JsonSerializer.Serialize Jan 5, 2020
@felipepessoto felipepessoto changed the title [WIP] - Fix OverflowException in JsonSerializer.Serialize Fix OverflowException in JsonSerializer.Serialize Jan 8, 2020
@azure-pipelines
Copy link

Commenter does not have sufficient privileges for PR 1308 in repo dotnet/runtime

@danmoseley
Copy link
Member

/azp run

@azure-pipelines
Copy link

You have several pipelines (over 10) configured to build pull requests in this repository. Specify which pipelines you would like to run by using /azp run [pipelines] command. You can specify multiple pipelines using a comma separated list.

@stephentoub
Copy link
Member

@ahsonkhan, @steveharter, can you take a look as well? Thanks.

@ahsonkhan
Copy link
Contributor

Fix #609

We should consider making a similar fix to the public ArrayBufferWriter as well, based on where we land in this PR.

if (sizeHint > FreeCapacity)
{
int growBy = Math.Max(sizeHint, _buffer.Length);
if (_buffer.Length == 0)
{
growBy = Math.Max(growBy, DefaultInitialBufferSize);
}
int newSize = checked(_buffer.Length + growBy);
Array.Resize(ref _buffer, newSize);
}

@stephentoub
Copy link
Member

@felipepessoto, sorry for the delay. This looks good and ready to go, but there's a conflict in the tests now... can you rebase to address it?

@felipepessoto
Copy link
Contributor Author

@felipepessoto, sorry for the delay. This looks good and ready to go, but there's a conflict in the tests now... can you rebase to address it?

Yes sure. But I still have some pending things to do. I still didn't stop to check how can I restrict the test to 64bits, and I couldn't run the coverage test yet, last time it took hours and didn't finish

@stephentoub
Copy link
Member

I still didn't stop to check how can I restrict the test to 64bits

e.g.


public static bool IsX64 { get; } = IntPtr.Size >= 8;

@ahsonkhan
Copy link
Contributor

ahsonkhan commented Feb 14, 2020

I couldn't run the coverage test yet, last time it took hours and didn't finish

You are probably building using a debug coreclr which causes tests to take a long time to run. If you build coreclr for release and then build the libraries using the release coreclr, the tests should run faster.

I was able to run the coverage after merging master into your branch (by changing your test to be part of inner loop, removed outerloop from attribute):
From root of repo run the following:
build.cmd -subsetCategory coreclr -c Release && build.cmd -subsetCategory libraries /p:CoreCLRConfiguration=Release
Then navigate to src\libraries\System.Text.Json\tests and run:
dotnet msbuild /t:BuildAndTest /p:Coverage=true

See #839 (comment)

cc @safern, @ViktorHofer - do we need to increase the visibility of this issue so fewer folks hit against running tests with debug coreclr, by default?

Looks like all the new branches introduced in this PR are covered by the test :)
image

The path that is likely not covered (and this sequence of code flow won't show up in test coverage):
(uint)(currentLength + growBy) > int.MaxValue is true but (uint)(currentLength + sizeHint) > int.MaxValue is false

If you do run all outerloop tests, it will take a lot longer (~10 minutes for me, but shouldn't take hours):
dotnet msbuild /t:RebuildAndTest /p:Coverage=true /p:Outerloop=true

===========================================================================================================
    Discovering: System.Text.Json.Tests (method display = ClassAndMethod, method display options = None)
    Discovered:  System.Text.Json.Tests (found 1692 of 1703 test cases)
    Starting:    System.Text.Json.Tests (parallel test collections = on, max threads = 8)
    Finished:    System.Text.Json.Tests
  === TEST EXECUTION SUMMARY ===
     System.Text.Json.Tests  Total: 11482, Errors: 0, Failed: 0, Skipped: 0, Time: 365.595s

At first I tried to create five internal references to CustomClassToExceedMaxBufferSize and create 25 levels deep instances, but the tests ran forever. So I changed it to have several big string properties, which lead to the System.OverflowException using 3 level deep instance.
…s to use growBy = sizeHint

If it still overflow, we set the newSize to int.MaxValue, which will throw an OutOfMemoryException when calling ArrayPool.Rent.
OuterLoop
Make the test simpler
Run test on 64 bits only
@felipepessoto
Copy link
Contributor Author

I still didn't stop to check how can I restrict the test to 64bits

e.g.

public static bool IsX64 { get; } = IntPtr.Size >= 8;

Thx, done

Separate OuterLoop attribute, to be consistent with the other tests in this solution.
@ahsonkhan
Copy link
Contributor

@felipepessoto - are you up for making a similar fix to ArrayBufferWriter<T>, in a separate PR to fix/close the issue?

private void CheckAndResizeBuffer(int sizeHint)
{
if (sizeHint < 0)
throw new ArgumentException(nameof(sizeHint));
if (sizeHint == 0)
{
sizeHint = 1;
}
if (sizeHint > FreeCapacity)
{
int growBy = Math.Max(sizeHint, _buffer.Length);
if (_buffer.Length == 0)
{
growBy = Math.Max(growBy, DefaultInitialBufferSize);
}
int newSize = checked(_buffer.Length + growBy);
Array.Resize(ref _buffer, newSize);
}
Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint);
}

@felipepessoto
Copy link
Contributor Author

@felipepessoto - are you up for making a similar fix to ArrayBufferWriter<T>, in a separate PR to fix/close the issue?

private void CheckAndResizeBuffer(int sizeHint)
{
if (sizeHint < 0)
throw new ArgumentException(nameof(sizeHint));
if (sizeHint == 0)
{
sizeHint = 1;
}
if (sizeHint > FreeCapacity)
{
int growBy = Math.Max(sizeHint, _buffer.Length);
if (_buffer.Length == 0)
{
growBy = Math.Max(growBy, DefaultInitialBufferSize);
}
int newSize = checked(_buffer.Length + growBy);
Array.Resize(ref _buffer, newSize);
}
Debug.Assert(FreeCapacity > 0 && FreeCapacity >= sizeHint);
}

Absolutely

@ahsonkhan ahsonkhan merged commit b5ea894 into dotnet:master Feb 20, 2020
@felipepessoto felipepessoto deleted the fix-609 branch February 20, 2020 22:13
@layomia layomia added this to the 5.0 milestone May 15, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Dec 11, 2020
Sign up for free to 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.

Json Arithmetic operation resulted in an overflow
6 participants