Skip to content

Commit e9193ba

Browse files
committed
Fix breaking change in Seccomp profile behavior
Multiple conditions were previously allowed to be placed upon the same syscall argument. Restore this behavior. Signed-off-by: Matthew Heon <[email protected]>
1 parent d5fc10a commit e9193ba

File tree

2 files changed

+135
-8
lines changed

2 files changed

+135
-8
lines changed

libcontainer/integration/seccomp_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,99 @@ func TestSeccompDenyWriteMultipleConditions(t *testing.T) {
321321
t.Fatalf("Expected output %s but got %s\n", expected, actual)
322322
}
323323
}
324+
325+
func TestSeccompMultipleConditionSameArgDeniesStdout(t *testing.T) {
326+
if testing.Short() {
327+
return
328+
}
329+
330+
rootfs, err := newRootfs()
331+
if err != nil {
332+
t.Fatal(err)
333+
}
334+
defer remove(rootfs)
335+
336+
// Prevent writing to both stdout and stderr
337+
config := newTemplateConfig(rootfs)
338+
config.Seccomp = &configs.Seccomp{
339+
DefaultAction: configs.Allow,
340+
Syscalls: []*configs.Syscall{
341+
{
342+
Name: "write",
343+
Action: configs.Errno,
344+
Args: []*configs.Arg{
345+
{
346+
Index: 0,
347+
Value: 1,
348+
Op: configs.EqualTo,
349+
},
350+
{
351+
Index: 0,
352+
Value: 2,
353+
Op: configs.EqualTo,
354+
},
355+
},
356+
},
357+
},
358+
}
359+
360+
buffers, exitCode, err := runContainer(config, "", "ls", "/")
361+
if err != nil {
362+
t.Fatalf("%s: %s", buffers, err)
363+
}
364+
if exitCode != 0 {
365+
t.Fatalf("exit code not 0. code %d buffers %s", exitCode, buffers)
366+
}
367+
// Verify that nothing was printed
368+
if len(buffers.Stdout.String()) != 0 {
369+
t.Fatalf("Something was written to stdout, write call succeeded!\n")
370+
}
371+
}
372+
373+
func TestSeccompMultipleConditionSameArgDeniesStderr(t *testing.T) {
374+
if testing.Short() {
375+
return
376+
}
377+
378+
rootfs, err := newRootfs()
379+
if err != nil {
380+
t.Fatal(err)
381+
}
382+
defer remove(rootfs)
383+
384+
// Prevent writing to both stdout and stderr
385+
config := newTemplateConfig(rootfs)
386+
config.Seccomp = &configs.Seccomp{
387+
DefaultAction: configs.Allow,
388+
Syscalls: []*configs.Syscall{
389+
{
390+
Name: "write",
391+
Action: configs.Errno,
392+
Args: []*configs.Arg{
393+
{
394+
Index: 0,
395+
Value: 1,
396+
Op: configs.EqualTo,
397+
},
398+
{
399+
Index: 0,
400+
Value: 2,
401+
Op: configs.EqualTo,
402+
},
403+
},
404+
},
405+
},
406+
}
407+
408+
buffers, exitCode, err := runContainer(config, "", "ls", "/does_not_exist")
409+
if err == nil {
410+
t.Fatalf("Expecting error return, instead got 0")
411+
}
412+
if exitCode == 0 {
413+
t.Fatalf("Busybox should fail with negative exit code, instead got %d!", exitCode)
414+
}
415+
// Verify nothing was printed
416+
if len(buffers.Stderr.String()) != 0 {
417+
t.Fatalf("Something was written to stderr, write call succeeded!\n")
418+
}
419+
}

libcontainer/seccomp/seccomp_linux.go

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ var (
2222
actErrno = libseccomp.ActErrno.SetReturnCode(int16(unix.EPERM))
2323
)
2424

25+
const (
26+
// Linux system calls can have at most 6 arguments
27+
syscallMaxArguments int = 6
28+
)
29+
2530
// Filters given syscalls in a container, preventing them from being used
2631
// Started in the container init process, and carried over to all child processes
2732
// Setns calls, however, require a separate invocation, as they are not children
@@ -45,11 +50,11 @@ func InitSeccomp(config *configs.Seccomp) error {
4550
for _, arch := range config.Architectures {
4651
scmpArch, err := libseccomp.GetArchFromString(arch)
4752
if err != nil {
48-
return err
53+
return fmt.Errorf("error validating Seccomp architecture: %s", err)
4954
}
5055

5156
if err := filter.AddArch(scmpArch); err != nil {
52-
return err
57+
return fmt.Errorf("error adding architecture to seccomp filter: %s", err)
5358
}
5459
}
5560

@@ -170,29 +175,55 @@ func matchCall(filter *libseccomp.ScmpFilter, call *configs.Syscall) error {
170175
// Convert the call's action to the libseccomp equivalent
171176
callAct, err := getAction(call.Action)
172177
if err != nil {
173-
return err
178+
return fmt.Errorf("action in seccomp profile is invalid: %s", err)
174179
}
175180

176181
// Unconditional match - just add the rule
177182
if len(call.Args) == 0 {
178183
if err = filter.AddRule(callNum, callAct); err != nil {
179-
return err
184+
return fmt.Errorf("error adding seccomp filter rule for syscall %s: %s", call.Name, err)
180185
}
181186
} else {
182-
// Conditional match - convert the per-arg rules into library format
187+
// If two or more arguments have the same condition,
188+
// Revert to old behavior, adding each condition as a separate rule
189+
argCounts := make([]uint, syscallMaxArguments)
183190
conditions := []libseccomp.ScmpCondition{}
184191

185192
for _, cond := range call.Args {
186193
newCond, err := getCondition(cond)
187194
if err != nil {
188-
return err
195+
return fmt.Errorf("error creating seccomp syscall condition for syscall %s: %s", call.Name, err)
189196
}
190197

198+
argCounts[cond.Index] += 1
199+
191200
conditions = append(conditions, newCond)
192201
}
193202

194-
if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
195-
return err
203+
hasMultipleArgs := false
204+
for _, count := range argCounts {
205+
if count > 1 {
206+
hasMultipleArgs = true
207+
break
208+
}
209+
}
210+
211+
if hasMultipleArgs {
212+
// Revert to old behavior
213+
// Add each condition attached to a separate rule
214+
for _, cond := range conditions {
215+
condArr := []libseccomp.ScmpCondition{cond}
216+
217+
if err = filter.AddRuleConditional(callNum, callAct, condArr); err != nil {
218+
return fmt.Errorf("error adding seccomp rule for syscall %s: %s", call.Name, err)
219+
}
220+
}
221+
} else {
222+
// No conditions share same argument
223+
// Use new, proper behavior
224+
if err = filter.AddRuleConditional(callNum, callAct, conditions); err != nil {
225+
return fmt.Errorf("error adding seccomp rule for syscall %s: %s", call.Name, err)
226+
}
196227
}
197228
}
198229

0 commit comments

Comments
 (0)