[PATCH v6 01/11] hvf: Add hypervisor entitlement to output binaries

Alexander Graf posted 11 patches 3 years, 9 months ago
Maintainers: Paolo Bonzini <pbonzini@redhat.com>, Alexander Graf <agraf@csgraf.de>, Richard Henderson <richard.henderson@linaro.org>, Eduardo Habkost <ehabkost@redhat.com>, Roman Bolshakov <r.bolshakov@yadro.com>, Cameron Esfahani <dirty@apple.com>, Peter Maydell <peter.maydell@linaro.org>
There is a newer version of this series
[PATCH v6 01/11] hvf: Add hypervisor entitlement to output binaries
Posted by Alexander Graf 3 years, 9 months ago
In macOS 11, QEMU only gets access to Hypervisor.framework if it has the
respective entitlement. Add an entitlement template and automatically self
sign and apply the entitlement in the build.

Signed-off-by: Alexander Graf <agraf@csgraf.de>
Reviewed-by: Roman Bolshakov <r.bolshakov@yadro.com>
Tested-by: Roman Bolshakov <r.bolshakov@yadro.com>

---

v1 -> v2:

  - Make safe to ctrl-C

v3 -> v4:

  - Remove unused exe_full variable
  - Reuse exe_name variable
---
 accel/hvf/entitlements.plist |  8 ++++++++
 meson.build                  | 29 +++++++++++++++++++++++++----
 scripts/entitlement.sh       | 13 +++++++++++++
 3 files changed, 46 insertions(+), 4 deletions(-)
 create mode 100644 accel/hvf/entitlements.plist
 create mode 100755 scripts/entitlement.sh

diff --git a/accel/hvf/entitlements.plist b/accel/hvf/entitlements.plist
new file mode 100644
index 0000000000..154f3308ef
--- /dev/null
+++ b/accel/hvf/entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+    <key>com.apple.security.hypervisor</key>
+    <true/>
+</dict>
+</plist>
diff --git a/meson.build b/meson.build
index 3d889857a0..c667d64498 100644
--- a/meson.build
+++ b/meson.build
@@ -2146,9 +2146,14 @@ foreach target : target_dirs
     }]
   endif
   foreach exe: execs
