ronantakizawa/codereview-bench huggingface dataset:
Repository: opencontainers/runc
File: libcontainer/configs/validate/validator.go (Go)

Code before:
		if !filepath.IsAbs(m.Destination) {
			return fmt.Errorf("invalid mount %+v: mount destination not absolute", m)
		}
	}

	return nil
}

func isHostNetNS(path string) (bool, error) {
	const currentProcessNetns = "/proc/self/ns/net"

	var st1, st2 unix.Stat_t

	if err := unix.Stat(currentProcessNetns, &st1); err != nil {
		return false, &os.PathError{Op: "stat", Path: currentProcessNetns, Err: err}
	}
	if err := unix.Stat(path, &st2); err != nil {
		return false, &os.PathError{Op: "stat", Path: path, Err: err}
	}

	return (st1.Dev == st2.Dev) && (st1.Ino == st2.Ino), nil
}

func ioPriority(config *configs.Config) error {
	ioPriroty := config.IOPriority
	if ioPriroty == nil {
		return nil
	}
	priority := config.IOPriority.Priority
	if priority < 0 || priority > 7 {
		return fmt.Errorf("invalid ioPriority.Priority: %d", priority)
	}
	return nil
}


Review comment (suggestion):
nit: your variable name has a typo.

nit: you can just do `if config.IOPriority == nil` instead of adding a variable which you don't use below.

Code after:

	if (c.Name != "" || c.Parent != "") && c.Path != "" {
		return fmt.Errorf("cgroup: either Path or Name and Parent should be used, got %+v", c)
	}

	r := c.Resources
	if r == nil {
		return nil
	}

	if !cgroups.IsCgroup2UnifiedMode() && r.Unified != nil {
		return cgroups.ErrV1NoUnified
	}

	if cgroups.IsCgroup2UnifiedMode() {
		_, err := cgroups.ConvertMemorySwapToCgroupV2Value(r.MemorySwap, r.Memory)
		if err != nil {
			return err
		}
	}

	return nil
}

func checkBindOptions(m *configs.Mount) error {
	if !m.IsBind() {
		return nil
	}
	// We must reject bind-mounts that also have filesystem-specific mount
	// options, because the kernel will completely ignore these flags and we
	// cannot set them per-mountpoint.
	//
	// It should be noted that (due to how the kernel caches superblocks), data
	// options could also silently ignored for other filesystems even when
	// doing a fresh mount, but there is no real way to avoid this (and it
	// matches how everything else works). There have been proposals to make it
	// possible for userspace to detect this caching, but this wouldn't help
	// runc because the behaviour wouldn't even be desirable for most users.
	if m.Data != "" {
		return errors.New("bind mounts cannot have any filesystem-specific options applied")
	}
	return nil
}

