Skip to content

Commit

Permalink
update patches loading to support v6.1.0 and cpu-max
Browse files Browse the repository at this point in the history
Signed-off-by: Tonis Tiigi <[email protected]>
  • Loading branch information
tonistiigi committed Aug 26, 2021
1 parent 99c76af commit fb32164
Show file tree
Hide file tree
Showing 13 changed files with 1,143 additions and 32 deletions.
58 changes: 41 additions & 17 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
# syntax=docker/dockerfile:1.2
# syntax=docker/dockerfile:1.3-labs

ARG ALPINE_BASE=alpine:3.14
ARG ALPINE_VERSION=3.14
ARG ALPINE_BASE=alpine:${ALPINE_VERSION}

ARG QEMU_VERSION
ARG QEMU_VERSION=head
ARG QEMU_REPO=https://github.com/qemu/qemu

# xx is a helper for cross-compilation
FROM --platform=$BUILDPLATFORM tonistiigi/xx@sha256:56b19a5fb89b99195ec494d59ad34370d14540858c1f56c560ec1e7f2d1c177f AS xx

FROM --platform=$BUILDPLATFORM ${ALPINE_BASE} AS alpine-patches
RUN apk add --no-cache git
ARG ALPINE_VERSION
RUN <<eof
set -ex
git clone --depth 1 -b ${ALPINE_VERSION}-stable https://github.com/alpinelinux/aports.git
mkdir -p /opt/alpine-patches
cp -a aports/community/qemu/*.patch /opt/alpine-patches/
rm -rf aports
eof

FROM --platform=$BUILDPLATFORM ${ALPINE_BASE} AS src
RUN apk add --no-cache git patch

WORKDIR /src
ARG QEMU_VERSION
ARG QEMU_REPO
WORKDIR /src
RUN git clone $QEMU_REPO && \
git clone --depth 1 -b 3.14-stable https://github.com/alpinelinux/aports.git && \
cd qemu && \
git checkout $QEMU_VERSION && \
for f in ../aports/community/qemu/*.patch; do patch -p1 < $f; done && \
scripts/git-submodule.sh update \
ui/keycodemapdb \
tests/fp/berkeley-testfloat-3 \
tests/fp/berkeley-softfloat-3 \
dtc slirp
RUN git clone $QEMU_REPO && cd qemu && git checkout $QEMU_VERSION
COPY patches patches
COPY --from=alpine-patches /opt/alpine-patches patches/alpine-patches
ARG QEMU_PATCHES=cpu-max
ARG QEMU_PATCHES_ALL=${QEMU_PATCHES},alpine-patches
RUN <<eof
set -ex
# remove patches not needed for 6.1.0+
if [ "$(printf "$(cat qemu/VERSION)\n6.0.90" | sort -V | head -n 1)" = "6.0.90" ]; then
# this issue has been fixed upstream by using non-glibc specific macro
rm -rf patches/alpine-patches/fix-sigevent-and-sigval_t.patch || true
# following patches are already applied upstream
rm -rf patches/alpine-patches/0001-virtio-host-input-use-safe-64-bit-time-accessors-for.patch || true
rm -rf patches/alpine-patches/0002-virtio-user-input-use-safe-64-bit-time-accessors-for.patch || true
rm -rf patches/alpine-patches/CVE-2021-3527.patch || true
fi
cd qemu
for p in $(echo $QEMU_PATCHES_ALL | tr ',' '\n'); do
for f in ../patches/$p/*.patch; do echo "apply $f"; patch -p1 < $f; done
done
scripts/git-submodule.sh update ui/keycodemapdb tests/fp/berkeley-testfloat-3 tests/fp/berkeley-softfloat-3 dtc slirp
eof

FROM --platform=$BUILDPLATFORM ${ALPINE_BASE} AS base
RUN apk add --no-cache git clang lld python3 llvm make ninja pkgconfig glib-dev gcc musl-dev perl bash
Expand All @@ -37,7 +62,6 @@ RUN set -e; \
[ "$(xx-info arch)" = "386" ] && XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple; \
true


FROM base AS build
ARG TARGETPLATFORM
ARG QEMU_VERSION QEMU_TARGETS
Expand All @@ -46,7 +70,7 @@ RUN --mount=target=.,from=src,src=/src/qemu,rw --mount=target=./install-scripts,
TARGETPLATFORM=${TARGETPLATFORM} configure_qemu.sh && \
make -j "$(getconf _NPROCESSORS_ONLN)" && \
make install && \
cd /usr/bin && for f in $(ls qemu-*); do xx-verify $f; done
cd /usr/bin && for f in $(ls qemu-*); do xx-verify --static $f; done

ARG BINARY_PREFIX
RUN cd /usr/bin; [ -z "$BINARY_PREFIX" ] || for f in $(ls qemu-*); do ln -s $f $BINARY_PREFIX$f; done
Expand All @@ -66,7 +90,7 @@ RUN --mount=target=. \
TARGETPLATFORM=$TARGETPLATFORM xx-go build \
-ldflags "-X main.revision=$(git rev-parse --short HEAD) -X main.qemuVersion=${QEMU_VERSION}" \
-o /go/bin/binfmt ./cmd/binfmt && \
xx-verify /go/bin/binfmt
xx-verify --static /go/bin/binfmt

FROM scratch AS binaries
ARG BINARY_PREFIX
Expand Down
21 changes: 6 additions & 15 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,17 @@ variable "REPO" {
default = "tonistiigi/binfmt"
}
variable "QEMU_REPO" {
default = ""
default = "https://github.com/qemu/qemu"
}
variable "QEMU_VERSION" {
default = ""
default = "v6.1.0"
}

// Special target: https://github.com/docker/metadata-action#bake-definition
target "meta-helper" {
tags = ["${REPO}:test"]
}

function "getdef" {
params = [val, default]
result = <<-EOT
%{ if val != "" }${val}%{ else }${default}%{ endif }
EOT
}

group "default" {
targets = ["binaries"]
}
Expand All @@ -45,8 +38,8 @@ target "all-arch" {
target "mainline" {
inherits = ["meta-helper"]
args = {
QEMU_REPO = trimspace(getdef("${QEMU_REPO}", "https://github.com/qemu/qemu"))
QEMU_VERSION = trimspace(getdef("${QEMU_VERSION}", "v6.0.0"))
QEMU_REPO = QEMU_REPO
QEMU_VERSION = QEMU_VERSION
}
cache-to = ["type=inline"]
cache-from = ["${REPO}:master"]
Expand All @@ -57,13 +50,11 @@ target "mainline-all" {
}

target "buildkit" {
inherits = ["meta-helper"]
inherits = ["mainline"]
args = {
QEMU_REPO = trimspace(getdef("${QEMU_REPO}", "https://github.com/crazy-max/qemu"))
QEMU_VERSION = trimspace(getdef("${QEMU_VERSION}", "be9b8cdfb3a8da33657a6df82e66b2601fe8a310"))
BINARY_PREFIX = "buildkit-"
QEMU_PATCHES = "cpu-max,buildkit-direct-execve-v6.1"
}
cache-to = ["type=inline"]
cache-from = ["${REPO}:buildkit-master"]
target = "binaries"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
From dc472d34ed686cba2804f254e3d803904791eaf6 Mon Sep 17 00:00:00 2001
From: Tibor Vass <[email protected]>
Date: Mon, 1 Jun 2020 23:08:25 +0000
Subject: [PATCH 1/5] linux-user: have execve call qemu via /proc/self/exe to
not rely on binfmt_misc

It is assumed that when a guest program calls execve syscall it wants to
execute a program on the same guest architecture and not the host architecture.

Previously, such a guest program would have execve syscall error out with:
"exec format error".

A common solution is to register the qemu binary in binfmt_misc but that is not a
userland-friendly solution, requiring to modify kernel state.

This patch injects /proc/self/exe as the first parameter and the qemu program name
as argv[0] to execve.

Signed-off-by: Tibor Vass <[email protected]>
(cherry picked from commit bc8e2fdae6cd4f9ff1d487056ca3bc598a3187bc)
Signed-off-by: Tibor Vass <[email protected]>
---
linux-user/syscall.c | 35 ++++++++++++++++++++++++++++++-----
1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 95d79ddc43..409686fdca 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -8498,10 +8498,37 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
envc++;
}

- argp = g_new0(char *, argc + 1);
+ argp = g_new0(char *, argc + 4);
envp = g_new0(char *, envc + 1);

- for (gp = guest_argp, q = argp; gp;
+ if (!(p = lock_user_string(arg1)))
+ goto execve_efault;
+
+ /* if pathname is /proc/self/exe then retrieve the path passed to qemu via command line */
+ if (is_proc_myself(p, "exe")) {
+ CPUState *cpu = env_cpu((CPUArchState *)cpu_env);
+ TaskState *ts = cpu->opaque;
+ p = ts->bprm->filename;
+ }
+
+ /* retrieve guest argv0 */
+ if (get_user_ual(addr, guest_argp))
+ goto execve_efault;
+
+ /*
+ * From the guest, the call
+ * execve(pathname, [argv0, argv1], envp)
+ * on the host, becomes:
+ * execve("/proc/self/exe", [qemu_progname, "-0", argv0, pathname, argv1], envp)
+ * where qemu_progname is the error message prefix for qemu
+ */
+ argp[0] = (char*)error_get_progname();
+ argp[1] = (char*)"-0";
+ argp[2] = (char*)lock_user_string(addr);
+ argp[3] = p;
+
+ /* copy guest argv1 onwards to host argv4 onwards */
+ for (gp = guest_argp + 1*sizeof(abi_ulong), q = argp + 4; gp;
gp += sizeof(abi_ulong), q++) {
if (get_user_ual(addr, gp))
goto execve_efault;
@@ -8525,8 +8552,6 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
}
*q = NULL;

