Merge pull request #25986 from Honny1/fix-unlimited-ulimits

Fix handling of "r_limits" in Podman REST API /libpod/containers/create
This commit is contained in:
openshift-merge-bot[bot]
2025-04-28 22:27:40 +00:00
committed by GitHub
3 changed files with 115 additions and 13 deletions

View File

@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"net/http"
"strconv"
@ -18,8 +19,28 @@ import (
"github.com/containers/podman/v5/pkg/specgen/generate"
"github.com/containers/podman/v5/pkg/specgenutil"
"github.com/containers/storage"
"github.com/opencontainers/runtime-spec/specs-go"
)
// The JSON decoder correctly cannot decode (overflow) negative values (e.g., `-1`) for fields of type `uint64`,
// as `-1` is used to represent `max` in `POSIXRlimit`. To address this, we use `tmpSpecGenerator` to decode the request body.
// The `tmpSpecGenerator` overrides the `POSIXRlimit` type with a `tmpRlimit` type that uses the `json.Number` type for decoding values.
// The `tmpRlimit` is then parsed into the `POSIXRlimit` type and assigned to the `SpecGenerator`.
// This serves as a workaround for the issue (https://github.com/containers/podman/issues/24886).
type tmpSpecGenerator struct {
specgen.SpecGenerator
Rlimits []tmpRlimit `json:"r_limits,omitempty"`
}
type tmpRlimit struct {
// Type of the rlimit to set
Type string `json:"type"`
// Hard is the hard limit for the specified type
Hard json.Number `json:"hard"`
// Soft is the soft limit for the specified type
Soft json.Number `json:"soft"`
}
// CreateContainer takes a specgenerator and makes a container. It returns
// the new container ID on success along with any warnings.
func CreateContainer(w http.ResponseWriter, r *http.Request) {
@ -35,25 +56,36 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
privileged := conf.Containers.Privileged
// we have to set the default before we decode to make sure the correct default is set when the field is unset
sg := specgen.SpecGenerator{
ContainerNetworkConfig: specgen.ContainerNetworkConfig{
UseImageHosts: &noHosts,
},
ContainerSecurityConfig: specgen.ContainerSecurityConfig{
Umask: conf.Containers.Umask,
Privileged: &privileged,
},
ContainerHealthCheckConfig: specgen.ContainerHealthCheckConfig{
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
tmpSg := tmpSpecGenerator{
SpecGenerator: specgen.SpecGenerator{
ContainerNetworkConfig: specgen.ContainerNetworkConfig{
UseImageHosts: &noHosts,
},
ContainerSecurityConfig: specgen.ContainerSecurityConfig{
Umask: conf.Containers.Umask,
Privileged: &privileged,
},
ContainerHealthCheckConfig: specgen.ContainerHealthCheckConfig{
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
},
},
}
if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
if err := json.NewDecoder(r.Body).Decode(&tmpSg); err != nil {
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
return
}
sg := tmpSg.SpecGenerator
rLimits, err := parseRLimits(tmpSg.Rlimits)
if err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("invalid rlimit: %w", err))
return
}
sg.Rlimits = rLimits
if sg.Passwd == nil {
t := true
sg.Passwd = &t
@ -100,3 +132,42 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
response := entities.ContainerCreateResponse{ID: ctr.ID(), Warnings: warn}
utils.WriteJSON(w, http.StatusCreated, response)
}
// parseRLimits parses slice of tmpLimit to slice of specs.POSIXRlimit.
func parseRLimits(rLimits []tmpRlimit) ([]specs.POSIXRlimit, error) {
rl := []specs.POSIXRlimit{}
// The "soft" and "hard" values are expected to be of type uint64.
// The JSON decoder cannot cast -1 as to uint64.
// We need to convert them to uint64, and handle the special case of -1
// which indicates an max value.
parseLimitNumber := func(limit json.Number) (uint64, error) {
limitString := limit.String()
if limitString == "-1" {
// uint64(-1) overflow to max uint64 value
return math.MaxUint64, nil
}
return strconv.ParseUint(limitString, 10, 64)
}
for _, rLimit := range rLimits {
soft, err := parseLimitNumber(rLimit.Soft)
if err != nil {
return nil, fmt.Errorf("invalid value for POSIXRlimit.soft: %w", err)
}
hard, err := parseLimitNumber(rLimit.Hard)
if err != nil {
return nil, fmt.Errorf("invalid value for POSIXRlimit.hard: %w", err)
}
if rLimit.Type == "" {
return nil, fmt.Errorf("invalid value for POSIXRlimit.type: %w", err)
}
rl = append(rl, specs.POSIXRlimit{
Type: rLimit.Type,
Soft: soft,
Hard: hard,
})
}
return rl, nil
}