Prechádzať zdrojové kódy

Merge pull request #47206 from thaJeztah/24.0_backport_vendor_runc_1.1.11

[24.0 backport] vendor: github.com/opencontainers/runc v1.1.11
Akihiro Suda 1 rok pred
rodič
commit
3205a51dc5

+ 1 - 1
vendor.mod

@@ -71,7 +71,7 @@ require (
 	github.com/morikuni/aec v1.0.0
 	github.com/opencontainers/go-digest v1.0.0
 	github.com/opencontainers/image-spec v1.1.0-rc3
-	github.com/opencontainers/runc v1.1.10
+	github.com/opencontainers/runc v1.1.11
 	github.com/opencontainers/runtime-spec v1.1.0-rc.2
 	github.com/opencontainers/selinux v1.11.0
 	github.com/pelletier/go-toml v1.9.5

+ 2 - 2
vendor.sum

@@ -1147,8 +1147,8 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm
 github.com/opencontainers/runc v1.0.0-rc92/go.mod h1:X1zlU4p7wOlX4+WRCz+hvlRv8phdL7UqbYD+vQwNMmE=
 github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
 github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
-github.com/opencontainers/runc v1.1.10 h1:EaL5WeO9lv9wmS6SASjszOeQdSctvpbu0DdBQBizE40=
-github.com/opencontainers/runc v1.1.10/go.mod h1:+/R6+KmDlh+hOO8NkjmgkG9Qzvypzk0yXxAPYYR65+M=
+github.com/opencontainers/runc v1.1.11 h1:9LjxyVlE0BPMRP2wuQDRlHV4941Jp9rc3F0+YKimopA=
+github.com/opencontainers/runc v1.1.11/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
 github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
 github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=

+ 2 - 0
vendor/github.com/opencontainers/runc/libcontainer/cgroups/stats.go

@@ -78,6 +78,8 @@ type MemoryStats struct {
 	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

+ 3 - 3
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go

@@ -21,9 +21,9 @@ type Rlimit struct {
 
 // 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

+ 24 - 6
vendor/github.com/opencontainers/runc/libcontainer/configs/config_linux.go

@@ -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 @@ func (c Config) HostUID(containerId int) (int, error) {
 		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 @@ func (c Config) HostGID(containerId int) (int, error) {
 		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 @@ func (c Config) HostRootGID() (int, error) {
 
 // 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)

+ 79 - 0
vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps.c

@@ -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);
+}

+ 186 - 0
vendor/github.com/opencontainers/runc/libcontainer/userns/userns_maps_linux.go

@@ -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
+}

+ 1 - 1
vendor/modules.txt

@@ -837,7 +837,7 @@ github.com/opencontainers/go-digest/digestset
 github.com/opencontainers/image-spec/identity
 github.com/opencontainers/image-spec/specs-go
 github.com/opencontainers/image-spec/specs-go/v1
-# github.com/opencontainers/runc v1.1.10
+# github.com/opencontainers/runc v1.1.11
 ## explicit; go 1.17
 github.com/opencontainers/runc/libcontainer/cgroups
 github.com/opencontainers/runc/libcontainer/configs