prevent out-of-memory errors (#5674)

impose a maximum size on body of incoming HTTP requests and responses.
This commit is contained in:
Alessandro Ros
2026-04-19 21:39:08 +02:00
committed by GitHub
parent 3c7c45efa4
commit caeccdceff
10 changed files with 37 additions and 15 deletions
+4
View File
@@ -19,6 +19,10 @@ import (
"github.com/bluenviron/mediamtx/internal/protocols/httpp"
)
const (
maxInboundConfigSize = 10 * 1024 * 1024
)
func interfaceIsEmpty(i any) bool {
return reflect.ValueOf(i).Kind() != reflect.Pointer || reflect.ValueOf(i).IsNil()
}
+2 -1
View File
@@ -1,6 +1,7 @@
package api //nolint:revive
import (
"io"
"net/http"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -18,7 +19,7 @@ func (a *API) onConfigGlobalGet(ctx *gin.Context) {
func (a *API) onConfigGlobalPatch(ctx *gin.Context) {
var c conf.OptionalGlobal
err := jsonwrapper.Decode(ctx.Request.Body, &c)
err := jsonwrapper.Decode(io.LimitReader(ctx.Request.Body, maxInboundConfigSize), &c)
if err != nil {
a.writeError(ctx, http.StatusBadRequest, err)
return
+2 -1
View File
@@ -1,6 +1,7 @@
package api //nolint:revive
import (
"io"
"net/http"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -18,7 +19,7 @@ func (a *API) onConfigPathDefaultsGet(ctx *gin.Context) {
func (a *API) onConfigPathDefaultsPatch(ctx *gin.Context) {
var p conf.OptionalPath
err := jsonwrapper.Decode(ctx.Request.Body, &p)
err := jsonwrapper.Decode(io.LimitReader(ctx.Request.Body, maxInboundConfigSize), &p)
if err != nil {
a.writeError(ctx, http.StatusBadRequest, err)
return
+4 -3
View File
@@ -3,6 +3,7 @@ package api //nolint:revive
import (
"errors"
"fmt"
"io"
"net/http"
"github.com/bluenviron/mediamtx/internal/conf"
@@ -63,7 +64,7 @@ func (a *API) onConfigPathsAdd(ctx *gin.Context) { //nolint:dupl
}
var p conf.OptionalPath
err := jsonwrapper.Decode(ctx.Request.Body, &p)
err := jsonwrapper.Decode(io.LimitReader(ctx.Request.Body, maxInboundConfigSize), &p)
if err != nil {
a.writeError(ctx, http.StatusBadRequest, err)
return
@@ -100,7 +101,7 @@ func (a *API) onConfigPathsPatch(ctx *gin.Context) { //nolint:dupl
}
var p conf.OptionalPath
err := jsonwrapper.Decode(ctx.Request.Body, &p)
err := jsonwrapper.Decode(io.LimitReader(ctx.Request.Body, maxInboundConfigSize), &p)
if err != nil {
a.writeError(ctx, http.StatusBadRequest, err)
return
@@ -141,7 +142,7 @@ func (a *API) onConfigPathsReplace(ctx *gin.Context) { //nolint:dupl
}
var p conf.OptionalPath
err := jsonwrapper.Decode(ctx.Request.Body, &p)
err := jsonwrapper.Decode(io.LimitReader(ctx.Request.Body, maxInboundConfigSize), &p)
if err != nil {
a.writeError(ctx, http.StatusBadRequest, err)
return
+5 -3
View File
@@ -24,7 +24,8 @@ const (
// PauseAfterError is the pause to apply after an authentication failure.
PauseAfterError = 2 * time.Second
jwksRefreshPeriod = 60 * 60 * time.Second
maxInboundBodySize = 128 * 1024
jwksRefreshPeriod = 60 * 60 * time.Second
)
func isHTTP(req *Request) bool {
@@ -234,7 +235,8 @@ func (m *Manager) authenticateHTTP(req *Request, token string) (string, error) {
defer res.Body.Close()
if res.StatusCode < 200 || res.StatusCode > 299 {
if resBody, err2 := io.ReadAll(res.Body); err2 == nil && len(resBody) != 0 {
resBody, err2 := io.ReadAll(io.LimitReader(res.Body, maxInboundBodySize))
if err2 == nil && len(resBody) != 0 {
return "", fmt.Errorf("server replied with code %d: %s", res.StatusCode, string(resBody))
}
@@ -304,7 +306,7 @@ func (m *Manager) pullJWTJWKS() (jwt.Keyfunc, error) {
defer res.Body.Close()
var raw json.RawMessage
err = json.NewDecoder(res.Body).Decode(&raw)
err = json.NewDecoder(io.LimitReader(res.Body, maxInboundBodySize)).Decode(&raw)
if err != nil {
return nil, err
}
+5 -1
View File
@@ -18,6 +18,10 @@ import (
"github.com/bluenviron/mediamtx/internal/protocols/webrtc"
)
const (
maxInboundSDPSize = 128 * 1024
)
// Client is a WHIP client.
type Client struct {
URL *url.URL
@@ -260,7 +264,7 @@ func (c *Client) postOffer(
return nil, fmt.Errorf("ETag is missing")
}
sdp, err := io.ReadAll(res.Body)
sdp, err := io.ReadAll(io.LimitReader(res.Body, maxInboundSDPSize))
if err != nil {
return nil, err
}
+5 -1
View File
@@ -15,6 +15,10 @@ import (
"strings"
)
const (
maxInboundHLSJSSize = 10 * 1024 * 1024
)
func do() error {
buf, err := os.ReadFile("./hlsjsdownloader/VERSION")
if err != nil {
@@ -34,7 +38,7 @@ func do() error {
return fmt.Errorf("bad status code: %v", res.StatusCode)
}
zipBuf, err := io.ReadAll(res.Body)
zipBuf, err := io.ReadAll(io.LimitReader(res.Body, maxInboundHLSJSSize))
if err != nil {
return err
}
+2 -2
View File
@@ -194,7 +194,7 @@ func (s *httpServer) onWHIPPost(ctx *gin.Context, pathName string, publish bool)
return
}
offer, err := io.ReadAll(ctx.Request.Body)
offer, err := io.ReadAll(io.LimitReader(ctx.Request.Body, maxInboundSDPSize))
if err != nil {
return
}
@@ -260,7 +260,7 @@ func (s *httpServer) onWHIPPatch(ctx *gin.Context, pathName string, rawSecret st
return
}
byts, err := io.ReadAll(ctx.Request.Body)
byts, err := io.ReadAll(io.LimitReader(ctx.Request.Body, maxInboundSDPSize))
if err != nil {
return
}
+3 -2
View File
@@ -32,7 +32,8 @@ import (
)
const (
webrtcTurnSecretExpiration = 24 * time.Hour
turnSecretExpiration = 24 * time.Hour
maxInboundSDPSize = 128 * 1024
)
// ErrSessionNotFound is returned when a session is not found.
@@ -475,7 +476,7 @@ func (s *Server) generateICEServers(clientConfig bool) ([]pwebrtc.ICEServer, err
for _, server := range s.ICEServers {
if !server.ClientOnly || clientConfig {
if server.Username == "AUTH_SECRET" {
expireDate := time.Now().Add(webrtcTurnSecretExpiration).Unix()
expireDate := time.Now().Add(turnSecretExpiration).Unix()
user, err := randomTurnUser()
if err != nil {
@@ -16,6 +16,10 @@ import (
"strings"
)
const (
maxInboundRPICameraSize = 10 * 1024 * 1024
)
func dumpTar(src io.Reader) error {
uncompressed, err := gzip.NewReader(src)
if err != nil {
@@ -75,7 +79,7 @@ func doSingle(version string, f string) error {
return fmt.Errorf("bad status code: %v", res.StatusCode)
}
buf, err := io.ReadAll(res.Body)
buf, err := io.ReadAll(io.LimitReader(res.Body, maxInboundRPICameraSize))
if err != nil {
return err
}