Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package runc for openSUSE:Factory checked in 
at 2024-01-04 15:56:37
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/runc (Old)
 and      /work/SRC/openSUSE:Factory/.runc.new.28375 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "runc"

Thu Jan  4 15:56:37 2024 rev:59 rq:1136047 version:1.1.11

Changes:
--------
--- /work/SRC/openSUSE:Factory/runc/runc.changes        2023-11-07 
21:25:43.438111684 +0100
+++ /work/SRC/openSUSE:Factory/.runc.new.28375/runc.changes     2024-01-04 
15:57:48.769429235 +0100
@@ -1,0 +2,6 @@
+Tue Jan  2 03:02:16 UTC 2024 - Aleksa Sarai <[email protected]>
+
+- Update to runc v1.1.11. Upstream changelog is available from
+  <https://github.com/opencontainers/runc/releases/tag/v1.1.11>.
+
+-------------------------------------------------------------------

Old:
----
  runc-1.1.10.tar.xz
  runc-1.1.10.tar.xz.asc

New:
----
  runc-1.1.11.tar.xz
  runc-1.1.11.tar.xz.asc

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ runc.spec ++++++
--- /var/tmp/diff_new_pack.LYcUy1/_old  2024-01-04 15:57:49.505456122 +0100
+++ /var/tmp/diff_new_pack.LYcUy1/_new  2024-01-04 15:57:49.505456122 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package runc
 #
-# Copyright (c) 2023 SUSE LLC
+# Copyright (c) 2024 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,13 +18,13 @@
 
 
 # MANUAL: Make sure you update this each time you update runc.
-%define git_version 18a0cb0f32bcac2ecc9a10f327d282759c144dab
-%define git_short   18a0cb0f32bc
+%define git_version 4bccb38cc9cf198d52bebf2b3a90cd14e7af8c06
+%define git_short   4bccb38cc9cf
 
 %define project github.com/opencontainers/runc
 
 Name:           runc
-Version:        1.1.10
+Version:        1.1.11
 Release:        0
 Summary:        Tool for spawning and running OCI containers
 License:        Apache-2.0

++++++ runc-1.1.10.tar.xz -> runc-1.1.11.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/CHANGELOG.md new/runc-1.1.11/CHANGELOG.md
--- old/runc-1.1.10/CHANGELOG.md        2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/CHANGELOG.md        2024-01-02 03:34:16.000000000 +0100
@@ -6,6 +6,23 @@
 
 ## [Unreleased 1.1.z]
 
