Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Fix bad usage of ArrayPool in TdsParserStateObject #37270

Merged
merged 1 commit into from
Apr 30, 2019

Conversation

bartonjs
Copy link
Member

If TdsParserStateObject.TryReadString or
TdsParserStateObject.TryReadStringWithEncoding hit the growth case they
would rent an array, save it into a field, use the array, return the array to the pool,
but keep it assigned to the field and continue using it.

Since other writes to _bTmp use fresh arrays in an instance-cached-growth
pattern, this change restores these two methods to that same approach, rather
than renting to a local buffer and not renting into a field.

If TdsParserStateObject.TryReadString or
TdsParserStateObject.TryReadStringWithEncoding hit the growth case they
would rent an array, save it into a field, use the array, return the array to the pool,
but keep it assigned to the field and continue using it.

Since other writes to _bTmp use fresh arrays in an instance-cached-growth
pattern, this change restores these two methods to that same approach, rather
than renting to a local buffer and not renting into a field.
@bartonjs bartonjs added this to the 3.0 milestone Apr 29, 2019
@bartonjs bartonjs self-assigned this Apr 29, 2019
@Wraith2
Copy link
Contributor

Wraith2 commented Apr 29, 2019

I'd noticed that I've introduced this problem and have a local branch to fiddle with a fix for it. I don't think the _bTmp variable needs to be used here at all since the entire contents are converted to a string at the end of the method.

@bartonjs
Copy link
Member Author

I don't think the _bTmp variable needs to be used here at all

Yeah, it does seem like it could be something like

buf = _bTmp;

if (buf is not big enough)
{
    buf = rentedBuf = Rent(big enough);
}

do stuff

if (rentedBuf != null)
{
    Return(rentedBuf);
}

That seems like a reasonable alternative to pursue, but the short term goal is to just eliminate where the array is being used after being returned to the pool.

@Wraith2
Copy link
Contributor

Wraith2 commented Apr 29, 2019

Sure, I've no argument against the revert for clearly broken behaviour, I should have been more careful.

I've been considering trying to remove that temp buffer entirely since it's never cleared and there's no telling what might get left in there, That's a future PR though.

I am curious how you found it though. I'm guessing you've got an analyser for arraypool usage so you can audit the crypto stuff?

@bartonjs
Copy link
Member Author

I am curious how you found it though. I'm guessing you've got an analyser for arraypool usage so you can audit the crypto stuff?

Exhaustive iterative analysis utilizing a speculative prediction via moderately trained wetware.

(I looked at literally everything that used ArrayPool to see what sorts of things might go wrong 😄)

@Wraith2
Copy link
Contributor

Wraith2 commented Apr 29, 2019

AKA the hard way 😁 . Well thanks for finding and fixing.

@danmoseley
Copy link
Member

would this help? https://github.com/dotnet/coreclr/issues/9885

@Wraith2
Copy link
Contributor

Wraith2 commented Apr 29, 2019

would this help? dotnet/coreclr#9885

Perhaps but only if we can catch the error during testing. Looking at the code the problem is reasonably easy to spot which is why I thought there might be an analyser. I think it should be quite rare to correctly assign a rented buffer to an instance variable which is what goes wrong here.

I've writtenref struct StackArrayPoolRental<T> and class HeapArrayPoolRental<T> disposable classes to help me not make mistakes with rentals elsewhere, I'm assuming there was some reason for this sort of thing not to be in the BCL.

@stephentoub stephentoub merged commit e89d17a into dotnet:master Apr 30, 2019
@divega
Copy link

divega commented Apr 30, 2019

I've writtenref struct StackArrayPoolRental and class HeapArrayPoolRental disposable classes to help me not make mistakes with rentals elsewhere, I'm assuming there was some reason for this sort of thing not to be in the BCL.

@Wraith2 you may want to raise this question in a new issue.

@bartonjs bartonjs deleted the fix_tdsparser_pooling branch May 1, 2019 22:28
David-Engel pushed a commit to dotnet/SqlClient that referenced this pull request Aug 30, 2019
yukiwongky pushed a commit to yukiwongky/SqlClient that referenced this pull request Nov 5, 2019
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
If TdsParserStateObject.TryReadString or
TdsParserStateObject.TryReadStringWithEncoding hit the growth case they
would rent an array, save it into a field, use the array, return the array to the pool,
but keep it assigned to the field and continue using it.

Since other writes to _bTmp use fresh arrays in an instance-cached-growth
pattern, this change restores these two methods to that same approach, rather
than renting to a local buffer and not renting into a field.

Commit migrated from dotnet/corefx@e89d17a
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.

5 participants