| 1 | //go:build linux |
| 2 | |
| 3 | package instanceexec |
| 4 | |
| 5 | import ( |
| 6 | "runtime" |
| 7 | "unsafe" |
| 8 | |
| 9 | "github.com/carlosframework/platform/internal/instancectl" |
| 10 | "golang.org/x/sys/unix" |
| 11 | ) |
| 12 | |
| 13 | // ScopeClaimGeneration closes descriptor-import routes before the claim |
| 14 | // handshake. Mount isolation alone cannot stop SCM_RIGHTS from a pathname |
| 15 | // socket in the shared instance directory. The locked launching thread and |
| 16 | // every tenant descendant retain this filter across fork and exec. |
| 17 | func ScopeClaimGeneration() error { |
| 18 | var arch uint32 |
| 19 | switch runtime.GOARCH { |
| 20 | case "amd64": |
| 21 | arch = unix.AUDIT_ARCH_X86_64 |
| 22 | case "arm64": |
| 23 | arch = unix.AUDIT_ARCH_AARCH64 |
| 24 | default: |
| 25 | return instancectl.ErrClaimUnit |
| 26 | } |
| 27 | filter := []unix.SockFilter{ |
| 28 | {Code: unix.BPF_LD | unix.BPF_W | unix.BPF_ABS, K: 4}, |
| 29 | {Code: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, Jt: 1, K: arch}, |
| 30 | {Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_KILL_PROCESS}, |
| 31 | {Code: unix.BPF_LD | unix.BPF_W | unix.BPF_ABS, K: 0}, |
| 32 | // Reject x32 as well as nonnative audit architectures. |
| 33 | {Code: unix.BPF_JMP | unix.BPF_JSET | unix.BPF_K, Jf: 1, K: 0x40000000}, |
| 34 | {Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_ERRNO | uint32(unix.EPERM)}, |
| 35 | } |
| 36 | for _, call := range []uint32{unix.SYS_RECVMSG, unix.SYS_RECVMMSG, unix.SYS_PTRACE, unix.SYS_PROCESS_VM_READV, unix.SYS_PROCESS_VM_WRITEV, unix.SYS_PIDFD_GETFD, unix.SYS_OPEN_BY_HANDLE_AT, unix.SYS_IO_URING_SETUP, unix.SYS_IO_URING_ENTER, unix.SYS_IO_URING_REGISTER, unix.SYS_SETNS, unix.SYS_UNSHARE} { |
| 37 | filter = append(filter, |
| 38 | unix.SockFilter{Code: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, Jf: 1, K: call}, |
| 39 | unix.SockFilter{Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_ERRNO | uint32(unix.EPERM)}) |
| 40 | } |
| 41 | filter = append(filter, |
| 42 | unix.SockFilter{Code: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, Jf: 1, K: unix.SYS_CLONE3}, |
| 43 | unix.SockFilter{Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_ERRNO | uint32(unix.ENOSYS)}, |
| 44 | unix.SockFilter{Code: unix.BPF_JMP | unix.BPF_JEQ | unix.BPF_K, Jf: 3, K: unix.SYS_CLONE}, |
| 45 | unix.SockFilter{Code: unix.BPF_LD | unix.BPF_W | unix.BPF_ABS, K: 16}, |
| 46 | unix.SockFilter{Code: unix.BPF_JMP | unix.BPF_JSET | unix.BPF_K, Jf: 1, K: unix.CLONE_NEWCGROUP | unix.CLONE_NEWIPC | unix.CLONE_NEWNET | unix.CLONE_NEWNS | unix.CLONE_NEWPID | unix.CLONE_NEWUSER | unix.CLONE_NEWUTS}, |
| 47 | unix.SockFilter{Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_ERRNO | uint32(unix.EPERM)}) |
| 48 | filter = append(filter, unix.SockFilter{Code: unix.BPF_RET | unix.BPF_K, K: unix.SECCOMP_RET_ALLOW}) |
| 49 | if err := unix.Prctl(unix.PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); err != nil { |
| 50 | return err |
| 51 | } |
| 52 | program := unix.SockFprog{Len: uint16(len(filter)), Filter: &filter[0]} |
| 53 | unsynced, _, errno := unix.Syscall(unix.SYS_SECCOMP, unix.SECCOMP_SET_MODE_FILTER, unix.SECCOMP_FILTER_FLAG_TSYNC, uintptr(unsafe.Pointer(&program))) |
| 54 | runtime.KeepAlive(filter) |
| 55 | if errno != 0 { |
| 56 | return errno |
| 57 | } |
| 58 | if unsynced != 0 { |
| 59 | return instancectl.ErrClaimUnit |
| 60 | } |
| 61 | return nil |
| 62 | } |
| 63 | |