Skip to content

Commit 101dad6

Browse files
committed
pkg/mgrconfig: allow exact matches bypass snapshot check
Allowing all enable_syscalls override (snapshot) attribute is too permissive and leads to unwanted syscalls being enabled in typical syzkaller configurations. Only allow it when the syscall is specified by its exact name in enable_syscalls. Refactor the method and add a test.
1 parent 8fc3779 commit 101dad6

File tree

4 files changed

+133
-10
lines changed

4 files changed

+133
-10
lines changed

docs/syscall_descriptions_syntax.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,10 @@ Call attributes are:
110110
string argument is a fsck-like command that will be called to verify the filesystem.
111111
"remote_cover": wait longer to collect remote coverage for this call.
112112
"kfuzz_test": the call is a kfuzztest target.
113-
"snapshot": the call is enabled by default only in snapshot fuzzing mode,
114-
but "enable_syscalls" and "disable_syscalls" config parameters override this.
115-
It is generally used to mark calls that are not safe to execute in non-snapshot mode
113+
"snapshot": the call is enabled by default only in snapshot fuzzing mode, but can also be enabled in
114+
the non-snasphot mode when listed in "enable_syscalls" with its full name (as opposed to a wildcard match).
115+
It can also always be disabled via "disable_syscalls".
116+
The attribute is generally used to mark calls that are not safe to execute in non-snapshot mode
116117
(can lead to false positives, or lost connections to test machines.
117118
```
118119

pkg/mgrconfig/load.go

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,11 @@ func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string,
427427
for _, c := range enabled {
428428
n := 0
429429
for _, call := range target.Syscalls {
430-
if MatchSyscall(call.Name, c) {
430+
if !MatchSyscall(call.Name, c) {
431+
continue
432+
}
433+
// Skip snapshot attr check for the calls that match exactly.
434+
if checkMode(call, descriptionsMode, call.Name != c) {
431435
syscalls[call.ID] = true
432436
n++
433437
}
@@ -438,18 +442,14 @@ func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string,
438442
}
439443
} else {
440444
for _, call := range target.Syscalls {
441-
if call.Attrs.Snapshot && (descriptionsMode&SnapshotDescriptions) == 0 {
445+
if !checkMode(call, descriptionsMode, true) {
442446
continue
443447
}
444448
syscalls[call.ID] = true
445449
}
446450
}
447-
448451
for call := range syscalls {
449-
if target.Syscalls[call].Attrs.Disabled ||
450-
(descriptionsMode&AutoDescriptions) == 0 && target.Syscalls[call].Attrs.Automatic ||
451-
(descriptionsMode&ManualDescriptions) == 0 &&
452-
!target.Syscalls[call].Attrs.Automatic && !target.Syscalls[call].Attrs.AutomaticHelper {
452+
if target.Syscalls[call].Attrs.Disabled {
453453
delete(syscalls, call)
454454
}
455455
}
@@ -475,6 +475,24 @@ func ParseEnabledSyscalls(target *prog.Target, enabled, disabled []string,
475475
return arr, nil
476476
}
477477

478+
func checkMode(syscall *prog.Syscall, descriptionsMode DescriptionsMode,
479+
checkSnapshot bool) bool {
480+
if syscall.Attrs.Automatic &&
481+
(descriptionsMode&AutoDescriptions) == 0 {
482+
return false
483+
}
484+
if !syscall.Attrs.Automatic &&
485+
!syscall.Attrs.AutomaticHelper &&
486+
(descriptionsMode&ManualDescriptions) == 0 {
487+
return false
488+
}
489+
if checkSnapshot && syscall.Attrs.Snapshot &&
490+
(descriptionsMode&SnapshotDescriptions) == 0 {
491+
return false
492+
}
493+
return true
494+
}
495+
478496
func ParseNoMutateSyscalls(target *prog.Target, syscalls []string) (map[int]bool, error) {
479497
var result = make(map[int]bool)
480498

pkg/mgrconfig/load_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2026 syzkaller project authors. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
4+
package mgrconfig
5+
6+
import (
7+
"testing"
8+
9+
"github.com/google/syzkaller/prog"
10+
"github.com/google/syzkaller/sys/targets"
11+
"github.com/stretchr/testify/assert"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
func TestParseEnabledSyscalls(t *testing.T) {
16+
target, err := prog.GetTarget(targets.TestOS, targets.TestArch64)
17+
require.NoError(t, err)
18+
19+
tests := []struct {
20+
name string
21+
mode DescriptionsMode
22+
enable []string
23+
// TODO: add disable tests as well.
24+
expectEnabled []string
25+
expectDisabled []string
26+
}{
27+
{
28+
name: "wildcard, no snapshot",
29+
mode: ManualDescriptions,
30+
enable: []string{"test"},
31+
expectDisabled: []string{"test$snapshot_only"},
32+
},
33+
{
34+
name: "wildcard, snapshot",
35+
mode: ManualDescriptions | SnapshotDescriptions,
36+
enable: []string{"test"},
37+
expectEnabled: []string{"test$snapshot_only"},
38+
},
39+
{
40+
name: "no wildcard, no snapshot",
41+
mode: ManualDescriptions,
42+
enable: []string{"test$snapshot_only"},
43+
expectEnabled: []string{"test$snapshot_only"},
44+
},
45+
{
46+
name: "no wildcard, snapshot",
47+
mode: ManualDescriptions | SnapshotDescriptions,
48+
enable: []string{"test$snapshot_only"},
49+
expectEnabled: []string{"test$snapshot_only"},
50+
},
51+
{
52+
name: "automatic allowed",
53+
mode: ManualDescriptions | AutoDescriptions,
54+
enable: []string{"test"},
55+
expectEnabled: []string{
56+
"test$automatic",
57+
"test$automatic_helper",
58+
"test$manual",
59+
},
60+
},
61+
{
62+
name: "manual only",
63+
mode: ManualDescriptions,
64+
enable: []string{"test"},
65+
expectEnabled: []string{
66+
"test$automatic_helper",
67+
"test$manual",
68+
},
69+
expectDisabled: []string{
70+
"test$automatic",
71+
},
72+
},
73+
{
74+
name: "auto only",
75+
mode: AutoDescriptions,
76+
enable: []string{"test"},
77+
expectEnabled: []string{
78+
"test$automatic",
79+
"test$automatic_helper",
80+
},
81+
expectDisabled: []string{
82+
"test$manual",
83+
},
84+
},
85+
}
86+
for _, test := range tests {
87+
t.Run(test.name, func(t *testing.T) {
88+
ids, err := ParseEnabledSyscalls(target, test.enable,
89+
nil, test.mode)
90+
require.NoError(t, err)
91+
for _, enabled := range test.expectEnabled {
92+
assert.Contains(t, ids, target.SyscallMap[enabled].ID)
93+
}
94+
for _, disabled := range test.expectDisabled {
95+
assert.NotContains(t, ids, target.SyscallMap[disabled].ID)
96+
}
97+
})
98+
}
99+
}

sys/test/test.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,3 +1002,8 @@ test$consume_common(val common)
10021002
test$consume_subtype_of_common(val subtype_of_common)
10031003

10041004
test$fsck_attr() (fsck["fsck.test -n"])
1005+
1006+
test$snapshot_only(a0 intptr) (snapshot)
1007+
test$automatic(a0 intptr) (automatic)
1008+
test$automatic_helper(a0 intptr) (automatic_helper)
1009+
test$manual(a0 intptr)

0 commit comments

Comments
 (0)