- if (!(p = lock_user_string(arg1)))
- goto execve_efault;
/* Although execve() is not an interruptible syscall it is
* a special case where we must use the safe_syscall wrapper:
* if we allow a signal to happen before we make the host
@@ -8537,7 +8562,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1,
* before the execve completes and makes it the other
* program's problem.
*/
- ret = get_errno(safe_execve(p, argp, envp));
+ ret = get_errno(safe_execve("/proc/self/exe", argp, envp));
unlock_user(p, arg1, 0);

goto execve_end;
--
2.29.2

Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
From 945c7a9e623d915c2e1535894aa90362ed114c08 Mon Sep 17 00:00:00 2001
From: Tibor Vass <[email protected]>
Date: Tue, 2 Jun 2020 10:39:48 +0000
Subject: [PATCH 2/5] linux-user: lookup user program in PATH

Signed-off-by: Tibor Vass <[email protected]>
(cherry picked from commit 1102f3dc7a4db75e72d25b2eab8503f52c44a542)
Signed-off-by: Tibor Vass <[email protected]>
---
linux-user/main.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 44 insertions(+), 1 deletion(-)

diff --git a/linux-user/main.c b/linux-user/main.c
index f956afccab..36f0c85957 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -551,6 +551,45 @@ static void usage(int exitcode)
exit(exitcode);
}

+/*
+ * path_lookup searches for an executable filename in the directories named by the PATH environment variable.
+ * Returns a copy of filename if it is an absolute path or could not find a match.
+ * Caller is responsible to free returned string.
+ * Adapted from musl's execvp implementation.
+ */
+static char *path_lookup(char *filename) {
+ const char *p, *z, *path = getenv("PATH");
+ size_t l, k;
+ struct stat buf;
+
+ /* if PATH is not set or filename is absolute path return filename */
+ if (!path || !filename || filename[0] == '/')
+ return strndup(filename, NAME_MAX+1);
+
+ k = strnlen(filename, NAME_MAX+1);
+ if (k > NAME_MAX) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ l = strnlen(path, PATH_MAX-1)+1;
+
+ for (p = path; ; p = z) {
+ char *b = calloc(l+k+1, sizeof(char));
+ z = strchrnul(p, ':');
+ if (z-p >= l) {
+ if (!*z++) break;
+ continue;
+ }
+ memcpy(b, p, z-p);
+ b[z-p] = '/';
+ memcpy(b+(z-p)+(z>p), filename, k+1);
+ if (!stat(b, &buf) && !(buf.st_mode & S_IFDIR) && (buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
+ return b;
+ if (!*z++) break;
+ }
+ return strndup(filename, NAME_MAX+1);
+}
+
static int parse_args(int argc, char **argv)
{
const char *r;
@@ -616,7 +655,11 @@ static int parse_args(int argc, char **argv)
exit(EXIT_FAILURE);
}

- exec_path = argv[optind];
+ /* not freeing exec_path as it is needed for the lifetime of the process */
+ if (!(exec_path = path_lookup(argv[optind]))) {
+ (void) fprintf(stderr, "qemu: could not find user program %s: %s\n", exec_path, strerror(errno));
+ exit(EXIT_FAILURE);
+ }

return optind;
}
--
2.29.2

Loading

0 comments on commit fb32164

Please sign in to comment.