Skip to content

Fix xattr copy failures on SELinux systems #6015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion solver/llbsolver/ops/exec_binfmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"

"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/platforms"
Expand Down Expand Up @@ -64,7 +65,7 @@ func (m *staticEmulatorMount) Mount() ([]mount.Mount, func() error, error) {
if err := copy.Copy(context.TODO(), filepath.Dir(m.path), filepath.Base(m.path), tmpdir, qemuMountName, func(ci *copy.CopyInfo) {
m := 0555
ci.Mode = &m
}, copy.WithChown(uid, gid)); err != nil {
}, copy.WithChown(uid, gid), copy.WithXAttrErrorHandler(ignoreSELinuxXAttrErrorHandler)); err != nil {
return nil, nil, err
}

Expand Down Expand Up @@ -123,3 +124,20 @@ func getEmulator(ctx context.Context, p *pb.Platform) (*emulator, error) {

return &emulator{path: fn}, nil
}

// ignoreSELinuxXAttrErrorHandler is an error handler for xattr copy operations
// that specifically ignores ENOTSUP errors for security.selinux extended attributes.
// This addresses SELinux compatibility issues where copying files to filesystems that
// don't support SELinux xattrs (like tmpfs) would fail with ENOTSUP, preventing
// qemu emulator setup on SELinux-enabled systems. Since the security.selinux xattr
// is not critical for the emulator functionality, we safely ignore these errors
// while preserving other xattr error handling.
func ignoreSELinuxXAttrErrorHandler(dst, src, xattrKey string, err error) error {
// Ignore ENOTSUP errors specifically for security.selinux xattr
// This allows qemu emulator setup to succeed on SELinux systems
// when copying to filesystems that don't support SELinux xattrs
if errors.Is(err, syscall.ENOTSUP) && xattrKey == "security.selinux" {
return nil
}
return err
}
91 changes: 91 additions & 0 deletions solver/llbsolver/ops/exec_binfmt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package ops

import (
"syscall"
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)

func TestBinfmtXAttrErrorHandler(t *testing.T) {
tests := []struct {
name string
dst string
src string
xattrKey string
inputErr error
expectErr bool
description string
}{
{
name: "ENOTSUP_security_selinux_ignored",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "security.selinux",
inputErr: syscall.ENOTSUP,
expectErr: false,
description: "ENOTSUP error for security.selinux should be ignored",
},
{
name: "ENOTSUP_other_xattr_propagated",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "user.some_attr",
inputErr: syscall.ENOTSUP,
expectErr: true,
description: "ENOTSUP error for non-selinux xattr should propagate",
},
{
name: "ENOTSUP_security_capability_propagated",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "security.capability",
inputErr: syscall.ENOTSUP,
expectErr: true,
description: "ENOTSUP error for other security xattr should propagate",
},
{
name: "other_error_security_selinux_propagated",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "security.selinux",
inputErr: syscall.EPERM,
expectErr: true,
description: "Non-ENOTSUP errors should always propagate",
},
{
name: "wrapped_ENOTSUP_error_handled",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "security.selinux",
inputErr: errors.Wrap(syscall.ENOTSUP, "wrapped error"),
expectErr: false,
description: "Wrapped ENOTSUP errors should be handled with errors.Is",
},
{
name: "nil_error_returns_nil",
dst: "/tmp/dest",
src: "/tmp/src",
xattrKey: "security.selinux",
inputErr: nil,
expectErr: false,
description: "Nil error should return nil",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

result := ignoreSELinuxXAttrErrorHandler(tt.dst, tt.src, tt.xattrKey, tt.inputErr)

if tt.expectErr {
require.Error(t, result, tt.description)
require.Equal(t, tt.inputErr, result, "Error should be propagated unchanged")
} else {
require.NoError(t, result, tt.description)
}
})
}
}