Skip to content

Commit ce0bac2

Browse files
Merge pull request #25986 from Honny1/fix-unlimited-ulimits
Fix handling of "r_limits" in Podman REST API /libpod/containers/create
2 parents 9e9b531 + e66ff39 commit ce0bac2

File tree

3 files changed

+115
-13
lines changed

3 files changed

+115
-13
lines changed

pkg/api/handlers/libpod/containers_create.go

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"errors"
88
"fmt"
9+
"math"
910
"net/http"
1011
"strconv"
1112

@@ -18,8 +19,28 @@ import (
1819
"github.com/containers/podman/v5/pkg/specgen/generate"
1920
"github.com/containers/podman/v5/pkg/specgenutil"
2021
"github.com/containers/storage"
22+
"github.com/opencontainers/runtime-spec/specs-go"
2123
)
2224

25+
// The JSON decoder correctly cannot decode (overflow) negative values (e.g., `-1`) for fields of type `uint64`,
26+
// as `-1` is used to represent `max` in `POSIXRlimit`. To address this, we use `tmpSpecGenerator` to decode the request body.
27+
// The `tmpSpecGenerator` overrides the `POSIXRlimit` type with a `tmpRlimit` type that uses the `json.Number` type for decoding values.
28+
// The `tmpRlimit` is then parsed into the `POSIXRlimit` type and assigned to the `SpecGenerator`.
29+
// This serves as a workaround for the issue (https://github.com/containers/podman/issues/24886).
30+
type tmpSpecGenerator struct {
31+
specgen.SpecGenerator
32+
Rlimits []tmpRlimit `json:"r_limits,omitempty"`
33+
}
34+
35+
type tmpRlimit struct {
36+
// Type of the rlimit to set
37+
Type string `json:"type"`
38+
// Hard is the hard limit for the specified type
39+
Hard json.Number `json:"hard"`
40+
// Soft is the soft limit for the specified type
41+
Soft json.Number `json:"soft"`
42+
}
43+
2344
// CreateContainer takes a specgenerator and makes a container. It returns
2445
// the new container ID on success along with any warnings.
2546
func CreateContainer(w http.ResponseWriter, r *http.Request) {
@@ -35,25 +56,36 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
3556
privileged := conf.Containers.Privileged
3657

3758
// we have to set the default before we decode to make sure the correct default is set when the field is unset
38-
sg := specgen.SpecGenerator{
39-
ContainerNetworkConfig: specgen.ContainerNetworkConfig{
40-
UseImageHosts: &noHosts,
41-
},
42-
ContainerSecurityConfig: specgen.ContainerSecurityConfig{
43-
Umask: conf.Containers.Umask,
44-
Privileged: &privileged,
45-
},
46-
ContainerHealthCheckConfig: specgen.ContainerHealthCheckConfig{
47-
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
48-
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
49-
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
59+
tmpSg := tmpSpecGenerator{
60+
SpecGenerator: specgen.SpecGenerator{
61+
ContainerNetworkConfig: specgen.ContainerNetworkConfig{
62+
UseImageHosts: &noHosts,
63+
},
64+
ContainerSecurityConfig: specgen.ContainerSecurityConfig{
65+
Umask: conf.Containers.Umask,
66+
Privileged: &privileged,
67+
},
68+
ContainerHealthCheckConfig: specgen.ContainerHealthCheckConfig{
69+
HealthLogDestination: define.DefaultHealthCheckLocalDestination,
70+
HealthMaxLogCount: define.DefaultHealthMaxLogCount,
71+
HealthMaxLogSize: define.DefaultHealthMaxLogSize,
72+
},
5073
},
5174
}
5275

53-
if err := json.NewDecoder(r.Body).Decode(&sg); err != nil {
76+
if err := json.NewDecoder(r.Body).Decode(&tmpSg); err != nil {
5477
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("decode(): %w", err))
5578
return
5679
}
80+
81+
sg := tmpSg.SpecGenerator
82+
rLimits, err := parseRLimits(tmpSg.Rlimits)
83+
if err != nil {
84+
utils.Error(w, http.StatusBadRequest, fmt.Errorf("invalid rlimit: %w", err))
85+
return
86+
}
87+
sg.Rlimits = rLimits
88+
5789
if sg.Passwd == nil {
5890
t := true
5991
sg.Passwd = &t
@@ -100,3 +132,42 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
100132
response := entities.ContainerCreateResponse{ID: ctr.ID(), Warnings: warn}
101133
utils.WriteJSON(w, http.StatusCreated, response)
102134
}
135+
136+
// parseRLimits parses slice of tmpLimit to slice of specs.POSIXRlimit.
137+
func parseRLimits(rLimits []tmpRlimit) ([]specs.POSIXRlimit, error) {
138+
rl := []specs.POSIXRlimit{}
139+
140+
// The "soft" and "hard" values are expected to be of type uint64.
141+
// The JSON decoder cannot cast -1 as to uint64.
142+
// We need to convert them to uint64, and handle the special case of -1
143+
// which indicates an max value.
144+
parseLimitNumber := func(limit json.Number) (uint64, error) {
145+
limitString := limit.String()
146+
if limitString == "-1" {
147+
// uint64(-1) overflow to max uint64 value
148+
return math.MaxUint64, nil
149+
}
150+
return strconv.ParseUint(limitString, 10, 64)
151+
}
152+
153+
for _, rLimit := range rLimits {
154+
soft, err := parseLimitNumber(rLimit.Soft)
155+
if err != nil {
156+
return nil, fmt.Errorf("invalid value for POSIXRlimit.soft: %w", err)
157+
}
158+
hard, err := parseLimitNumber(rLimit.Hard)
159+
if err != nil {
160+
return nil, fmt.Errorf("invalid value for POSIXRlimit.hard: %w", err)
161+
}
162+
if rLimit.Type == "" {
163+
return nil, fmt.Errorf("invalid value for POSIXRlimit.type: %w", err)
164+
}
165+
166+
rl = append(rl, specs.POSIXRlimit{
167+
Type: rLimit.Type,
168+
Soft: soft,
169+
Hard: hard,
170+
})
171+
}
172+
return rl, nil
173+
}

test/apiv2/20-containers.at

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,29 @@ if root; then
772772
podman rm -f updateCtr
773773
fi
774774

775+
776+
# test if API support -1 for ulimits https://github.com/containers/podman/issues/24886
777+
778+
# Compat API
779+
t POST containers/create \
780+
Image=$IMAGE \
781+
HostConfig='{"Ulimits":[{"Name":"memlock","Soft":-1,"Hard":-1}]}' \
782+
201 \
783+
.Id~[0-9a-f]\\{64\\}
784+
cid=$(jq -r '.Id' <<<"$output")
785+
786+
t DELETE containers/$cid 204
787+
788+
# Libpod API
789+
t POST libpod/containers/create \
790+
Image=$IMAGE \
791+
r_limits='[{"type":"memlock","soft":-1,"hard":-1}]' \
792+
201 \
793+
.Id~[0-9a-f]\\{64\\}
794+
cid=$(jq -r '.Id' <<<"$output")
795+
796+
t DELETE containers/$cid 204
797+
775798
rm -rf $TMPD
776799

777800
podman container rm -fa

test/system/030-run.bats

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,14 @@ EOF
13091309
fi
13101310
fi
13111311

1312+
ctr="c-h-$(safename)"
1313+
run_podman run -d --name $ctr --ulimit core=-1:-1 $IMAGE /home/podman/pause
1314+
1315+
run_podman inspect $ctr --format "{{.HostConfig.Ulimits}}"
1316+
assert "$output" =~ "RLIMIT_CORE -1 -1" "ulimit core is not set to unlimited"
1317+
1318+
run_podman rm -f $ctr
1319+
13121320
run_podman run --ulimit core=-1:-1 --rm $IMAGE grep core /proc/self/limits
13131321
assert "$output" =~ " ${max} * ${max} * bytes"
13141322

0 commit comments

Comments
 (0)