github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/strace/strace.go (about) 1 // Copyright 2018 The gVisor 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 // Package strace implements the logic to print out the input and the return value 16 // of each traced syscall. 17 package strace 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 "time" 24 25 "github.com/nicocha30/gvisor-ligolo/pkg/abi" 26 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 27 "github.com/nicocha30/gvisor-ligolo/pkg/bits" 28 "github.com/nicocha30/gvisor-ligolo/pkg/eventchannel" 29 "github.com/nicocha30/gvisor-ligolo/pkg/marshal/primitive" 30 "github.com/nicocha30/gvisor-ligolo/pkg/seccomp" 31 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/arch" 32 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 33 pb "github.com/nicocha30/gvisor-ligolo/pkg/sentry/strace/strace_go_proto" 34 slinux "github.com/nicocha30/gvisor-ligolo/pkg/sentry/syscalls/linux" 35 36 "github.com/nicocha30/gvisor-ligolo/pkg/hostarch" 37 ) 38 39 // DefaultLogMaximumSize is the default LogMaximumSize. 40 const DefaultLogMaximumSize = 1024 41 42 // LogMaximumSize determines the maximum display size for data blobs (read, 43 // write, etc.). 44 var LogMaximumSize uint = DefaultLogMaximumSize 45 46 // EventMaximumSize determines the maximum size for data blobs (read, write, 47 // etc.) sent over the event channel. Default is 0 because most clients cannot 48 // do anything useful with binary text dump of byte array arguments. 49 var EventMaximumSize uint 50 51 // LogAppDataAllowed is set to true when printing application data in strace 52 // logs is allowed. 53 var LogAppDataAllowed = true 54 55 // ItimerTypes are the possible itimer types. 56 var ItimerTypes = abi.ValueSet{ 57 linux.ITIMER_REAL: "ITIMER_REAL", 58 linux.ITIMER_VIRTUAL: "ITIMER_VIRTUAL", 59 linux.ITIMER_PROF: "ITIMER_PROF", 60 } 61 62 func hexNum(num uint64) string { 63 return "0x" + strconv.FormatUint(num, 16) 64 } 65 66 func hexArg(arg arch.SyscallArgument) string { 67 return hexNum(arg.Uint64()) 68 } 69 70 func iovecs(t *kernel.Task, addr hostarch.Addr, iovcnt int, printContent bool, maxBytes uint64) string { 71 if iovcnt < 0 || iovcnt > linux.UIO_MAXIOV { 72 return fmt.Sprintf("%#x (error decoding iovecs: invalid iovcnt)", addr) 73 } 74 ars, err := t.CopyInIovecs(addr, iovcnt) 75 if err != nil { 76 return fmt.Sprintf("%#x (error decoding iovecs: %v)", addr, err) 77 } 78 79 var totalBytes uint64 80 var truncated bool 81 iovs := make([]string, iovcnt) 82 for i := 0; !ars.IsEmpty(); i, ars = i+1, ars.Tail() { 83 ar := ars.Head() 84 if ar.Length() == 0 || !printContent { 85 iovs[i] = fmt.Sprintf("{base=%#x, len=%d}", ar.Start, ar.Length()) 86 continue 87 } 88 89 size := uint64(ar.Length()) 90 if truncated || totalBytes+size > maxBytes { 91 truncated = true 92 size = maxBytes - totalBytes 93 } else { 94 totalBytes += uint64(ar.Length()) 95 } 96 97 b := make([]byte, size) 98 amt, err := t.CopyInBytes(ar.Start, b) 99 if err != nil { 100 iovs[i] = fmt.Sprintf("{base=%#x, len=%d, %q..., error decoding string: %v}", ar.Start, ar.Length(), b[:amt], err) 101 continue 102 } 103 104 dot := "" 105 if truncated { 106 // Indicate truncation. 107 dot = "..." 108 } 109 iovs[i] = fmt.Sprintf("{base=%#x, len=%d, %q%s}", ar.Start, ar.Length(), b[:amt], dot) 110 } 111 112 return fmt.Sprintf("%#x %s", addr, strings.Join(iovs, ", ")) 113 } 114 115 func dump(t *kernel.Task, addr hostarch.Addr, size uint, maximumBlobSize uint, printContent bool) string { 116 if !printContent { 117 return fmt.Sprintf("{base=%#x, len=%d}", addr, size) 118 } 119 origSize := size 120 if size > maximumBlobSize { 121 size = maximumBlobSize 122 } 123 if size == 0 { 124 return "" 125 } 126 127 b := make([]byte, size) 128 amt, err := t.CopyInBytes(addr, b) 129 if err != nil { 130 return fmt.Sprintf("%#x (error decoding string: %s)", addr, err) 131 } 132 133 dot := "" 134 if uint(amt) < origSize { 135 // ... if we truncated the dump. 136 dot = "..." 137 } 138 139 return fmt.Sprintf("%#x %q%s", addr, b[:amt], dot) 140 } 141 142 func path(t *kernel.Task, addr hostarch.Addr) string { 143 if addr == 0 { 144 return "<null>" 145 } 146 path, err := t.CopyInString(addr, linux.PATH_MAX) 147 if err != nil { 148 return fmt.Sprintf("%#x (error decoding path: %s)", addr, err) 149 } 150 return fmt.Sprintf("%#x %s", addr, path) 151 } 152 153 func fd(t *kernel.Task, fd int32) string { 154 root := t.FSContext().RootDirectory() 155 defer root.DecRef(t) 156 157 vfsObj := root.Mount().Filesystem().VirtualFilesystem() 158 if fd == linux.AT_FDCWD { 159 wd := t.FSContext().WorkingDirectory() 160 defer wd.DecRef(t) 161 162 name, _ := vfsObj.PathnameWithDeleted(t, root, wd) 163 return fmt.Sprintf("AT_FDCWD %s", name) 164 } 165 166 file := t.GetFile(fd) 167 if file == nil { 168 // Cast FD to uint64 to avoid printing negative hex. 169 return fmt.Sprintf("%#x (bad FD)", uint64(fd)) 170 } 171 defer file.DecRef(t) 172 173 name, _ := vfsObj.PathnameWithDeleted(t, root, file.VirtualDentry()) 174 return fmt.Sprintf("%#x %s", fd, name) 175 } 176 177 func fdpair(t *kernel.Task, addr hostarch.Addr) string { 178 var fds [2]int32 179 _, err := primitive.CopyInt32SliceIn(t, addr, fds[:]) 180 if err != nil { 181 return fmt.Sprintf("%#x (error decoding fds: %s)", addr, err) 182 } 183 184 return fmt.Sprintf("%#x [%d %d]", addr, fds[0], fds[1]) 185 } 186 187 func uname(t *kernel.Task, addr hostarch.Addr) string { 188 var u linux.UtsName 189 if _, err := u.CopyIn(t, addr); err != nil { 190 return fmt.Sprintf("%#x (error decoding utsname: %s)", addr, err) 191 } 192 193 return fmt.Sprintf("%#x %s", addr, u) 194 } 195 196 func utimensTimespec(t *kernel.Task, addr hostarch.Addr) string { 197 if addr == 0 { 198 return "null" 199 } 200 201 var tim linux.Timespec 202 if _, err := tim.CopyIn(t, addr); err != nil { 203 return fmt.Sprintf("%#x (error decoding timespec: %s)", addr, err) 204 } 205 206 var ns string 207 switch tim.Nsec { 208 case linux.UTIME_NOW: 209 ns = "UTIME_NOW" 210 case linux.UTIME_OMIT: 211 ns = "UTIME_OMIT" 212 default: 213 ns = fmt.Sprintf("%v", tim.Nsec) 214 } 215 return fmt.Sprintf("%#x {sec=%v nsec=%s}", addr, tim.Sec, ns) 216 } 217 218 func timespec(t *kernel.Task, addr hostarch.Addr) string { 219 if addr == 0 { 220 return "null" 221 } 222 223 var tim linux.Timespec 224 if _, err := tim.CopyIn(t, addr); err != nil { 225 return fmt.Sprintf("%#x (error decoding timespec: %s)", addr, err) 226 } 227 return fmt.Sprintf("%#x {sec=%v nsec=%v}", addr, tim.Sec, tim.Nsec) 228 } 229 230 func timeval(t *kernel.Task, addr hostarch.Addr) string { 231 if addr == 0 { 232 return "null" 233 } 234 235 var tim linux.Timeval 236 if _, err := tim.CopyIn(t, addr); err != nil { 237 return fmt.Sprintf("%#x (error decoding timeval: %s)", addr, err) 238 } 239 240 return fmt.Sprintf("%#x {sec=%v usec=%v}", addr, tim.Sec, tim.Usec) 241 } 242 243 func utimbuf(t *kernel.Task, addr hostarch.Addr) string { 244 if addr == 0 { 245 return "null" 246 } 247 248 var utim linux.Utime 249 if _, err := utim.CopyIn(t, addr); err != nil { 250 return fmt.Sprintf("%#x (error decoding utimbuf: %s)", addr, err) 251 } 252 253 return fmt.Sprintf("%#x {actime=%v, modtime=%v}", addr, utim.Actime, utim.Modtime) 254 } 255 256 func stat(t *kernel.Task, addr hostarch.Addr) string { 257 if addr == 0 { 258 return "null" 259 } 260 261 var stat linux.Stat 262 if _, err := stat.CopyIn(t, addr); err != nil { 263 return fmt.Sprintf("%#x (error decoding stat: %s)", addr, err) 264 } 265 return fmt.Sprintf("%#x {dev=%d, ino=%d, mode=%s, nlink=%d, uid=%d, gid=%d, rdev=%d, size=%d, blksize=%d, blocks=%d, atime=%s, mtime=%s, ctime=%s}", addr, stat.Dev, stat.Ino, linux.FileMode(stat.Mode), stat.Nlink, stat.UID, stat.GID, stat.Rdev, stat.Size, stat.Blksize, stat.Blocks, time.Unix(stat.ATime.Sec, stat.ATime.Nsec), time.Unix(stat.MTime.Sec, stat.MTime.Nsec), time.Unix(stat.CTime.Sec, stat.CTime.Nsec)) 266 } 267 268 func itimerval(t *kernel.Task, addr hostarch.Addr) string { 269 if addr == 0 { 270 return "null" 271 } 272 273 interval := timeval(t, addr) 274 value := timeval(t, addr+hostarch.Addr((*linux.Timeval)(nil).SizeBytes())) 275 return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value) 276 } 277 278 func itimerspec(t *kernel.Task, addr hostarch.Addr) string { 279 if addr == 0 { 280 return "null" 281 } 282 283 interval := timespec(t, addr) 284 value := timespec(t, addr+hostarch.Addr((*linux.Timespec)(nil).SizeBytes())) 285 return fmt.Sprintf("%#x {interval=%s, value=%s}", addr, interval, value) 286 } 287 288 func stringVector(t *kernel.Task, addr hostarch.Addr) string { 289 vec, err := t.CopyInVector(addr, slinux.ExecMaxElemSize, slinux.ExecMaxTotalSize) 290 if err != nil { 291 return fmt.Sprintf("%#x {error copying vector: %v}", addr, err) 292 } 293 s := fmt.Sprintf("%#x [", addr) 294 for i, v := range vec { 295 if i != 0 { 296 s += ", " 297 } 298 s += fmt.Sprintf("%q", v) 299 } 300 s += "]" 301 return s 302 } 303 304 func rusage(t *kernel.Task, addr hostarch.Addr) string { 305 if addr == 0 { 306 return "null" 307 } 308 309 var ru linux.Rusage 310 if _, err := ru.CopyIn(t, addr); err != nil { 311 return fmt.Sprintf("%#x (error decoding rusage: %s)", addr, err) 312 } 313 return fmt.Sprintf("%#x %+v", addr, ru) 314 } 315 316 func capHeader(t *kernel.Task, addr hostarch.Addr) string { 317 if addr == 0 { 318 return "null" 319 } 320 321 var hdr linux.CapUserHeader 322 if _, err := hdr.CopyIn(t, addr); err != nil { 323 return fmt.Sprintf("%#x (error decoding header: %s)", addr, err) 324 } 325 326 var version string 327 switch hdr.Version { 328 case linux.LINUX_CAPABILITY_VERSION_1: 329 version = "1" 330 case linux.LINUX_CAPABILITY_VERSION_2: 331 version = "2" 332 case linux.LINUX_CAPABILITY_VERSION_3: 333 version = "3" 334 default: 335 version = strconv.FormatUint(uint64(hdr.Version), 16) 336 } 337 338 return fmt.Sprintf("%#x {Version: %s, Pid: %d}", addr, version, hdr.Pid) 339 } 340 341 func capData(t *kernel.Task, hdrAddr, dataAddr hostarch.Addr) string { 342 if dataAddr == 0 { 343 return "null" 344 } 345 346 var hdr linux.CapUserHeader 347 if _, err := hdr.CopyIn(t, hdrAddr); err != nil { 348 return fmt.Sprintf("%#x (error decoding header: %v)", dataAddr, err) 349 } 350 351 var p, i, e uint64 352 353 switch hdr.Version { 354 case linux.LINUX_CAPABILITY_VERSION_1: 355 var data linux.CapUserData 356 if _, err := data.CopyIn(t, dataAddr); err != nil { 357 return fmt.Sprintf("%#x (error decoding data: %v)", dataAddr, err) 358 } 359 p = uint64(data.Permitted) 360 i = uint64(data.Inheritable) 361 e = uint64(data.Effective) 362 case linux.LINUX_CAPABILITY_VERSION_2, linux.LINUX_CAPABILITY_VERSION_3: 363 var data [2]linux.CapUserData 364 if _, err := linux.CopyCapUserDataSliceIn(t, dataAddr, data[:]); err != nil { 365 return fmt.Sprintf("%#x (error decoding data: %v)", dataAddr, err) 366 } 367 p = uint64(data[0].Permitted) | (uint64(data[1].Permitted) << 32) 368 i = uint64(data[0].Inheritable) | (uint64(data[1].Inheritable) << 32) 369 e = uint64(data[0].Effective) | (uint64(data[1].Effective) << 32) 370 default: 371 return fmt.Sprintf("%#x (unknown version %d)", dataAddr, hdr.Version) 372 } 373 374 return fmt.Sprintf("%#x {Permitted: %s, Inheritable: %s, Effective: %s}", dataAddr, CapabilityBitset.Parse(p), CapabilityBitset.Parse(i), CapabilityBitset.Parse(e)) 375 } 376 377 // pre fills in the pre-execution arguments for a system call. If an argument 378 // cannot be interpreted before the system call is executed, then a hex value 379 // will be used. Note that a full output slice will always be provided, that is 380 // len(return) == len(args). 381 func (i *SyscallInfo) pre(t *kernel.Task, args arch.SyscallArguments, maximumBlobSize uint) []string { 382 var output []string 383 384 for arg := range args { 385 if arg >= len(i.format) { 386 break 387 } 388 switch i.format[arg] { 389 case FD: 390 output = append(output, fd(t, args[arg].Int())) 391 case WriteBuffer: 392 output = append(output, dump(t, args[arg].Pointer(), args[arg+1].SizeT(), maximumBlobSize, LogAppDataAllowed /* content */)) 393 case WriteIOVec: 394 output = append(output, iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), LogAppDataAllowed /* content */, uint64(maximumBlobSize))) 395 case IOVec: 396 output = append(output, iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), false /* content */, uint64(maximumBlobSize))) 397 case SendMsgHdr: 398 output = append(output, msghdr(t, args[arg].Pointer(), LogAppDataAllowed /* content */, uint64(maximumBlobSize))) 399 case RecvMsgHdr: 400 output = append(output, msghdr(t, args[arg].Pointer(), false /* content */, uint64(maximumBlobSize))) 401 case Path: 402 output = append(output, path(t, args[arg].Pointer())) 403 case ExecveStringVector: 404 output = append(output, stringVector(t, args[arg].Pointer())) 405 case SetSockOptVal: 406 output = append(output, sockOptVal(t, args[arg-2].Uint64() /* level */, args[arg-1].Uint64() /* optName */, args[arg].Pointer() /* optVal */, args[arg+1].Uint64() /* optLen */, maximumBlobSize)) 407 case SockOptLevel: 408 output = append(output, sockOptLevels.Parse(args[arg].Uint64())) 409 case SockOptName: 410 output = append(output, sockOptNames[args[arg-1].Uint64() /* level */].Parse(args[arg].Uint64())) 411 case SockAddr: 412 output = append(output, sockAddr(t, args[arg].Pointer(), uint32(args[arg+1].Uint64()))) 413 case SockLen: 414 output = append(output, sockLenPointer(t, args[arg].Pointer())) 415 case SockFamily: 416 output = append(output, SocketFamily.Parse(uint64(args[arg].Int()))) 417 case SockType: 418 output = append(output, sockType(args[arg].Int())) 419 case SockProtocol: 420 output = append(output, sockProtocol(args[arg-2].Int(), args[arg].Int())) 421 case SockFlags: 422 output = append(output, sockFlags(args[arg].Int())) 423 case Timespec: 424 output = append(output, timespec(t, args[arg].Pointer())) 425 case UTimeTimespec: 426 output = append(output, utimensTimespec(t, args[arg].Pointer())) 427 case ItimerVal: 428 output = append(output, itimerval(t, args[arg].Pointer())) 429 case ItimerSpec: 430 output = append(output, itimerspec(t, args[arg].Pointer())) 431 case Timeval: 432 output = append(output, timeval(t, args[arg].Pointer())) 433 case Utimbuf: 434 output = append(output, utimbuf(t, args[arg].Pointer())) 435 case CloneFlags: 436 output = append(output, CloneFlagSet.Parse(uint64(args[arg].Uint()))) 437 case OpenFlags: 438 output = append(output, open(uint64(args[arg].Uint()))) 439 case Mode: 440 output = append(output, linux.FileMode(args[arg].ModeT()).String()) 441 case FutexOp: 442 output = append(output, futex(uint64(args[arg].Uint()))) 443 case PtraceRequest: 444 output = append(output, PtraceRequestSet.Parse(args[arg].Uint64())) 445 case ItimerType: 446 output = append(output, ItimerTypes.Parse(uint64(args[arg].Int()))) 447 case Signal: 448 output = append(output, signalNames.ParseDecimal(args[arg].Uint64())) 449 case SignalMaskAction: 450 output = append(output, signalMaskActions.Parse(uint64(args[arg].Int()))) 451 case SigSet: 452 output = append(output, sigSet(t, args[arg].Pointer())) 453 case SigAction: 454 output = append(output, sigAction(t, args[arg].Pointer())) 455 case CapHeader: 456 output = append(output, capHeader(t, args[arg].Pointer())) 457 case CapData: 458 output = append(output, capData(t, args[arg-1].Pointer(), args[arg].Pointer())) 459 case PollFDs: 460 output = append(output, pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), false)) 461 case EpollCtlOp: 462 output = append(output, epollCtlOps.Parse(uint64(args[arg].Int()))) 463 case EpollEvent: 464 output = append(output, epollEvent(t, args[arg].Pointer())) 465 case EpollEvents: 466 output = append(output, epollEvents(t, args[arg].Pointer(), 0 /* numEvents */, uint64(maximumBlobSize))) 467 case SelectFDSet: 468 output = append(output, fdSet(t, int(args[0].Int()), args[arg].Pointer())) 469 case MmapProt: 470 output = append(output, ProtectionFlagSet.Parse(uint64(args[arg].Uint()))) 471 case MmapFlags: 472 output = append(output, MmapFlagSet.Parse(uint64(args[arg].Uint()))) 473 case CloseRangeFlags: 474 output = append(output, CloseRangeFlagSet.Parse(uint64(args[arg].Uint()))) 475 case Oct: 476 output = append(output, "0o"+strconv.FormatUint(args[arg].Uint64(), 8)) 477 case Hex: 478 fallthrough 479 default: 480 output = append(output, hexArg(args[arg])) 481 } 482 } 483 484 return output 485 } 486 487 // post fills in the post-execution arguments for a system call. This modifies 488 // the given output slice in place with arguments that may only be interpreted 489 // after the system call has been executed. 490 func (i *SyscallInfo) post(t *kernel.Task, args arch.SyscallArguments, rval uintptr, output []string, maximumBlobSize uint) { 491 for arg := range output { 492 if arg >= len(i.format) { 493 break 494 } 495 switch i.format[arg] { 496 case ReadBuffer: 497 output[arg] = dump(t, args[arg].Pointer(), uint(rval), maximumBlobSize, LogAppDataAllowed /* content */) 498 case ReadIOVec: 499 printLength := uint64(rval) 500 if printLength > uint64(maximumBlobSize) { 501 printLength = uint64(maximumBlobSize) 502 } 503 output[arg] = iovecs(t, args[arg].Pointer(), int(args[arg+1].Int()), LogAppDataAllowed /* content */, printLength) 504 case WriteIOVec, IOVec, WriteBuffer: 505 // We already have a big blast from write. 506 output[arg] = "..." 507 case SendMsgHdr: 508 output[arg] = msghdr(t, args[arg].Pointer(), false /* content */, uint64(maximumBlobSize)) 509 case RecvMsgHdr: 510 output[arg] = msghdr(t, args[arg].Pointer(), LogAppDataAllowed /* content */, uint64(maximumBlobSize)) 511 case PostPath: 512 output[arg] = path(t, args[arg].Pointer()) 513 case PipeFDs: 514 output[arg] = fdpair(t, args[arg].Pointer()) 515 case Uname: 516 output[arg] = uname(t, args[arg].Pointer()) 517 case Stat: 518 output[arg] = stat(t, args[arg].Pointer()) 519 case PostSockAddr: 520 output[arg] = postSockAddr(t, args[arg].Pointer(), args[arg+1].Pointer()) 521 case SockLen: 522 output[arg] = sockLenPointer(t, args[arg].Pointer()) 523 case PostTimespec: 524 output[arg] = timespec(t, args[arg].Pointer()) 525 case PostItimerVal: 526 output[arg] = itimerval(t, args[arg].Pointer()) 527 case PostItimerSpec: 528 output[arg] = itimerspec(t, args[arg].Pointer()) 529 case Timeval: 530 output[arg] = timeval(t, args[arg].Pointer()) 531 case Rusage: 532 output[arg] = rusage(t, args[arg].Pointer()) 533 case PostSigSet: 534 output[arg] = sigSet(t, args[arg].Pointer()) 535 case PostSigAction: 536 output[arg] = sigAction(t, args[arg].Pointer()) 537 case PostCapData: 538 output[arg] = capData(t, args[arg-1].Pointer(), args[arg].Pointer()) 539 case PollFDs: 540 output[arg] = pollFDs(t, args[arg].Pointer(), uint(args[arg+1].Uint()), true) 541 case EpollEvents: 542 output[arg] = epollEvents(t, args[arg].Pointer(), uint64(rval), uint64(maximumBlobSize)) 543 case GetSockOptVal: 544 output[arg] = getSockOptVal(t, args[arg-2].Uint64() /* level */, args[arg-1].Uint64() /* optName */, args[arg].Pointer() /* optVal */, args[arg+1].Pointer() /* optLen */, maximumBlobSize, rval) 545 case SetSockOptVal: 546 // No need to print the value again. While it usually 547 // isn't, the string version of this arg can be long. 548 output[arg] = hexArg(args[arg]) 549 } 550 } 551 } 552 553 // printEntry prints the given system call entry. 554 func (i *SyscallInfo) printEnter(t *kernel.Task, args arch.SyscallArguments) []string { 555 output := i.pre(t, args, LogMaximumSize) 556 557 switch len(output) { 558 case 0: 559 t.Infof("%s E %s()", t.Name(), i.name) 560 case 1: 561 t.Infof("%s E %s(%s)", t.Name(), i.name, 562 output[0]) 563 case 2: 564 t.Infof("%s E %s(%s, %s)", t.Name(), i.name, 565 output[0], output[1]) 566 case 3: 567 t.Infof("%s E %s(%s, %s, %s)", t.Name(), i.name, 568 output[0], output[1], output[2]) 569 case 4: 570 t.Infof("%s E %s(%s, %s, %s, %s)", t.Name(), i.name, 571 output[0], output[1], output[2], output[3]) 572 case 5: 573 t.Infof("%s E %s(%s, %s, %s, %s, %s)", t.Name(), i.name, 574 output[0], output[1], output[2], output[3], output[4]) 575 case 6: 576 t.Infof("%s E %s(%s, %s, %s, %s, %s, %s)", t.Name(), i.name, 577 output[0], output[1], output[2], output[3], output[4], output[5]) 578 } 579 580 return output 581 } 582 583 // printExit prints the given system call exit. 584 func (i *SyscallInfo) printExit(t *kernel.Task, elapsed time.Duration, output []string, args arch.SyscallArguments, retval uintptr, err error, errno int) { 585 var rval string 586 if err == nil { 587 // Fill in the output after successful execution. 588 i.post(t, args, retval, output, LogMaximumSize) 589 rval = fmt.Sprintf("%d (%#x) (%v)", retval, retval, elapsed) 590 } else { 591 rval = fmt.Sprintf("%d (%#x) errno=%d (%s) (%v)", retval, retval, errno, err, elapsed) 592 } 593 594 switch len(output) { 595 case 0: 596 t.Infof("%s X %s() = %s", t.Name(), i.name, 597 rval) 598 case 1: 599 t.Infof("%s X %s(%s) = %s", t.Name(), i.name, 600 output[0], rval) 601 case 2: 602 t.Infof("%s X %s(%s, %s) = %s", t.Name(), i.name, 603 output[0], output[1], rval) 604 case 3: 605 t.Infof("%s X %s(%s, %s, %s) = %s", t.Name(), i.name, 606 output[0], output[1], output[2], rval) 607 case 4: 608 t.Infof("%s X %s(%s, %s, %s, %s) = %s", t.Name(), i.name, 609 output[0], output[1], output[2], output[3], rval) 610 case 5: 611 t.Infof("%s X %s(%s, %s, %s, %s, %s) = %s", t.Name(), i.name, 612 output[0], output[1], output[2], output[3], output[4], rval) 613 case 6: 614 t.Infof("%s X %s(%s, %s, %s, %s, %s, %s) = %s", t.Name(), i.name, 615 output[0], output[1], output[2], output[3], output[4], output[5], rval) 616 } 617 } 618 619 // sendEnter sends the syscall enter to event log. 620 func (i *SyscallInfo) sendEnter(t *kernel.Task, args arch.SyscallArguments) []string { 621 output := i.pre(t, args, EventMaximumSize) 622 623 event := pb.Strace{ 624 Process: t.Name(), 625 Function: i.name, 626 Info: &pb.Strace_Enter{ 627 Enter: &pb.StraceEnter{}, 628 }, 629 } 630 for _, arg := range output { 631 event.Args = append(event.Args, arg) 632 } 633 eventchannel.Emit(&event) 634 635 return output 636 } 637 638 // sendExit sends the syscall exit to event log. 639 func (i *SyscallInfo) sendExit(t *kernel.Task, elapsed time.Duration, output []string, args arch.SyscallArguments, rval uintptr, err error, errno int) { 640 if err == nil { 641 // Fill in the output after successful execution. 642 i.post(t, args, rval, output, EventMaximumSize) 643 } 644 645 exit := &pb.StraceExit{ 646 Return: fmt.Sprintf("%#x", rval), 647 ElapsedNs: elapsed.Nanoseconds(), 648 } 649 if err != nil { 650 exit.Error = err.Error() 651 exit.ErrNo = int64(errno) 652 } 653 event := pb.Strace{ 654 Process: t.Name(), 655 Function: i.name, 656 Info: &pb.Strace_Exit{Exit: exit}, 657 } 658 for _, arg := range output { 659 event.Args = append(event.Args, arg) 660 } 661 eventchannel.Emit(&event) 662 } 663 664 type syscallContext struct { 665 info SyscallInfo 666 args arch.SyscallArguments 667 start time.Time 668 logOutput []string 669 eventOutput []string 670 flags uint32 671 } 672 673 // SyscallEnter implements kernel.Stracer.SyscallEnter. It logs the syscall 674 // entry trace. 675 func (s SyscallMap) SyscallEnter(t *kernel.Task, sysno uintptr, args arch.SyscallArguments, flags uint32) any { 676 info, ok := s[sysno] 677 if !ok { 678 info = SyscallInfo{ 679 name: fmt.Sprintf("sys_%d", sysno), 680 format: defaultFormat, 681 } 682 } 683 684 var output, eventOutput []string 685 if bits.IsOn32(flags, kernel.StraceEnableLog) { 686 output = info.printEnter(t, args) 687 } 688 if bits.IsOn32(flags, kernel.StraceEnableEvent) { 689 eventOutput = info.sendEnter(t, args) 690 } 691 692 return &syscallContext{ 693 info: info, 694 args: args, 695 start: time.Now(), 696 logOutput: output, 697 eventOutput: eventOutput, 698 flags: flags, 699 } 700 } 701 702 // SyscallExit implements kernel.Stracer.SyscallExit. It logs the syscall 703 // exit trace. 704 func (s SyscallMap) SyscallExit(context any, t *kernel.Task, sysno, rval uintptr, err error) { 705 errno := kernel.ExtractErrno(err, int(sysno)) 706 c := context.(*syscallContext) 707 708 elapsed := time.Since(c.start) 709 if bits.IsOn32(c.flags, kernel.StraceEnableLog) { 710 c.info.printExit(t, elapsed, c.logOutput, c.args, rval, err, errno) 711 } 712 if bits.IsOn32(c.flags, kernel.StraceEnableEvent) { 713 c.info.sendExit(t, elapsed, c.eventOutput, c.args, rval, err, errno) 714 } 715 } 716 717 // ConvertToSysnoMap converts the names to a map keyed on the syscall number 718 // and value set to true. 719 // 720 // The map is in a convenient format to pass to SyscallFlagsTable.Enable(). 721 func (s SyscallMap) ConvertToSysnoMap(syscalls []string) (map[uintptr]bool, error) { 722 if syscalls == nil { 723 // Sentinel: no list. 724 return nil, nil 725 } 726 727 l := make(map[uintptr]bool) 728 for _, sc := range syscalls { 729 // Try to match this system call. 730 sysno, ok := s.ConvertToSysno(sc) 731 if !ok { 732 return nil, fmt.Errorf("syscall %q not found", sc) 733 } 734 l[sysno] = true 735 } 736 737 // Success. 738 return l, nil 739 } 740 741 // ConvertToSysno converts the name to system call number. Returns false 742 // if syscall with same name is not found. 743 func (s SyscallMap) ConvertToSysno(syscall string) (uintptr, bool) { 744 for sysno, info := range s { 745 if info.name != "" && info.name == syscall { 746 return sysno, true 747 } 748 } 749 return 0, false 750 } 751 752 // Name returns the syscall name. 753 func (s SyscallMap) Name(sysno uintptr) string { 754 if info, ok := s[sysno]; ok { 755 return info.name 756 } 757 return fmt.Sprintf("sys_%d", sysno) 758 } 759 760 // Initialize prepares all syscall tables for use by this package. 761 // 762 // N.B. This is not in an init function because we can't be sure all syscall 763 // tables are registered with the kernel when init runs. 764 func Initialize() { 765 for _, table := range kernel.SyscallTables() { 766 // Is this known? 767 sys, ok := Lookup(table.OS, table.Arch) 768 if !ok { 769 continue 770 } 771 772 table.Stracer = sys 773 } 774 } 775 776 // SinkType defines where to send straces to. 777 type SinkType uint32 778 779 const ( 780 // SinkTypeLog sends straces to text log 781 SinkTypeLog SinkType = 1 << iota 782 783 // SinkTypeEvent sends strace to event log 784 SinkTypeEvent 785 ) 786 787 func convertToSyscallFlag(sinks SinkType) uint32 { 788 ret := uint32(0) 789 if bits.IsOn32(uint32(sinks), uint32(SinkTypeLog)) { 790 ret |= kernel.StraceEnableLog 791 } 792 if bits.IsOn32(uint32(sinks), uint32(SinkTypeEvent)) { 793 ret |= kernel.StraceEnableEvent 794 } 795 return ret 796 } 797 798 // Enable enables the syscalls in allowlist in all syscall tables. 799 // 800 // Preconditions: Initialize has been called. 801 func Enable(allowlist []string, sinks SinkType) error { 802 flags := convertToSyscallFlag(sinks) 803 for _, table := range kernel.SyscallTables() { 804 // Is this known? 805 sys, ok := Lookup(table.OS, table.Arch) 806 if !ok { 807 continue 808 } 809 810 // Convert to a set of system calls numbers. 811 wl, err := sys.ConvertToSysnoMap(allowlist) 812 if err != nil { 813 return err 814 } 815 816 table.FeatureEnable.Enable(flags, wl, true) 817 } 818 819 // Done. 820 return nil 821 } 822 823 // Disable will disable Strace for all system calls and missing syscalls. 824 // 825 // Preconditions: Initialize has been called. 826 func Disable(sinks SinkType) { 827 flags := convertToSyscallFlag(sinks) 828 for _, table := range kernel.SyscallTables() { 829 // Strace will be disabled for all syscalls including missing. 830 table.FeatureEnable.Enable(flags, nil, false) 831 } 832 } 833 834 // EnableAll enables all syscalls in all syscall tables. 835 // 836 // Preconditions: Initialize has been called. 837 func EnableAll(sinks SinkType) { 838 flags := convertToSyscallFlag(sinks) 839 for _, table := range kernel.SyscallTables() { 840 // Is this known? 841 if _, ok := Lookup(table.OS, table.Arch); !ok { 842 continue 843 } 844 845 table.FeatureEnable.EnableAll(flags) 846 } 847 } 848 849 func init() { 850 t, ok := Lookup(abi.Host, arch.Host) 851 if ok { 852 // Provide the native table as the lookup for seccomp 853 // debugging. This is best-effort. This is provided this way to 854 // avoid dependencies from seccomp to this package. 855 seccomp.SyscallName = t.Name 856 } 857 }