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

Multipart handling errors #6705

Closed
morki opened this issue Jan 3, 2022 · 5 comments · Fixed by #6764
Closed

Multipart handling errors #6705

morki opened this issue Jan 3, 2022 · 5 comments · Fixed by #6764
Assignees
Labels
type: bug Something isn't working
Milestone

Comments

@morki
Copy link
Contributor

morki commented Jan 3, 2022

Expected Behavior

No error are present when using multipart.disk = true and multipart.mixed = true.

Actual Behaviour

Errors presented below are present.

Steps To Reproduce

There are 2 described issues:

On route / there is login form for micronaut security login endpoint. You can write anything in those fields and click submit. After that, error Internal Server Error: refCnt: 0, decrement: 1 is thrown. But when you remove configuration mixed: true and disk: true, everything is running fine.

On route /upload there is multipart form with file selection. Please select file big.txt contained in the root of this repository and click submit. Request in browser will hang. But, when you stop the server, the Entity too large response is sent.

Environment Information

No response

Example Application

https://github.com/morki/micronaut-multipart-issue

Version

3.2.3

@yawkat
Copy link
Member

yawkat commented Jan 3, 2022

thanks

yawkat added a commit that referenced this issue Jan 3, 2022
Attributes returned by `postRequestDecoder.next()` are released twice when the decoder is destroyed:
- Once because it's stored in the `bodyListHttpData` list
- A second time because the `HttpDataFactory` keeps a copy around.
This means that even if user code retains the attribute in question once, it may still be collected.

In the test case, this issue is visible because the attribute is only parsed to a map later downstream in an IO thread, when the `FormDataHttpContentProcessor` has already completed.

This patch uses `removeHttpDataFromClean` to avoid the duplicate release for HTTP data where it could happen. `removeHttpDataFromClean` is a no-op for data that isn't created by the `HttpDataFactory`, so this patch doesn't break in those cases.

This patch risks exposing downstream buffer leaks: If the user code doesn't properly release attributes it retains, they would previously still have been collected because of this duplicate release. After this change, they may leak, potentially even leaving files on disk. However, imo this patch implements the "right" behavior, and we should deal with other issues as they appear.

I've done some testing, but it's inconclusive. I ran `./gradlew check` with and without this patch, and in both cases, attribute files were retained in `/tmp`. Can't tell whether this patch introduced new leaks.

Addresses #6705
@yawkat
Copy link
Member

yawkat commented Jan 3, 2022

fyi multipart.mixed does nothing when multipart.disk is also set. You should probably only use the former

@yawkat yawkat added the type: bug Something isn't working label Jan 3, 2022
@yawkat
Copy link
Member

yawkat commented Jan 3, 2022

i think the hang is fixed by #6696, i will check once that is merged

jameskleeh pushed a commit that referenced this issue Jan 3, 2022
* Fix double release of form attributes
Attributes returned by `postRequestDecoder.next()` are released twice when the decoder is destroyed:
- Once because it's stored in the `bodyListHttpData` list
- A second time because the `HttpDataFactory` keeps a copy around.
This means that even if user code retains the attribute in question once, it may still be collected.

In the test case, this issue is visible because the attribute is only parsed to a map later downstream in an IO thread, when the `FormDataHttpContentProcessor` has already completed.

This patch uses `removeHttpDataFromClean` to avoid the duplicate release for HTTP data where it could happen. `removeHttpDataFromClean` is a no-op for data that isn't created by the `HttpDataFactory`, so this patch doesn't break in those cases.

This patch risks exposing downstream buffer leaks: If the user code doesn't properly release attributes it retains, they would previously still have been collected because of this duplicate release. After this change, they may leak, potentially even leaving files on disk. However, imo this patch implements the "right" behavior, and we should deal with other issues as they appear.

I've done some testing, but it's inconclusive. I ran `./gradlew check` with and without this patch, and in both cases, attribute files were retained in `/tmp`. Can't tell whether this patch introduced new leaks.

Addresses #6705

* not PendingFeature
@graemerocher graemerocher added this to the 3.2.4 milestone Jan 3, 2022
@morki
Copy link
Contributor Author

morki commented Jan 3, 2022

Thank you very much @yawkat, it seems to be fixed :)

@morki morki closed this as completed Jan 3, 2022
@morki
Copy link
Contributor Author

morki commented Jan 4, 2022

Ok, so it only resolved the issue with multipart.disk but multipart.mixed now throws this on login endpoint:

image

@morki morki reopened this Jan 4, 2022
@graemerocher graemerocher modified the milestones: 3.2.4, 3.2.6 Jan 12, 2022
@graemerocher graemerocher added the status: pr submitted A pull request has been submitted for the issue label Jan 13, 2022
@yawkat yawkat removed the status: pr submitted A pull request has been submitted for the issue label Jan 14, 2022
yawkat added a commit that referenced this issue Jan 17, 2022
`NettyHttpRequest.buildBody` distinguishes between requests with "received data" and "received content", where the former only applies when `AbstractHttpData` instances are present. `AbstractHttpData` is a base class of both `DiskAttribute` and `MemoryAttribute`, but not of `MixedAttribute`. This makes `buildBody` use the "content" path for mixed requests.

This change adds an exception for `MixedAttribute` so that it is also covered by the "data" path.

Fixes #6705
@jameskleeh jameskleeh modified the milestones: 3.2.6, 3.2.7 Jan 18, 2022
jameskleeh pushed a commit that referenced this issue Jan 18, 2022
`NettyHttpRequest.buildBody` distinguishes between requests with "received data" and "received content", where the former only applies when `AbstractHttpData` instances are present. `AbstractHttpData` is a base class of both `DiskAttribute` and `MemoryAttribute`, but not of `MixedAttribute`. This makes `buildBody` use the "content" path for mixed requests.

This change adds an exception for `MixedAttribute` so that it is also covered by the "data" path.

Fixes #6705
yawkat added a commit that referenced this issue Jan 21, 2022
`NettyHttpRequest.buildBody` distinguishes between requests with "received data" and "received content", where the former only applies when `AbstractHttpData` instances are present. `AbstractHttpData` is a base class of both `DiskAttribute` and `MemoryAttribute`, but not of `MixedAttribute`. This makes `buildBody` use the "content" path for mixed requests.

This change adds an exception for `MixedAttribute` so that it is also covered by the "data" path.

Fixes #6705
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants