From 112713279a45f7d3cc4dc766e26476a4c7d708a4 Mon Sep 17 00:00:00 2001 From: Badr Ezzir Date: Wed, 8 Apr 2026 01:09:59 +0100 Subject: [PATCH] ENG-54618 Fix concurrent map write bug in `seedFormFromMultipart` and optimize form value handling --- view/state/kind/locator/form.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/view/state/kind/locator/form.go b/view/state/kind/locator/form.go index 2b919053..cc8563ab 100644 --- a/view/state/kind/locator/form.go +++ b/view/state/kind/locator/form.go @@ -97,19 +97,18 @@ func NewForm(opts ...Option) (kind.Locator, error) { return ret, nil } -// seedFormFromMultipart parses multipart/form-data (if needed) and copies textual values to the shared form +// seedFormFromMultipart parses multipart/form-data and copies values into shared maps. +// Mutex is required because multiple Form locators (one per parameter) can call this +// concurrently on the same request. Uses form.Values directly instead of form.Set to +// avoid deadlock (form.Set locks the same mutex). func (r *Form) seedFormFromMultipart() { if r.request == nil || r.form == nil { return } if r.request.MultipartForm == nil && len(r.form.Values) == 0 { - // Only ParseMultipartForm for form-data; other multipart types aren't - // supported by ParseMultipartForm. If the shared form already has - // values, treat it as authoritative and avoid parsing. ct := r.request.Header.Get("Content-Type") if ct != "" { if mediaType, _, err := mime.ParseMediaType(ct); err == nil && shared.IsFormData(mediaType) { - // Use the same default memory threshold as Body locator const maxMultipartMemory = 32 << 20 // 32 MiB _ = r.request.ParseMultipartForm(maxMultipartMemory) } @@ -118,14 +117,18 @@ func (r *Form) seedFormFromMultipart() { if r.request.MultipartForm == nil { return } - if len(r.request.Form) == 0 { + // BUG FIX (concurrent map writes): + mu := r.form.Mutex() + mu.Lock() + defer mu.Unlock() + if r.request.Form == nil { r.request.Form = url.Values{} } for k, vs := range r.request.MultipartForm.Value { if len(vs) == 0 { continue } - r.form.Set(k, vs...) + r.form.Values[k] = vs r.request.Form[k] = vs } }