6
6
"encoding/json"
7
7
"errors"
8
8
"fmt"
9
+ "math"
9
10
"net/http"
10
11
"strconv"
11
12
@@ -18,8 +19,28 @@ import (
18
19
"github.com/containers/podman/v5/pkg/specgen/generate"
19
20
"github.com/containers/podman/v5/pkg/specgenutil"
20
21
"github.com/containers/storage"
22
+ "github.com/opencontainers/runtime-spec/specs-go"
21
23
)
22
24
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
+
23
44
// CreateContainer takes a specgenerator and makes a container. It returns
24
45
// the new container ID on success along with any warnings.
25
46
func CreateContainer (w http.ResponseWriter , r * http.Request ) {
@@ -35,25 +56,36 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
35
56
privileged := conf .Containers .Privileged
36
57
37
58
// 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
+ },
50
73
},
51
74
}
52
75
53
- if err := json .NewDecoder (r .Body ).Decode (& sg ); err != nil {
76
+ if err := json .NewDecoder (r .Body ).Decode (& tmpSg ); err != nil {
54
77
utils .Error (w , http .StatusInternalServerError , fmt .Errorf ("decode(): %w" , err ))
55
78
return
56
79
}
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
+
57
89
if sg .Passwd == nil {
58
90
t := true
59
91
sg .Passwd = & t
@@ -100,3 +132,42 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
100
132
response := entities.ContainerCreateResponse {ID : ctr .ID (), Warnings : warn }
101
133
utils .WriteJSON (w , http .StatusCreated , response )
102
134
}
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
+ }
0 commit comments