Description
Summary
The BSON and Protobuf binders read the entire request body into memory before parsing it.
If an application uses these binders without an application-level or proxy-level body size limit, a large request body can force large server-side memory allocations and cause resource exhaustion.
Affected Code
File: binding/bson.go
func (b bsonBinding) Bind(req *http.Request, obj any) error {
buf, err := io.ReadAll(req.Body)
if err == nil {
err = b.BindBody(buf, obj)
}
return err
}
File: binding/protobuf.go
func (b protobufBinding) Bind(req *http.Request, obj any) error {
buf, err := io.ReadAll(req.Body)
if err != nil {
return err
}
return b.BindBody(buf, obj)
}
Both paths call io.ReadAll(req.Body) without applying a maximum size.
Reproduction
I reproduced the behavior against a minimal Gin server that directly calls the real binding.BSON.Bind path.
A small request body only caused a small allocation:
binding.BSON.Bind err=invalid document length
HeapAlloc=0.7 MiB TotalAlloc=0.7 MiB (delta vs baseline = +0.1 MiB)
A 200 MB request body caused the request body to be fully read into memory before parsing failed:
binding.BSON.Bind err=invalid document length
HeapAlloc=516.6 MiB TotalAlloc=516.8 MiB (delta vs baseline = +516.2 MiB)
The parse error happens after the full body has already been read, so the memory consumption is triggered even when the body is invalid.
The Protobuf binder uses the same io.ReadAll(req.Body) pattern.
Expected Behavior
Binders should avoid unbounded request body reads, or Gin should provide a clear built-in way to enforce a maximum body size for these binders.
Actual Behavior
The BSON and Protobuf binders read the full request body into memory without a size limit.
Impact
A client can send a very large request body to an endpoint using these binders and force large memory allocations. This may lead to resource exhaustion or service degradation if no external request size limit is configured.
Suggested Fix
- Add a configurable maximum body size for binders that need to buffer the full request body.
- Use a bounded reader before calling
io.ReadAll.
- Document the default behavior clearly so applications using BSON or Protobuf binding know they must enforce request size limits.
Gin Version
34dac20
Can you reproduce the bug?
Yes
Source Code
package main
import (
"fmt"
"net/http"
"runtime"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
func main() {
r := gin.Default()
r.POST("/bson", func(c *gin.Context) {
var before, after runtime.MemStats
runtime.ReadMemStats(&before)
var obj any
err := binding.BSON.Bind(c.Request, &obj)
runtime.ReadMemStats(&after)
c.String(http.StatusOK,
"binding.BSON.Bind err=%v\nHeapAlloc=%.1f MiB TotalAlloc=%.1f MiB\n",
err,
float64(after.HeapAlloc)/1024/1024,
float64(after.TotalAlloc)/1024/1024,
)
})
if err := r.Run(":8080"); err != nil {
panic(err)
}
}
Run the server, then send a large request body:
go run .
head -c 209715200 /dev/zero | curl -X POST http://localhost:8080/bson --data-binary @-
Observed output from my reproduction:
binding.BSON.Bind err=invalid document length
HeapAlloc=516.6 MiB TotalAlloc=516.8 MiB
The BSON parse error occurs after the full request body has already been read into memory.
Go Version
go1.26.5
Operating System
Docker container based on golang:1.26
Description
Summary
The BSON and Protobuf binders read the entire request body into memory before parsing it.
If an application uses these binders without an application-level or proxy-level body size limit, a large request body can force large server-side memory allocations and cause resource exhaustion.
Affected Code
File:
binding/bson.goFile:
binding/protobuf.goBoth paths call
io.ReadAll(req.Body)without applying a maximum size.Reproduction
I reproduced the behavior against a minimal Gin server that directly calls the real
binding.BSON.Bindpath.A small request body only caused a small allocation:
A 200 MB request body caused the request body to be fully read into memory before parsing failed:
The parse error happens after the full body has already been read, so the memory consumption is triggered even when the body is invalid.
The Protobuf binder uses the same
io.ReadAll(req.Body)pattern.Expected Behavior
Binders should avoid unbounded request body reads, or Gin should provide a clear built-in way to enforce a maximum body size for these binders.
Actual Behavior
The BSON and Protobuf binders read the full request body into memory without a size limit.
Impact
A client can send a very large request body to an endpoint using these binders and force large memory allocations. This may lead to resource exhaustion or service degradation if no external request size limit is configured.
Suggested Fix
io.ReadAll.Gin Version
34dac20
Can you reproduce the bug?
Yes
Source Code
Run the server, then send a large request body:
Observed output from my reproduction:
The BSON parse error occurs after the full request body has already been read into memory.
Go Version
go1.26.5
Operating System
Docker container based on golang:1.26