func checkIDMapMounts(config *configs.Config, m *configs.Mount) error {
	// Make sure MOUNT_ATTR_IDMAP is not set on any of our mounts. This
	// attribute is handled differently to all other attributes (through
	// m.IDMapping), so make sure we never store it in the actual config. This
	// really shouldn't ever happen.
	if m.RecAttr != nil && (m.RecAttr.Attr_set|m.RecAttr.Attr_clr)&unix.MOUNT_ATTR_IDMAP != 0 {
		return errors.New("mount configuration cannot contain recAttr for MOUNT_ATTR_IDMAP")
Repository: opencontainers/runc
File: libcontainer/process_linux.go (Go)

Code before:
	process         *Process
	bootstrapData   io.Reader
	initProcessPid  int
}

func (p *setnsProcess) startTime() (uint64, error) {
	stat, err := system.Stat(p.pid())
	return stat.StartTime, err
}

func (p *setnsProcess) signal(sig os.Signal) error {
	s, ok := sig.(unix.Signal)
	if !ok {
		return errors.New("os: unsupported signal type")
	}
	return unix.Kill(p.pid(), s)
}

func (p *setnsProcess) start() (retErr error) {
	defer p.comm.closeParent()

	if p.process.IOPriority != nil {
		if err := utils.SetIOPriority(p.process.IOPriority); err != nil {
			return err
		}
	}

	// get the "before" value of oom kill count
	oom, _ := p.manager.OOMKillCount()
	err := p.cmd.Start()
	// close the child-side of the pipes (controlled by child)
	p.comm.closeChild()
	if err != nil {
		return fmt.Errorf("error starting setns process: %w", err)
	}

	waitInit := initWaiter(p.comm.initSockParent)
	defer func() {
		if retErr != nil {
			if newOom, err := p.manager.OOMKillCount(); err == nil && newOom != oom {
				// Someone in this cgroup was killed, this _might_ be us.
				retErr = fmt.Errorf("%w (possibly OOM-killed)", retErr)
			}
			werr := <-waitInit
			if werr != nil {
				logrus.WithError(werr).Warn()
			}
			err := ignoreTerminateErrors(p.terminate())
			if err != nil {
				logrus.WithError(err).Warn("unable to terminate setnsProcess")
			}

Review comment (suggestion):
It would be way better to add
```go
if prio == nil {
   return nil
}
```

to the beginning of `utils.SetIOPriority`, and remove the condition here and in other places that call this.

Code after:
	manager         cgroups.Manager
	intelRdtPath    string
	config          *initConfig
	fds             []string
	process         *Process
	bootstrapData   io.Reader
	initProcessPid  int
}

func (p *setnsProcess) startTime() (uint64, error) {
	stat, err := system.Stat(p.pid())
	return stat.StartTime, err
}

func (p *setnsProcess) signal(sig os.Signal) error {
	s, ok := sig.(unix.Signal)
	if !ok {
		return errors.New("os: unsupported signal type")
	}
	return unix.Kill(p.pid(), s)
}

func (p *setnsProcess) start() (retErr error) {
	defer p.comm.closeParent()

	if p.process.IOPriority != nil {
		if err := setIOPriority(p.process.IOPriority); err != nil {
			return err
		}
	}

	// get the "before" value of oom kill count
	oom, _ := p.manager.OOMKillCount()
	err := p.cmd.Start()
	// close the child-side of the pipes (controlled by child)
	p.comm.closeChild()
	if err != nil {
		return fmt.Errorf("error starting setns process: %w", err)
	}

	waitInit := initWaiter(p.comm.initSockParent)
	defer func() {
		if retErr != nil {
			if newOom, err := p.manager.OOMKillCount(); err == nil && newOom != oom {
				// Someone in this cgroup was killed, this _might_ be us.
				retErr = fmt.Errorf("%w (possibly OOM-killed)", retErr)
			}
			werr := <-waitInit
			if werr != nil {
				logrus.WithError(werr).Warn()
			}
Repository: opencontainers/runc
File: libcontainer/nsenter/nsexec.c (C)

Code before:

void receive_idmapsources(int sockfd)
{
	receive_fd_sources(sockfd, "_LIBCONTAINER_IDMAP_FDS");
}

void nsexec(void)
{
	int pipenum;
	jmp_buf env;
	int sync_child_pipe[2], sync_grandchild_pipe[2];
	struct nlconfig_t config = { 0 };

	/*
	 * Setup a pipe to send logs to the parent. This should happen
	 * first, because bail will use that pipe.
	 */
	setup_logpipe();

	/*
	 * Get the init pipe fd from the environment. The init pipe is used to
	 * read the bootstrap data and tell the parent what the new pids are
	 * after the setup is done.
	 */
	pipenum = getenv_int("_LIBCONTAINER_INITPIPE");
	if (pipenum < 0) {
		/* We are not a runc init. Just return to go runtime. */
		return;
	}

	/*
	 * We need to re-exec if we are not in a cloned binary. This is necessary
	 * to ensure that containers won't be able to access the host binary
	 * through /proc/self/exe. See CVE-2019-5736.
	 */
	if (ensure_cloned_binary() < 0)
		bail("could not ensure we are a cloned binary");

	/*
	 * Inform the parent we're past initial setup.
	 * For the other side of this, see initWaiter.
	 */
	if (write(pipenum, "", 1) != 1)
		bail("could not inform the parent we are past initial setup");

	write_log(DEBUG, "=> nsexec container setup");

	/* Parse all of the netlink configuration. */
	nl_parse(pipenum, &config);

	/* Set oom_score_adj. This has to be done before !dumpable because

Review comment (bug):
```suggestion
	if (write_file(map, map_len, "/proc/self/timens_offsets") < 0)
		bail("failed to update /proc/self/timens_offsets");
```

Code after:
			bail("error closing fd_tree");
		}

		idmap_src += strlen(idmap_src) + 1;
	}

	if (close(userns_fd) < 0) {
		sane_kill(pid, SIGKILL);
		bail("error closing userns fd");
	}
}

void receive_idmapsources(int sockfd)
{
	receive_fd_sources(sockfd, "_LIBCONTAINER_IDMAP_FDS");
}

static void update_timens(char *map, size_t map_len)
{
	if (map == NULL || map_len == 0)
		return;
	write_log(DEBUG, "update /proc/self/timens_offsets to '%s'", map);
	if (write_file(map, map_len, "/proc/self/timens_offsets") < 0) {
		if (errno != EPERM)
			bail("failed to update /proc/self/timens_offsets");
	}
}

void nsexec(void)
{
	int pipenum;
	jmp_buf env;
	int sync_child_pipe[2], sync_grandchild_pipe[2];
	struct nlconfig_t config = { 0 };

	/*
	 * Setup a pipe to send logs to the parent. This should happen
	 * first, because bail will use that pipe.
	 */
	setup_logpipe();

	/*
	 * Get the init pipe fd from the environment. The init pipe is used to
	 * read the bootstrap data and tell the parent what the new pids are
	 * after the setup is done.
	 */
	pipenum = getenv_int("_LIBCONTAINER_INITPIPE");
	if (pipenum < 0) {
		/* We are not a runc init. Just return to go runtime. */
		return;
	}
Repository: opencontainers/runc
File: libcontainer/utils/utils_unix.go (Go)

Code before:
		return err
	}
	defer fdDir.Close()

	if err := EnsureProcHandle(fdDir); err != nil {
		return err
	}

	fdList, err := fdDir.Readdirnames(-1)
	if err != nil {
		return err
	}
	for _, fdStr := range fdList {
		fd, err := strconv.Atoi(fdStr)
		// Ignore non-numeric file names.
		if err != nil {
			continue
		}
		// Ignore descriptors lower than our specified minimum.
		if fd < minFd {
			continue
		}
		// Intentionally ignore errors from unix.CloseOnExec -- the cases where
		// this might fail are basically file descriptors that have already
		// been closed (including and especially the one that was created when
		// os.ReadDir did the "opendir" syscall).
		unix.CloseOnExec(fd)
	}
	return nil
}

// NewSockPair returns a new unix socket pair
func NewSockPair(name string) (parent *os.File, child *os.File, err error) {
	fds, err := unix.Socketpair(unix.AF_LOCAL, unix.SOCK_STREAM|unix.SOCK_CLOEXEC, 0)
	if err != nil {
		return nil, nil, err
	}
	return os.NewFile(uintptr(fds[1]), name+"-p"), os.NewFile(uintptr(fds[0]), name+"-c"), nil
}


Review comment (bug):
@kolyshkin Do you think we should just do a proper close here, since `close_range(2)` is more efficient and without race conditions? Or does it not really matter?

Code after:
)

func haveCloseRangeCloexec() bool {
	haveCloseRangeCloexecOnce.Do(func() {
		// Make sure we're not closing a random file descriptor.
		tmpFd, err := unix.FcntlInt(0, unix.F_DUPFD_CLOEXEC, 0)
		if err != nil {
			return
		}
		defer unix.Close(tmpFd)

		err = unix.CloseRange(uint(tmpFd), uint(tmpFd), unix.CLOSE_RANGE_CLOEXEC)
		// Any error means we cannot use close_range(CLOSE_RANGE_CLOEXEC).
		// -ENOSYS and -EINVAL ultimately mean we don't have support, but any
		// other potential error would imply that even the most basic close
		// operation wouldn't work.
		haveCloseRangeCloexecBool = err == nil
	})
	return haveCloseRangeCloexecBool
}

// CloseExecFrom applies O_CLOEXEC to all file descriptors currently open for
// the process (except for those below the given fd value).
func CloseExecFrom(minFd int) error {
	if haveCloseRangeCloexec() {
		err := unix.CloseRange(uint(minFd), math.MaxUint, unix.CLOSE_RANGE_CLOEXEC)
		return os.NewSyscallError("close_range", err)
	}

	fdDir, err := os.Open("/proc/self/fd")
	if err != nil {
		return err
	}
	defer fdDir.Close()

	if err := EnsureProcHandle(fdDir); err != nil {
		return err
	}

	fdList, err := fdDir.Readdirnames(-1)
	if err != nil {
		return err
	}
	for _, fdStr := range fdList {
		fd, err := strconv.Atoi(fdStr)
		// Ignore non-numeric file names.
		if err != nil {
			continue
		}
		// Ignore descriptors lower than our specified minimum.
		if fd < minFd {
Repository: opencontainers/runc
File: libcontainer/cgroups/fs2/memory.go (Go)

Code before:
	}
	// never write empty string to `memory.swap.max`, it means set to 0.
	if swapStr != "" {
		if err := cgroups.WriteFile(dirPath, "memory.swap.max", swapStr); err != nil {
			return err
		}
	}

	if val := numToStr(r.Memory); val != "" {
		if err := cgroups.WriteFile(dirPath, "memory.max", val); err != nil {
			return err
		}
	}

	// cgroup.Resources.KernelMemory is ignored

	if val := numToStr(r.MemoryReservation); val != "" {
		if err := cgroups.WriteFile(dirPath, "memory.low", val); err != nil {
			return err
		}
	}

	return nil
}

func statMemory(rootCgroupPath, dirPath string, stats *cgroups.Stats) error {
	const file = "memory.stat"
	statsFile, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY)
	if err != nil {
		return err
	}
	defer statsFile.Close()

	sc := bufio.NewScanner(statsFile)
	for sc.Scan() {
		t, v, err := fscommon.ParseKeyValue(sc.Text())
		if err != nil {
			return &parseError{Path: dirPath, File: file, Err: err}
		}
		stats.MemoryStats.Stats[t] = v
	}
	if err := sc.Err(); err != nil {
		return &parseError{Path: dirPath, File: file, Err: err}
	}
	stats.MemoryStats.Cache = stats.MemoryStats.Stats["file"]
	// Unlike cgroup v1 which has memory.use_hierarchy binary knob,
	// cgroup v2 is always hierarchical.
	stats.MemoryStats.UseHierarchy = true

	memoryUsage, err := getMemoryDataV2(dirPath, "")
	if err != nil {

Review comment (suggestion):
I don't love this, but it made unit testing simpler and keeps all exposed APIs unchanged.

Welcome better testing suggestions, especially some way to diff v1/v2 (either mock or e2e on real machines?)

Code after:
	}
	// never write empty string to `memory.swap.max`, it means set to 0.
	if swapStr != "" {
		if err := cgroups.WriteFile(dirPath, "memory.swap.max", swapStr); err != nil {
			return err
		}
	}

	if val := numToStr(r.Memory); val != "" {
		if err := cgroups.WriteFile(dirPath, "memory.max", val); err != nil {
			return err
		}
	}

	// cgroup.Resources.KernelMemory is ignored

	if val := numToStr(r.MemoryReservation); val != "" {
		if err := cgroups.WriteFile(dirPath, "memory.low", val); err != nil {
			return err
		}
	}

	return nil
}

func statMemory(dirPath string, stats *cgroups.Stats) error {
	const file = "memory.stat"
	statsFile, err := cgroups.OpenFile(dirPath, file, os.O_RDONLY)
	if err != nil {
		return err
	}
	defer statsFile.Close()

	sc := bufio.NewScanner(statsFile)
	for sc.Scan() {
		t, v, err := fscommon.ParseKeyValue(sc.Text())
		if err != nil {
			return &parseError{Path: dirPath, File: file, Err: err}
		}
		stats.MemoryStats.Stats[t] = v
	}
	if err := sc.Err(); err != nil {
		return &parseError{Path: dirPath, File: file, Err: err}
	}
	stats.MemoryStats.Cache = stats.MemoryStats.Stats["file"]
	// Unlike cgroup v1 which has memory.use_hierarchy binary knob,
	// cgroup v2 is always hierarchical.
	stats.MemoryStats.UseHierarchy = true

	memoryUsage, err := getMemoryDataV2(dirPath, "")
	if err != nil {
Repository: opencontainers/runc
File: libcontainer/configs/config_linux.go (Go)

Code before:
package configs

import "errors"

var (
	errNoUIDMap   = errors.New("User namespaces enabled, but no uid mappings found.")
	errNoUserMap  = errors.New("User namespaces enabled, but no user mapping found.")
	errNoGIDMap   = errors.New("User namespaces enabled, but no gid mappings found.")
	errNoGroupMap = errors.New("User namespaces enabled, but no group mapping found.")
)

// HostUID gets the translated uid for the process on host which could be
// different when user namespaces are enabled.
func (c Config) HostUID(containerId int) (int, error) {
	if c.Namespaces.Contains(NEWUSER) {
		if c.UIDMappings == nil || len(c.UIDMappings) == 0 {
			return -1, errNoUIDMap
		}
		id, found := c.hostIDFromMapping(containerId, c.UIDMappings)
		if !found {
			return -1, errNoUserMap
		}
		return id, nil
	}
	// Return unchanged id.
	return containerId, nil
}

// HostRootUID gets the root uid for the process on host which could be non-zero
// when user namespaces are enabled.
func (c Config) HostRootUID() (int, error) {
	return c.HostUID(0)
}

// HostGID gets the translated gid for the process on host which could be
// different when user namespaces are enabled.
func (c Config) HostGID(containerId int) (int, error) {
	if c.Namespaces.Contains(NEWUSER) {
		if c.GIDMappings == nil {
			return -1, errNoGIDMap
		}

Review comment (suggestion):
Very tiny nit: this could be simplified to just;

```suggestion
		if len(c.UIDMappings) == 0 {
```

a `nil` slice has length `0`; https://go.dev/play/p/tSRvH2e-yH0

```go
package main

import "fmt"

func main() {
	var foo []int = nil
	if len(foo) == 0 {
		fmt.Println("YUP!")
	}
}
```



Code after:
package configs

import "errors"

var (
	errNoUIDMap   = errors.New("User namespaces enabled, but no uid mappings found.")
	errNoUserMap  = errors.New("User namespaces enabled, but no user mapping found.")
	errNoGIDMap   = errors.New("User namespaces enabled, but no gid mappings found.")
	errNoGroupMap = errors.New("User namespaces enabled, but no group mapping found.")
)

// HostUID gets the translated uid for the process on host which could be
// different when user namespaces are enabled.
func (c Config) HostUID(containerId int) (int, error) {
	if c.Namespaces.Contains(NEWUSER) {
		if c.UIDMappings == nil {
			return -1, errNoUIDMap
		}
		id, found := c.hostIDFromMapping(containerId, c.UIDMappings)
		if !found {
			return -1, errNoUserMap
		}
		return id, nil
	}
	// Return unchanged id.
	return containerId, nil
}

// HostRootUID gets the root uid for the process on host which could be non-zero
// when user namespaces are enabled.
func (c Config) HostRootUID() (int, error) {
	return c.HostUID(0)
}

// HostGID gets the translated gid for the process on host which could be
// different when user namespaces are enabled.
func (c Config) HostGID(containerId int) (int, error) {
	if c.Namespaces.Contains(NEWUSER) {
		if c.GIDMappings == nil {
			return -1, errNoGIDMap
		}
Repository: opencontainers/runc
File: libcontainer/configs/validate/rootless_test.go (Go)

Code before:
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when uid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "uid=5"
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting uid=5 in mount options")
	}

	config.Mounts[0].Data = "uid=0"
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting uid=0 in mount options: %+v", err)
	}

	config.Mounts[0].Data = "uid=2"
	config.UIDMappings[0].Size = 10
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting uid=2 in mount options and UidMapping[0].size is 10")
	}

	config.Mounts[0].Data = "uid=20"
	config.UIDMappings[0].Size = 10
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting uid=20 in mount options and UidMapping[0].size is 10")
	}
}

func TestValidateRootlessEUIDMountGid(t *testing.T) {
	config := rootlessEUIDConfig()
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when gid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	if err := Validate(config); err == nil {

Review comment (suggestion):
In this error msg(Line 98), we should change `UidMapping` to `UIDMapping`.

Code after:
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when uid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "uid=5"
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting uid=5 in mount options")
	}

	config.Mounts[0].Data = "uid=0"
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting uid=0 in mount options: %+v", err)
	}

	config.Mounts[0].Data = "uid=2"
	config.UIDMappings[0].Size = 10
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting uid=2 in mount options and UIDMappings[0].size is 10")
	}

	config.Mounts[0].Data = "uid=20"
	config.UIDMappings[0].Size = 10
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting uid=20 in mount options and UIDMappings[0].size is 10")
	}
}

func TestValidateRootlessEUIDMountGid(t *testing.T) {
	config := rootlessEUIDConfig()
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when gid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	if err := Validate(config); err == nil {
Repository: opencontainers/runc
File: libcontainer/configs/validate/rootless_test.go (Go)

Code before:
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when gid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting gid=5 in mount options")
	}

	config.Mounts[0].Data = "gid=0"
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting gid=0 in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	config.GIDMappings[0].Size = 10
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting gid=5 in mount options and GidMapping[0].size is 10")
	}

	config.Mounts[0].Data = "gid=11"
	config.GIDMappings[0].Size = 10
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting gid=11 in mount options and GidMapping[0].size is 10")
	}
}

