1// Copyright 2023 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build freebsd
6
7package syscall_test
8
9import (
10	"fmt"
11	"internal/testenv"
12	"os"
13	"os/exec"
14	"path/filepath"
15	"syscall"
16	"testing"
17	"unsafe"
18)
19
20const (
21	flagJailCreate = uintptr(0x1)
22)
23
24func prepareJail(t *testing.T) (int, string) {
25	t.Helper()
26
27	root := t.TempDir()
28	paramPath := []byte("path\x00")
29	conf := make([]syscall.Iovec, 4)
30	conf[0].Base = &paramPath[0]
31	conf[0].SetLen(len(paramPath))
32	p, err := syscall.BytePtrFromString(root)
33	if err != nil {
34		t.Fatal(err)
35	}
36	conf[1].Base = p
37	conf[1].SetLen(len(root) + 1)
38
39	paramPersist := []byte("persist\x00")
40	conf[2].Base = &paramPersist[0]
41	conf[2].SetLen(len(paramPersist))
42	conf[3].Base = nil
43	conf[3].SetLen(0)
44
45	id, _, err1 := syscall.Syscall(syscall.SYS_JAIL_SET,
46		uintptr(unsafe.Pointer(&conf[0])), uintptr(len(conf)), flagJailCreate)
47	if err1 != 0 {
48		t.Fatalf("jail_set: %v", err1)
49	}
50	t.Cleanup(func() {
51		_, _, err1 := syscall.Syscall(syscall.SYS_JAIL_REMOVE, id, 0, 0)
52		if err1 != 0 {
53			t.Errorf("failed to cleanup jail: %v", err)
54		}
55	})
56
57	return int(id), root
58}
59
60func TestJailAttach(t *testing.T) {
61	if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
62		jailed, err := syscall.SysctlUint32("security.jail.jailed")
63		if err != nil {
64			fmt.Fprintln(os.Stderr, err)
65			os.Exit(2)
66		}
67		if jailed != 1 {
68			t.Fatalf("jailed = %d, want 1", jailed)
69		}
70		return
71	}
72
73	testenv.MustHaveGoBuild(t)
74	// Make sure we are running as root, so we have permissions to create
75	// and remove jails.
76	if os.Getuid() != 0 {
77		t.Skip("kernel prohibits jail system calls in unprivileged process")
78	}
79
80	jid, root := prepareJail(t)
81
82	// Since jail attach does an implicit chroot to the jail's path,
83	// we need the binary there, and it must be statically linked.
84	x := filepath.Join(root, "syscall.test")
85	cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
86	cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
87	if o, err := cmd.CombinedOutput(); err != nil {
88		t.Fatalf("Build of syscall in jail root failed, output %v, err %v", o, err)
89	}
90
91	cmd = exec.Command("/syscall.test", "-test.run=TestJailAttach", "/")
92	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
93	cmd.SysProcAttr = &syscall.SysProcAttr{Jail: jid}
94	out, err := cmd.CombinedOutput()
95	if err != nil {
96		t.Fatalf("Cmd failed with err %v, output: %s", err, out)
97	}
98}
99