Introduce qemu_security_policy_taint() which allows unsafe (read
"not very maintained") code to 'taint' QEMU security policy.
The "security policy" is the @SecurityPolicy QAPI enum, composed of:
- "none" (no policy, current behavior)
- "warn" (display a warning when the policy is tainted, keep going)
- "strict" (once tainted, exit QEMU before starting the VM)
The qemu_security_policy_is_strict() helper is also provided, which
will be proved useful once a VM is started (example we do not want
to kill a running VM if an unsafe device is hot-added).
Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
---
qapi/run-state.json | 16 +++++++++++
include/qemu-common.h | 19 ++++++++++++
softmmu/vl.c | 67 +++++++++++++++++++++++++++++++++++++++++++
qemu-options.hx | 17 +++++++++++
4 files changed, 119 insertions(+)
diff --git a/qapi/run-state.json b/qapi/run-state.json
index 43d66d700fc..b15a107fa01 100644
--- a/qapi/run-state.json
+++ b/qapi/run-state.json
@@ -638,3 +638,19 @@
{ 'struct': 'MemoryFailureFlags',
'data': { 'action-required': 'bool',
'recursive': 'bool'} }
+
+##
+# @SecurityPolicy:
+#
+# An enumeration of the actions taken when the security policy is tainted.
+#
+# @none: do nothing.
+#
+# @warn: display a warning.
+#
+# @strict: prohibit QEMU to start a VM.
+#
+# Since: 6.2
+##
+{ 'enum': 'SecurityPolicy',
+ 'data': [ 'none', 'warn', 'strict' ] }
diff --git a/include/qemu-common.h b/include/qemu-common.h
index 73bcf763ed8..bf0b054bb66 100644
--- a/include/qemu-common.h
+++ b/include/qemu-common.h
@@ -139,4 +139,23 @@ void page_size_init(void);
* returned. */
bool dump_in_progress(void);
+/**
+ * qemu_security_policy_taint:
+ * @tainting whether any security policy is tainted (compromised).
+ * @fmt: taint reason format string
+ * ...: list of arguments to interpolate into @fmt, like printf().
+ *
+ * Allow unsafe code path to taint the global security policy.
+ * See #SecurityPolicy.
+ */
+void qemu_security_policy_taint(bool tainting, const char *fmt, ...)
+ GCC_FMT_ATTR(2, 3);
+
+/**
+ * qemu_security_policy_is_strict:
+ *
+ * Return %true if the global security policy is 'strict', %false otherwise.
+ */
+bool qemu_security_policy_is_strict(void);
+
#endif
diff --git a/softmmu/vl.c b/softmmu/vl.c
index 55ab70eb97f..92c05ac97ee 100644
--- a/softmmu/vl.c
+++ b/softmmu/vl.c
@@ -489,6 +489,20 @@ static QemuOptsList qemu_action_opts = {
},
};
+static QemuOptsList qemu_security_policy_opts = {
+ .name = "security-policy",
+ .implied_opt_name = "policy",
+ .merge_lists = true,
+ .head = QTAILQ_HEAD_INITIALIZER(qemu_security_policy_opts.head),
+ .desc = {
+ {
+ .name = "policy",
+ .type = QEMU_OPT_STRING,
+ },
+ { /* end of list */ }
+ },
+};
+
const char *qemu_get_vm_name(void)
{
return qemu_name;
@@ -600,6 +614,52 @@ static int cleanup_add_fd(void *opaque, QemuOpts *opts, Error **errp)
}
#endif
+static SecurityPolicy security_policy = SECURITY_POLICY_NONE;
+
+bool qemu_security_policy_is_strict(void)
+{
+ return security_policy == SECURITY_POLICY_STRICT;
+}
+
+static int select_security_policy(const char *p)
+{
+ int policy;
+ char *qapi_value;
+
+ qapi_value = g_ascii_strdown(p, -1);
+ policy = qapi_enum_parse(&SecurityPolicy_lookup, qapi_value, -1, NULL);
+ g_free(qapi_value);
+ if (policy < 0) {
+ return -1;
+ }
+ security_policy = policy;
+
+ return 0;
+}
+
+void qemu_security_policy_taint(bool tainting, const char *fmt, ...)
+{
+ va_list ap;
+ g_autofree char *efmt = NULL;
+
+ if (security_policy == SECURITY_POLICY_NONE || !tainting) {
+ return;
+ }
+
+ va_start(ap, fmt);
+ if (security_policy == SECURITY_POLICY_STRICT) {
+ efmt = g_strdup_printf("%s taints QEMU security policy, exiting.", fmt);
+ error_vreport(efmt, ap);
+ exit(EXIT_FAILURE);
+ } else if (security_policy == SECURITY_POLICY_WARN) {
+ efmt = g_strdup_printf("%s taints QEMU security policy.", fmt);
+ warn_vreport(efmt, ap);
+ } else {
+ g_assert_not_reached();
+ }
+ va_end(ap);
+}
+
/***********************************************************/
/* QEMU Block devices */
@@ -2764,6 +2824,7 @@ void qemu_init(int argc, char **argv, char **envp)
qemu_add_opts(&qemu_semihosting_config_opts);
qemu_add_opts(&qemu_fw_cfg_opts);
qemu_add_opts(&qemu_action_opts);
+ qemu_add_opts(&qemu_security_policy_opts);
module_call_init(MODULE_INIT_OPTS);
error_init(argv[0]);
@@ -3230,6 +3291,12 @@ void qemu_init(int argc, char **argv, char **envp)
exit(1);
}
break;
+ case QEMU_OPTION_security_policy:
+ if (select_security_policy(optarg) == -1) {
+ error_report("unknown -security-policy parameter");
+ exit(1);
+ }
+ break;
case QEMU_OPTION_parallel:
add_device_config(DEV_PARALLEL, optarg);
default_parallel = 0;
diff --git a/qemu-options.hx b/qemu-options.hx
index 8f603cc7e65..d9939f7ae1d 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4298,6 +4298,23 @@ SRST
ERST
+DEF("security-policy", HAS_ARG, QEMU_OPTION_security_policy, \
+ "-security-policy none|warn|strict\n" \
+ " action when security policy is tainted [default=none]\n",
+ QEMU_ARCH_ALL)
+SRST
+``-security-policy policy``
+ The policy controls what QEMU will do when an unsecure feature is
+ used, tainting the process security. The default is ``none`` (do
+ nothing). Other possible actions are: ``warn`` (display a warning
+ and keep going) or ``strict`` (exits QEMU before launching a VM).
+
+ Examples:
+
+ ``-security-policy warn``; \ ``-security-policy strict``
+
+ERST
+
DEF("echr", HAS_ARG, QEMU_OPTION_echr, \
"-echr chr set terminal escape character instead of ctrl-a\n",
QEMU_ARCH_ALL)
--
2.31.1
On 09/09/21 01:20, Philippe Mathieu-Daudé wrote:
> +static QemuOptsList qemu_security_policy_opts = {
> + .name = "security-policy",
> + .implied_opt_name = "policy",
> + .merge_lists = true,
> + .head = QTAILQ_HEAD_INITIALIZER(qemu_security_policy_opts.head),
> + .desc = {
> + {
> + .name = "policy",
> + .type = QEMU_OPT_STRING,
> + },
> + { /* end of list */ }
> + },
> +};
No new command line options please. You could rename -compat-policy to
just -policy, and make this a "security" suboption.
Paolo
On Thu, Sep 09, 2021 at 01:20:15AM +0200, Philippe Mathieu-Daudé wrote:
> Introduce qemu_security_policy_taint() which allows unsafe (read
> "not very maintained") code to 'taint' QEMU security policy.
>
> The "security policy" is the @SecurityPolicy QAPI enum, composed of:
> - "none" (no policy, current behavior)
> - "warn" (display a warning when the policy is tainted, keep going)
> - "strict" (once tainted, exit QEMU before starting the VM)
>
> The qemu_security_policy_is_strict() helper is also provided, which
> will be proved useful once a VM is started (example we do not want
s/be proved/prove/
> to kill a running VM if an unsafe device is hot-added).
>
> Signed-off-by: Philippe Mathieu-Daudé <philmd@redhat.com>
> ---
> qapi/run-state.json | 16 +++++++++++
> include/qemu-common.h | 19 ++++++++++++
> softmmu/vl.c | 67 +++++++++++++++++++++++++++++++++++++++++++
> qemu-options.hx | 17 +++++++++++
> 4 files changed, 119 insertions(+)
>
> diff --git a/qapi/run-state.json b/qapi/run-state.json
> index 43d66d700fc..b15a107fa01 100644
> --- a/qapi/run-state.json
> +++ b/qapi/run-state.json
> @@ -638,3 +638,19 @@
> { 'struct': 'MemoryFailureFlags',
> 'data': { 'action-required': 'bool',
> 'recursive': 'bool'} }
> +
> +##
> +# @SecurityPolicy:
> +#
> +# An enumeration of the actions taken when the security policy is tainted.
> +#
> +# @none: do nothing.
> +#
> +# @warn: display a warning.
> +#
> +# @strict: prohibit QEMU to start a VM.
s/to start/from starting/
> +#
> +# Since: 6.2
> +##
> +{ 'enum': 'SecurityPolicy',
> + 'data': [ 'none', 'warn', 'strict' ] }
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3266
Virtualization: qemu.org | libvirt.org
© 2016 - 2026 Red Hat, Inc.