func BenchmarkRootlessEUIDMount(b *testing.B) {
	config := rootlessEUIDConfig()
	config.GIDMappings[0].Size = 10
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
			Data:        "newinstance,ptmxmode=0666,mode=0620,uid=0,gid=5",
		},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := rootlessEUIDMount(config)
		if err != nil {

Review comment (suggestion):
In this error msg, we should change `GidMapping` to `GIDMapping`.

Code after:
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
		},
	}

	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when gid= not set in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting gid=5 in mount options")
	}

	config.Mounts[0].Data = "gid=0"
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting gid=0 in mount options: %+v", err)
	}

	config.Mounts[0].Data = "gid=5"
	config.GIDMappings[0].Size = 10
	if err := Validate(config); err != nil {
		t.Errorf("Expected error to not occur when setting gid=5 in mount options and GIDMappings[0].size is 10")
	}

	config.Mounts[0].Data = "gid=11"
	config.GIDMappings[0].Size = 10
	if err := Validate(config); err == nil {
		t.Errorf("Expected error to occur when setting gid=11 in mount options and GIDMappings[0].size is 10")
	}
}

func BenchmarkRootlessEUIDMount(b *testing.B) {
	config := rootlessEUIDConfig()
	config.GIDMappings[0].Size = 10
	config.Mounts = []*configs.Mount{
		{
			Source:      "devpts",
			Destination: "/dev/pts",
			Device:      "devpts",
			Data:        "newinstance,ptmxmode=0666,mode=0620,uid=0,gid=5",
		},
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		err := rootlessEUIDMount(config)
		if err != nil {