-    emulators += {exe['name']:
-         executable(exe['name'], exe['sources'],
-               install: true,
+    exe_name = exe['name']
+    exe_sign = 'CONFIG_HVF' in config_target
+    if exe_sign
+      exe_name += '-unsigned'
+    endif
+
+    emulator = executable(exe_name, exe['sources'],
+               install: not exe_sign,
                c_args: c_args,
                dependencies: arch_deps + deps + exe['dependencies'],
                objects: lib.extract_all_objects(recursive: true),
@@ -2156,7 +2161,23 @@ foreach target : target_dirs
                link_depends: [block_syms, qemu_syms] + exe.get('link_depends', []),
                link_args: link_args,
                gui_app: exe['gui'])
-    }
+
+    if exe_sign
+      emulators += {exe['name'] : custom_target(exe['name'],
+                   install: true,
+                   install_dir: get_option('bindir'),
+                   depends: emulator,
+                   output: exe['name'],
+                   command: [
+                     meson.current_source_dir() / 'scripts/entitlement.sh',
+                     meson.current_build_dir() / exe_name,
+                     meson.current_build_dir() / exe['name'],
+                     meson.current_source_dir() / 'accel/hvf/entitlements.plist'
+                   ])
+      }
+    else
+      emulators += {exe['name']: emulator}
+    endif
 
     if 'CONFIG_TRACE_SYSTEMTAP' in config_host
       foreach stp: [
diff --git a/scripts/entitlement.sh b/scripts/entitlement.sh
new file mode 100755
index 0000000000..c540fa6435
--- /dev/null
+++ b/scripts/entitlement.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+#
+# Helper script for the build process to apply entitlements
+
+SRC="$1"
+DST="$2"
+ENTITLEMENT="$3"
+
+trap 'rm "$DST.tmp"' exit
+cp -af "$SRC" "$DST.tmp"
+codesign --entitlements "$ENTITLEMENT" --force -s - "$DST.tmp"
+mv "$DST.tmp" "$DST"
+trap '' exit
-- 
2.24.3 (Apple Git-128)


Re: [PATCH v6 01/11] hvf: Add hypervisor entitlement to output binaries
Posted by Akihiko Odaki 3 years, 7 months ago
Hi,

I use your patches when running QEMU on M1 MacBook Air.

I noticed that the installation process corrupts the code signature 
because meson modifies the file to fix dynamic shared library install 
names. Also, stripping apparently does not work because the signed file 
is not considered as "executable" by meson. Here is some change I wrote 
for my own use, just for reference:
https://github.com/akihikodaki/qemu/commit/6a9b5d7e4ea03b1e757be1eedf256871bb6a5bdd

Also, the patch series do no longer apply to master. Here is my merge 
with conflict resolution (It is not a rebase and was done for my own 
purpose. Just for reference.):
https://github.com/akihikodaki/qemu/commit/b7885e4370a2fe426e80d32afe6eb5d01a71640d

Regards,
Akihiko Odaki

On 2021/01/21 7:44, Alexander Graf wrote:
> In macOS 11, QEMU only gets access to Hypervisor.framework if it has the
> respective entitlement. Add an entitlement template and automatically self
> sign and apply the entitlement in the build.
> 
> Signed-off-by: Alexander Graf <agraf@csgraf.de>
> Reviewed-by: Roman Bolshakov <r.bolshakov@yadro.com>
> Tested-by: Roman Bolshakov <r.bolshakov@yadro.com>
> 
> ---
> 
> v1 -> v2:
> 
>    - Make safe to ctrl-C
> 
> v3 -> v4:
> 
>    - Remove unused exe_full variable
>    - Reuse exe_name variable
> ---
>   accel/hvf/entitlements.plist |  8 ++++++++
>   meson.build                  | 29 +++++++++++++++++++++++++----
>   scripts/entitlement.sh       | 13 +++++++++++++
>   3 files changed, 46 insertions(+), 4 deletions(-)
>   create mode 100644 accel/hvf/entitlements.plist
>   create mode 100755 scripts/entitlement.sh
> 
> diff --git a/accel/hvf/entitlements.plist b/accel/hvf/entitlements.plist
> new file mode 100644
> index 0000000000..154f3308ef
> --- /dev/null
> +++ b/accel/hvf/entitlements.plist
> @@ -0,0 +1,8 @@
> +<?xml version="1.0" encoding="UTF-8"?>
> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
> +<plist version="1.0">
> +<dict>
> +    <key>com.apple.security.hypervisor</key>
> +    <true/>
> +</dict>
> +</plist>
> diff --git a/meson.build b/meson.build
> index 3d889857a0..c667d64498 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2146,9 +2146,14 @@ foreach target : target_dirs
>       }]
>     endif
>     foreach exe: execs
> -    emulators += {exe['name']:
> -         executable(exe['name'], exe['sources'],
> -               install: true,
> +    exe_name = exe['name']
> +    exe_sign = 'CONFIG_HVF' in config_target
> +    if exe_sign
> +      exe_name += '-unsigned'
> +    endif
> +
> +    emulator = executable(exe_name, exe['sources'],
> +               install: not exe_sign,
>                  c_args: c_args,
>                  dependencies: arch_deps + deps + exe['dependencies'],
>                  objects: lib.extract_all_objects(recursive: true),
> @@ -2156,7 +2161,23 @@ foreach target : target_dirs
>                  link_depends: [block_syms, qemu_syms] + exe.get('link_depends', []),
>                  link_args: link_args,
>                  gui_app: exe['gui'])
> -    }
> +
> +    if exe_sign
> +      emulators += {exe['name'] : custom_target(exe['name'],
> +                   install: true,
> +                   install_dir: get_option('bindir'),
> +                   depends: emulator,
> +                   output: exe['name'],
> +                   command: [
> +                     meson.current_source_dir() / 'scripts/entitlement.sh',
> +                     meson.current_build_dir() / exe_name,
> +                     meson.current_build_dir() / exe['name'],
> +                     meson.current_source_dir() / 'accel/hvf/entitlements.plist'
> +                   ])
> +      }
> +    else
> +      emulators += {exe['name']: emulator}
> +    endif
>   
>       if 'CONFIG_TRACE_SYSTEMTAP' in config_host
>         foreach stp: [
> diff --git a/scripts/entitlement.sh b/scripts/entitlement.sh
> new file mode 100755
> index 0000000000..c540fa6435
> --- /dev/null
> +++ b/scripts/entitlement.sh
> @@ -0,0 +1,13 @@
> +#!/bin/sh -e
> +#
> +# Helper script for the build process to apply entitlements
> +
> +SRC="$1"
> +DST="$2"
> +ENTITLEMENT="$3"
> +
> +trap 'rm "$DST.tmp"' exit
> +cp -af "$SRC" "$DST.tmp"
> +codesign --entitlements "$ENTITLEMENT" --force -s - "$DST.tmp"
> +mv "$DST.tmp" "$DST"
> +trap '' exit
> 

Re: [PATCH v6 01/11] hvf: Add hypervisor entitlement to output binaries
Posted by Paolo Bonzini 3 years, 7 months ago
On 23/02/21 12:56, Akihiko Odaki wrote:
> 
> I noticed that the installation process corrupts the code signature 
> because meson modifies the file to fix dynamic shared library install 
> names. Also, stripping apparently does not work because the signed file 
> is not considered as "executable" by meson. Here is some change I wrote 
> for my own use, just for reference:
> https://github.com/akihikodaki/qemu/commit/6a9b5d7e4ea03b1e757be1eedf256871bb6a5bdd 
> 

That seems like a feasible way to do it.  We could also have a single 
script with --build and --install as the first argument.

Since entitlement support is already part of the upstream tree, would 
you like to submit the patch and Cc me so that I can include it?

Thanks,

Paolo


[PATCH] hvf: Sign the code after installation
Posted by Akihiko Odaki 3 years, 7 months ago
Before this change, the code signed during the build was installed
directly.

However, the signature gets invalidated because meson modifies the code
to fix dynamic library install names during the install process.

It also prevents meson to strip the code because the pre-signed file is
not marked as an executable (although it is somehow able to perform the
modification described above).

With this change, the unsigned code will be installed and modified by
meson first, and a script signs it later.

Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
---
 meson.build                                      | 11 +++++++----
 scripts/{entitlement.sh => entitlement/build.sh} |  0
 scripts/entitlement/install.sh                   | 11 +++++++++++
 3 files changed, 18 insertions(+), 4 deletions(-)
 rename scripts/{entitlement.sh => entitlement/build.sh} (100%)
 create mode 100755 scripts/entitlement/install.sh

diff --git a/meson.build b/meson.build
index 05a67c20d93..76691023c2c 100644
--- a/meson.build
+++ b/meson.build
@@ -2224,7 +2224,7 @@ foreach target : target_dirs
     endif
 
     emulator = executable(exe_name, exe['sources'],
-               install: not exe_sign,
+               install: true,
                c_args: c_args,
                dependencies: arch_deps + deps + exe['dependencies'],
                objects: lib.extract_all_objects(recursive: true),
@@ -2235,17 +2235,20 @@ foreach target : target_dirs
 
     if exe_sign
       emulators += {exe['name'] : custom_target(exe['name'],
-                   install: true,
-                   install_dir: get_option('bindir'),
                    depends: emulator,
                    output: exe['name'],
                    command: [
-                     meson.current_source_dir() / 'scripts/entitlement.sh',
+                     meson.current_source_dir() / 'scripts/entitlement/build.sh',
                      meson.current_build_dir() / exe_name,
                      meson.current_build_dir() / exe['name'],
                      meson.current_source_dir() / 'accel/hvf/entitlements.plist'
                    ])
       }
+
+      meson.add_install_script('scripts/entitlement/install.sh',
+                               get_option('bindir') / exe_name,
+                               get_option('bindir') / exe['name'],
+                               meson.current_source_dir() / 'accel/hvf/entitlements.plist')
     else
       emulators += {exe['name']: emulator}
     endif
diff --git a/scripts/entitlement.sh b/scripts/entitlement/build.sh
similarity index 100%
rename from scripts/entitlement.sh
rename to scripts/entitlement/build.sh
diff --git a/scripts/entitlement/install.sh b/scripts/entitlement/install.sh
new file mode 100755
index 00000000000..0c88d48110d
--- /dev/null
+++ b/scripts/entitlement/install.sh
@@ -0,0 +1,11 @@
+#!/bin/sh -e
+#
+# Helper script for the install process to apply entitlements
+
+SRC="$1"
+DST="$2"
+ENTITLEMENT="$3"
+
+cd "$MESON_INSTALL_DESTDIR_PREFIX"
+mv -f "$SRC" "$DST"
+codesign --entitlements "$ENTITLEMENT" --force -s - "$DST"
-- 
2.24.3 (Apple Git-128)


Re: [PATCH] hvf: Sign the code after installation
Posted by Paolo Bonzini 3 years, 7 months ago
On 25/02/21 01:06, Akihiko Odaki wrote:
> Before this change, the code signed during the build was installed
> directly.
> 
> However, the signature gets invalidated because meson modifies the code
> to fix dynamic library install names during the install process.
> 
> It also prevents meson to strip the code because the pre-signed file is
> not marked as an executable (although it is somehow able to perform the
> modification described above).
> 
> With this change, the unsigned code will be installed and modified by
> meson first, and a script signs it later.
> 
> Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>

Thanks very much!  As mentioned in the other message, I would prefer to 
have a single script so here is what I came up with.

#!/bin/sh -e
#
# Helper script for the build process to apply entitlements

copy=:
if [ "$1" = --install ]; then
   shift
   copy=false
   cd "$MESON_INSTALL_DESTDIR_PREFIX"
fi

SRC="$1"
DST="$2"
ENTITLEMENT="$3"

if $copy; then
   trap 'rm "$DST.tmp"' exit
   cp -af "$SRC" "$DST.tmp"
   SRC="$DST.tmp"
fi

codesign --entitlements "$ENTITLEMENT" --force -s - "$SRC"
mv -f "$SRC" "$DST"
trap '' exit


I'll include this in the next pull request, since I was able to test it 
with Cirrus CI.

Thanks,

Paolo


Re: [PATCH] hvf: Sign the code after installation
Posted by Akihiko Odaki 3 years, 7 months ago
2021年2月25日(木) 22:48 Paolo Bonzini <pbonzini@redhat.com>:
>
> On 25/02/21 01:06, Akihiko Odaki wrote:
> > Before this change, the code signed during the build was installed
> > directly.
> >
> > However, the signature gets invalidated because meson modifies the code
> > to fix dynamic library install names during the install process.
> >
> > It also prevents meson to strip the code because the pre-signed file is
> > not marked as an executable (although it is somehow able to perform the
> > modification described above).
> >
> > With this change, the unsigned code will be installed and modified by
> > meson first, and a script signs it later.
> >
> > Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com>
>
> Thanks very much!  As mentioned in the other message, I would prefer to
> have a single script so here is what I came up with.
>
> #!/bin/sh -e
> #
> # Helper script for the build process to apply entitlements
>
> copy=:
> if [ "$1" = --install ]; then
>    shift
>    copy=false
>    cd "$MESON_INSTALL_DESTDIR_PREFIX"
> fi
>
> SRC="$1"
> DST="$2"
> ENTITLEMENT="$3"
>
> if $copy; then
>    trap 'rm "$DST.tmp"' exit
>    cp -af "$SRC" "$DST.tmp"
>    SRC="$DST.tmp"
> fi
>
> codesign --entitlements "$ENTITLEMENT" --force -s - "$SRC"
> mv -f "$SRC" "$DST"
> trap '' exit
>
>
> I'll include this in the next pull request, since I was able to test it
> with Cirrus CI.
>
> Thanks,
>
> Paolo
>

I wonder what happens if codesign fails when modifying "$SRC" during
installation. The half-modified binary is still at "$SRC" and mtime is
newer than the binary in the build directory, so meson given
--only-changed may think it is "not changed" and leave it corrupted.
"mv" should be performed earlier to avoid such a case.

It is kind of theoretical and *very* unlikely to happen anyway, so it
is fine for me to include it. Anything else looks good for me and
should solve the problem nicely.

Thanks,
Akihiko Odaki