@@ -259,6 +259,38 @@ func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath
259259 return GetExecUser (userSpec , defaults , passwd , group )
260260}
261261
262+ // parseNumeric parses the given UID or GID value to an integer and within
263+ // the minID - maxID range.
264+ //
265+ // While the Linux kernel allows the max UID to be MaxUint32 - 2,
266+ // and the OCI Runtime Spec has no definition about the max UID, we require
267+ // the UID to be <= MaxInt32.
268+ //
269+ // See https://github.com/containerd/containerd/commit/de1341c201ffb0effebbf51d00376181968c8779
270+ func parseNumeric (val string ) (int , bool , error ) {
271+ if val == "" {
272+ return 0 , false , nil
273+ }
274+ id , err := strconv .Atoi (val )
275+ if err != nil {
276+ if errors .Is (err , strconv .ErrSyntax ) {
277+ // Discard the error, because non-numeric values are expected
278+ // when passing a username or group-name.
279+ return 0 , false , nil
280+ }
281+ if errors .Is (err , strconv .ErrRange ) {
282+ return 0 , true , ErrRange
283+ }
284+ // Other errors ("invalid base", "invalid bit size"); should never
285+ // happen with strconv.Atoi.
286+ return 0 , false , err
287+ }
288+ if id < minID || id > maxID {
289+ return 0 , true , ErrRange
290+ }
291+ return id , true , nil
292+ }
293+
262294// GetExecUser parses a user specification string (using the passwd and group
263295// readers as sources for /etc/passwd and /etc/group data, respectively). In
264296// the case of blank fields or missing data from the sources, the values in
@@ -304,8 +336,14 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
304336
305337 // Convert userArg and groupArg to be numeric, so we don't have to execute
306338 // Atoi *twice* for each iteration over lines.
307- uidArg , uidErr := strconv .Atoi (userArg )
308- gidArg , gidErr := strconv .Atoi (groupArg )
339+ uidArg , isUID , err := parseNumeric (userArg )
340+ if err != nil {
341+ return nil , err
342+ }
343+ gidArg , isGID , err := parseNumeric (groupArg )
344+ if err != nil {
345+ return nil , err
346+ }
309347
310348 // Find the matching user.
311349 users , err := ParsePasswdFilter (passwd , func (u User ) bool {
@@ -314,8 +352,8 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
314352 return u .Uid == user .Uid
315353 }
316354
317- if uidErr == nil {
318- // If the userArg is numeric, always treat it as a UID.
355+ if isUID {
356+ // If the userArg is a valid numeric value , always treat it as a UID.
319357 return uidArg == u .Uid
320358 }
321359
@@ -341,18 +379,11 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
341379 // If we can't find a user with the given username, the only other valid
342380 // option is if it's a numeric username with no associated entry in passwd.
343381
344- if uidErr != nil {
382+ if ! isUID {
345383 // Not numeric.
346384 return nil , fmt .Errorf ("unable to find user %s: %w" , userArg , ErrNoPasswdEntries )
347385 }
348386 user .Uid = uidArg
349-
350- // Must be inside valid uid range.
351- if user .Uid < minID || user .Uid > maxID {
352- return nil , ErrRange
353- }
354-
355- // Okay, so it's numeric. We can just roll with this.
356387 }
357388
358389 // On to the groups. If we matched a username, we need to do this because of
@@ -370,7 +401,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
370401 return false
371402 }
372403
373- if gidErr == nil {
404+ if isGID {
374405 // If the groupArg is numeric, always treat it as a GID.
375406 return gidArg == g .Gid
376407 }
@@ -390,18 +421,11 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
390421 // If we can't find a group with the given name, the only other valid
391422 // option is if it's a numeric group name with no associated entry in group.
392423
393- if gidErr != nil {
424+ if ! isGID {
394425 // Not numeric.
395426 return nil , fmt .Errorf ("unable to find group %s: %w" , groupArg , ErrNoGroupEntries )
396427 }
397428 user .Gid = gidArg
398-
399- // Must be inside valid gid range.
400- if user .Gid < minID || user .Gid > maxID {
401- return nil , ErrRange
402- }
403-
404- // Okay, so it's numeric. We can just roll with this.
405429 }
406430 } else if len (groups ) > 0 {
407431 // Supplementary group ids only make sense if in the implicit form.
0 commit comments