aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/sandbox/ptrace.go
blob: b6e4130ee901c1abe4c51aacb211dbc9eef820da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package sandbox

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"strings"
	"syscall"
	"time"
	"unsafe"
)

const (
	NULL = 0

	PTRACE_ATTACH             = 16
	PTRACE_DETACH             = 17
	PTRACE_SECCOMP_GET_FILTER = 0x420c
)

type ptraceError struct {
	op    string
	errno syscall.Errno
}

func (p *ptraceError) Error() string { return fmt.Sprintf("%s: %v", p.op, p.errno) }

func (p *ptraceError) Unwrap() error {
	if p.errno == 0 {
		return nil
	}
	return p.errno
}

func ptrace(op uintptr, pid, addr int, data unsafe.Pointer) (r uintptr, errno syscall.Errno) {
	r, _, errno = syscall.Syscall6(syscall.SYS_PTRACE, op, uintptr(pid), uintptr(addr), uintptr(data), NULL, NULL)
	return
}

func ptraceAttach(pid int) error {
	const (
		statePrefix = "State:"
		stateSuffix = "t (tracing stop)"
	)

	var r io.ReadSeekCloser
	if f, err := os.Open(fmt.Sprintf("/proc/%d/status", pid)); err != nil {
		return err
	} else {
		r = f
	}

	if _, errno := ptrace(PTRACE_ATTACH, pid, 0, nil); errno != 0 {
		return &ptraceError{"PTRACE_ATTACH", errno}
	}

	// ugly! but there does not appear to be another way
	for {
		time.Sleep(10 * time.Millisecond)

		if _, err := r.Seek(0, io.SeekStart); err != nil {
			return err
		}
		s := bufio.NewScanner(r)

		var found bool
		for s.Scan() {
			found = strings.HasPrefix(s.Text(), statePrefix)
			if found {
				break
			}
		}
		if err := s.Err(); err != nil {
			return err
		}

		if !found {
			return syscall.EBADE
		}

		if strings.HasSuffix(s.Text(), stateSuffix) {
			break
		}
	}

	return nil
}

func ptraceDetach(pid int) error {
	if _, errno := ptrace(PTRACE_DETACH, pid, 0, nil); errno != 0 {
		return &ptraceError{"PTRACE_DETACH", errno}
	}
	return nil
}

type sockFilter struct { /* Filter block */
	code uint16 /* Actual filter code */
	jt   uint8  /* Jump true */
	jf   uint8  /* Jump false */
	k    uint32 /* Generic multiuse field */
}

func getFilter[T comparable](pid, index int) ([]T, error) {
	if s := unsafe.Sizeof(*new(T)); s != 8 {
		panic(fmt.Sprintf("invalid filter block size %d", s))
	}

	var buf []T
	if n, errno := ptrace(PTRACE_SECCOMP_GET_FILTER, pid, index, nil); errno != 0 {
		return nil, &ptraceError{"PTRACE_SECCOMP_GET_FILTER", errno}
	} else {
		buf = make([]T, n)
	}
	if _, errno := ptrace(PTRACE_SECCOMP_GET_FILTER, pid, index, unsafe.Pointer(&buf[0])); errno != 0 {
		return nil, &ptraceError{"PTRACE_SECCOMP_GET_FILTER", errno}
	}
	return buf, nil
}