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