From 9d8fa6d695cfe172c5e16fbe245d1205611706b7 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Mon, 30 Oct 2023 19:26:39 +1100 Subject: [PATCH] libcontainer: dmz: fix "go get" builds Because runc-dmz is not checked into Git, go get will end up creating a copy of libcontainer/dmz with no runc-dmz binary, which causes external libcontainer users to have compilation errors. Unfortunately, we cannot get go:embed to just ignore that there are no files matching the provided pattern, so instead we need to create a dummy file that matches the go:embed (which we check into Git and so go get _will_ copy) and switch to embed.FS. This is a little bit uglier, but at least it will fix external libcontainer users. Signed-off-by: Aleksa Sarai --- libcontainer/dmz/Makefile | 2 +- libcontainer/dmz/{ => binary}/.gitignore | 0 libcontainer/dmz/binary/dummy-file.txt | 0 libcontainer/dmz/dmz_linux.go | 51 ++++++++++++++++-------- 4 files changed, 36 insertions(+), 17 deletions(-) rename libcontainer/dmz/{ => binary}/.gitignore (100%) create mode 100644 libcontainer/dmz/binary/dummy-file.txt diff --git a/libcontainer/dmz/Makefile b/libcontainer/dmz/Makefile index 3c30db243d9..959529c5fd9 100644 --- a/libcontainer/dmz/Makefile +++ b/libcontainer/dmz/Makefile @@ -14,6 +14,6 @@ else CFLAGS += -DRUNC_USE_STDLIB endif -runc-dmz: _dmz.c +binary/runc-dmz: _dmz.c $(CC) $(CFLAGS) -static -o $@ $^ $(STRIP) -gs $@ diff --git a/libcontainer/dmz/.gitignore b/libcontainer/dmz/binary/.gitignore similarity index 100% rename from libcontainer/dmz/.gitignore rename to libcontainer/dmz/binary/.gitignore diff --git a/libcontainer/dmz/binary/dummy-file.txt b/libcontainer/dmz/binary/dummy-file.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libcontainer/dmz/dmz_linux.go b/libcontainer/dmz/dmz_linux.go index 12f9709a269..0985677ea5b 100644 --- a/libcontainer/dmz/dmz_linux.go +++ b/libcontainer/dmz/dmz_linux.go @@ -6,20 +6,32 @@ package dmz import ( "bytes" "debug/elf" - _ "embed" + "embed" "os" + "sync" "github.com/sirupsen/logrus" ) -// Try to build the runc-dmz binary. If it fails, replace it with an empty file -// (this will trigger us to fall back to a clone of /proc/self/exe). Yeah, this -// is a bit ugly but it makes sure that weird cross-compilation setups don't -// break because of runc-dmz. +// Try to build the runc-dmz binary on "go generate". If it fails (or +// libcontainer is being imported as a library), the embed.FS will not contain +// the file, which will then cause us to fall back to a clone of +// /proc/self/exe. // -//go:generate sh -c "make -B runc-dmz || echo -n >runc-dmz" -//go:embed runc-dmz -var runcDmzBinary []byte +// There is an empty file called dummy-file.txt in libcontainer/dmz/binary in +// order to work around the restriction that go:embed requires at least one +// file to match the pattern. +// +//go:generate make -B binary/runc-dmz +//go:embed binary +var runcDmzFs embed.FS + +// A cached copy of the contents of runc-dmz. +var ( + runcDmzBinaryOnce sync.Once + runcDmzBinaryIsValid bool + runcDmzBinary []byte +) // Binary returns a cloned copy (see CloneBinary) of a very minimal C program // that just does an execve() of its arguments. This is used in the final @@ -31,18 +43,25 @@ var runcDmzBinary []byte // If the runc-dmz binary is not embedded into the runc binary, Binary will // return ErrNoDmzBinary as the error. func Binary(tmpDir string) (*os.File, error) { - rdr := bytes.NewBuffer(runcDmzBinary) - // Verify that our embedded binary has a standard ELF header. - if !bytes.HasPrefix(rdr.Bytes(), []byte(elf.ELFMAG)) { - if rdr.Len() != 0 { - logrus.Infof("misconfigured build: embedded runc-dmz binary is non-empty but is missing a proper ELF header") - } - return nil, ErrNoDmzBinary - } // Setting RUNC_DMZ=legacy disables this dmz method. if os.Getenv("RUNC_DMZ") == "legacy" { logrus.Debugf("RUNC_DMZ=legacy set -- switching back to classic /proc/self/exe cloning") return nil, ErrNoDmzBinary } + runcDmzBinaryOnce.Do(func() { + runcDmzBinary, _ = runcDmzFs.ReadFile("binary/runc-dmz") + // Verify that our embedded binary has a standard ELF header. + if !bytes.HasPrefix(runcDmzBinary, []byte(elf.ELFMAG)) { + if len(runcDmzBinary) != 0 { + logrus.Infof("misconfigured build: embedded runc-dmz binary is non-empty but is missing a proper ELF header") + } + } else { + runcDmzBinaryIsValid = true + } + }) + if !runcDmzBinaryIsValid { + return nil, ErrNoDmzBinary + } + rdr := bytes.NewBuffer(runcDmzBinary) return CloneBinary(rdr, int64(rdr.Len()), "runc-dmz", tmpDir) }