|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH 4/4] xen/version: Introduce non-truncating XENVER_* subops
Recently in XenServer, we have encountered problems caused by both
XENVER_extraversion and XENVER_commandline having fixed bounds.
More than just the invariant size, the APIs/ABIs also broken by typedef-ing an
array, and using an unqualified 'char' which has implementation-specific
signed-ness.
Provide brand new ops, which are capable of expressing variable length
strings, and mark the older ops as broken.
This fixes all issues around XENVER_extraversion being longer than 15 chars.
More work is required to remove other assumptions about XENVER_commandline
being 1023 chars long.
Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
---
CC: George Dunlap <George.Dunlap@xxxxxxxxxxxxx>
CC: Jan Beulich <JBeulich@xxxxxxxx>
CC: Stefano Stabellini <sstabellini@xxxxxxxxxx>
CC: Wei Liu <wl@xxxxxxx>
CC: Julien Grall <julien@xxxxxxx>
Tested by forcing XENVER_extraversion to be 20 chars long, and confirming that
an untruncated version can be obtained.
API/ABI wise, XENVER_build_id could be merged into xenver_varstring_op(), but
the internal infrastructure is awkward.
---
xen/common/kernel.c | 69 ++++++++++++++++++++++++++++++++++++++++++++
xen/include/public/version.h | 56 +++++++++++++++++++++++++++++++++--
xen/include/xlat.lst | 1 +
xen/xsm/flask/hooks.c | 4 +++
4 files changed, 128 insertions(+), 2 deletions(-)
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index 70e7dff87488..56bd6c6f5d9c 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -24,6 +24,7 @@
CHECK_compile_info;
CHECK_feature_info;
CHECK_build_id;
+CHECK_var_string;
#endif
enum system_state system_state = SYS_STATE_early_boot;
@@ -469,6 +470,66 @@ static int __init cf_check param_init(void)
__initcall(param_init);
#endif
+static long xenver_varstring_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
+{
+ const char *str = NULL;
+ size_t sz = 0;
+ union {
+ xen_capabilities_info_t info;
+ } u;
+ struct xen_var_string user_str;
+
+ switch ( cmd )
+ {
+ case XENVER_extraversion2:
+ str = xen_extra_version();
+ break;
+
+ case XENVER_changeset2:
+ str = xen_changeset();
+ break;
+
+ case XENVER_commandline2:
+ str = saved_cmdline;
+ break;
+
+ case XENVER_capabilities2:
+ memset(u.info, 0, sizeof(u.info));
+ arch_get_xen_caps(&u.info);
+ str = u.info;
+ break;
+
+ default:
+ ASSERT_UNREACHABLE();
+ break;
+ }
+
+ if ( !str ||
+ !(sz = strlen(str)) )
+ return -ENODATA; /* failsafe */
+
+ if ( sz > INT32_MAX )
+ return -E2BIG; /* Compat guests. 2G ought to be plenty. */
+
+ if ( guest_handle_is_null(arg) ) /* Length request */
+ return sz;
+
+ if ( copy_from_guest(&user_str, arg, 1) )
+ return -EFAULT;
+
+ if ( user_str.len == 0 )
+ return -EINVAL;
+
+ if ( sz > user_str.len )
+ return -ENOBUFS;
+
+ if ( copy_to_guest_offset(arg, offsetof(struct xen_var_string, buf),
+ str, sz) )
+ return -EFAULT;
+
+ return sz;
+}
+
long do_xen_version(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
{
bool_t deny = !!xsm_xen_version(XSM_OTHER, cmd);
@@ -670,6 +731,14 @@ long do_xen_version(int cmd, XEN_GUEST_HANDLE_PARAM(void)
arg)
return sz;
}
+
+ case XENVER_extraversion2:
+ case XENVER_capabilities2:
+ case XENVER_changeset2:
+ case XENVER_commandline2:
+ if ( deny )
+ return -EPERM;
+ return xenver_varstring_op(cmd, arg);
}
return -ENOSYS;
diff --git a/xen/include/public/version.h b/xen/include/public/version.h
index c8325219f648..cf2d2ef38b54 100644
--- a/xen/include/public/version.h
+++ b/xen/include/public/version.h
@@ -19,12 +19,20 @@
/* arg == NULL; returns major:minor (16:16). */
#define XENVER_version 0
-/* arg == xen_extraversion_t. */
+/*
+ * arg == xen_extraversion_t.
+ *
+ * This API/ABI is broken. Use XENVER_extraversion2 instead.
+ */
#define XENVER_extraversion 1
typedef char xen_extraversion_t[16];
#define XEN_EXTRAVERSION_LEN (sizeof(xen_extraversion_t))
-/* arg == xen_compile_info_t. */
+/*
+ * arg == xen_compile_info_t.
+ *
+ * This API/ABI is broken and truncates data.
+ */
#define XENVER_compile_info 2
struct xen_compile_info {
char compiler[64];
@@ -34,10 +42,20 @@ struct xen_compile_info {
};
typedef struct xen_compile_info xen_compile_info_t;
+/*
+ * arg == xen_capabilities_info_t.
+ *
+ * This API/ABI is broken. Use XENVER_capabilities2 instead.
+ */
#define XENVER_capabilities 3
typedef char xen_capabilities_info_t[1024];
#define XEN_CAPABILITIES_INFO_LEN (sizeof(xen_capabilities_info_t))
+/*
+ * arg == xen_changeset_info_t.
+ *
+ * This API/ABI is broken. Use XENVER_changeset2 instead.
+ */
#define XENVER_changeset 4
typedef char xen_changeset_info_t[64];
#define XEN_CHANGESET_INFO_LEN (sizeof(xen_changeset_info_t))
@@ -88,6 +106,11 @@ typedef struct xen_feature_info xen_feature_info_t;
*/
#define XENVER_guest_handle 8
+/*
+ * arg == xen_commandline_t.
+ *
+ * This API/ABI is broken. Use XENVER_commandline2 instead.
+ */
#define XENVER_commandline 9
typedef char xen_commandline_t[1024];
@@ -103,6 +126,35 @@ struct xen_build_id {
};
typedef struct xen_build_id xen_build_id_t;
+/*
+ * Container for an arbitrary variable length string.
+ */
+struct xen_var_string {
+ uint32_t len; /* IN: size of buf[] in bytes. */
+ unsigned char buf[XEN_FLEX_ARRAY_DIM]; /* OUT: requested data. */
+};
+typedef struct xen_var_string xen_var_string_t;
+
+/*
+ * arg == xenver_string_t
+ *
+ * Equivalent to the original ops, but with a non-truncating API/ABI.
+ *
+ * Passing arg == NULL is a request for size. The returned size does not
+ * include a NUL terminator, and has a practical upper limit of INT32_MAX for
+ * 32bit guests. This is expected to be plenty for the purpose.
+ *
+ * Otherwise, the input xenver_string_t provides the size of the following
+ * buffer. Xen will fill the buffer, and return the number of bytes written
+ * (e.g. if the input buffer was longer than necessary).
+ *
+ * These hypercalls can fail, in which case they'll return -XEN_Exx.
+ */
+#define XENVER_extraversion2 11
+#define XENVER_capabilities2 12
+#define XENVER_changeset2 13
+#define XENVER_commandline2 14
+
#endif /* __XEN_PUBLIC_VERSION_H__ */
/*
diff --git a/xen/include/xlat.lst b/xen/include/xlat.lst
index f2bae220a6df..19cef4424add 100644
--- a/xen/include/xlat.lst
+++ b/xen/include/xlat.lst
@@ -172,6 +172,7 @@
? compile_info version.h
? feature_info version.h
? build_id version.h
+? var_string version.h
? xenoprof_init xenoprof.h
? xenoprof_passive xenoprof.h
? flask_access xsm/flask_op.h
diff --git a/xen/xsm/flask/hooks.c b/xen/xsm/flask/hooks.c
index 78225f68c15c..a671dcd0322e 100644
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1777,15 +1777,18 @@ static int cf_check flask_xen_version(uint32_t op)
/* These sub-ops ignore the permission checks and return data. */
return 0;
case XENVER_extraversion:
+ case XENVER_extraversion2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_EXTRAVERSION, NULL);
case XENVER_compile_info:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_COMPILE_INFO, NULL);
case XENVER_capabilities:
+ case XENVER_capabilities2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_CAPABILITIES, NULL);
case XENVER_changeset:
+ case XENVER_changeset2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_CHANGESET, NULL);
case XENVER_pagesize:
@@ -1795,6 +1798,7 @@ static int cf_check flask_xen_version(uint32_t op)
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_GUEST_HANDLE, NULL);
case XENVER_commandline:
+ case XENVER_commandline2:
return avc_has_perm(dsid, SECINITSID_XEN, SECCLASS_VERSION,
VERSION__XEN_COMMANDLINE, NULL);
case XENVER_build_id:
--
2.11.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |