diff --git a/pkg/abi/linux/seccomp.go b/pkg/abi/linux/seccomp.go index 12d5cea88..5e98dd8c0 100644 --- a/pkg/abi/linux/seccomp.go +++ b/pkg/abi/linux/seccomp.go @@ -50,11 +50,19 @@ func (a BPFAction) String() string { case SECCOMP_RET_KILL_THREAD: return "kill thread" case SECCOMP_RET_TRAP: - return fmt.Sprintf("trap (%d)", a.Data()) + data := a.Data() + if data == 0 { + return "trap" + } + return fmt.Sprintf("trap (data=%#x)", data) case SECCOMP_RET_ERRNO: - return fmt.Sprintf("errno (%d)", a.Data()) + return fmt.Sprintf("return errno=%#x", a.Data()) case SECCOMP_RET_TRACE: - return fmt.Sprintf("trace (%d)", a.Data()) + data := a.Data() + if data == 0 { + return "trace" + } + return fmt.Sprintf("trace (data=%#x)", data) case SECCOMP_RET_ALLOW: return "allow" } diff --git a/pkg/seccomp/seccomp.go b/pkg/seccomp/seccomp.go index ab414c2aa..4ea30cc2d 100644 --- a/pkg/seccomp/seccomp.go +++ b/pkg/seccomp/seccomp.go @@ -19,7 +19,6 @@ package seccomp import ( "fmt" "sort" - "strconv" "strings" "time" @@ -465,19 +464,22 @@ func (ssrs singleSyscallRuleSet) Render(program *syscallProgram, ls *labelSet, n // `singleSyscallRuleSet`. func (ssrs singleSyscallRuleSet) String() string { var sb strings.Builder - sb.WriteString("sysno=") - sb.WriteString(strconv.Itoa(int(ssrs.sysno))) if ssrs.vsyscall { - sb.WriteString("[vsyscall]") - } - sb.WriteString(": ") - if len(ssrs.rules) == 0 { - sb.WriteString("(no rules)") + sb.WriteString("Vsyscall ") } else { + sb.WriteString("Syscall ") + } + sb.WriteString(fmt.Sprintf("%3d: ", ssrs.sysno)) + switch len(ssrs.rules) { + case 0: + sb.WriteString("(no rules)") + case 1: + sb.WriteString(ssrs.rules[0].String()) + default: sb.WriteRune('{') for i, r := range ssrs.rules { if i != 0 { - sb.WriteString(", ") + sb.WriteString("; ") } sb.WriteString(r.String()) } @@ -495,6 +497,9 @@ type syscallRuleAction struct { // String returns a human-friendly representation of the `syscallRuleAction`. func (sra syscallRuleAction) String() string { + if _, isMatchAll := sra.rule.(MatchAll); isMatchAll { + return sra.action.String() + } return fmt.Sprintf("(%v) => %v", sra.rule.String(), sra.action) } diff --git a/pkg/seccomp/seccomp_rules.go b/pkg/seccomp/seccomp_rules.go index c63b40aa7..d78c25a96 100644 --- a/pkg/seccomp/seccomp_rules.go +++ b/pkg/seccomp/seccomp_rules.go @@ -17,7 +17,6 @@ package seccomp import ( "fmt" "sort" - "strconv" "strings" "golang.org/x/sys/unix" @@ -52,6 +51,8 @@ func seccompDataOffsetArgHigh(i int) uint32 { // or RIP value. type ValueMatcher interface { // String returns a human-readable representation of the match rule. + // If the returned string contains "VAL", it will be replaced with + // the symbolic name of the value being matched against. String() string // Repr returns a string that will be used for asserting equality between @@ -73,6 +74,12 @@ type ValueMatcher interface { // halfValueMatcher verifies a 32-bit value. type halfValueMatcher interface { + // String returns a human-friendly representation of the check being done + // against the 32-bit value. + // The string "x.(high|low) {{halfValueMatcher.String()}}" should read well, + // e.g. "x.low == 0xffff". + String() string + // Repr returns a string that will be used for asserting equality between // two `halfValueMatcher` instances. It must therefore be unique to the // `halfValueMatcher` implementation and to its parameters. @@ -93,6 +100,11 @@ type halfValueMatcher interface { // halfAnyValue implements `halfValueMatcher` and matches any value. type halfAnyValue struct{} +// String implements `halfValueMatcher.String`. +func (halfAnyValue) String() string { + return "== *" +} + // Repr implements `halfValueMatcher.Repr`. func (halfAnyValue) Repr() string { return "halfAnyValue" @@ -106,6 +118,14 @@ func (halfAnyValue) HalfRender(program *syscallProgram, labelSet *labelSet) { // halfEqualTo implements `halfValueMatcher` and matches a specific 32-bit value. type halfEqualTo uint32 +// String implements `halfValueMatcher.String`. +func (heq halfEqualTo) String() string { + if heq == 0 { + return "== 0" + } + return fmt.Sprintf("== %#x", uint32(heq)) +} + // Repr implements `halfValueMatcher.Repr`. func (heq halfEqualTo) Repr() string { return fmt.Sprintf("halfEq(%#x)", uint32(heq)) @@ -121,6 +141,11 @@ func (heq halfEqualTo) HalfRender(program *syscallProgram, labelSet *labelSet) { // bitwise operation. type halfNotSet uint32 +// String implements `halfValueMatcher.String`. +func (hns halfNotSet) String() string { + return fmt.Sprintf("& %#x == 0", uint32(hns)) +} + // Repr implements `halfValueMatcher.Repr`. func (hns halfNotSet) Repr() string { return fmt.Sprintf("halfNotSet(%#x)", uint32(hns)) @@ -139,6 +164,14 @@ type halfMaskedEqual struct { value uint32 } +// String implements `halfValueMatcher.String`. +func (hmeq halfMaskedEqual) String() string { + if hmeq.value == 0 { + return fmt.Sprintf("& %#x == 0", hmeq.mask) + } + return fmt.Sprintf("& %#x == %#x", hmeq.mask, hmeq.value) +} + // Repr implements `halfValueMatcher.Repr`. func (hmeq halfMaskedEqual) Repr() string { return fmt.Sprintf("halfMaskedEqual(%#x, %#x)", hmeq.mask, hmeq.value) @@ -167,7 +200,21 @@ type splitMatcher struct { // String implements `ValueMatcher.String`. func (sm splitMatcher) String() string { - return sm.Repr() + if sm.repr == "" { + _, highIsAnyValue := sm.highMatcher.(halfAnyValue) + _, lowIsAnyValue := sm.lowMatcher.(halfAnyValue) + if highIsAnyValue && lowIsAnyValue { + return "== *" + } + if highIsAnyValue { + return fmt.Sprintf("VAL.low %s", sm.lowMatcher.String()) + } + if lowIsAnyValue { + return fmt.Sprintf("VAL.high %s", sm.highMatcher.String()) + } + return fmt.Sprintf("(VAL.high %s && VAL.low %s)", sm.highMatcher.String(), sm.lowMatcher.String()) + } + return sm.repr } // Repr implements `ValueMatcher.Repr`. @@ -290,6 +337,9 @@ type EqualTo uintptr // String implements `ValueMatcher.String`. func (eq EqualTo) String() string { + if eq == 0 { + return "== 0" + } return fmt.Sprintf("== %#x", uintptr(eq)) } @@ -459,12 +509,12 @@ type NonNegativeFD struct{} // String implements `ValueMatcher.String`. func (NonNegativeFD) String() string { - return fmt.Sprintf("NonNegativeFD") + return "is non-negative FD" } // Repr implements `ValueMatcher.Repr`. func (NonNegativeFD) Repr() string { - return NonNegativeFD{}.String() + return "NonNegativeFD" } // Render implements `ValueMatcher.Render`. @@ -762,39 +812,32 @@ func (pa PerArg) String() string { writtenArgs := 0 for i, arg := range pa { if arg == nil { - arg = AnyValue{} + continue } if _, isAny := arg.(AnyValue); isAny { - // Check if all future arguments are also "any value"; if so, stop here. - allIsAny := true - for j := i + 1; j < len(pa); j++ { - if pa[j] == nil { - continue - } - if _, isAny := pa[j].(AnyValue); !isAny { - allIsAny = false - break - } - } - if allIsAny { - break - } + continue } - if i != 0 { + if writtenArgs != 0 { sb.WriteString(" && ") } + str := arg.String() + var varName string if i == RuleIP { - sb.WriteString("rip") + varName = "rip" } else { - sb.WriteString("arg") - sb.WriteString(strconv.Itoa(i)) + varName = fmt.Sprintf("arg[%d]", i) + } + if strings.Contains(str, "VAL") { + sb.WriteString(strings.ReplaceAll(str, "VAL", varName)) + } else { + sb.WriteString(varName) + sb.WriteRune(' ') + sb.WriteString(str) } - sb.WriteRune(' ') - sb.WriteString(arg.String()) writtenArgs++ } if writtenArgs == 0 { - return "*" + return "true" } if writtenArgs == 1 { return sb.String()