+## [1.1.11] - 2024-01-01
+
+> Happy New Year!
+
+### Fixed
+
+* Fix several issues with userns path handling. (#4122, #4124, #4134, #4144)
+
+### Changed
+
+ * Support memory.peak and memory.swap.peak in cgroups v2.
+   Add `swapOnlyUsage` in `MemoryStats`. This field reports swap-only usage.
+   For cgroupv1, `Usage` and `Failcnt` are set by subtracting memory usage
+   from memory+swap usage. For cgroupv2, `Usage`, `Limit`, and `MaxUsage`
+   are set. (#4000, #4010, #4131)
+ * build(deps): bump github.com/cyphar/filepath-securejoin. (#4140)
+
 ## [1.1.10] - 2023-10-31
 
 > Śruba, przykręcona we śnie, nie zmieni sytuacji, jaka panuje na jawie.
@@ -476,7 +493,8 @@
 [1.0.1]: https://github.com/opencontainers/runc/compare/v1.0.0...v1.0.1
 
 <!-- 1.1.z patch releases -->
-[Unreleased 1.1.z]: 
https://github.com/opencontainers/runc/compare/v1.1.10...release-1.1
+[Unreleased 1.1.z]: 
https://github.com/opencontainers/runc/compare/v1.1.11...release-1.1
+[1.1.11]: https://github.com/opencontainers/runc/compare/v1.1.10...v1.1.11
 [1.1.10]: https://github.com/opencontainers/runc/compare/v1.1.9...v1.1.10
 [1.1.9]: https://github.com/opencontainers/runc/compare/v1.1.8...v1.1.9
 [1.1.8]: https://github.com/opencontainers/runc/compare/v1.1.7...v1.1.8
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/VERSION new/runc-1.1.11/VERSION
--- old/runc-1.1.10/VERSION     2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/VERSION     2024-01-02 03:34:16.000000000 +0100
@@ -1 +1 @@
-1.1.10
+1.1.11
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/go.mod new/runc-1.1.11/go.mod
--- old/runc-1.1.10/go.mod      2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/go.mod      2024-01-02 03:34:16.000000000 +0100
@@ -7,7 +7,7 @@
        github.com/cilium/ebpf v0.7.0
        github.com/containerd/console v1.0.3
        github.com/coreos/go-systemd/v22 v22.3.2
-       github.com/cyphar/filepath-securejoin v0.2.3
+       github.com/cyphar/filepath-securejoin v0.2.4
        github.com/docker/go-units v0.4.0
        github.com/godbus/dbus/v5 v5.0.6
        github.com/moby/sys/mountinfo v0.5.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/go.sum new/runc-1.1.11/go.sum
--- old/runc-1.1.10/go.sum      2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/go.sum      2024-01-02 03:34:16.000000000 +0100
@@ -9,8 +9,8 @@
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod 
h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d 
h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod 
h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cyphar/filepath-securejoin v0.2.3 
h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI=
-github.com/cyphar/filepath-securejoin v0.2.3/go.mod 
h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
+github.com/cyphar/filepath-securejoin v0.2.4 
h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
+github.com/cyphar/filepath-securejoin v0.2.4/go.mod 
h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
 github.com/davecgh/go-spew v1.1.1 
h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod 
h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/docker/go-units v0.4.0 
h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/cgroups/fs/memory.go 
new/runc-1.1.11/libcontainer/cgroups/fs/memory.go
--- old/runc-1.1.10/libcontainer/cgroups/fs/memory.go   2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/cgroups/fs/memory.go   2024-01-02 
03:34:16.000000000 +0100
@@ -170,6 +170,10 @@
                return err
        }
        stats.MemoryStats.SwapUsage = swapUsage
+       stats.MemoryStats.SwapOnlyUsage = cgroups.MemoryData{
+               Usage:   swapUsage.Usage - memoryUsage.Usage,
+               Failcnt: swapUsage.Failcnt - memoryUsage.Failcnt,
+       }
        kernelUsage, err := getMemoryData(path, "kmem")
        if err != nil {
                return err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/cgroups/fs/memory_test.go 
new/runc-1.1.11/libcontainer/cgroups/fs/memory_test.go
--- old/runc-1.1.10/libcontainer/cgroups/fs/memory_test.go      2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/cgroups/fs/memory_test.go      2024-01-02 
03:34:16.000000000 +0100
@@ -249,12 +249,13 @@
                t.Fatal(err)
        }
        expectedStats := cgroups.MemoryStats{
-               Cache:        512,
-               Usage:        cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
-               SwapUsage:    cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
-               KernelUsage:  cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
-               Stats:        map[string]uint64{"cache": 512, "rss": 1024},
-               UseHierarchy: true,
+               Cache:         512,
+               Usage:         cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
+               SwapUsage:     cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
+               SwapOnlyUsage: cgroups.MemoryData{Usage: 0, MaxUsage: 0, 
Failcnt: 0, Limit: 0},
+               KernelUsage:   cgroups.MemoryData{Usage: 2048, MaxUsage: 4096, 
Failcnt: 100, Limit: 8192},
+               Stats:         map[string]uint64{"cache": 512, "rss": 1024},
+               UseHierarchy:  true,
                PageUsageByNUMA: cgroups.PageUsageByNUMA{
                        PageUsageByNUMAInner: cgroups.PageUsageByNUMAInner{
                                Total:       cgroups.PageStats{Total: 44611, 
Nodes: map[uint8]uint64{0: 32631, 1: 7501, 2: 1982, 3: 2497}},
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/cgroups/fs2/memory.go 
new/runc-1.1.11/libcontainer/cgroups/fs2/memory.go
--- old/runc-1.1.10/libcontainer/cgroups/fs2/memory.go  2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/cgroups/fs2/memory.go  2024-01-02 
03:34:16.000000000 +0100
@@ -100,7 +100,7 @@
        memoryUsage, err := getMemoryDataV2(dirPath, "")
        if err != nil {
                if errors.Is(err, unix.ENOENT) && dirPath == UnifiedMountpoint {
-                       // The root cgroup does not have memory.{current,max}
+                       // The root cgroup does not have 
memory.{current,max,peak}
                        // so emulate those using data from /proc/meminfo and
                        // /sys/fs/cgroup/memory.stat
                        return rootStatsFromMeminfo(stats)
@@ -108,10 +108,12 @@
                return err
        }
        stats.MemoryStats.Usage = memoryUsage
-       swapUsage, err := getMemoryDataV2(dirPath, "swap")
+       swapOnlyUsage, err := getMemoryDataV2(dirPath, "swap")
        if err != nil {
                return err
        }
+       stats.MemoryStats.SwapOnlyUsage = swapOnlyUsage
+       swapUsage := swapOnlyUsage
        // As cgroup v1 reports SwapUsage values as mem+swap combined,
        // while in cgroup v2 swap values do not include memory,
        // report combined mem+swap for v1 compatibility.
@@ -119,6 +121,9 @@
        if swapUsage.Limit != math.MaxUint64 {
                swapUsage.Limit += memoryUsage.Limit
        }
+       // The `MaxUsage` of mem+swap cannot simply combine mem with
+       // swap. So set it to 0 for v1 compatibility.
+       swapUsage.MaxUsage = 0
        stats.MemoryStats.SwapUsage = swapUsage
 
        return nil
@@ -133,6 +138,7 @@
        }
        usage := moduleName + ".current"
        limit := moduleName + ".max"
+       maxUsage := moduleName + ".peak"
 
        value, err := fscommon.GetCgroupParamUint(path, usage)
        if err != nil {
@@ -152,6 +158,14 @@
        }
        memoryData.Limit = value
 
+       // `memory.peak` since kernel 5.19
+       // `memory.swap.peak` since kernel 6.5
+       value, err = fscommon.GetCgroupParamUint(path, maxUsage)
+       if err != nil && !os.IsNotExist(err) {
+               return cgroups.MemoryData{}, err
+       }
+       memoryData.MaxUsage = value
+
        return memoryData, nil
 }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/cgroups/fs2/memory_test.go 
new/runc-1.1.11/libcontainer/cgroups/fs2/memory_test.go
--- old/runc-1.1.10/libcontainer/cgroups/fs2/memory_test.go     2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/cgroups/fs2/memory_test.go     2024-01-02 
03:34:16.000000000 +0100
@@ -94,6 +94,10 @@
                t.Fatal(err)
        }
 
+       if err := os.WriteFile(filepath.Join(fakeCgroupDir, "memory.peak"), 
[]byte("987654321"), 0o644); err != nil {
+               t.Fatal(err)
+       }
+
        gotStats := cgroups.NewStats()
 
        // use a fake root path to trigger the pod cgroup lookup.
@@ -107,6 +111,18 @@
        if gotStats.MemoryStats.Usage.Usage != expectedUsageBytes {
                t.Errorf("parsed cgroupv2 memory.stat doesn't match expected 
result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Usage, 
expectedUsageBytes)
        }
+
+       // result should be "memory.max"
+       var expectedLimitBytes uint64 = 999999999
+       if gotStats.MemoryStats.Usage.Limit != expectedLimitBytes {
+               t.Errorf("parsed cgroupv2 memory.stat doesn't match expected 
result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.Limit, 
expectedLimitBytes)
+       }
+
+       // result should be "memory.peak"
+       var expectedMaxUsageBytes uint64 = 987654321
+       if gotStats.MemoryStats.Usage.MaxUsage != expectedMaxUsageBytes {
+               t.Errorf("parsed cgroupv2 memory.stat doesn't match expected 
result: \ngot %#v\nexpected %#v\n", gotStats.MemoryStats.Usage.MaxUsage, 
expectedMaxUsageBytes)
+       }
 }
 
 func TestRootStatsFromMeminfo(t *testing.T) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/cgroups/stats.go 
new/runc-1.1.11/libcontainer/cgroups/stats.go
--- old/runc-1.1.10/libcontainer/cgroups/stats.go       2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/cgroups/stats.go       2024-01-02 
03:34:16.000000000 +0100
@@ -78,6 +78,8 @@
        Usage MemoryData `json:"usage,omitempty"`
        // usage of memory + swap
        SwapUsage MemoryData `json:"swap_usage,omitempty"`
+       // usage of swap only
+       SwapOnlyUsage MemoryData `json:"swap_only_usage,omitempty"`
        // usage of kernel memory
        KernelUsage MemoryData `json:"kernel_usage,omitempty"`
        // usage of kernel TCP memory
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/configs/config.go 
new/runc-1.1.11/libcontainer/configs/config.go
--- old/runc-1.1.10/libcontainer/configs/config.go      2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/configs/config.go      2024-01-02 
03:34:16.000000000 +0100
@@ -21,9 +21,9 @@
 
 // IDMap represents UID/GID Mappings for User Namespaces.
 type IDMap struct {
-       ContainerID int `json:"container_id"`
-       HostID      int `json:"host_id"`
-       Size        int `json:"size"`
+       ContainerID int64 `json:"container_id"`
+       HostID      int64 `json:"host_id"`
+       Size        int64 `json:"size"`
 }
 
 // Seccomp represents syscall restrictions
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/configs/config_linux.go 
new/runc-1.1.11/libcontainer/configs/config_linux.go
--- old/runc-1.1.10/libcontainer/configs/config_linux.go        2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/configs/config_linux.go        2024-01-02 
03:34:16.000000000 +0100
@@ -1,6 +1,10 @@
 package configs
 
-import "errors"
+import (
+       "errors"
+       "fmt"
+       "math"
+)
 
 var (
        errNoUIDMap   = errors.New("User namespaces enabled, but no uid 
mappings found.")
@@ -16,11 +20,18 @@
                if c.UidMappings == nil {
                        return -1, errNoUIDMap
                }
-               id, found := c.hostIDFromMapping(containerId, c.UidMappings)
+               id, found := c.hostIDFromMapping(int64(containerId), 
c.UidMappings)
                if !found {
                        return -1, errNoUserMap
                }
-               return id, nil
+               // If we are a 32-bit binary running on a 64-bit system, it's 
possible
+               // the mapped user is too large to store in an int, which means 
we
+               // cannot do the mapping. We can't just return an int64, because
+               // os.Setuid() takes an int.
+               if id > math.MaxInt {
+                       return -1, fmt.Errorf("mapping for uid %d (host id %d) 
is larger than native integer size (%d)", containerId, id, math.MaxInt)
+               }
+               return int(id), nil
        }
        // Return unchanged id.
        return containerId, nil
@@ -39,11 +50,18 @@
                if c.GidMappings == nil {
                        return -1, errNoGIDMap
                }
-               id, found := c.hostIDFromMapping(containerId, c.GidMappings)
+               id, found := c.hostIDFromMapping(int64(containerId), 
c.GidMappings)
                if !found {
                        return -1, errNoGroupMap
                }
-               return id, nil
+               // If we are a 32-bit binary running on a 64-bit system, it's 
possible
+               // the mapped user is too large to store in an int, which means 
we
+               // cannot do the mapping. We can't just return an int64, because
+               // os.Setgid() takes an int.
+               if id > math.MaxInt {
+                       return -1, fmt.Errorf("mapping for gid %d (host id %d) 
is larger than native integer size (%d)", containerId, id, math.MaxInt)
+               }
+               return int(id), nil
        }
        // Return unchanged id.
        return containerId, nil
@@ -57,7 +75,7 @@
 
 // Utility function that gets a host ID for a container ID from user namespace 
map
 // if that ID is present in the map.
-func (c Config) hostIDFromMapping(containerID int, uMap []IDMap) (int, bool) {
+func (c Config) hostIDFromMapping(containerID int64, uMap []IDMap) (int64, 
bool) {
        for _, m := range uMap {
                if (containerID >= m.ContainerID) && (containerID <= 
(m.ContainerID + m.Size - 1)) {
                        hostID := m.HostID + (containerID - m.ContainerID)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/libcontainer/configs/validate/rootless.go 
new/runc-1.1.11/libcontainer/configs/validate/rootless.go
--- old/runc-1.1.10/libcontainer/configs/validate/rootless.go   2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/configs/validate/rootless.go   2024-01-02 
03:34:16.000000000 +0100
@@ -28,25 +28,18 @@
        return nil
 }
 
-func hasIDMapping(id int, mappings []configs.IDMap) bool {
-       for _, m := range mappings {
-               if id >= m.ContainerID && id < m.ContainerID+m.Size {
-                       return true
-               }
-       }
-       return false
-}
-
 func rootlessEUIDMappings(config *configs.Config) error {
        if !config.Namespaces.Contains(configs.NEWUSER) {
                return errors.New("rootless container requires user namespaces")
        }
-
-       if len(config.UidMappings) == 0 {
-               return errors.New("rootless containers requires at least one 
UID mapping")
-       }
-       if len(config.GidMappings) == 0 {
-               return errors.New("rootless containers requires at least one 
GID mapping")
+       // We only require mappings if we are not joining another userns.
+       if path := config.Namespaces.PathOf(configs.NEWUSER); path == "" {
+               if len(config.UidMappings) == 0 {
+                       return errors.New("rootless containers requires at 
least one UID mapping")
+               }
+               if len(config.GidMappings) == 0 {
+                       return errors.New("rootless containers requires at 
least one GID mapping")
+               }
        }
        return nil
 }
@@ -70,8 +63,8 @@
                                        // Ignore unknown mount options.
                                        continue
                                }
-                               if !hasIDMapping(uid, config.UidMappings) {
-                                       return errors.New("cannot specify uid= 
mount options for unmapped uid in rootless containers")
+                               if _, err := config.HostUID(uid); err != nil {
+                                       return fmt.Errorf("cannot specify 
uid=%d mount option for rootless container: %w", uid, err)
                                }
                        }
 
@@ -82,8 +75,8 @@
                                        // Ignore unknown mount options.
                                        continue
                                }
-                               if !hasIDMapping(gid, config.GidMappings) {
-                                       return errors.New("cannot specify gid= 
mount options for unmapped gid in rootless containers")
+                               if _, err := config.HostGID(gid); err != nil {
+                                       return fmt.Errorf("cannot specify 
gid=%d mount option for rootless container: %w", gid, err)
                                }
                        }
                }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/libcontainer/configs/validate/validator.go 
new/runc-1.1.11/libcontainer/configs/validate/validator.go
--- old/runc-1.1.10/libcontainer/configs/validate/validator.go  2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/configs/validate/validator.go  2024-01-02 
03:34:16.000000000 +0100
@@ -109,11 +109,19 @@
 func (v *ConfigValidator) usernamespace(config *configs.Config) error {
        if config.Namespaces.Contains(configs.NEWUSER) {
                if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
-                       return errors.New("USER namespaces aren't enabled in 
the kernel")
+                       return errors.New("user namespaces aren't enabled in 
the kernel")
                }
+               hasPath := config.Namespaces.PathOf(configs.NEWUSER) != ""
+               hasMappings := config.UidMappings != nil || config.GidMappings 
!= nil
+               if !hasPath && !hasMappings {
+                       return errors.New("user namespaces enabled, but no 
namespace path to join nor mappings to apply specified")
+               }
+               // The hasPath && hasMappings validation case is handled in 
specconv --
+               // we cache the mappings in Config during specconv in the 
hasPath case,
+               // so we cannot do that validation here.
        } else {
                if config.UidMappings != nil || config.GidMappings != nil {
-                       return errors.New("User namespace mappings specified, 
but USER namespace isn't enabled in the config")
+                       return errors.New("user namespace mappings specified, 
but user namespace isn't enabled in the config")
                }
        }
        return nil
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/libcontainer/configs/validate/validator_test.go 
new/runc-1.1.11/libcontainer/configs/validate/validator_test.go
--- old/runc-1.1.10/libcontainer/configs/validate/validator_test.go     
2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/configs/validate/validator_test.go     
2024-01-02 03:34:16.000000000 +0100
@@ -150,7 +150,7 @@
        }
 }
 
-func TestValidateUsernamespace(t *testing.T) {
+func TestValidateUserNamespace(t *testing.T) {
        if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
                t.Skip("Test requires userns.")
        }
@@ -161,6 +161,8 @@
                                {Type: configs.NEWUSER},
                        },
                ),
+               UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, 
Size: 100}},
+               GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, 
Size: 100}},
        }
 
        validator := New()
@@ -170,11 +172,11 @@
        }
 }
 
-func TestValidateUsernamespaceWithoutUserNS(t *testing.T) {
-       uidMap := configs.IDMap{ContainerID: 123}
+func TestValidateUsernsMappingWithoutNamespace(t *testing.T) {
        config := &configs.Config{
                Rootfs:      "/var",
-               UidMappings: []configs.IDMap{uidMap},
+               UidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, 
Size: 100}},
+               GidMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, 
Size: 100}},
        }
 
        validator := New()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/container_linux.go 
new/runc-1.1.11/libcontainer/container_linux.go
--- old/runc-1.1.10/libcontainer/container_linux.go     2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/container_linux.go     2024-01-02 
03:34:16.000000000 +0100
@@ -2268,7 +2268,7 @@
 
 func requiresRootOrMappingTool(c *configs.Config) bool {
        gidMap := []configs.IDMap{
-               {ContainerID: 0, HostID: os.Getegid(), Size: 1},
+               {ContainerID: 0, HostID: int64(os.Getegid()), Size: 1},
        }
        return !reflect.DeepEqual(c.GidMappings, gidMap)
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/integration/exec_test.go 
new/runc-1.1.11/libcontainer/integration/exec_test.go
--- old/runc-1.1.10/libcontainer/integration/exec_test.go       2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/integration/exec_test.go       2024-01-02 
03:34:16.000000000 +0100
@@ -18,6 +18,7 @@
        "github.com/opencontainers/runc/libcontainer/cgroups"
        "github.com/opencontainers/runc/libcontainer/cgroups/systemd"
        "github.com/opencontainers/runc/libcontainer/configs"
+       "github.com/opencontainers/runc/libcontainer/userns"
        "github.com/opencontainers/runtime-spec/specs-go"
 
        "golang.org/x/sys/unix"
@@ -1588,6 +1589,11 @@
        config2 := newTemplateConfig(t, &tParam{userns: true})
        config2.Namespaces.Add(configs.NEWNET, netns1)
        config2.Namespaces.Add(configs.NEWUSER, userns1)
+       // Emulate specconv.setupUserNamespace().
+       uidMap, gidMap, err := userns.GetUserNamespaceMappings(userns1)
+       ok(t, err)
+       config2.UidMappings = uidMap
+       config2.GidMappings = gidMap
        config2.Cgroups.Path = "integration/test2"
        container2, err := newContainer(t, config2)
        ok(t, err)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/specconv/spec_linux.go 
new/runc-1.1.11/libcontainer/specconv/spec_linux.go
--- old/runc-1.1.10/libcontainer/specconv/spec_linux.go 2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/specconv/spec_linux.go 2024-01-02 
03:34:16.000000000 +0100
@@ -18,6 +18,7 @@
        "github.com/opencontainers/runc/libcontainer/configs"
        "github.com/opencontainers/runc/libcontainer/devices"
        "github.com/opencontainers/runc/libcontainer/seccomp"
+       "github.com/opencontainers/runc/libcontainer/userns"
        libcontainerUtils "github.com/opencontainers/runc/libcontainer/utils"
        "github.com/opencontainers/runtime-spec/specs-go"
        "github.com/sirupsen/logrus"
@@ -926,9 +927,9 @@
 func setupUserNamespace(spec *specs.Spec, config *configs.Config) error {
        create := func(m specs.LinuxIDMapping) configs.IDMap {
                return configs.IDMap{
-                       HostID:      int(m.HostID),
-                       ContainerID: int(m.ContainerID),
-                       Size:        int(m.Size),
+                       HostID:      int64(m.HostID),
+                       ContainerID: int64(m.ContainerID),
+                       Size:        int64(m.Size),
                }
        }
        if spec.Linux != nil {
@@ -939,6 +940,40 @@
                        config.GidMappings = append(config.GidMappings, 
create(m))
                }
        }
+       if path := config.Namespaces.PathOf(configs.NEWUSER); path != "" {
+               // Cache the current userns mappings in our configuration, so 
that we
+               // can calculate uid and gid mappings within runc. These 
mappings are
+               // never used for configuring the container if the path is set.
+               uidMap, gidMap, err := userns.GetUserNamespaceMappings(path)
+               if err != nil {
+                       return fmt.Errorf("failed to cache mappings for userns: 
%w", err)
+               }
+               // We cannot allow uid or gid mappings to be set if we are also 
asked
+               // to join a userns.
+               if config.UidMappings != nil || config.GidMappings != nil {
+                       // FIXME: It turns out that containerd and CRIO pass 
both a userns
+                       // path and the mappings of the namespace in the same 
config.json.
+                       // Such a configuration is technically not valid, but 
we used to
+                       // require mappings be specified, and thus users worked 
around our
+                       // bug -- so we can't regress it at the moment. But we 
also don't
+                       // want to produce broken behaviour if the mapping 
doesn't match
+                       // the userns. So (for now) we output a warning if the 
actual
+                       // userns mappings match the configuration, otherwise 
we return an
+                       // error.
+                       if !userns.IsSameMapping(uidMap, config.UidMappings) ||
+                               !userns.IsSameMapping(gidMap, 
config.GidMappings) {
+                               return errors.New("user namespaces enabled, but 
both namespace path and non-matching mapping specified -- you may only provide 
one")
+                       }
+                       logrus.Warnf("config.json has both a userns path to 
join and a matching userns mapping specified -- you may only provide one. 
Future versions of runc may return an error with this configuration, please 
report a bug on <https://github.com/opencontainers/runc> if you see this 
warning and cannot update your configuration.")
+               }
+
+               config.UidMappings = uidMap
+               config.GidMappings = gidMap
+               logrus.WithFields(logrus.Fields{
+                       "uid_map": uidMap,
+                       "gid_map": gidMap,
+               }).Debugf("config uses path-based userns configuration -- 
current uid and gid mappings cached")
+       }
        rootUID, err := config.HostRootUID()
        if err != nil {
                return err
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/specconv/spec_linux_test.go 
new/runc-1.1.11/libcontainer/specconv/spec_linux_test.go
--- old/runc-1.1.10/libcontainer/specconv/spec_linux_test.go    2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/libcontainer/specconv/spec_linux_test.go    2024-01-02 
03:34:16.000000000 +0100
@@ -610,6 +610,40 @@
        }
 }
 
+func TestUserNamespaceMappingAndPath(t *testing.T) {
+       if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
+               t.Skip("Test requires userns.")
+       }
+
+       spec := &specs.Spec{
+               Root: &specs.Root{
+                       Path: "rootfs",
+               },
+               Linux: &specs.Linux{
+                       UIDMappings: []specs.LinuxIDMapping{
+                               {ContainerID: 0, HostID: 1000, Size: 1000},
+                       },
+                       GIDMappings: []specs.LinuxIDMapping{
+                               {ContainerID: 0, HostID: 2000, Size: 1000},
+                       },
+                       Namespaces: []specs.LinuxNamespace{
+                               {
+                                       Type: "user",
+                                       Path: "/proc/1/ns/user",
+                               },
+                       },
+               },
+       }
+
+       _, err := CreateLibcontainerConfig(&CreateOpts{
+               Spec: spec,
+       })
+
+       if !strings.Contains(err.Error(), "both namespace path and non-matching 
mapping specified") {
+               t.Errorf("user namespace with path and non-matching mapping 
should be forbidden, got error %v", err)
+       }
+}
+
 func TestNonZeroEUIDCompatibleSpecconvValidate(t *testing.T) {
        if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
                t.Skip("Test requires userns.")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/userns/userns_maps.c 
new/runc-1.1.11/libcontainer/userns/userns_maps.c
--- old/runc-1.1.10/libcontainer/userns/userns_maps.c   1970-01-01 
01:00:00.000000000 +0100
+++ new/runc-1.1.11/libcontainer/userns/userns_maps.c   2024-01-02 
03:34:16.000000000 +0100
@@ -0,0 +1,79 @@
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <sched.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+/*
+ * All of the code here is run inside an aync-signal-safe context, so we need
+ * to be careful to not call any functions that could cause issues. In theory,
+ * since we are a Go program, there are fewer restrictions in practice, it's
+ * better to be safe than sorry.
+ *
+ * The only exception is exit, which we need to call to make sure we don't
+ * return into runc.
+ */
+
+void bail(int pipefd, const char *fmt, ...)
+{
+       va_list args;
+
+       va_start(args, fmt);
+       vdprintf(pipefd, fmt, args);
+       va_end(args);
+
+       exit(1);
+}
+
+int spawn_userns_cat(char *userns_path, char *path, int outfd, int errfd)
+{
+       char buffer[4096] = { 0 };
+
+       pid_t child = fork();
+       if (child != 0)
+               return child;
+       /* in child */
+
+       /* Join the target userns. */
+       int nsfd = open(userns_path, O_RDONLY);
+       if (nsfd < 0)
+               bail(errfd, "open userns path %s failed: %m", userns_path);
+
+       int err = setns(nsfd, CLONE_NEWUSER);
+       if (err < 0)
+               bail(errfd, "setns %s failed: %m", userns_path);
+
+       close(nsfd);
+
+       /* Pipe the requested file contents. */
+       int fd = open(path, O_RDONLY);
+       if (fd < 0)
+               bail(errfd, "open %s in userns %s failed: %m", path, 
userns_path);
+
+       int nread, ntotal = 0;
+       while ((nread = read(fd, buffer, sizeof(buffer))) != 0) {
+               if (nread < 0)
+                       bail(errfd, "read bytes from %s failed (after %d total 
bytes read): %m", path, ntotal);
+               ntotal += nread;
+
+               int nwritten = 0;
+               while (nwritten < nread) {
+                       int n = write(outfd, buffer, nread - nwritten);
+                       if (n < 0)
+                               bail(errfd, "write %d bytes from %s failed 
(after %d bytes written): %m",
+                                    nread - nwritten, path, nwritten);
+                       nwritten += n;
+               }
+               if (nread != nwritten)
+                       bail(errfd, "mismatch for bytes read and written: %d 
read != %d written", nread, nwritten);
+       }
+
+       close(fd);
+       close(outfd);
+       close(errfd);
+
+       /* We must exit here, otherwise we would return into a forked runc. */
+       exit(0);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/libcontainer/userns/userns_maps_linux.go 
new/runc-1.1.11/libcontainer/userns/userns_maps_linux.go
--- old/runc-1.1.10/libcontainer/userns/userns_maps_linux.go    1970-01-01 
01:00:00.000000000 +0100
+++ new/runc-1.1.11/libcontainer/userns/userns_maps_linux.go    2024-01-02 
03:34:16.000000000 +0100
@@ -0,0 +1,186 @@
+//go:build linux
+
+package userns
+
+import (
+       "bufio"
+       "bytes"
+       "fmt"
+       "io"
+       "os"
+       "unsafe"
+
+       "github.com/opencontainers/runc/libcontainer/configs"
+       "github.com/sirupsen/logrus"
+)
+
+/*
+#include <stdlib.h>
+extern int spawn_userns_cat(char *userns_path, char *path, int outfd, int 
errfd);
+*/
+import "C"
+
+func parseIdmapData(data []byte) (ms []configs.IDMap, err error) {
+       scanner := bufio.NewScanner(bytes.NewReader(data))
+       for scanner.Scan() {
+               var m configs.IDMap
+               line := scanner.Text()
+               if _, err := fmt.Sscanf(line, "%d %d %d", &m.ContainerID, 
&m.HostID, &m.Size); err != nil {
+                       return nil, fmt.Errorf("parsing id map failed: invalid 
format in line %q: %w", line, err)
+               }
+               ms = append(ms, m)
+       }
+       if err := scanner.Err(); err != nil {
+               return nil, fmt.Errorf("parsing id map failed: %w", err)
+       }
+       return ms, nil
+}
+
+// Do something equivalent to nsenter --user=<nsPath> cat <path>, but more
+// efficiently. Returns the contents of the requested file from within the user
+// namespace.
+func spawnUserNamespaceCat(nsPath string, path string) ([]byte, error) {
+       rdr, wtr, err := os.Pipe()
+       if err != nil {
+               return nil, fmt.Errorf("create pipe for userns spawn failed: 
%w", err)
+       }
+       defer rdr.Close()
+       defer wtr.Close()
+
+       errRdr, errWtr, err := os.Pipe()
+       if err != nil {
+               return nil, fmt.Errorf("create error pipe for userns spawn 
failed: %w", err)
+       }
+       defer errRdr.Close()
+       defer errWtr.Close()
+
+       cNsPath := C.CString(nsPath)
+       defer C.free(unsafe.Pointer(cNsPath))
+       cPath := C.CString(path)
+       defer C.free(unsafe.Pointer(cPath))
+
+       childPid := C.spawn_userns_cat(cNsPath, cPath, C.int(wtr.Fd()), 
C.int(errWtr.Fd()))
+
+       if childPid < 0 {
+               return nil, fmt.Errorf("failed to spawn fork for userns")
+       } else if childPid == 0 {
+               // this should never happen
+               panic("runc executing inside fork child -- unsafe state!")
+       }
+
+       // We are in the parent -- close the write end of the pipe before 
reading.
+       wtr.Close()
+       output, err := io.ReadAll(rdr)
+       rdr.Close()
+       if err != nil {
+               return nil, fmt.Errorf("reading from userns spawn failed: %w", 
err)
+       }
+
+       // Ditto for the error pipe.
+       errWtr.Close()
+       errOutput, err := io.ReadAll(errRdr)
+       errRdr.Close()
+       if err != nil {
+               return nil, fmt.Errorf("reading from userns spawn error pipe 
failed: %w", err)
+       }
+       errOutput = bytes.TrimSpace(errOutput)
+
+       // Clean up the child.
+       child, err := os.FindProcess(int(childPid))
+       if err != nil {
+               return nil, fmt.Errorf("could not find userns spawn process: 
%w", err)
+       }
+       state, err := child.Wait()
+       if err != nil {
+               return nil, fmt.Errorf("failed to wait for userns spawn 
process: %w", err)
+       }
+       if !state.Success() {
+               errStr := string(errOutput)
+               if errStr == "" {
+                       errStr = fmt.Sprintf("unknown error (status code %d)", 
state.ExitCode())
+               }
+               return nil, fmt.Errorf("userns spawn: %s", errStr)
+       } else if len(errOutput) > 0 {
+               // We can just ignore weird output in the error pipe if the 
process
+               // didn't bail(), but for completeness output for debugging.
+               logrus.Debugf("userns spawn succeeded but unexpected error 
message found: %s", string(errOutput))
+       }
+       // The subprocess succeeded, return whatever it wrote to the pipe.
+       return output, nil
+}
+
+func GetUserNamespaceMappings(nsPath string) (uidMap, gidMap []configs.IDMap, 
err error) {
+       var (
+               pid         int
+               extra       rune
+               tryFastPath bool
+       )
+
+       // nsPath is usually of the form /proc/<pid>/ns/user, which means that 
we
+       // already have a pid that is part of the user namespace and thus we can
+       // just use the pid to read from /proc/<pid>/*id_map.
+       //
+       // Note that Sscanf doesn't consume the whole input, so we check for any
+       // trailing data with %c. That way, we can be sure the pattern matched
+       // /proc/$pid/ns/user _exactly_ iff n === 1.
+       if n, _ := fmt.Sscanf(nsPath, "/proc/%d/ns/user%c", &pid, &extra); n == 
1 {
+               tryFastPath = pid > 0
+       }
+
+       for _, mapType := range []struct {
+               name  string
+               idMap *[]configs.IDMap
+       }{
+               {"uid_map", &uidMap},
+               {"gid_map", &gidMap},
+       } {
+               var mapData []byte
+
+               if tryFastPath {
+                       path := fmt.Sprintf("/proc/%d/%s", pid, mapType.name)
+                       data, err := os.ReadFile(path)
+                       if err != nil {
+                               // Do not error out here -- we need to try the 
slow path if the
+                               // fast path failed.
+                               logrus.Debugf("failed to use fast path to read 
%s from userns %s (error: %s), falling back to slow userns-join path", 
mapType.name, nsPath, err)
+                       } else {
+                               mapData = data
+                       }
+               } else {
+                       logrus.Debugf("cannot use fast path to read %s from 
userns %s, falling back to slow userns-join path", mapType.name, nsPath)
+               }
+
+               if mapData == nil {
+                       // We have to actually join the namespace if we cannot 
take the
+                       // fast path. The path is resolved with respect to the 
child
+                       // process, so just use /proc/self.
+                       data, err := spawnUserNamespaceCat(nsPath, 
"/proc/self/"+mapType.name)
+                       if err != nil {
+                               return nil, nil, err
+                       }
+                       mapData = data
+               }
+               idMap, err := parseIdmapData(mapData)
+               if err != nil {
+                       return nil, nil, fmt.Errorf("failed to parse %s of 
userns %s: %w", mapType.name, nsPath, err)
+               }
+               *mapType.idMap = idMap
+       }
+
+       return uidMap, gidMap, nil
+}
+
+// IsSameMapping returns whether or not the two id mappings are the same. Note
+// that if the order of the mappings is different, or a mapping has been split,
+// the mappings will be considered different.
+func IsSameMapping(a, b []configs.IDMap) bool {
+       if len(a) != len(b) {
+               return false
+       }
+       for idx := range a {
+               if a[idx] != b[idx] {
+                       return false
+               }
+       }
+       return true
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/tests/integration/run.bats 
new/runc-1.1.11/tests/integration/run.bats
--- old/runc-1.1.10/tests/integration/run.bats  2023-11-01 08:01:51.000000000 
+0100
+++ new/runc-1.1.11/tests/integration/run.bats  2024-01-02 03:34:16.000000000 
+0100
@@ -108,3 +108,60 @@
        [ "$status" -eq 0 ]
        [ "$output" = "410" ]
 }
+
+@test "runc run [joining existing container namespaces]" {
+       # Create a detached container with the namespaces we want. We notably 
want
+       # to include userns, which requires config-related configuration.
+       if [ $EUID -eq 0 ]; then
+               update_config '.linux.namespaces += [{"type": "user"}]
+                       | .linux.uidMappings += [{"containerID": 0, "hostID": 
100000, "size": 100}]
+                       | .linux.gidMappings += [{"containerID": 0, "hostID": 
200000, "size": 200}]'
+               mkdir -p rootfs/{proc,sys,tmp}
+       fi
+       update_config '.process.args = ["sleep", "infinity"]'
+
+       runc run -d --console-socket "$CONSOLE_SOCKET" target_ctr
+       [ "$status" -eq 0 ]
+
+       # Modify our container's configuration such that it is just going to
+       # inherit all of the namespaces of the target container.
+       #
+       # NOTE: We cannot join the mount namespace of another container because 
of
+       # some quirks of the runtime-spec. In particular, we MUST pivot_root 
into
+       # root.path and root.path MUST be set in the config, so runc cannot just
+       # ignore root.path when joining namespaces (and root.path doesn't exist
+       # inside root.path, for obvious reasons).
+       #
+       # We could hack around this (create a copy of the rootfs inside the 
rootfs,
+       # or use a simpler mount namespace target), but those wouldn't be 
similar
+       # tests to the other namespace joining tests.
+       target_pid="$(__runc state target_ctr | jq .pid)"
+       update_config '.linux.namespaces |= map_values(.path = if .type == 
"mount" then "" else "/proc/'"$target_pid"'/ns/" + ({"network": "net", "mount": 
"mnt"}[.type] // .type) end)'
+       # Remove the userns configuration (it cannot be changed).
+       update_config '.linux |= (del(.uidMappings) | del(.gidMappings))'
+
+       runc run -d --console-socket "$CONSOLE_SOCKET" attached_ctr
+       [ "$status" -eq 0 ]
+
+       # Make sure there are two sleep processes in our container.
+       runc exec attached_ctr ps aux
+       [ "$status" -eq 0 ]
+       run -0 grep "sleep infinity" <<<"$output"
+       [ "${#lines[@]}" -eq 2 ]
+
+       # ... that the userns mappings are the same...
+       runc exec attached_ctr cat /proc/self/uid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+100000\s+100$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+       runc exec attached_ctr cat /proc/self/gid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+200000\s+200$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/tests/integration/userns.bats 
new/runc-1.1.11/tests/integration/userns.bats
--- old/runc-1.1.10/tests/integration/userns.bats       2023-11-01 
08:01:51.000000000 +0100
+++ new/runc-1.1.11/tests/integration/userns.bats       2024-01-02 
03:34:16.000000000 +0100
@@ -15,15 +15,25 @@
        mkdir -p rootfs/{proc,sys,tmp}
        mkdir -p rootfs/tmp/mount-{1,2}
 
+       to_umount_list="$(mktemp "$BATS_RUN_TMPDIR/userns-mounts.XXXXXX")"
        if [ "$ROOTLESS" -eq 0 ]; then
                update_config ' .linux.namespaces += [{"type": "user"}]
                        | .linux.uidMappings += [{"hostID": 100000, 
"containerID": 0, "size": 65534}]
-                       | .linux.gidMappings += [{"hostID": 100000, 
"containerID": 0, "size": 65534}] '
+                       | .linux.gidMappings += [{"hostID": 200000, 
"containerID": 0, "size": 65534}] '
        fi
 }
 
 function teardown() {
        teardown_bundle
+
+       if [ -v to_umount_list ]; then
+               while read -r mount_path; do
+                       umount -l "$mount_path" || :
+                       rm -f "$mount_path"
+               done <"$to_umount_list"
+               rm -f "$to_umount_list"
+               unset to_umount_list
+       fi
 }
 
 @test "userns with simple mount" {
@@ -83,3 +93,74 @@
        runc exec test_busybox cat /sys/fs/cgroup/{pids,memory}/tasks
        [ "$status" -eq 0 ]
 }
+
+@test "userns join other container userns" {
+       # Create a detached container with the id-mapping we want.
+       update_config '.process.args = ["sleep", "infinity"]'
+       runc run -d --console-socket "$CONSOLE_SOCKET" target_userns
+       [ "$status" -eq 0 ]
+
+       # Configure our container to attach to the first container's userns.
+       target_pid="$(__runc state target_userns | jq .pid)"
+       update_config '.linux.namespaces |= map(if .type == "user" then (.path 
= "/proc/'"$target_pid"'/ns/" + .type) else . end)
+               | del(.linux.uidMappings)
+               | del(.linux.gidMappings)'
+       runc run -d --console-socket "$CONSOLE_SOCKET" in_userns
+       [ "$status" -eq 0 ]
+
+       runc exec in_userns cat /proc/self/uid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+100000\s+65534$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+
+       runc exec in_userns cat /proc/self/gid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+200000\s+65534$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+}
+
+@test "userns join other container userns [bind-mounted nsfd]" {
+       requires root
+
+       # Create a detached container with the id-mapping we want.
+       update_config '.process.args = ["sleep", "infinity"]'
+       runc run -d --console-socket "$CONSOLE_SOCKET" target_userns
+       [ "$status" -eq 0 ]
+
+       # Bind-mount the first containers userns nsfd to a different path, to
+       # exercise the non-fast-path (where runc has to join the userns to get 
the
+       # mappings).
+       target_pid="$(__runc state target_userns | jq .pid)"
+       userns_path=$(mktemp "$BATS_RUN_TMPDIR/userns.XXXXXX")
+       mount --bind "/proc/$target_pid/ns/user" "$userns_path"
+       echo "$userns_path" >>"$to_umount_list"
+
+       # Configure our container to attach to the first container's userns.
+       update_config '.linux.namespaces |= map(if .type == "user" then (.path 
= "'"$userns_path"'") else . end)
+               | del(.linux.uidMappings)
+               | del(.linux.gidMappings)'
+       runc run -d --console-socket "$CONSOLE_SOCKET" in_userns
+       [ "$status" -eq 0 ]
+
+       runc exec in_userns cat /proc/self/uid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+100000\s+65534$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+
+       runc exec in_userns cat /proc/self/gid_map
+       [ "$status" -eq 0 ]
+       if [ $EUID -eq 0 ]; then
+               grep -E '^\s+0\s+200000\s+65534$' <<<"$output"
+       else
+               grep -E '^\s+0\s+'$EUID'\s+1$' <<<"$output"
+       fi
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/.travis.yml 
new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/.travis.yml
--- old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/.travis.yml    
2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/.travis.yml    
1970-01-01 01:00:00.000000000 +0100
@@ -1,21 +0,0 @@
-# Copyright (C) 2017 SUSE LLC. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-language: go
-go:
-    - 1.13.x
-    - 1.16.x
-    - tip
-arch:
-    - AMD64
-    - ppc64le
-os:
-    - linux
-    - osx
-
-script:
-    - go test -cover -v ./...
-
-notifications:
-    email: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/README.md 
new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/README.md
--- old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/README.md      
2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/README.md      
2024-01-02 03:34:16.000000000 +0100
@@ -1,6 +1,6 @@
 ## `filepath-securejoin` ##
 
-[![Build 
Status](https://travis-ci.org/cyphar/filepath-securejoin.svg?branch=master)](https://travis-ci.org/cyphar/filepath-securejoin)
+[![Build 
Status](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml/badge.svg)](https://github.com/cyphar/filepath-securejoin/actions/workflows/ci.yml)
 
 An implementation of `SecureJoin`, a [candidate for inclusion in the Go
 standard library][go#20126]. The purpose of this function is to be a "secure"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/VERSION 
new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/VERSION
--- old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/VERSION        
2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/VERSION        
2024-01-02 03:34:16.000000000 +0100
@@ -1 +1 @@
-0.2.3
+0.2.4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/join.go 
new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/join.go
--- old/runc-1.1.10/vendor/github.com/cyphar/filepath-securejoin/join.go        
2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/vendor/github.com/cyphar/filepath-securejoin/join.go        
2024-01-02 03:34:16.000000000 +0100
@@ -39,17 +39,27 @@
 // components in the returned string are not modified (in other words are not
 // replaced with symlinks on the filesystem) after this function has returned.
 // Such a symlink race is necessarily out-of-scope of SecureJoin.
+//
+// Volume names in unsafePath are always discarded, regardless if they are
+// provided via direct input or when evaluating symlinks. Therefore:
+//
+// "C:\Temp" + "D:\path\to\file.txt" results in "C:\Temp\path\to\file.txt"
 func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
        // Use the os.* VFS implementation if none was specified.
        if vfs == nil {
                vfs = osVFS{}
        }
 
+       unsafePath = filepath.FromSlash(unsafePath)
        var path bytes.Buffer
        n := 0
        for unsafePath != "" {
                if n > 255 {
-                       return "", &os.PathError{Op: "SecureJoin", Path: root + 
"/" + unsafePath, Err: syscall.ELOOP}
+                       return "", &os.PathError{Op: "SecureJoin", Path: root + 
string(filepath.Separator) + unsafePath, Err: syscall.ELOOP}
+               }
+
+               if v := filepath.VolumeName(unsafePath); v != "" {
+                       unsafePath = unsafePath[len(v):]
                }
 
                // Next path component, p.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/runc-1.1.10/vendor/modules.txt 
new/runc-1.1.11/vendor/modules.txt
--- old/runc-1.1.10/vendor/modules.txt  2023-11-01 08:01:51.000000000 +0100
+++ new/runc-1.1.11/vendor/modules.txt  2024-01-02 03:34:16.000000000 +0100
@@ -20,7 +20,7 @@
 # github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d
 ## explicit; go 1.12
 github.com/cpuguy83/go-md2man/v2/md2man
-# github.com/cyphar/filepath-securejoin v0.2.3
+# github.com/cyphar/filepath-securejoin v0.2.4
 ## explicit; go 1.13
 github.com/cyphar/filepath-securejoin
 # github.com/docker/go-units v0.4.0

Reply via email to