1// +build !windows
2
3package main
4
5import (
6	"bufio"
7	"context"
8	"encoding/json"
9	"fmt"
10	"io/ioutil"
11	"os"
12	"os/exec"
13	"path/filepath"
14	"regexp"
15	"strconv"
16	"strings"
17	"syscall"
18	"testing"
19	"time"
20
21	"github.com/creack/pty"
22	"github.com/docker/docker/client"
23	"github.com/docker/docker/integration-cli/cli"
24	"github.com/docker/docker/integration-cli/cli/build"
25	"github.com/docker/docker/pkg/homedir"
26	"github.com/docker/docker/pkg/parsers"
27	"github.com/docker/docker/pkg/sysinfo"
28	"github.com/moby/sys/mount"
29	"gotest.tools/v3/assert"
30	"gotest.tools/v3/icmd"
31)
32
33// #6509
34func (s *DockerSuite) TestRunRedirectStdout(c *testing.T) {
35	checkRedirect := func(command string) {
36		_, tty, err := pty.Open()
37		assert.Assert(c, err == nil, "Could not open pty")
38		cmd := exec.Command("sh", "-c", command)
39		cmd.Stdin = tty
40		cmd.Stdout = tty
41		cmd.Stderr = tty
42		assert.NilError(c, cmd.Start())
43		ch := make(chan error, 1)
44		go func() {
45			ch <- cmd.Wait()
46			close(ch)
47		}()
48
49		select {
50		case <-time.After(10 * time.Second):
51			c.Fatal("command timeout")
52		case err := <-ch:
53			assert.Assert(c, err == nil, "wait err")
54		}
55	}
56
57	checkRedirect(dockerBinary + " run -i busybox cat /etc/passwd | grep -q root")
58	checkRedirect(dockerBinary + " run busybox cat /etc/passwd | grep -q root")
59}
60
61// Test recursive bind mount works by default
62func (s *DockerSuite) TestRunWithVolumesIsRecursive(c *testing.T) {
63	// /tmp gets permission denied
64	testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon)
65	tmpDir, err := ioutil.TempDir("", "docker_recursive_mount_test")
66	assert.NilError(c, err)
67
68	defer os.RemoveAll(tmpDir)
69
70	// Create a temporary tmpfs mount.
71	tmpfsDir := filepath.Join(tmpDir, "tmpfs")
72	assert.Assert(c, os.MkdirAll(tmpfsDir, 0777) == nil, "failed to mkdir at %s", tmpfsDir)
73	assert.Assert(c, mount.Mount("tmpfs", tmpfsDir, "tmpfs", "") == nil, "failed to create a tmpfs mount at %s", tmpfsDir)
74
75	f, err := ioutil.TempFile(tmpfsDir, "touch-me")
76	assert.NilError(c, err)
77	defer f.Close()
78
79	out, _ := dockerCmd(c, "run", "--name", "test-data", "--volume", fmt.Sprintf("%s:/tmp:ro", tmpDir), "busybox:latest", "ls", "/tmp/tmpfs")
80	assert.Assert(c, strings.Contains(out, filepath.Base(f.Name())), "Recursive bind mount test failed. Expected file not found")
81}
82
83func (s *DockerSuite) TestRunDeviceDirectory(c *testing.T) {
84	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm)
85	if _, err := os.Stat("/dev/snd"); err != nil {
86		c.Skip("Host does not have /dev/snd")
87	}
88
89	out, _ := dockerCmd(c, "run", "--device", "/dev/snd:/dev/snd", "busybox", "sh", "-c", "ls /dev/snd/")
90	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "timer"), "expected output /dev/snd/timer")
91	out, _ = dockerCmd(c, "run", "--device", "/dev/snd:/dev/othersnd", "busybox", "sh", "-c", "ls /dev/othersnd/")
92	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "seq"), "expected output /dev/othersnd/seq")
93}
94
95// TestRunAttachDetach checks attaching and detaching with the default escape sequence.
96func (s *DockerSuite) TestRunAttachDetach(c *testing.T) {
97	name := "attach-detach"
98
99	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
100
101	cmd := exec.Command(dockerBinary, "attach", name)
102	stdout, err := cmd.StdoutPipe()
103	assert.NilError(c, err)
104	cpty, tty, err := pty.Open()
105	assert.NilError(c, err)
106	defer cpty.Close()
107	cmd.Stdin = tty
108	assert.NilError(c, cmd.Start())
109	assert.Assert(c, waitRun(name) == nil)
110
111	_, err = cpty.Write([]byte("hello\n"))
112	assert.NilError(c, err)
113
114	out, err := bufio.NewReader(stdout).ReadString('\n')
115	assert.NilError(c, err)
116	assert.Equal(c, strings.TrimSpace(out), "hello")
117
118	// escape sequence
119	_, err = cpty.Write([]byte{16})
120	assert.NilError(c, err)
121	time.Sleep(100 * time.Millisecond)
122	_, err = cpty.Write([]byte{17})
123	assert.NilError(c, err)
124
125	ch := make(chan struct{}, 1)
126	go func() {
127		cmd.Wait()
128		ch <- struct{}{}
129	}()
130
131	select {
132	case <-ch:
133	case <-time.After(10 * time.Second):
134		c.Fatal("timed out waiting for container to exit")
135	}
136
137	running := inspectField(c, name, "State.Running")
138	assert.Equal(c, running, "true", "expected container to still be running")
139
140	out, _ = dockerCmd(c, "events", "--since=0", "--until", daemonUnixTime(c), "-f", "container="+name)
141	// attach and detach event should be monitored
142	assert.Assert(c, strings.Contains(out, "attach"))
143	assert.Assert(c, strings.Contains(out, "detach"))
144}
145
146// TestRunAttachDetachFromFlag checks attaching and detaching with the escape sequence specified via flags.
147func (s *DockerSuite) TestRunAttachDetachFromFlag(c *testing.T) {
148	name := "attach-detach"
149	keyCtrlA := []byte{1}
150	keyA := []byte{97}
151
152	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
153
154	cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name)
155	stdout, err := cmd.StdoutPipe()
156	if err != nil {
157		c.Fatal(err)
158	}
159	cpty, tty, err := pty.Open()
160	if err != nil {
161		c.Fatal(err)
162	}
163	defer cpty.Close()
164	cmd.Stdin = tty
165	if err := cmd.Start(); err != nil {
166		c.Fatal(err)
167	}
168	assert.Assert(c, waitRun(name) == nil)
169
170	if _, err := cpty.Write([]byte("hello\n")); err != nil {
171		c.Fatal(err)
172	}
173
174	out, err := bufio.NewReader(stdout).ReadString('\n')
175	if err != nil {
176		c.Fatal(err)
177	}
178	if strings.TrimSpace(out) != "hello" {
179		c.Fatalf("expected 'hello', got %q", out)
180	}
181
182	// escape sequence
183	if _, err := cpty.Write(keyCtrlA); err != nil {
184		c.Fatal(err)
185	}
186	time.Sleep(100 * time.Millisecond)
187	if _, err := cpty.Write(keyA); err != nil {
188		c.Fatal(err)
189	}
190
191	ch := make(chan struct{}, 1)
192	go func() {
193		cmd.Wait()
194		ch <- struct{}{}
195	}()
196
197	select {
198	case <-ch:
199	case <-time.After(10 * time.Second):
200		c.Fatal("timed out waiting for container to exit")
201	}
202
203	running := inspectField(c, name, "State.Running")
204	assert.Equal(c, running, "true", "expected container to still be running")
205}
206
207// TestRunAttachDetachFromInvalidFlag checks attaching and detaching with the escape sequence specified via flags.
208func (s *DockerSuite) TestRunAttachDetachFromInvalidFlag(c *testing.T) {
209	name := "attach-detach"
210	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "top")
211	assert.Assert(c, waitRun(name) == nil)
212
213	// specify an invalid detach key, container will ignore it and use default
214	cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-A,a", name)
215	stdout, err := cmd.StdoutPipe()
216	if err != nil {
217		c.Fatal(err)
218	}
219	cpty, tty, err := pty.Open()
220	if err != nil {
221		c.Fatal(err)
222	}
223	defer cpty.Close()
224	cmd.Stdin = tty
225	if err := cmd.Start(); err != nil {
226		c.Fatal(err)
227	}
228	go cmd.Wait()
229
230	bufReader := bufio.NewReader(stdout)
231	out, err := bufReader.ReadString('\n')
232	if err != nil {
233		c.Fatal(err)
234	}
235	// it should print a warning to indicate the detach key flag is invalid
236	errStr := "Invalid detach keys (ctrl-A,a) provided"
237	assert.Equal(c, strings.TrimSpace(out), errStr)
238}
239
240// TestRunAttachDetachFromConfig checks attaching and detaching with the escape sequence specified via config file.
241func (s *DockerSuite) TestRunAttachDetachFromConfig(c *testing.T) {
242	keyCtrlA := []byte{1}
243	keyA := []byte{97}
244
245	// Setup config
246	homeKey := homedir.Key()
247	homeVal := homedir.Get()
248	tmpDir, err := ioutil.TempDir("", "fake-home")
249	assert.NilError(c, err)
250	defer os.RemoveAll(tmpDir)
251
252	dotDocker := filepath.Join(tmpDir, ".docker")
253	os.Mkdir(dotDocker, 0600)
254	tmpCfg := filepath.Join(dotDocker, "config.json")
255
256	defer func() { os.Setenv(homeKey, homeVal) }()
257	os.Setenv(homeKey, tmpDir)
258
259	data := `{
260		"detachKeys": "ctrl-a,a"
261	}`
262
263	err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
264	assert.NilError(c, err)
265
266	// Then do the work
267	name := "attach-detach"
268	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
269
270	cmd := exec.Command(dockerBinary, "attach", name)
271	stdout, err := cmd.StdoutPipe()
272	if err != nil {
273		c.Fatal(err)
274	}
275	cpty, tty, err := pty.Open()
276	if err != nil {
277		c.Fatal(err)
278	}
279	defer cpty.Close()
280	cmd.Stdin = tty
281	if err := cmd.Start(); err != nil {
282		c.Fatal(err)
283	}
284	assert.Assert(c, waitRun(name) == nil)
285
286	if _, err := cpty.Write([]byte("hello\n")); err != nil {
287		c.Fatal(err)
288	}
289
290	out, err := bufio.NewReader(stdout).ReadString('\n')
291	if err != nil {
292		c.Fatal(err)
293	}
294	if strings.TrimSpace(out) != "hello" {
295		c.Fatalf("expected 'hello', got %q", out)
296	}
297
298	// escape sequence
299	if _, err := cpty.Write(keyCtrlA); err != nil {
300		c.Fatal(err)
301	}
302	time.Sleep(100 * time.Millisecond)
303	if _, err := cpty.Write(keyA); err != nil {
304		c.Fatal(err)
305	}
306
307	ch := make(chan struct{}, 1)
308	go func() {
309		cmd.Wait()
310		ch <- struct{}{}
311	}()
312
313	select {
314	case <-ch:
315	case <-time.After(10 * time.Second):
316		c.Fatal("timed out waiting for container to exit")
317	}
318
319	running := inspectField(c, name, "State.Running")
320	assert.Equal(c, running, "true", "expected container to still be running")
321}
322
323// TestRunAttachDetachKeysOverrideConfig checks attaching and detaching with the detach flags, making sure it overrides config file
324func (s *DockerSuite) TestRunAttachDetachKeysOverrideConfig(c *testing.T) {
325	keyCtrlA := []byte{1}
326	keyA := []byte{97}
327
328	// Setup config
329	homeKey := homedir.Key()
330	homeVal := homedir.Get()
331	tmpDir, err := ioutil.TempDir("", "fake-home")
332	assert.NilError(c, err)
333	defer os.RemoveAll(tmpDir)
334
335	dotDocker := filepath.Join(tmpDir, ".docker")
336	os.Mkdir(dotDocker, 0600)
337	tmpCfg := filepath.Join(dotDocker, "config.json")
338
339	defer func() { os.Setenv(homeKey, homeVal) }()
340	os.Setenv(homeKey, tmpDir)
341
342	data := `{
343		"detachKeys": "ctrl-e,e"
344	}`
345
346	err = ioutil.WriteFile(tmpCfg, []byte(data), 0600)
347	assert.NilError(c, err)
348
349	// Then do the work
350	name := "attach-detach"
351	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
352
353	cmd := exec.Command(dockerBinary, "attach", "--detach-keys=ctrl-a,a", name)
354	stdout, err := cmd.StdoutPipe()
355	if err != nil {
356		c.Fatal(err)
357	}
358	cpty, tty, err := pty.Open()
359	if err != nil {
360		c.Fatal(err)
361	}
362	defer cpty.Close()
363	cmd.Stdin = tty
364	if err := cmd.Start(); err != nil {
365		c.Fatal(err)
366	}
367	assert.Assert(c, waitRun(name) == nil)
368
369	if _, err := cpty.Write([]byte("hello\n")); err != nil {
370		c.Fatal(err)
371	}
372
373	out, err := bufio.NewReader(stdout).ReadString('\n')
374	if err != nil {
375		c.Fatal(err)
376	}
377	if strings.TrimSpace(out) != "hello" {
378		c.Fatalf("expected 'hello', got %q", out)
379	}
380
381	// escape sequence
382	if _, err := cpty.Write(keyCtrlA); err != nil {
383		c.Fatal(err)
384	}
385	time.Sleep(100 * time.Millisecond)
386	if _, err := cpty.Write(keyA); err != nil {
387		c.Fatal(err)
388	}
389
390	ch := make(chan struct{}, 1)
391	go func() {
392		cmd.Wait()
393		ch <- struct{}{}
394	}()
395
396	select {
397	case <-ch:
398	case <-time.After(10 * time.Second):
399		c.Fatal("timed out waiting for container to exit")
400	}
401
402	running := inspectField(c, name, "State.Running")
403	assert.Equal(c, running, "true", "expected container to still be running")
404}
405
406func (s *DockerSuite) TestRunAttachInvalidDetachKeySequencePreserved(c *testing.T) {
407	name := "attach-detach"
408	keyA := []byte{97}
409	keyB := []byte{98}
410
411	dockerCmd(c, "run", "--name", name, "-itd", "busybox", "cat")
412
413	cmd := exec.Command(dockerBinary, "attach", "--detach-keys=a,b,c", name)
414	stdout, err := cmd.StdoutPipe()
415	if err != nil {
416		c.Fatal(err)
417	}
418	cpty, tty, err := pty.Open()
419	if err != nil {
420		c.Fatal(err)
421	}
422	defer cpty.Close()
423	cmd.Stdin = tty
424	if err := cmd.Start(); err != nil {
425		c.Fatal(err)
426	}
427	go cmd.Wait()
428	assert.Assert(c, waitRun(name) == nil)
429
430	// Invalid escape sequence aba, should print aba in output
431	if _, err := cpty.Write(keyA); err != nil {
432		c.Fatal(err)
433	}
434	time.Sleep(100 * time.Millisecond)
435	if _, err := cpty.Write(keyB); err != nil {
436		c.Fatal(err)
437	}
438	time.Sleep(100 * time.Millisecond)
439	if _, err := cpty.Write(keyA); err != nil {
440		c.Fatal(err)
441	}
442	time.Sleep(100 * time.Millisecond)
443	if _, err := cpty.Write([]byte("\n")); err != nil {
444		c.Fatal(err)
445	}
446
447	out, err := bufio.NewReader(stdout).ReadString('\n')
448	if err != nil {
449		c.Fatal(err)
450	}
451	if strings.TrimSpace(out) != "aba" {
452		c.Fatalf("expected 'aba', got %q", out)
453	}
454}
455
456// "test" should be printed
457func (s *DockerSuite) TestRunWithCPUQuota(c *testing.T) {
458	testRequires(c, cpuCfsQuota)
459
460	file := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
461	out, _ := dockerCmd(c, "run", "--cpu-quota", "8000", "--name", "test", "busybox", "cat", file)
462	assert.Equal(c, strings.TrimSpace(out), "8000")
463
464	out = inspectField(c, "test", "HostConfig.CpuQuota")
465	assert.Equal(c, out, "8000", "setting the CPU CFS quota failed")
466}
467
468func (s *DockerSuite) TestRunWithCpuPeriod(c *testing.T) {
469	testRequires(c, cpuCfsPeriod)
470
471	file := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
472	out, _ := dockerCmd(c, "run", "--cpu-period", "50000", "--name", "test", "busybox", "cat", file)
473	assert.Equal(c, strings.TrimSpace(out), "50000")
474
475	out, _ = dockerCmd(c, "run", "--cpu-period", "0", "busybox", "cat", file)
476	assert.Equal(c, strings.TrimSpace(out), "100000")
477
478	out = inspectField(c, "test", "HostConfig.CpuPeriod")
479	assert.Equal(c, out, "50000", "setting the CPU CFS period failed")
480}
481
482func (s *DockerSuite) TestRunWithInvalidCpuPeriod(c *testing.T) {
483	testRequires(c, cpuCfsPeriod)
484	out, _, err := dockerCmdWithError("run", "--cpu-period", "900", "busybox", "true")
485	assert.ErrorContains(c, err, "")
486	expected := "CPU cfs period can not be less than 1ms (i.e. 1000) or larger than 1s (i.e. 1000000)"
487	assert.Assert(c, strings.Contains(out, expected))
488
489	out, _, err = dockerCmdWithError("run", "--cpu-period", "2000000", "busybox", "true")
490	assert.ErrorContains(c, err, "")
491	assert.Assert(c, strings.Contains(out, expected))
492
493	out, _, err = dockerCmdWithError("run", "--cpu-period", "-3", "busybox", "true")
494	assert.ErrorContains(c, err, "")
495	assert.Assert(c, strings.Contains(out, expected))
496}
497
498func (s *DockerSuite) TestRunWithKernelMemory(c *testing.T) {
499	testRequires(c, DaemonIsLinux, kernelMemorySupport)
500
501	file := "/sys/fs/cgroup/memory/memory.kmem.limit_in_bytes"
502	cli.DockerCmd(c, "run", "--kernel-memory", "50M", "--name", "test1", "busybox", "cat", file).Assert(c, icmd.Expected{
503		Out: "52428800",
504	})
505
506	cli.InspectCmd(c, "test1", cli.Format(".HostConfig.KernelMemory")).Assert(c, icmd.Expected{
507		Out: "52428800",
508	})
509}
510
511func (s *DockerSuite) TestRunWithInvalidKernelMemory(c *testing.T) {
512	testRequires(c, DaemonIsLinux, kernelMemorySupport)
513
514	out, _, err := dockerCmdWithError("run", "--kernel-memory", "2M", "busybox", "true")
515	assert.ErrorContains(c, err, "")
516	expected := "Minimum kernel memory limit allowed is 4MB"
517	assert.Assert(c, strings.Contains(out, expected))
518
519	out, _, err = dockerCmdWithError("run", "--kernel-memory", "-16m", "--name", "test2", "busybox", "echo", "test")
520	assert.ErrorContains(c, err, "")
521	expected = "invalid size"
522	assert.Assert(c, strings.Contains(out, expected))
523}
524
525func (s *DockerSuite) TestRunWithCPUShares(c *testing.T) {
526	testRequires(c, cpuShare)
527
528	file := "/sys/fs/cgroup/cpu/cpu.shares"
529	out, _ := dockerCmd(c, "run", "--cpu-shares", "1000", "--name", "test", "busybox", "cat", file)
530	assert.Equal(c, strings.TrimSpace(out), "1000")
531
532	out = inspectField(c, "test", "HostConfig.CPUShares")
533	assert.Equal(c, out, "1000")
534}
535
536// "test" should be printed
537func (s *DockerSuite) TestRunEchoStdoutWithCPUSharesAndMemoryLimit(c *testing.T) {
538	testRequires(c, cpuShare)
539	testRequires(c, memoryLimitSupport)
540	cli.DockerCmd(c, "run", "--cpu-shares", "1000", "-m", "32m", "busybox", "echo", "test").Assert(c, icmd.Expected{
541		Out: "test\n",
542	})
543}
544
545func (s *DockerSuite) TestRunWithCpusetCpus(c *testing.T) {
546	testRequires(c, cgroupCpuset)
547
548	file := "/sys/fs/cgroup/cpuset/cpuset.cpus"
549	out, _ := dockerCmd(c, "run", "--cpuset-cpus", "0", "--name", "test", "busybox", "cat", file)
550	assert.Equal(c, strings.TrimSpace(out), "0")
551
552	out = inspectField(c, "test", "HostConfig.CpusetCpus")
553	assert.Equal(c, out, "0")
554}
555
556func (s *DockerSuite) TestRunWithCpusetMems(c *testing.T) {
557	testRequires(c, cgroupCpuset)
558
559	file := "/sys/fs/cgroup/cpuset/cpuset.mems"
560	out, _ := dockerCmd(c, "run", "--cpuset-mems", "0", "--name", "test", "busybox", "cat", file)
561	assert.Equal(c, strings.TrimSpace(out), "0")
562
563	out = inspectField(c, "test", "HostConfig.CpusetMems")
564	assert.Equal(c, out, "0")
565}
566
567func (s *DockerSuite) TestRunWithBlkioWeight(c *testing.T) {
568	testRequires(c, blkioWeight)
569
570	file := "/sys/fs/cgroup/blkio/blkio.weight"
571	out, _ := dockerCmd(c, "run", "--blkio-weight", "300", "--name", "test", "busybox", "cat", file)
572	assert.Equal(c, strings.TrimSpace(out), "300")
573
574	out = inspectField(c, "test", "HostConfig.BlkioWeight")
575	assert.Equal(c, out, "300")
576}
577
578func (s *DockerSuite) TestRunWithInvalidBlkioWeight(c *testing.T) {
579	testRequires(c, blkioWeight)
580	out, _, err := dockerCmdWithError("run", "--blkio-weight", "5", "busybox", "true")
581	assert.ErrorContains(c, err, "", out)
582	expected := "Range of blkio weight is from 10 to 1000"
583	assert.Assert(c, strings.Contains(out, expected))
584}
585
586func (s *DockerSuite) TestRunWithInvalidPathforBlkioWeightDevice(c *testing.T) {
587	testRequires(c, blkioWeight)
588	out, _, err := dockerCmdWithError("run", "--blkio-weight-device", "/dev/sdX:100", "busybox", "true")
589	assert.ErrorContains(c, err, "", out)
590}
591
592func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadBps(c *testing.T) {
593	testRequires(c, blkioWeight)
594	out, _, err := dockerCmdWithError("run", "--device-read-bps", "/dev/sdX:500", "busybox", "true")
595	assert.ErrorContains(c, err, "", out)
596}
597
598func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteBps(c *testing.T) {
599	testRequires(c, blkioWeight)
600	out, _, err := dockerCmdWithError("run", "--device-write-bps", "/dev/sdX:500", "busybox", "true")
601	assert.ErrorContains(c, err, "", out)
602}
603
604func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceReadIOps(c *testing.T) {
605	testRequires(c, blkioWeight)
606	out, _, err := dockerCmdWithError("run", "--device-read-iops", "/dev/sdX:500", "busybox", "true")
607	assert.ErrorContains(c, err, "", out)
608}
609
610func (s *DockerSuite) TestRunWithInvalidPathforBlkioDeviceWriteIOps(c *testing.T) {
611	testRequires(c, blkioWeight)
612	out, _, err := dockerCmdWithError("run", "--device-write-iops", "/dev/sdX:500", "busybox", "true")
613	assert.ErrorContains(c, err, "", out)
614}
615
616func (s *DockerSuite) TestRunOOMExitCode(c *testing.T) {
617	testRequires(c, memoryLimitSupport, swapMemorySupport, NotPpc64le)
618	errChan := make(chan error, 1)
619	go func() {
620		defer close(errChan)
621		// memory limit lower than 8MB will raise an error of "device or resource busy" from docker-runc.
622		out, exitCode, _ := dockerCmdWithError("run", "-m", "8MB", "busybox", "sh", "-c", "x=a; while true; do x=$x$x$x$x; done")
623		if expected := 137; exitCode != expected {
624			errChan <- fmt.Errorf("wrong exit code for OOM container: expected %d, got %d (output: %q)", expected, exitCode, out)
625		}
626	}()
627
628	select {
629	case err := <-errChan:
630		assert.NilError(c, err)
631	case <-time.After(600 * time.Second):
632		c.Fatal("Timeout waiting for container to die on OOM")
633	}
634}
635
636func (s *DockerSuite) TestRunWithMemoryLimit(c *testing.T) {
637	testRequires(c, memoryLimitSupport)
638
639	file := "/sys/fs/cgroup/memory/memory.limit_in_bytes"
640	cli.DockerCmd(c, "run", "-m", "32M", "--name", "test", "busybox", "cat", file).Assert(c, icmd.Expected{
641		Out: "33554432",
642	})
643	cli.InspectCmd(c, "test", cli.Format(".HostConfig.Memory")).Assert(c, icmd.Expected{
644		Out: "33554432",
645	})
646}
647
648// TestRunWithoutMemoryswapLimit sets memory limit and disables swap
649// memory limit, this means the processes in the container can use
650// 16M memory and as much swap memory as they need (if the host
651// supports swap memory).
652func (s *DockerSuite) TestRunWithoutMemoryswapLimit(c *testing.T) {
653	testRequires(c, DaemonIsLinux)
654	testRequires(c, memoryLimitSupport)
655	testRequires(c, swapMemorySupport)
656	dockerCmd(c, "run", "-m", "32m", "--memory-swap", "-1", "busybox", "true")
657}
658
659func (s *DockerSuite) TestRunWithSwappiness(c *testing.T) {
660	testRequires(c, memorySwappinessSupport)
661	file := "/sys/fs/cgroup/memory/memory.swappiness"
662	out, _ := dockerCmd(c, "run", "--memory-swappiness", "0", "--name", "test", "busybox", "cat", file)
663	assert.Equal(c, strings.TrimSpace(out), "0")
664
665	out = inspectField(c, "test", "HostConfig.MemorySwappiness")
666	assert.Equal(c, out, "0")
667}
668
669func (s *DockerSuite) TestRunWithSwappinessInvalid(c *testing.T) {
670	testRequires(c, memorySwappinessSupport)
671	out, _, err := dockerCmdWithError("run", "--memory-swappiness", "101", "busybox", "true")
672	assert.ErrorContains(c, err, "")
673	expected := "Valid memory swappiness range is 0-100"
674	assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected)
675	out, _, err = dockerCmdWithError("run", "--memory-swappiness", "-10", "busybox", "true")
676	assert.ErrorContains(c, err, "")
677	assert.Assert(c, strings.Contains(out, expected), "Expected output to contain %q, not %q", out, expected)
678}
679
680func (s *DockerSuite) TestRunWithMemoryReservation(c *testing.T) {
681	testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport)
682
683	file := "/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"
684	out, _ := dockerCmd(c, "run", "--memory-reservation", "200M", "--name", "test", "busybox", "cat", file)
685	assert.Equal(c, strings.TrimSpace(out), "209715200")
686
687	out = inspectField(c, "test", "HostConfig.MemoryReservation")
688	assert.Equal(c, out, "209715200")
689}
690
691func (s *DockerSuite) TestRunWithMemoryReservationInvalid(c *testing.T) {
692	testRequires(c, memoryLimitSupport)
693	testRequires(c, testEnv.IsLocalDaemon, memoryReservationSupport)
694	out, _, err := dockerCmdWithError("run", "-m", "500M", "--memory-reservation", "800M", "busybox", "true")
695	assert.ErrorContains(c, err, "")
696	expected := "Minimum memory limit can not be less than memory reservation limit"
697	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation")
698	out, _, err = dockerCmdWithError("run", "--memory-reservation", "1k", "busybox", "true")
699	assert.ErrorContains(c, err, "")
700	expected = "Minimum memory reservation allowed is 4MB"
701	assert.Assert(c, strings.Contains(strings.TrimSpace(out), expected), "run container should fail with invalid memory reservation")
702}
703
704func (s *DockerSuite) TestStopContainerSignal(c *testing.T) {
705	out, _ := dockerCmd(c, "run", "--stop-signal", "SIGUSR1", "-d", "busybox", "/bin/sh", "-c", `trap 'echo "exit trapped"; exit 0' USR1; while true; do sleep 1; done`)
706	containerID := strings.TrimSpace(out)
707
708	assert.Assert(c, waitRun(containerID) == nil)
709
710	dockerCmd(c, "stop", containerID)
711	out, _ = dockerCmd(c, "logs", containerID)
712
713	assert.Assert(c, strings.Contains(out, "exit trapped"), "Expected `exit trapped` in the log")
714}
715
716func (s *DockerSuite) TestRunSwapLessThanMemoryLimit(c *testing.T) {
717	testRequires(c, memoryLimitSupport)
718	testRequires(c, swapMemorySupport)
719	out, _, err := dockerCmdWithError("run", "-m", "16m", "--memory-swap", "15m", "busybox", "echo", "test")
720	expected := "Minimum memoryswap limit should be larger than memory limit"
721	assert.ErrorContains(c, err, "")
722
723	assert.Assert(c, strings.Contains(out, expected))
724}
725
726func (s *DockerSuite) TestRunInvalidCpusetCpusFlagValue(c *testing.T) {
727	testRequires(c, cgroupCpuset, testEnv.IsLocalDaemon)
728
729	sysInfo := sysinfo.New(true)
730	cpus, err := parsers.ParseUintList(sysInfo.Cpus)
731	assert.NilError(c, err)
732	var invalid int
733	for i := 0; i <= len(cpus)+1; i++ {
734		if !cpus[i] {
735			invalid = i
736			break
737		}
738	}
739	out, _, err := dockerCmdWithError("run", "--cpuset-cpus", strconv.Itoa(invalid), "busybox", "true")
740	assert.ErrorContains(c, err, "")
741	expected := fmt.Sprintf("Error response from daemon: Requested CPUs are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Cpus)
742	assert.Assert(c, strings.Contains(out, expected))
743}
744
745func (s *DockerSuite) TestRunInvalidCpusetMemsFlagValue(c *testing.T) {
746	testRequires(c, cgroupCpuset)
747
748	sysInfo := sysinfo.New(true)
749	mems, err := parsers.ParseUintList(sysInfo.Mems)
750	assert.NilError(c, err)
751	var invalid int
752	for i := 0; i <= len(mems)+1; i++ {
753		if !mems[i] {
754			invalid = i
755			break
756		}
757	}
758	out, _, err := dockerCmdWithError("run", "--cpuset-mems", strconv.Itoa(invalid), "busybox", "true")
759	assert.ErrorContains(c, err, "")
760	expected := fmt.Sprintf("Error response from daemon: Requested memory nodes are not available - requested %s, available: %s", strconv.Itoa(invalid), sysInfo.Mems)
761	assert.Assert(c, strings.Contains(out, expected))
762}
763
764func (s *DockerSuite) TestRunInvalidCPUShares(c *testing.T) {
765	testRequires(c, cpuShare, DaemonIsLinux)
766	out, _, err := dockerCmdWithError("run", "--cpu-shares", "1", "busybox", "echo", "test")
767	assert.ErrorContains(c, err, "", out)
768	expected := "minimum allowed cpu-shares is 2"
769	assert.Assert(c, strings.Contains(out, expected))
770
771	out, _, err = dockerCmdWithError("run", "--cpu-shares", "-1", "busybox", "echo", "test")
772	assert.ErrorContains(c, err, "", out)
773	expected = "shares: invalid argument"
774	assert.Assert(c, strings.Contains(out, expected))
775
776	out, _, err = dockerCmdWithError("run", "--cpu-shares", "99999999", "busybox", "echo", "test")
777	assert.ErrorContains(c, err, "", out)
778	expected = "maximum allowed cpu-shares is"
779	assert.Assert(c, strings.Contains(out, expected))
780}
781
782func (s *DockerSuite) TestRunWithDefaultShmSize(c *testing.T) {
783	testRequires(c, DaemonIsLinux)
784
785	name := "shm-default"
786	out, _ := dockerCmd(c, "run", "--name", name, "busybox", "mount")
787	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
788	if !shmRegex.MatchString(out) {
789		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
790	}
791	shmSize := inspectField(c, name, "HostConfig.ShmSize")
792	assert.Equal(c, shmSize, "67108864")
793}
794
795func (s *DockerSuite) TestRunWithShmSize(c *testing.T) {
796	testRequires(c, DaemonIsLinux)
797
798	name := "shm"
799	out, _ := dockerCmd(c, "run", "--name", name, "--shm-size=1G", "busybox", "mount")
800	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
801	if !shmRegex.MatchString(out) {
802		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
803	}
804	shmSize := inspectField(c, name, "HostConfig.ShmSize")
805	assert.Equal(c, shmSize, "1073741824")
806}
807
808func (s *DockerSuite) TestRunTmpfsMountsEnsureOrdered(c *testing.T) {
809	tmpFile, err := ioutil.TempFile("", "test")
810	assert.NilError(c, err)
811	defer tmpFile.Close()
812	out, _ := dockerCmd(c, "run", "--tmpfs", "/run", "-v", tmpFile.Name()+":/run/test", "busybox", "ls", "/run")
813	assert.Assert(c, strings.Contains(out, "test"))
814}
815
816func (s *DockerSuite) TestRunTmpfsMounts(c *testing.T) {
817	// TODO Windows (Post TP5): This test cannot run on a Windows daemon as
818	// Windows does not support tmpfs mounts.
819	testRequires(c, DaemonIsLinux)
820	if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "busybox", "touch", "/run/somefile"); err != nil {
821		c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out)
822	}
823	if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec", "busybox", "touch", "/run/somefile"); err != nil {
824		c.Fatalf("/run directory not mounted on tmpfs %q %s", err, out)
825	}
826	if out, _, err := dockerCmdWithError("run", "--tmpfs", "/run:noexec,nosuid,rw,size=5k,mode=700", "busybox", "touch", "/run/somefile"); err != nil {
827		c.Fatalf("/run failed to mount on tmpfs with valid options %q %s", err, out)
828	}
829	if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run:foobar", "busybox", "touch", "/run/somefile"); err == nil {
830		c.Fatalf("/run mounted on tmpfs when it should have vailed within invalid mount option")
831	}
832	if _, _, err := dockerCmdWithError("run", "--tmpfs", "/run", "-v", "/run:/run", "busybox", "touch", "/run/somefile"); err == nil {
833		c.Fatalf("Should have generated an error saying Duplicate mount  points")
834	}
835}
836
837func (s *DockerSuite) TestRunTmpfsMountsOverrideImageVolumes(c *testing.T) {
838	name := "img-with-volumes"
839	buildImageSuccessfully(c, name, build.WithDockerfile(`
840    FROM busybox
841    VOLUME /run
842    RUN touch /run/stuff
843    `))
844	out, _ := dockerCmd(c, "run", "--tmpfs", "/run", name, "ls", "/run")
845	assert.Assert(c, !strings.Contains(out, "stuff"))
846}
847
848// Test case for #22420
849func (s *DockerSuite) TestRunTmpfsMountsWithOptions(c *testing.T) {
850	testRequires(c, DaemonIsLinux)
851
852	expectedOptions := []string{"rw", "nosuid", "nodev", "noexec", "relatime"}
853	out, _ := dockerCmd(c, "run", "--tmpfs", "/tmp", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
854	for _, option := range expectedOptions {
855		assert.Assert(c, strings.Contains(out, option))
856	}
857	assert.Assert(c, !strings.Contains(out, "size="))
858	expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime"}
859	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
860	for _, option := range expectedOptions {
861		assert.Assert(c, strings.Contains(out, option))
862	}
863	assert.Assert(c, !strings.Contains(out, "size="))
864	expectedOptions = []string{"rw", "nosuid", "nodev", "relatime", "size=8192k"}
865	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw,exec,size=8192k", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
866	for _, option := range expectedOptions {
867		assert.Assert(c, strings.Contains(out, option))
868	}
869
870	expectedOptions = []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k"}
871	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:rw,size=8192k,exec,size=4096k,noexec", "busybox", "sh", "-c", "mount | grep 'tmpfs on /tmp'")
872	for _, option := range expectedOptions {
873		assert.Assert(c, strings.Contains(out, option))
874	}
875
876	// We use debian:bullseye as there is no findmnt in busybox. Also the output will be in the format of
877	// TARGET PROPAGATION
878	// /tmp   shared
879	// so we only capture `shared` here.
880	expectedOptions = []string{"shared"}
881	out, _ = dockerCmd(c, "run", "--tmpfs", "/tmp:shared", "debian:bullseye", "findmnt", "-o", "TARGET,PROPAGATION", "/tmp")
882	for _, option := range expectedOptions {
883		assert.Assert(c, strings.Contains(out, option))
884	}
885}
886
887func (s *DockerSuite) TestRunSysctls(c *testing.T) {
888	testRequires(c, DaemonIsLinux)
889	var err error
890
891	out, _ := dockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=1", "--name", "test", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward")
892	assert.Equal(c, strings.TrimSpace(out), "1")
893
894	out = inspectFieldJSON(c, "test", "HostConfig.Sysctls")
895
896	sysctls := make(map[string]string)
897	err = json.Unmarshal([]byte(out), &sysctls)
898	assert.NilError(c, err)
899	assert.Equal(c, sysctls["net.ipv4.ip_forward"], "1")
900
901	out, _ = dockerCmd(c, "run", "--sysctl", "net.ipv4.ip_forward=0", "--name", "test1", "busybox", "cat", "/proc/sys/net/ipv4/ip_forward")
902	assert.Equal(c, strings.TrimSpace(out), "0")
903
904	out = inspectFieldJSON(c, "test1", "HostConfig.Sysctls")
905
906	err = json.Unmarshal([]byte(out), &sysctls)
907	assert.NilError(c, err)
908	assert.Equal(c, sysctls["net.ipv4.ip_forward"], "0")
909
910	icmd.RunCommand(dockerBinary, "run", "--sysctl", "kernel.foobar=1", "--name", "test2",
911		"busybox", "cat", "/proc/sys/kernel/foobar").Assert(c, icmd.Expected{
912		ExitCode: 125,
913		Err:      "invalid argument",
914	})
915}
916
917// TestRunSeccompProfileDenyUnshare checks that 'docker run --security-opt seccomp=/tmp/profile.json debian:bullseye unshare' exits with operation not permitted.
918func (s *DockerSuite) TestRunSeccompProfileDenyUnshare(c *testing.T) {
919	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
920	jsonData := `{
921	"defaultAction": "SCMP_ACT_ALLOW",
922	"syscalls": [
923		{
924			"name": "unshare",
925			"action": "SCMP_ACT_ERRNO"
926		}
927	]
928}`
929	tmpFile, err := ioutil.TempFile("", "profile.json")
930	if err != nil {
931		c.Fatal(err)
932	}
933	defer tmpFile.Close()
934
935	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
936		c.Fatal(err)
937	}
938	icmd.RunCommand(dockerBinary, "run", "--security-opt", "apparmor=unconfined",
939		"--security-opt", "seccomp="+tmpFile.Name(),
940		"debian:bullseye", "unshare", "-p", "-m", "-f", "-r", "mount", "-t", "proc", "none", "/proc").Assert(c, icmd.Expected{
941		ExitCode: 1,
942		Err:      "Operation not permitted",
943	})
944}
945
946// TestRunSeccompProfileDenyChmod checks that 'docker run --security-opt seccomp=/tmp/profile.json busybox chmod 400 /etc/hostname' exits with operation not permitted.
947func (s *DockerSuite) TestRunSeccompProfileDenyChmod(c *testing.T) {
948	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
949	jsonData := `{
950	"defaultAction": "SCMP_ACT_ALLOW",
951	"syscalls": [
952		{
953			"name": "chmod",
954			"action": "SCMP_ACT_ERRNO"
955		},
956		{
957			"name":"fchmod",
958			"action": "SCMP_ACT_ERRNO"
959		},
960		{
961			"name": "fchmodat",
962			"action":"SCMP_ACT_ERRNO"
963		}
964	]
965}`
966	tmpFile, err := ioutil.TempFile("", "profile.json")
967	assert.NilError(c, err)
968	defer tmpFile.Close()
969
970	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
971		c.Fatal(err)
972	}
973	icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp="+tmpFile.Name(),
974		"busybox", "chmod", "400", "/etc/hostname").Assert(c, icmd.Expected{
975		ExitCode: 1,
976		Err:      "Operation not permitted",
977	})
978}
979
980// TestRunSeccompProfileDenyUnshareUserns checks that 'docker run debian:bullseye unshare --map-root-user --user sh -c whoami' with a specific profile to
981// deny unshare of a userns exits with operation not permitted.
982func (s *DockerSuite) TestRunSeccompProfileDenyUnshareUserns(c *testing.T) {
983	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotArm, Apparmor)
984	// from sched.h
985	jsonData := fmt.Sprintf(`{
986	"defaultAction": "SCMP_ACT_ALLOW",
987	"syscalls": [
988		{
989			"name": "unshare",
990			"action": "SCMP_ACT_ERRNO",
991			"args": [
992				{
993					"index": 0,
994					"value": %d,
995					"op": "SCMP_CMP_EQ"
996				}
997			]
998		}
999	]
1000}`, uint64(0x10000000))
1001	tmpFile, err := ioutil.TempFile("", "profile.json")
1002	if err != nil {
1003		c.Fatal(err)
1004	}
1005	defer tmpFile.Close()
1006
1007	if _, err := tmpFile.Write([]byte(jsonData)); err != nil {
1008		c.Fatal(err)
1009	}
1010	icmd.RunCommand(dockerBinary, "run",
1011		"--security-opt", "apparmor=unconfined", "--security-opt", "seccomp="+tmpFile.Name(),
1012		"debian:bullseye", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami").Assert(c, icmd.Expected{
1013		ExitCode: 1,
1014		Err:      "Operation not permitted",
1015	})
1016}
1017
1018// TestRunSeccompProfileDenyCloneUserns checks that 'docker run syscall-test'
1019// with a the default seccomp profile exits with operation not permitted.
1020func (s *DockerSuite) TestRunSeccompProfileDenyCloneUserns(c *testing.T) {
1021	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
1022	ensureSyscallTest(c)
1023
1024	icmd.RunCommand(dockerBinary, "run", "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{
1025		ExitCode: 1,
1026		Err:      "clone failed: Operation not permitted",
1027	})
1028}
1029
1030// TestRunSeccompUnconfinedCloneUserns checks that
1031// 'docker run --security-opt seccomp=unconfined syscall-test' allows creating a userns.
1032func (s *DockerSuite) TestRunSeccompUnconfinedCloneUserns(c *testing.T) {
1033	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace, unprivilegedUsernsClone)
1034	ensureSyscallTest(c)
1035
1036	// make sure running w privileged is ok
1037	icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined",
1038		"syscall-test", "userns-test", "id").Assert(c, icmd.Expected{
1039		Out: "nobody",
1040	})
1041}
1042
1043// TestRunSeccompAllowPrivCloneUserns checks that 'docker run --privileged syscall-test'
1044// allows creating a userns.
1045func (s *DockerSuite) TestRunSeccompAllowPrivCloneUserns(c *testing.T) {
1046	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, UserNamespaceInKernel, NotUserNamespace)
1047	ensureSyscallTest(c)
1048
1049	// make sure running w privileged is ok
1050	icmd.RunCommand(dockerBinary, "run", "--privileged", "syscall-test", "userns-test", "id").Assert(c, icmd.Expected{
1051		Out: "nobody",
1052	})
1053}
1054
1055// TestRunSeccompProfileAllow32Bit checks that 32 bit code can run on x86_64
1056// with the default seccomp profile.
1057func (s *DockerSuite) TestRunSeccompProfileAllow32Bit(c *testing.T) {
1058	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, IsAmd64)
1059	ensureSyscallTest(c)
1060
1061	icmd.RunCommand(dockerBinary, "run", "syscall-test", "exit32-test").Assert(c, icmd.Success)
1062}
1063
1064// TestRunSeccompAllowSetrlimit checks that 'docker run debian:bullseye ulimit -v 1048510' succeeds.
1065func (s *DockerSuite) TestRunSeccompAllowSetrlimit(c *testing.T) {
1066	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
1067
1068	// ulimit uses setrlimit, so we want to make sure we don't break it
1069	icmd.RunCommand(dockerBinary, "run", "debian:bullseye", "bash", "-c", "ulimit -v 1048510").Assert(c, icmd.Success)
1070}
1071
1072func (s *DockerSuite) TestRunSeccompDefaultProfileAcct(c *testing.T) {
1073	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace)
1074	ensureSyscallTest(c)
1075
1076	out, _, err := dockerCmdWithError("run", "syscall-test", "acct-test")
1077	if err == nil || !strings.Contains(out, "Operation not permitted") {
1078		c.Fatalf("test 0: expected Operation not permitted, got: %s", out)
1079	}
1080
1081	out, _, err = dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "acct-test")
1082	if err == nil || !strings.Contains(out, "Operation not permitted") {
1083		c.Fatalf("test 1: expected Operation not permitted, got: %s", out)
1084	}
1085
1086	out, _, err = dockerCmdWithError("run", "--cap-add", "sys_pacct", "syscall-test", "acct-test")
1087	if err == nil || !strings.Contains(out, "No such file or directory") {
1088		c.Fatalf("test 2: expected No such file or directory, got: %s", out)
1089	}
1090
1091	out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "acct-test")
1092	if err == nil || !strings.Contains(out, "No such file or directory") {
1093		c.Fatalf("test 3: expected No such file or directory, got: %s", out)
1094	}
1095
1096	out, _, err = dockerCmdWithError("run", "--cap-drop", "ALL", "--cap-add", "sys_pacct", "syscall-test", "acct-test")
1097	if err == nil || !strings.Contains(out, "No such file or directory") {
1098		c.Fatalf("test 4: expected No such file or directory, got: %s", out)
1099	}
1100}
1101
1102func (s *DockerSuite) TestRunSeccompDefaultProfileNS(c *testing.T) {
1103	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled, NotUserNamespace)
1104	ensureSyscallTest(c)
1105
1106	out, _, err := dockerCmdWithError("run", "syscall-test", "ns-test", "echo", "hello0")
1107	if err == nil || !strings.Contains(out, "Operation not permitted") {
1108		c.Fatalf("test 0: expected Operation not permitted, got: %s", out)
1109	}
1110
1111	out, _, err = dockerCmdWithError("run", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello1")
1112	if err != nil || !strings.Contains(out, "hello1") {
1113		c.Fatalf("test 1: expected hello1, got: %s, %v", out, err)
1114	}
1115
1116	out, _, err = dockerCmdWithError("run", "--cap-drop", "all", "--cap-add", "sys_admin", "syscall-test", "ns-test", "echo", "hello2")
1117	if err != nil || !strings.Contains(out, "hello2") {
1118		c.Fatalf("test 2: expected hello2, got: %s, %v", out, err)
1119	}
1120
1121	out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "syscall-test", "ns-test", "echo", "hello3")
1122	if err != nil || !strings.Contains(out, "hello3") {
1123		c.Fatalf("test 3: expected hello3, got: %s, %v", out, err)
1124	}
1125
1126	out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "acct-test")
1127	if err == nil || !strings.Contains(out, "No such file or directory") {
1128		c.Fatalf("test 4: expected No such file or directory, got: %s", out)
1129	}
1130
1131	out, _, err = dockerCmdWithError("run", "--cap-add", "ALL", "--security-opt", "seccomp=unconfined", "syscall-test", "ns-test", "echo", "hello4")
1132	if err != nil || !strings.Contains(out, "hello4") {
1133		c.Fatalf("test 5: expected hello4, got: %s, %v", out, err)
1134	}
1135}
1136
1137// TestRunNoNewPrivSetuid checks that --security-opt='no-new-privileges=true' prevents
1138// effective uid transitions on executing setuid binaries.
1139func (s *DockerSuite) TestRunNoNewPrivSetuid(c *testing.T) {
1140	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
1141	ensureNNPTest(c)
1142
1143	// test that running a setuid binary results in no effective uid transition
1144	icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges=true", "--user", "1000",
1145		"nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{
1146		Out: "EUID=1000",
1147	})
1148}
1149
1150// TestLegacyRunNoNewPrivSetuid checks that --security-opt=no-new-privileges prevents
1151// effective uid transitions on executing setuid binaries.
1152func (s *DockerSuite) TestLegacyRunNoNewPrivSetuid(c *testing.T) {
1153	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
1154	ensureNNPTest(c)
1155
1156	// test that running a setuid binary results in no effective uid transition
1157	icmd.RunCommand(dockerBinary, "run", "--security-opt", "no-new-privileges", "--user", "1000",
1158		"nnp-test", "/usr/bin/nnp-test").Assert(c, icmd.Expected{
1159		Out: "EUID=1000",
1160	})
1161}
1162
1163func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChown(c *testing.T) {
1164	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1165	ensureSyscallTest(c)
1166
1167	// test that a root user has default capability CAP_CHOWN
1168	dockerCmd(c, "run", "busybox", "chown", "100", "/tmp")
1169	// test that non root user does not have default capability CAP_CHOWN
1170	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chown", "100", "/tmp").Assert(c, icmd.Expected{
1171		ExitCode: 1,
1172		Err:      "Operation not permitted",
1173	})
1174	// test that root user can drop default capability CAP_CHOWN
1175	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "chown", "busybox", "chown", "100", "/tmp").Assert(c, icmd.Expected{
1176		ExitCode: 1,
1177		Err:      "Operation not permitted",
1178	})
1179}
1180
1181func (s *DockerSuite) TestUserNoEffectiveCapabilitiesDacOverride(c *testing.T) {
1182	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1183	ensureSyscallTest(c)
1184
1185	// test that a root user has default capability CAP_DAC_OVERRIDE
1186	dockerCmd(c, "run", "busybox", "sh", "-c", "echo test > /etc/passwd")
1187	// test that non root user does not have default capability CAP_DAC_OVERRIDE
1188	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "sh", "-c", "echo test > /etc/passwd").Assert(c, icmd.Expected{
1189		ExitCode: 1,
1190		Err:      "Permission denied",
1191	})
1192}
1193
1194func (s *DockerSuite) TestUserNoEffectiveCapabilitiesFowner(c *testing.T) {
1195	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1196	ensureSyscallTest(c)
1197
1198	// test that a root user has default capability CAP_FOWNER
1199	dockerCmd(c, "run", "busybox", "chmod", "777", "/etc/passwd")
1200	// test that non root user does not have default capability CAP_FOWNER
1201	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chmod", "777", "/etc/passwd").Assert(c, icmd.Expected{
1202		ExitCode: 1,
1203		Err:      "Operation not permitted",
1204	})
1205	// TODO test that root user can drop default capability CAP_FOWNER
1206}
1207
1208// TODO CAP_KILL
1209
1210func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetuid(c *testing.T) {
1211	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1212	ensureSyscallTest(c)
1213
1214	// test that a root user has default capability CAP_SETUID
1215	dockerCmd(c, "run", "syscall-test", "setuid-test")
1216	// test that non root user does not have default capability CAP_SETUID
1217	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "setuid-test").Assert(c, icmd.Expected{
1218		ExitCode: 1,
1219		Err:      "Operation not permitted",
1220	})
1221	// test that root user can drop default capability CAP_SETUID
1222	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "setuid", "syscall-test", "setuid-test").Assert(c, icmd.Expected{
1223		ExitCode: 1,
1224		Err:      "Operation not permitted",
1225	})
1226}
1227
1228func (s *DockerSuite) TestUserNoEffectiveCapabilitiesSetgid(c *testing.T) {
1229	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1230	ensureSyscallTest(c)
1231
1232	// test that a root user has default capability CAP_SETGID
1233	dockerCmd(c, "run", "syscall-test", "setgid-test")
1234	// test that non root user does not have default capability CAP_SETGID
1235	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "setgid-test").Assert(c, icmd.Expected{
1236		ExitCode: 1,
1237		Err:      "Operation not permitted",
1238	})
1239	// test that root user can drop default capability CAP_SETGID
1240	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "setgid", "syscall-test", "setgid-test").Assert(c, icmd.Expected{
1241		ExitCode: 1,
1242		Err:      "Operation not permitted",
1243	})
1244}
1245
1246// TODO CAP_SETPCAP
1247
1248// sysctlExists checks if a sysctl exists; runc will error if we add any that do not actually
1249// exist, so do not add the default ones if running on an old kernel.
1250func sysctlExists(s string) bool {
1251	f := filepath.Join("/proc", "sys", strings.Replace(s, ".", "/", -1))
1252	_, err := os.Stat(f)
1253	return err == nil
1254}
1255
1256func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetBindService(c *testing.T) {
1257	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1258	ensureSyscallTest(c)
1259
1260	// test that a root user has default capability CAP_NET_BIND_SERVICE
1261	dockerCmd(c, "run", "syscall-test", "socket-test")
1262	// test that non root user does not have default capability CAP_NET_BIND_SERVICE
1263	// as we allow this via sysctl, also tweak the sysctl back to default
1264	args := []string{"run", "--user", "1000:1000"}
1265	if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
1266		args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024")
1267	}
1268	args = append(args, "syscall-test", "socket-test")
1269	icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{
1270		ExitCode: 1,
1271		Err:      "Permission denied",
1272	})
1273	// test that root user can drop default capability CAP_NET_BIND_SERVICE
1274	args = []string{"run", "--cap-drop", "net_bind_service"}
1275	if sysctlExists("net.ipv4.ip_unprivileged_port_start") {
1276		args = append(args, "--sysctl", "net.ipv4.ip_unprivileged_port_start=1024")
1277	}
1278	args = append(args, "syscall-test", "socket-test")
1279	icmd.RunCommand(dockerBinary, args...).Assert(c, icmd.Expected{
1280		ExitCode: 1,
1281		Err:      "Permission denied",
1282	})
1283}
1284
1285func (s *DockerSuite) TestUserNoEffectiveCapabilitiesNetRaw(c *testing.T) {
1286	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1287	ensureSyscallTest(c)
1288
1289	// test that a root user has default capability CAP_NET_RAW
1290	dockerCmd(c, "run", "syscall-test", "raw-test")
1291	// test that non root user does not have default capability CAP_NET_RAW
1292	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "syscall-test", "raw-test").Assert(c, icmd.Expected{
1293		ExitCode: 1,
1294		Err:      "Operation not permitted",
1295	})
1296	// test that root user can drop default capability CAP_NET_RAW
1297	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "net_raw", "syscall-test", "raw-test").Assert(c, icmd.Expected{
1298		ExitCode: 1,
1299		Err:      "Operation not permitted",
1300	})
1301}
1302
1303func (s *DockerSuite) TestUserNoEffectiveCapabilitiesChroot(c *testing.T) {
1304	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon)
1305	ensureSyscallTest(c)
1306
1307	// test that a root user has default capability CAP_SYS_CHROOT
1308	dockerCmd(c, "run", "busybox", "chroot", "/", "/bin/true")
1309	// test that non root user does not have default capability CAP_SYS_CHROOT
1310	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "chroot", "/", "/bin/true").Assert(c, icmd.Expected{
1311		ExitCode: 1,
1312		Err:      "Operation not permitted",
1313	})
1314	// test that root user can drop default capability CAP_SYS_CHROOT
1315	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "sys_chroot", "busybox", "chroot", "/", "/bin/true").Assert(c, icmd.Expected{
1316		ExitCode: 1,
1317		Err:      "Operation not permitted",
1318	})
1319}
1320
1321func (s *DockerSuite) TestUserNoEffectiveCapabilitiesMknod(c *testing.T) {
1322	testRequires(c, DaemonIsLinux, NotUserNamespace, testEnv.IsLocalDaemon)
1323	ensureSyscallTest(c)
1324
1325	// test that a root user has default capability CAP_MKNOD
1326	dockerCmd(c, "run", "busybox", "mknod", "/tmp/node", "b", "1", "2")
1327	// test that non root user does not have default capability CAP_MKNOD
1328	// test that root user can drop default capability CAP_SYS_CHROOT
1329	icmd.RunCommand(dockerBinary, "run", "--user", "1000:1000", "busybox", "mknod", "/tmp/node", "b", "1", "2").Assert(c, icmd.Expected{
1330		ExitCode: 1,
1331		Err:      "Operation not permitted",
1332	})
1333	// test that root user can drop default capability CAP_MKNOD
1334	icmd.RunCommand(dockerBinary, "run", "--cap-drop", "mknod", "busybox", "mknod", "/tmp/node", "b", "1", "2").Assert(c, icmd.Expected{
1335		ExitCode: 1,
1336		Err:      "Operation not permitted",
1337	})
1338}
1339
1340// TODO CAP_AUDIT_WRITE
1341// TODO CAP_SETFCAP
1342
1343func (s *DockerSuite) TestRunApparmorProcDirectory(c *testing.T) {
1344	testRequires(c, testEnv.IsLocalDaemon, Apparmor)
1345
1346	// running w seccomp unconfined tests the apparmor profile
1347	result := icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", "busybox", "chmod", "777", "/proc/1/cgroup")
1348	result.Assert(c, icmd.Expected{ExitCode: 1})
1349	if !(strings.Contains(result.Combined(), "Permission denied") || strings.Contains(result.Combined(), "Operation not permitted")) {
1350		c.Fatalf("expected chmod 777 /proc/1/cgroup to fail, got %s: %v", result.Combined(), result.Error)
1351	}
1352
1353	result = icmd.RunCommand(dockerBinary, "run", "--security-opt", "seccomp=unconfined", "busybox", "chmod", "777", "/proc/1/attr/current")
1354	result.Assert(c, icmd.Expected{ExitCode: 1})
1355	if !(strings.Contains(result.Combined(), "Permission denied") || strings.Contains(result.Combined(), "Operation not permitted")) {
1356		c.Fatalf("expected chmod 777 /proc/1/attr/current to fail, got %s: %v", result.Combined(), result.Error)
1357	}
1358}
1359
1360// make sure the default profile can be successfully parsed (using unshare as it is
1361// something which we know is blocked in the default profile)
1362func (s *DockerSuite) TestRunSeccompWithDefaultProfile(c *testing.T) {
1363	testRequires(c, testEnv.IsLocalDaemon, seccompEnabled)
1364
1365	out, _, err := dockerCmdWithError("run", "--security-opt", "seccomp=../profiles/seccomp/default.json", "debian:bullseye", "unshare", "--map-root-user", "--user", "sh", "-c", "whoami")
1366	assert.ErrorContains(c, err, "", out)
1367	assert.Equal(c, strings.TrimSpace(out), "unshare: unshare failed: Operation not permitted")
1368}
1369
1370// TestRunDeviceSymlink checks run with device that follows symlink (#13840 and #22271)
1371func (s *DockerSuite) TestRunDeviceSymlink(c *testing.T) {
1372	testRequires(c, DaemonIsLinux, NotUserNamespace, NotArm, testEnv.IsLocalDaemon)
1373	if _, err := os.Stat("/dev/zero"); err != nil {
1374		c.Skip("Host does not have /dev/zero")
1375	}
1376
1377	// Create a temporary directory to create symlink
1378	tmpDir, err := ioutil.TempDir("", "docker_device_follow_symlink_tests")
1379	assert.NilError(c, err)
1380
1381	defer os.RemoveAll(tmpDir)
1382
1383	// Create a symbolic link to /dev/zero
1384	symZero := filepath.Join(tmpDir, "zero")
1385	err = os.Symlink("/dev/zero", symZero)
1386	assert.NilError(c, err)
1387
1388	// Create a temporary file "temp" inside tmpDir, write some data to "tmpDir/temp",
1389	// then create a symlink "tmpDir/file" to the temporary file "tmpDir/temp".
1390	tmpFile := filepath.Join(tmpDir, "temp")
1391	err = ioutil.WriteFile(tmpFile, []byte("temp"), 0666)
1392	assert.NilError(c, err)
1393	symFile := filepath.Join(tmpDir, "file")
1394	err = os.Symlink(tmpFile, symFile)
1395	assert.NilError(c, err)
1396
1397	// Create a symbolic link to /dev/zero, this time with a relative path (#22271)
1398	err = os.Symlink("zero", "/dev/symzero")
1399	if err != nil {
1400		c.Fatal("/dev/symzero creation failed")
1401	}
1402	// We need to remove this symbolic link here as it is created in /dev/, not temporary directory as above
1403	defer os.Remove("/dev/symzero")
1404
1405	// md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23
1406	out, _ := dockerCmd(c, "run", "--device", symZero+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
1407	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23")
1408	// symlink "tmpDir/file" to a file "tmpDir/temp" will result in an error as it is not a device.
1409	out, _, err = dockerCmdWithError("run", "--device", symFile+":/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
1410	assert.ErrorContains(c, err, "")
1411	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "not a device node"), "expected output 'not a device node'")
1412	// md5sum of 'dd if=/dev/zero bs=4K count=8' is bb7df04e1b0a2570657527a7e108ae23 (this time check with relative path backed, see #22271)
1413	out, _ = dockerCmd(c, "run", "--device", "/dev/symzero:/dev/symzero", "busybox", "sh", "-c", "dd if=/dev/symzero bs=4K count=8 | md5sum")
1414	assert.Assert(c, strings.Contains(strings.Trim(out, "\r\n"), "bb7df04e1b0a2570657527a7e108ae23"), "expected output bb7df04e1b0a2570657527a7e108ae23")
1415}
1416
1417// TestRunPIDsLimit makes sure the pids cgroup is set with --pids-limit
1418func (s *DockerSuite) TestRunPIDsLimit(c *testing.T) {
1419	testRequires(c, testEnv.IsLocalDaemon, pidsLimit)
1420
1421	file := "/sys/fs/cgroup/pids/pids.max"
1422	out, _ := dockerCmd(c, "run", "--name", "skittles", "--pids-limit", "4", "busybox", "cat", file)
1423	assert.Equal(c, strings.TrimSpace(out), "4")
1424
1425	out = inspectField(c, "skittles", "HostConfig.PidsLimit")
1426	assert.Equal(c, out, "4", "setting the pids limit failed")
1427}
1428
1429func (s *DockerSuite) TestRunPrivilegedAllowedDevices(c *testing.T) {
1430	testRequires(c, DaemonIsLinux, NotUserNamespace)
1431
1432	file := "/sys/fs/cgroup/devices/devices.list"
1433	out, _ := dockerCmd(c, "run", "--privileged", "busybox", "cat", file)
1434	c.Logf("out: %q", out)
1435	assert.Equal(c, strings.TrimSpace(out), "a *:* rwm")
1436}
1437
1438func (s *DockerSuite) TestRunUserDeviceAllowed(c *testing.T) {
1439	testRequires(c, DaemonIsLinux)
1440
1441	fi, err := os.Stat("/dev/snd/timer")
1442	if err != nil {
1443		c.Skip("Host does not have /dev/snd/timer")
1444	}
1445	stat, ok := fi.Sys().(*syscall.Stat_t)
1446	if !ok {
1447		c.Skip("Could not stat /dev/snd/timer")
1448	}
1449
1450	file := "/sys/fs/cgroup/devices/devices.list"
1451	out, _ := dockerCmd(c, "run", "--device", "/dev/snd/timer:w", "busybox", "cat", file)
1452	assert.Assert(c, strings.Contains(out, fmt.Sprintf("c %d:%d w", stat.Rdev/256, stat.Rdev%256)))
1453}
1454
1455func (s *DockerDaemonSuite) TestRunSeccompJSONNewFormat(c *testing.T) {
1456	testRequires(c, seccompEnabled)
1457
1458	s.d.StartWithBusybox(c)
1459
1460	jsonData := `{
1461	"defaultAction": "SCMP_ACT_ALLOW",
1462	"syscalls": [
1463		{
1464			"names": ["chmod", "fchmod", "fchmodat"],
1465			"action": "SCMP_ACT_ERRNO"
1466		}
1467	]
1468}`
1469	tmpFile, err := ioutil.TempFile("", "profile.json")
1470	assert.NilError(c, err)
1471	defer tmpFile.Close()
1472	_, err = tmpFile.Write([]byte(jsonData))
1473	assert.NilError(c, err)
1474
1475	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
1476	assert.ErrorContains(c, err, "")
1477	assert.Assert(c, strings.Contains(out, "Operation not permitted"))
1478}
1479
1480func (s *DockerDaemonSuite) TestRunSeccompJSONNoNameAndNames(c *testing.T) {
1481	testRequires(c, seccompEnabled)
1482
1483	s.d.StartWithBusybox(c)
1484
1485	jsonData := `{
1486	"defaultAction": "SCMP_ACT_ALLOW",
1487	"syscalls": [
1488		{
1489			"name": "chmod",
1490			"names": ["fchmod", "fchmodat"],
1491			"action": "SCMP_ACT_ERRNO"
1492		}
1493	]
1494}`
1495	tmpFile, err := ioutil.TempFile("", "profile.json")
1496	assert.NilError(c, err)
1497	defer tmpFile.Close()
1498	_, err = tmpFile.Write([]byte(jsonData))
1499	assert.NilError(c, err)
1500
1501	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
1502	assert.ErrorContains(c, err, "")
1503	assert.Assert(c, strings.Contains(out, "'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'"))
1504}
1505
1506func (s *DockerDaemonSuite) TestRunSeccompJSONNoArchAndArchMap(c *testing.T) {
1507	testRequires(c, seccompEnabled)
1508
1509	s.d.StartWithBusybox(c)
1510
1511	jsonData := `{
1512	"archMap": [
1513		{
1514			"architecture": "SCMP_ARCH_X86_64",
1515			"subArchitectures": [
1516				"SCMP_ARCH_X86",
1517				"SCMP_ARCH_X32"
1518			]
1519		}
1520	],
1521	"architectures": [
1522		"SCMP_ARCH_X32"
1523	],
1524	"defaultAction": "SCMP_ACT_ALLOW",
1525	"syscalls": [
1526		{
1527			"names": ["chmod", "fchmod", "fchmodat"],
1528			"action": "SCMP_ACT_ERRNO"
1529		}
1530	]
1531}`
1532	tmpFile, err := ioutil.TempFile("", "profile.json")
1533	assert.NilError(c, err)
1534	defer tmpFile.Close()
1535	_, err = tmpFile.Write([]byte(jsonData))
1536	assert.NilError(c, err)
1537
1538	out, err := s.d.Cmd("run", "--security-opt", "seccomp="+tmpFile.Name(), "busybox", "chmod", "777", ".")
1539	assert.ErrorContains(c, err, "")
1540	assert.Assert(c, strings.Contains(out, "'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'"))
1541}
1542
1543func (s *DockerDaemonSuite) TestRunWithDaemonDefaultSeccompProfile(c *testing.T) {
1544	testRequires(c, seccompEnabled)
1545
1546	s.d.StartWithBusybox(c)
1547
1548	// 1) verify I can run containers with the Docker default shipped profile which allows chmod
1549	_, err := s.d.Cmd("run", "busybox", "chmod", "777", ".")
1550	assert.NilError(c, err)
1551
1552	jsonData := `{
1553	"defaultAction": "SCMP_ACT_ALLOW",
1554	"syscalls": [
1555		{
1556			"name": "chmod",
1557			"action": "SCMP_ACT_ERRNO"
1558		},
1559		{
1560			"name": "fchmodat",
1561			"action": "SCMP_ACT_ERRNO"
1562		}
1563	]
1564}`
1565	tmpFile, err := ioutil.TempFile("", "profile.json")
1566	assert.NilError(c, err)
1567	defer tmpFile.Close()
1568	_, err = tmpFile.Write([]byte(jsonData))
1569	assert.NilError(c, err)
1570
1571	// 2) restart the daemon and add a custom seccomp profile in which we deny chmod
1572	s.d.Restart(c, "--seccomp-profile="+tmpFile.Name())
1573
1574	out, err := s.d.Cmd("run", "busybox", "chmod", "777", ".")
1575	assert.ErrorContains(c, err, "")
1576	assert.Assert(c, strings.Contains(out, "Operation not permitted"))
1577}
1578
1579func (s *DockerSuite) TestRunWithNanoCPUs(c *testing.T) {
1580	testRequires(c, cpuCfsQuota, cpuCfsPeriod)
1581
1582	file1 := "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"
1583	file2 := "/sys/fs/cgroup/cpu/cpu.cfs_period_us"
1584	out, _ := dockerCmd(c, "run", "--cpus", "0.5", "--name", "test", "busybox", "sh", "-c", fmt.Sprintf("cat %s && cat %s", file1, file2))
1585	assert.Equal(c, strings.TrimSpace(out), "50000\n100000")
1586
1587	clt, err := client.NewClientWithOpts(client.FromEnv)
1588	assert.NilError(c, err)
1589	inspect, err := clt.ContainerInspect(context.Background(), "test")
1590	assert.NilError(c, err)
1591	assert.Equal(c, inspect.HostConfig.NanoCPUs, int64(500000000))
1592
1593	out = inspectField(c, "test", "HostConfig.CpuQuota")
1594	assert.Equal(c, out, "0", "CPU CFS quota should be 0")
1595	out = inspectField(c, "test", "HostConfig.CpuPeriod")
1596	assert.Equal(c, out, "0", "CPU CFS period should be 0")
1597
1598	out, _, err = dockerCmdWithError("run", "--cpus", "0.5", "--cpu-quota", "50000", "--cpu-period", "100000", "busybox", "sh")
1599	assert.ErrorContains(c, err, "")
1600	assert.Assert(c, strings.Contains(out, "Conflicting options: Nano CPUs and CPU Period cannot both be set"))
1601}
1602