Files
buildkit/solver/llbsolver/ops/exec_binfmt.go
Tiger Kaovilai 31e3fca484 Fix xattr copy failures on SELinux systems
When copying the buildkit-qemu-emulator binary on systems with SELinux
enabled, the copy operation fails with "operation not supported" errors
when attempting to copy security.selinux xattrs.

This change adds an XAttrErrorHandler to the copy.Copy call that ignores
ENOTSUP errors, allowing the copy to succeed on SELinux-enabled systems.

Fixes #5544

🤖 Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>

Refactor xattr error handler to be a simple function

Changed ignoreSELinuxXAttrErrorHandler from a function that returns
a function to a direct error handler function. This simplifies the
code while maintaining the same functionality.

🤖 Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>

Add tests for xattr error handling in exec_binfmt

Signed-off-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>

Update xattr error handling to ignore ENOTSUP for security.selinux only

Signed-off-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>

Refactor xattr error handling in exec_binfmt to use a single error handler function

Signed-off-by: Tiger Kaovilai <passawit.kaovilai@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-17 12:02:27 -05:00

144 lines
3.9 KiB
Go

package ops
import (
"context"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"github.com/containerd/containerd/v2/core/mount"
"github.com/containerd/platforms"
"github.com/moby/buildkit/snapshot"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/archutil"
"github.com/moby/buildkit/util/bklog"
"github.com/moby/sys/user"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
copy "github.com/tonistiigi/fsutil/copy"
)
const qemuMountName = "/dev/.buildkit_qemu_emulator"
var qemuArchMap = map[string]string{
"arm64": "aarch64",
"amd64": "x86_64",
"riscv64": "riscv64",
"arm": "arm",
"s390x": "s390x",
"ppc64le": "ppc64le",
"386": "i386",
}
type emulator struct {
path string
idmap *user.IdentityMapping
}
func (e *emulator) Mount(ctx context.Context, readonly bool) (snapshot.Mountable, error) {
return &staticEmulatorMount{path: e.path, idmap: e.idmap}, nil
}
type staticEmulatorMount struct {
path string
idmap *user.IdentityMapping
}
func (m *staticEmulatorMount) Mount() ([]mount.Mount, func() error, error) {
tmpdir, err := os.MkdirTemp("", "buildkit-qemu-emulator")
if err != nil {
return nil, nil, err
}
var ret bool
defer func() {
if !ret {
os.RemoveAll(tmpdir)
}
}()
var uid, gid int
if m.idmap != nil {
uid, gid = m.idmap.RootPair()
}
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), copy.WithXAttrErrorHandler(ignoreSELinuxXAttrErrorHandler)); err != nil {
return nil, nil, err
}
ret = true
return []mount.Mount{{
Type: "bind",
Source: filepath.Join(tmpdir, qemuMountName),
Options: []string{"ro", "bind"},
}}, func() error {
return os.RemoveAll(tmpdir)
}, nil
}
func (m *staticEmulatorMount) IdentityMapping() *user.IdentityMapping {
return m.idmap
}
func getEmulator(ctx context.Context, p *pb.Platform) (*emulator, error) {
all := archutil.SupportedPlatforms(false)
pp := platforms.Normalize(ocispecs.Platform{
Architecture: p.Architecture,
OS: p.OS,
OSVersion: p.OSVersion,
OSFeatures: p.OSFeatures,
Variant: p.Variant,
})
for _, p := range all {
if platforms.Only(p).Match(pp) {
return nil, nil
}
}
if pp.Architecture == "amd64" {
if pp.Variant != "" && pp.Variant != "v2" {
var supported []string
for _, p := range all {
if p.Architecture == "amd64" {
supported = append(supported, platforms.Format(p))
}
}
return nil, errors.Errorf("no support for running processes with %s platform, supported: %s", platforms.Format(pp), strings.Join(supported, ", "))
}
}
a, ok := qemuArchMap[pp.Architecture]
if !ok {
a = pp.Architecture
}
fn, err := exec.LookPath("buildkit-qemu-" + a)
if err != nil {
bklog.G(ctx).Warn(err.Error()) // TODO: remove this with pull support
return nil, nil // no emulator available
}
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
}