Skip to content

fix: close multipart file handles after mime-type validation - #1085

Open
stareezy-1 wants to merge 1 commit into
danielgtaylor:mainfrom
stareezy-1:fix/1077-mimetype-validator-close
Open

fix: close multipart file handles after mime-type validation#1085
stareezy-1 wants to merge 1 commit into
danielgtaylor:mainfrom
stareezy-1:fix/1077-mimetype-validator-close

Conversation

@stareezy-1

@stareezy-1 stareezy-1 commented Aug 5, 2026

Copy link
Copy Markdown

Summary

Fixes #1077

Problem

MimeTypeValidator.Validate opens the uploaded file to sniff its content type and never closes it. For any part above the adapter multipart memory threshold (8 KiB for humago), that handle is a real file on disk, so the descriptor stays held until the GC finalizer runs. readFile also opens a handle before validation and returns without closing it when validation fails.

multipart.Form.RemoveAll() only unlinks the temp file; it does not close handles returned by FileHeader.Open(). A service doing a lot of uploads can drift on file descriptors, and bursts can exhaust the descriptor limit before the finalizer runs.

Reproduction: upload a file larger than the adapter memory threshold with a mime type that fails validation in a loop; the process accumulates two open file descriptors per request.

Fix

  1. Validate: defer file.Close() immediately after the open check, covering the success, sniff-error, and invalid-mime-type paths.
  2. readFile: close its handle before returning the validation error (the success path transfers ownership to the caller).

Tests

Two regression tests added to formdata_internal_test.go, both driving a disk-backed part with the GC disabled and RLIMIT_NOFILE lowered to 80 so leaked descriptors are observable:

Test Verifies
TestMimeTypeValidatorClosesFile 200 validations leak no descriptors (fails without the fix with EMFILE after ~50 iterations)
TestReadFileClosesOnValidationError 200 rejected reads keep returning the mime-type error, not Failed to open file

All existing tests continue to pass (go test -race ./...).

MimeTypeValidator.Validate opened the uploaded file to sniff its content
type but never closed the handle. For parts stored on disk (above the
adapter memory threshold) each call leaked a file descriptor until the GC
finalizer ran, and readFile leaked its own handle on the validation-error
path. Close both handles so upload bursts do not exhaust the descriptor
limit.
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.

MimeTypeValidator.Validate never closes the file it opens

1 participant