Skip to content

MultipartDeserialize: guard against nil part on malformed body#69

Merged
Alonza0314 merged 2 commits intofree5gc:mainfrom
SAY-5:fix/multipart-deserialize-nil-part-1026
Apr 27, 2026
Merged

MultipartDeserialize: guard against nil part on malformed body#69
Alonza0314 merged 2 commits intofree5gc:mainfrom
SAY-5:fix/multipart-deserialize-nil-part-1026

Conversation

@SAY-5
Copy link
Copy Markdown
Contributor

@SAY-5 SAY-5 commented Apr 21, 2026

Summary

mime/multipart.Reader.NextPart can return a nil *Part alongside a non-EOF error when the body declares multipart/related but is not valid MIME multipart data — plain text, JSON, empty body, truncated part, or a mismatched boundary. The previous MultipartDeserialize loop treated any non-EOF error from NextPart as a silent "break and fall through", then dereferenced part.Header.Get("Content-Type") on line 611 and panicked.

Every NF using this helper (SMF on POST /nsmf-pdusession/v1/sm-contexts, UDM, AUSF, ...) returned HTTP 500 on a single crafted request, and Gin's recovery middleware was the only thing preventing the whole process from dying.

Fix

  • Rewrite the NextPart branch so EOF breaks cleanly, any other error is wrapped and returned, and an unexpected nil part is rejected explicitly.
  • Fix the inverted check on part.Read: the old code returned when err == nil, which dropped every successfully read part; replace with err != nil && err != io.EOF so only real failures abort the loop.
  • Add TestMultipartDeserialize_MalformedBody covering plain-text / empty / JSON bodies. Pre-fix the plain-text case panicked on part.Header.Get; post-fix all three return an error with no panic.

Test

go test ./... -count=1   # all packages pass
go vet ./...             # clean
gofmt                    # clean

Fixes free5gc/free5gc#1026.

Signed-off-by: SAY-5 SAY-5@users.noreply.github.com

mime/multipart.Reader.NextPart can return a nil *Part alongside a
non-EOF error when the body is not valid MIME multipart data (plain
text, JSON, truncated multipart, mismatched boundary, empty body, ...).
The previous loop treated any non-EOF error as "just break" and then
dereferenced part.Header, so every NF that calls MultipartDeserialize
(SMF on POST /nsmf-pdusession/v1/sm-contexts, UDM, AUSF) panicked on a
crafted request and returned HTTP 500.

Treat non-EOF errors from NextPart and non-EOF errors from part.Read as
real failures and return them wrapped. Also fix the inverted check on
part.Read: the code was returning from the loop when err == nil, which
dropped every successfully read part; swap to the correct `err != nil`
branch (ignoring io.EOF, which just marks end-of-part).

Add a regression test that feeds three malformed bodies (plain text,
empty, JSON) through MultipartDeserialize and asserts no panic plus an
error. Pre-fix the plain-text case panicked on part.Header.Get.

Fixes free5gc/free5gc#1026.

Signed-off-by: SAY-5 <SAY-5@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hardens multipart/related deserialization to avoid panics and correctly surface errors when the request body is malformed (e.g., declared multipart but actually plain text/JSON/empty), addressing free5gc/free5gc#1026.

Changes:

  • Update MultipartDeserialize to handle NextPart() EOF vs non-EOF errors correctly and explicitly reject unexpected nil parts.
  • Fix inverted part.Read error handling so successful reads don’t abort the loop.
  • Add a regression test ensuring malformed bodies return an error without panicking.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
client.go Improves multipart parsing error handling to prevent nil deref panics and propagate malformed-body errors.
client_test.go Adds coverage for malformed multipart bodies to ensure no panic and an error is returned.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread client.go
Comment on lines 622 to +623
n, err = part.Read(multipartBody)
if err == nil {
return err
// Read returns err == io.EOF when the part is fully consumed; any
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

part.Read(multipartBody) reads only once into a fixed 1400-byte buffer. If a multipart part exceeds 1400 bytes, Read can return n==len(buf) with err==nil, and the code will proceed with truncated data (e.g., incomplete JSON or binary). Consider reading the full part (e.g., io.ReadAll / io.ReadAll(io.LimitReader(...)) or a loop until io.EOF) so valid requests aren’t corrupted or rejected.

Copilot uses AI. Check for mistakes.
Comment thread client_test.go Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@d11nn
Copy link
Copy Markdown
Contributor

d11nn commented Apr 25, 2026

@Alonza0314 LGTM

@Alonza0314 Alonza0314 merged commit 1bc3831 into free5gc:main Apr 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants