inspektor-gadget

Форк
0
247 строк · 6.3 Кб
1
// Copyright 2019-2023 The Inspektor Gadget authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
//go:build !withoutebpf
16

17
package tracer
18

19
import (
20
	"fmt"
21
	"io"
22
	"os"
23
	"path/filepath"
24
	"regexp"
25
	"strings"
26

27
	"github.com/inspektor-gadget/inspektor-gadget/pkg/utils/syscalls"
28
)
29

30
const syscallsPath = `/sys/kernel/debug/tracing/events/syscalls/`
31

32
type param struct {
33
	position  int
34
	name      string
35
	isPointer bool
36
}
37

38
type syscallDeclaration struct {
39
	name   string
40
	params []param
41
}
42

43
func syscallGetName(nr uint16) string {
44
	name, ok := syscalls.GetSyscallNameByNumber(int(nr))
45
	// Just do like strace (https://man7.org/linux/man-pages/man1/strace.1.html):
46
	// Syscalls unknown to strace are printed raw
47
	if !ok {
48
		return fmt.Sprintf("syscall_%x", nr)
49
	}
50

51
	return name
52
}
53

54
// TODO Find all syscalls which take a char * as argument and add them there.
55
var syscallDefs = map[string][6]uint64{
56
	"execve":      {useNullByteLength, 0, 0, 0, 0, 0},
57
	"access":      {useNullByteLength, 0, 0, 0, 0, 0},
58
	"open":        {useNullByteLength, 0, 0, 0, 0, 0},
59
	"openat":      {0, useNullByteLength, 0, 0, 0, 0},
60
	"mkdir":       {useNullByteLength, 0, 0, 0, 0, 0},
61
	"chdir":       {useNullByteLength, 0, 0, 0, 0, 0},
62
	"pivot_root":  {useNullByteLength, useNullByteLength, 0, 0, 0, 0},
63
	"mount":       {useNullByteLength, useNullByteLength, useNullByteLength, 0, 0, 0},
64
	"umount2":     {useNullByteLength, 0, 0, 0, 0, 0},
65
	"sethostname": {useNullByteLength, 0, 0, 0, 0, 0},
66
	"statfs":      {useNullByteLength, 0, 0, 0, 0, 0},
67
	"stat":        {useNullByteLength, 0, 0, 0, 0, 0},
68
	"statx":       {0, useNullByteLength, 0, 0, 0, 0},
69
	"lstat":       {useNullByteLength, 0, 0, 0, 0, 0},
70
	"fgetxattr":   {0, useNullByteLength, 0, 0, 0, 0},
71
	"lgetxattr":   {useNullByteLength, useNullByteLength, 0, 0, 0, 0},
72
	"getxattr":    {useNullByteLength, useNullByteLength, 0, 0, 0, 0},
73
	"newfstatat":  {0, useNullByteLength, 0, 0, 0, 0},
74
	"read":        {0, useRetAsParamLength | paramProbeAtExitMask, 0, 0, 0, 0},
75
	"write":       {0, useArgIndexAsParamLength + 2, 0, 0, 0, 0},
76
	"getcwd":      {useNullByteLength | paramProbeAtExitMask, 0, 0, 0, 0, 0},
77
	"pread64":     {0, useRetAsParamLength | paramProbeAtExitMask, 0, 0, 0, 0},
78
}
79

80
var re = regexp.MustCompile(`\s+field:(?P<type>.*?) (?P<name>[a-z_0-9]+);.*`)
81

82
func parseLine(l string, idx int) (*param, error) {
83
	n1 := re.SubexpNames()
84

85
	r := re.FindAllStringSubmatch(l, -1)
86
	if len(r) == 0 {
87
		return nil, nil
88
	}
89
	res := r[0]
90

91
	mp := map[string]string{}
92
	for i, n := range res {
93
		mp[n1[i]] = n
94
	}
95

96
	if _, ok := mp["type"]; !ok {
97
		return nil, nil
98
	}
99
	if _, ok := mp["name"]; !ok {
100
		return nil, nil
101
	}
102

103
	// ignore
104
	if mp["name"] == "__syscall_nr" {
105
		return nil, nil
106
	}
107

108
	var cParam param
109
	cParam.name = mp["name"]
110

111
	// The position is calculated based on the event format. The actual parameters
112
	// start from 8th index, hence we subtract that from idx to get position
113
	// of the parameter to the syscall
114
	cParam.position = idx - 8
115

116
	cParam.isPointer = strings.Contains(mp["type"], "*")
117

118
	return &cParam, nil
119
}
120

121
// Map sys_enter_NAME to syscall name as in /usr/include/asm/unistd_64.h
122
// TODO Check if this is also true for arm64.
123
func relateSyscallName(name string) string {
124
	switch name {
125
	case "newfstat":
126
		return "fstat"
127
	case "newlstat":
128
		return "lstat"
129
	case "newstat":
130
		return "stat"
131
	case "newuname":
132
		return "uname"
133
	case "sendfile64":
134
		return "sendfile"
135
	case "sysctl":
136
		return "_sysctl"
137
	case "umount":
138
		return "umount2"
139
	default:
140
		return name
141
	}
142
}
143

144
func parseSyscall(name, format string) (*syscallDeclaration, error) {
145
	syscallParts := strings.Split(format, "\n")
146
	var skipped bool
147

148
	var cParams []param
149
	for idx, line := range syscallParts {
150
		if !skipped {
151
			if len(line) != 0 {
152
				continue
153
			} else {
154
				skipped = true
155
			}
156
		}
157
		cp, err := parseLine(line, idx)
158
		if err != nil {
159
			return nil, err
160
		}
161
		if cp != nil {
162
			cParams = append(cParams, *cp)
163
		}
164
	}
165

166
	return &syscallDeclaration{
167
		name:   name,
168
		params: cParams,
169
	}, nil
170
}
171

172
func gatherSyscallsDeclarations() (map[string]syscallDeclaration, error) {
173
	cSyscalls := make(map[string]syscallDeclaration)
174
	err := filepath.Walk(syscallsPath, func(path string, f os.FileInfo, err error) error {
175
		if err != nil {
176
			return err
177
		}
178

179
		if path == "syscalls" {
180
			return nil
181
		}
182

183
		if !f.IsDir() {
184
			return nil
185
		}
186

187
		eventName := f.Name()
188
		if strings.HasPrefix(eventName, "sys_exit") {
189
			return nil
190
		}
191

192
		syscallName := strings.TrimPrefix(eventName, "sys_enter_")
193
		syscallName = relateSyscallName(syscallName)
194

195
		formatFilePath := filepath.Join(syscallsPath, eventName, "format")
196
		formatFile, err := os.Open(formatFilePath)
197
		if err != nil {
198
			return nil
199
		}
200
		defer formatFile.Close()
201

202
		formatBytes, err := io.ReadAll(formatFile)
203
		if err != nil {
204
			return err
205
		}
206

207
		cSyscall, err := parseSyscall(syscallName, string(formatBytes))
208
		if err != nil {
209
			return err
210
		}
211

212
		cSyscalls[cSyscall.name] = *cSyscall
213

214
		return nil
215
	})
216
	if err != nil {
217
		return nil, fmt.Errorf("walking %q: %w", syscallsPath, err)
218
	}
219
	return cSyscalls, nil
220
}
221

222
func getSyscallDeclaration(syscallsDeclarations map[string]syscallDeclaration, syscallName string) (syscallDeclaration, error) {
223
	declaration, ok := syscallsDeclarations[syscallName]
224
	if !ok {
225
		return syscallDeclaration{}, fmt.Errorf("no syscall correspond to %q", syscallName)
226
	}
227

228
	return declaration, nil
229
}
230

231
func (s syscallDeclaration) getParameterCount() uint8 {
232
	return uint8(len(s.params))
233
}
234

235
func (s syscallDeclaration) paramIsPointer(paramNumber uint8) (bool, error) {
236
	if int(paramNumber) >= len(s.params) {
237
		return false, fmt.Errorf("param number %d out of bounds for syscall %q", paramNumber, s.name)
238
	}
239
	return s.params[paramNumber].isPointer, nil
240
}
241

242
func (s syscallDeclaration) getParameterName(paramNumber uint8) (string, error) {
243
	if int(paramNumber) >= len(s.params) {
244
		return "", fmt.Errorf("param number %d out of bounds for syscall %q", paramNumber, s.name)
245
	}
246
	return s.params[paramNumber].name, nil
247
}
248

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.