diff --git a/qemu.git-56f101ecce0eafd09e2daf1c4eeb1377d6959261.patch b/qemu.git-56f101ecce0eafd09e2daf1c4eeb1377d6959261.patch new file mode 100644 index 0000000..c628307 --- /dev/null +++ b/qemu.git-56f101ecce0eafd09e2daf1c4eeb1377d6959261.patch @@ -0,0 +1,126 @@ +From 56f101ecce0eafd09e2daf1c4eeb1377d6959261 Mon Sep 17 00:00:00 2001 +From: Greg Kurz +Date: Tue, 30 Aug 2016 17:02:27 +0200 +Subject: [PATCH] 9pfs: handle walk of ".." in the root directory + +The 9P spec at http://man.cat-v.org/plan_9/5/intro says: + +All directories must support walks to the directory .. (dot-dot) meaning +parent directory, although by convention directories contain no explicit +entry for .. or . (dot). The parent of the root directory of a server's +tree is itself. + +This means that a client cannot walk further than the root directory +exported by the server. In other words, if the client wants to walk +"/.." or "/foo/../..", the server should answer like the request was +to walk "/". + +This patch just does that: +- we cache the QID of the root directory at attach time +- during the walk we compare the QID of each path component with the root + QID to detect if we're in a "/.." situation +- if so, we skip the current component and go to the next one + +Signed-off-by: Greg Kurz +Reviewed-by: Eric Blake +Signed-off-by: Peter Maydell +--- + hw/9pfs/virtio-9p.c | 40 +++++++++++++++++++++++++++++++--------- + hw/9pfs/virtio-9p.h | 1 + + 2 files changed, 32 insertions(+), 9 deletions(-) + +diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c +index 51c6f98..dfe293d 100644 +--- a/hw/9pfs/virtio-9p.c ++++ b/hw/9pfs/virtio-9p.c +@@ -1010,6 +1010,7 @@ static void v9fs_attach(void *opaque) + goto out; + } + err += offset; ++ memcpy(&s->root_qid, &qid, sizeof(qid)); + trace_v9fs_attach_return(pdu->tag, pdu->id, + qid.type, qid.version, qid.path); + /* +@@ -1261,6 +1262,14 @@ static bool name_is_illegal(const char *name) + return !*name || strchr(name, '/') != NULL; + } + ++static bool not_same_qid(const V9fsQID *qid1, const V9fsQID *qid2) ++{ ++ return ++ qid1->type != qid2->type || ++ qid1->version != qid2->version || ++ qid1->path != qid2->path; ++} ++ + static void v9fs_walk(void *opaque) + { + int name_idx; +@@ -1276,6 +1285,7 @@ static void v9fs_walk(void *opaque) + V9fsFidState *newfidp = NULL; + V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; ++ V9fsQID qid; + + err = pdu_unmarshal(pdu, offset, "ddw", &fid, &newfid, &nwnames); + if (err < 0) { +@@ -1309,6 +1319,12 @@ static void v9fs_walk(void *opaque) + err = -ENOENT; + goto out_nofid; + } ++ ++ err = fid_to_qid(pdu, fidp, &qid); ++ if (err < 0) { ++ goto out; ++ } ++ + v9fs_path_init(&dpath); + v9fs_path_init(&path); + /* +@@ -1318,16 +1334,22 @@ static void v9fs_walk(void *opaque) + v9fs_path_copy(&dpath, &fidp->path); + v9fs_path_copy(&path, &fidp->path); + for (name_idx = 0; name_idx < nwnames; name_idx++) { +- err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, &path); +- if (err < 0) { +- goto out; +- } +- err = v9fs_co_lstat(pdu, &path, &stbuf); +- if (err < 0) { +- goto out; ++ if (not_same_qid(&pdu->s->root_qid, &qid) || ++ strcmp("..", wnames[name_idx].data)) { ++ err = v9fs_co_name_to_path(pdu, &dpath, wnames[name_idx].data, ++ &path); ++ if (err < 0) { ++ goto out; ++ } ++ ++ err = v9fs_co_lstat(pdu, &path, &stbuf); ++ if (err < 0) { ++ goto out; ++ } ++ stat_to_qid(&stbuf, &qid); ++ v9fs_path_copy(&dpath, &path); + } +- stat_to_qid(&stbuf, &qids[name_idx]); +- v9fs_path_copy(&dpath, &path); ++ memcpy(&qids[name_idx], &qid, sizeof(qid)); + } + if (fid == newfid) { + BUG_ON(fidp->fid_type != P9_FID_NONE); +diff --git a/hw/9pfs/virtio-9p.h b/hw/9pfs/virtio-9p.h +index b4f757a..a386033 100644 +--- a/hw/9pfs/virtio-9p.h ++++ b/hw/9pfs/virtio-9p.h +@@ -236,6 +236,7 @@ typedef struct V9fsState + int32_t root_fid; + Error *migration_blocker; + V9fsConf fsconf; ++ V9fsQID root_qid; + } V9fsState; + + typedef struct V9fsStatState { +-- +1.7.0.4 + diff --git a/qemu.git-805b5d98c649d26fc44d2d7755a97f18e62b438a.patch b/qemu.git-805b5d98c649d26fc44d2d7755a97f18e62b438a.patch new file mode 100644 index 0000000..694e819 --- /dev/null +++ b/qemu.git-805b5d98c649d26fc44d2d7755a97f18e62b438a.patch @@ -0,0 +1,159 @@ +From 805b5d98c649d26fc44d2d7755a97f18e62b438a Mon Sep 17 00:00:00 2001 +From: Greg Kurz +Date: Tue, 30 Aug 2016 19:13:11 +0200 +Subject: [PATCH] 9pfs: forbid . and .. in file names + +According to the 9P spec http://man.cat-v.org/plan_9/5/open about the +create request: + +The names . and .. are special; it is illegal to create files with these +names. + +This patch causes the create and lcreate requests to fail with EINVAL if +the file name is either "." or "..". + +Even if it isn't explicitly written in the spec, this patch extends the +checking to all requests that may cause a directory entry to be created: + + - mknod + - rename + - renameat + - mkdir + - link + - symlink + +The unlinkat request also gets patched for consistency (even if +rmdir("foo/..") is expected to fail according to POSIX.1-2001). + +The various error values come from the linux manual pages. + +Suggested-by: Peter Maydell +Signed-off-by: Greg Kurz +Reviewed-by: Eric Blake +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Peter Maydell +--- + hw/9pfs/virtio-9p.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 51 insertions(+), 0 deletions(-) + +diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c +index 385269e..51c6f98 100644 +--- a/hw/9pfs/virtio-9p.c ++++ b/hw/9pfs/virtio-9p.c +@@ -1497,6 +1497,11 @@ static void v9fs_lcreate(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, dfid); + if (fidp == NULL) { + err = -ENOENT; +@@ -2096,6 +2101,11 @@ static void v9fs_create(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; +@@ -2266,6 +2276,11 @@ static void v9fs_symlink(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; +@@ -2345,6 +2360,11 @@ static void v9fs_link(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -ENOENT; +@@ -2433,6 +2453,16 @@ static void v9fs_unlinkat(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data)) { ++ err = -EINVAL; ++ goto out_nofid; ++ } ++ ++ if (!strcmp("..", name.data)) { ++ err = -ENOTEMPTY; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; +@@ -2545,6 +2575,11 @@ static void v9fs_rename(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EISDIR; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +@@ -2662,6 +2697,12 @@ static void v9fs_renameat(void *opaque) + goto out_err; + } + ++ if (!strcmp(".", old_name.data) || !strcmp("..", old_name.data) || ++ !strcmp(".", new_name.data) || !strcmp("..", new_name.data)) { ++ err = -EISDIR; ++ goto out_err; ++ } ++ + v9fs_path_write_lock(s); + err = v9fs_complete_renameat(pdu, olddirfid, + &old_name, newdirfid, &new_name); +@@ -2877,6 +2918,11 @@ static void v9fs_mknod(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +@@ -3033,6 +3079,11 @@ static void v9fs_mkdir(void *opaque) + goto out_nofid; + } + ++ if (!strcmp(".", name.data) || !strcmp("..", name.data)) { ++ err = -EEXIST; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +-- +1.7.0.4 + diff --git a/qemu.git-a0d1cbdacff5df4ded16b753b38fdd9da6092968.patch b/qemu.git-a0d1cbdacff5df4ded16b753b38fdd9da6092968.patch new file mode 100644 index 0000000..96a10b3 --- /dev/null +++ b/qemu.git-a0d1cbdacff5df4ded16b753b38fdd9da6092968.patch @@ -0,0 +1,35 @@ +From a0d1cbdacff5df4ded16b753b38fdd9da6092968 Mon Sep 17 00:00:00 2001 +From: chaojianhu +Date: Tue, 9 Aug 2016 11:52:54 +0800 +Subject: [PATCH] hw/net: Fix a heap overflow in xlnx.xps-ethernetlite + +The .receive callback of xlnx.xps-ethernetlite doesn't check the length +of data before calling memcpy. As a result, the NetClientState object in +heap will be overflowed. All versions of qemu with xlnx.xps-ethernetlite +will be affected. + +Reported-by: chaojianhu +Signed-off-by: chaojianhu +Signed-off-by: Jason Wang +--- + hw/net/xilinx_ethlite.c | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c +index 54db2b8..35de353 100644 +--- a/hw/net/xilinx_ethlite.c ++++ b/hw/net/xilinx_ethlite.c +@@ -197,6 +197,10 @@ static ssize_t eth_rx(NetClientState *nc, const uint8_t *buf, size_t size) + } + + D(qemu_log("%s %zd rxbase=%x\n", __func__, size, rxbase)); ++ if (size > (R_MAX - R_RX_BUF0 - rxbase) * 4) { ++ D(qemu_log("ethlite packet is too big, size=%x\n", size)); ++ return -1; ++ } + memcpy(&s->regs[rxbase + R_RX_BUF0], buf, size); + + s->regs[rxbase + R_RX_CTRL0] |= CTRL_S; +-- +1.7.0.4 + diff --git a/qemu.git-fff39a7ad09da07ef490de05c92c91f22f8002f2.patch b/qemu.git-fff39a7ad09da07ef490de05c92c91f22f8002f2.patch new file mode 100644 index 0000000..d71afa7 --- /dev/null +++ b/qemu.git-fff39a7ad09da07ef490de05c92c91f22f8002f2.patch @@ -0,0 +1,178 @@ +From fff39a7ad09da07ef490de05c92c91f22f8002f2 Mon Sep 17 00:00:00 2001 +From: Greg Kurz +Date: Tue, 30 Aug 2016 19:11:05 +0200 +Subject: [PATCH] 9pfs: forbid illegal path names + +Empty path components don't make sense for most commands and may cause +undefined behavior, depending on the backend. + +Also, the walk request described in the 9P spec [1] clearly shows that +the client is supposed to send individual path components: the official +linux client never sends portions of path containing the / character for +example. + +Moreover, the 9P spec [2] also states that a system can decide to restrict +the set of supported characters used in path components, with an explicit +mention "to remove slashes from name components". + +This patch introduces a new name_is_illegal() helper that checks the +names sent by the client are not empty and don't contain unwanted chars. +Since 9pfs is only supported on linux hosts, only the / character is +checked at the moment. When support for other hosts (AKA. win32) is added, +other chars may need to be blacklisted as well. + +If a client sends an illegal path component, the request will fail and +ENOENT is returned to the client. + +[1] http://man.cat-v.org/plan_9/5/walk +[2] http://man.cat-v.org/plan_9/5/intro + +Suggested-by: Peter Maydell +Signed-off-by: Greg Kurz +Reviewed-by: Eric Blake +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Peter Maydell +--- + hw/9pfs/virtio-9p.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 56 insertions(+), 0 deletions(-) + +diff --git a/hw/9pfs/virtio-9p.c b/hw/9pfs/virtio-9p.c +index b6b02b4..385269e 100644 +--- a/hw/9pfs/virtio-9p.c ++++ b/hw/9pfs/virtio-9p.c +@@ -1256,6 +1256,11 @@ static int v9fs_walk_marshal(V9fsPDU *pdu, uint16_t nwnames, V9fsQID *qids) + return offset; + } + ++static bool name_is_illegal(const char *name) ++{ ++ return !*name || strchr(name, '/') != NULL; ++} ++ + static void v9fs_walk(void *opaque) + { + int name_idx; +@@ -1289,6 +1294,10 @@ static void v9fs_walk(void *opaque) + if (err < 0) { + goto out_nofid; + } ++ if (name_is_illegal(wnames[i].data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } + offset += err; + } + } else if (nwnames > P9_MAXWELEM) { +@@ -1483,6 +1492,11 @@ static void v9fs_lcreate(void *opaque) + } + trace_v9fs_lcreate(pdu->tag, pdu->id, dfid, flags, mode, gid); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, dfid); + if (fidp == NULL) { + err = -ENOENT; +@@ -2077,6 +2091,11 @@ static void v9fs_create(void *opaque) + } + trace_v9fs_create(pdu->tag, pdu->id, fid, name.data, perm, mode); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -EINVAL; +@@ -2242,6 +2261,11 @@ static void v9fs_symlink(void *opaque) + } + trace_v9fs_symlink(pdu->tag, pdu->id, dfid, name.data, symname.data, gid); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; +@@ -2316,6 +2340,11 @@ static void v9fs_link(void *opaque) + } + trace_v9fs_link(pdu->tag, pdu->id, dfid, oldfid, name.data); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -ENOENT; +@@ -2398,6 +2427,12 @@ static void v9fs_unlinkat(void *opaque) + if (err < 0) { + goto out_nofid; + } ++ ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + dfidp = get_fid(pdu, dfid); + if (dfidp == NULL) { + err = -EINVAL; +@@ -2504,6 +2539,12 @@ static void v9fs_rename(void *opaque) + if (err < 0) { + goto out_nofid; + } ++ ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +@@ -2616,6 +2657,11 @@ static void v9fs_renameat(void *opaque) + goto out_err; + } + ++ if (name_is_illegal(old_name.data) || name_is_illegal(new_name.data)) { ++ err = -ENOENT; ++ goto out_err; ++ } ++ + v9fs_path_write_lock(s); + err = v9fs_complete_renameat(pdu, olddirfid, + &old_name, newdirfid, &new_name); +@@ -2826,6 +2872,11 @@ static void v9fs_mknod(void *opaque) + } + trace_v9fs_mknod(pdu->tag, pdu->id, fid, mode, major, minor); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +@@ -2977,6 +3028,11 @@ static void v9fs_mkdir(void *opaque) + } + trace_v9fs_mkdir(pdu->tag, pdu->id, fid, name.data, mode, gid); + ++ if (name_is_illegal(name.data)) { ++ err = -ENOENT; ++ goto out_nofid; ++ } ++ + fidp = get_fid(pdu, fid); + if (fidp == NULL) { + err = -ENOENT; +-- +1.7.0.4 + diff --git a/xen.hypervisor.config b/xen.hypervisor.config new file mode 100644 index 0000000..847e88a --- /dev/null +++ b/xen.hypervisor.config @@ -0,0 +1,53 @@ +# +# Automatically generated file; DO NOT EDIT. +# Xen/x86 4.7.0 Configuration +# +CONFIG_X86_64=y +CONFIG_X86=y +CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig" + +# +# Architecture Features +# +CONFIG_NR_CPUS=256 +CONFIG_SHADOW_PAGING=y +# CONFIG_BIGMEM is not set + +# +# Common Features +# +CONFIG_COMPAT=y +CONFIG_CORE_PARKING=y +CONFIG_HAS_MEM_ACCESS=y +CONFIG_HAS_MEM_PAGING=y +CONFIG_HAS_MEM_SHARING=y +CONFIG_HAS_PDX=y +CONFIG_HAS_KEXEC=y +CONFIG_HAS_GDBSX=y +CONFIG_HAS_IOPORTS=y +CONFIG_KEXEC=y +CONFIG_TMEM=y +CONFIG_XENOPROF=y +# CONFIG_XSM is not set +CONFIG_SCHED_CREDIT=y +CONFIG_SCHED_CREDIT2=y +CONFIG_SCHED_RTDS=y +CONFIG_SCHED_ARINC653=y +CONFIG_SCHED_DEFAULT="credit" +CONFIG_LIVEPATCH=y +CONFIG_FAST_SYMBOL_LOOKUP=y + +# +# Device Drivers +# +CONFIG_ACPI=y +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y +CONFIG_NUMA=y +CONFIG_HAS_NS16550=y +CONFIG_HAS_EHCI=y +CONFIG_HAS_CPUFREQ=y +CONFIG_HAS_PASSTHROUGH=y +CONFIG_HAS_PCI=y +CONFIG_VIDEO=y +CONFIG_VGA=y +CONFIG_DEFCONFIG_LIST="$ARCH_DEFCONFIG" diff --git a/xen.spec b/xen.spec index 83f122a..59bf3a1 100644 --- a/xen.spec +++ b/xen.spec @@ -41,7 +41,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.7.0 -Release: 5%{?dist} +Release: 6%{?dist} Group: Development/Libraries License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ @@ -56,6 +56,8 @@ Source14: grub-0.97.tar.gz Source15: polarssl-1.1.4-gpl.tgz # systemd file for xen driver domain Source20: xendriverdomain.service +# .config file for xen hypervisor +Source21: xen.hypervisor.config Patch1: xen-net-disable-iptables-on-bridge.patch Patch2: xen.use.fedora.ipxe.patch @@ -126,6 +128,11 @@ Patch66: xsa185.patch Patch67: xsa186-0001-x86-emulate-Correct-boundary-interactions-of-emulate.patch Patch68: xsa187-4.7-0001-x86-shadow-Avoid-overflowing-sh_ctxt-seg.patch Patch69: xsa187-4.7-0002-x86-segment-Bounds-check-accesses-to-emulation-ctx.patch +Patch70: qemu.git-fff39a7ad09da07ef490de05c92c91f22f8002f2.patch +Patch71: qemu.git-805b5d98c649d26fc44d2d7755a97f18e62b438a.patch +Patch72: qemu.git-56f101ecce0eafd09e2daf1c4eeb1377d6959261.patch +Patch73: qemu.git-a0d1cbdacff5df4ded16b753b38fdd9da6092968.patch +Patch74: xsa190.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root @@ -358,6 +365,7 @@ manage Xen virtual machines. %patch67 -p1 %patch68 -p1 %patch69 -p1 +%patch74 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -370,10 +378,16 @@ pushd tools/qemu-xen %patch61 -p1 %patch62 -p1 %patch63 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 popd # stubdom sources cp -v %{SOURCE10} %{SOURCE11} %{SOURCE12} %{SOURCE13} %{SOURCE14} %{SOURCE15} stubdom +# copy xen hypervisor .config file to change settings +cp -v %{SOURCE21} xen/.config %build @@ -844,6 +858,15 @@ rm -rf %{buildroot} %endif %changelog +* Tue Oct 04 2016 Michael Young - 4.7.0-6 +- enable xen livepatch in hypervisor via .config file +- qemu-kvm: Directory traversal flaw in 9p virtio backend [CVE-2016-7116] + (#1371400) +- qemu: hw: net: Heap overflow in xlnx.xps-ethernetlite [CVE-2016-7161] + (#1379299) +- CR0.TS and CR0.EM not always honored for x86 HVM guest [XSA-190, + CVE-2016-7777] (#1381576) + * Thu Sep 08 2016 Michael Young - 4.7.0-5 - pandoc (documentation) has dependency issues again on F25 diff --git a/xsa190.patch b/xsa190.patch new file mode 100644 index 0000000..3c242e6 --- /dev/null +++ b/xsa190.patch @@ -0,0 +1,173 @@ +x86emul: honor guest CR0.TS and CR0.EM + +We must not emulate any instructions accessing respective registers +when either of these flags is set in the guest view of the register, or +else we may do so on data not belonging to the guest's current task. + +Being architecturally required behavior, the logic gets placed in the +instruction emulator instead of hvmemul_get_fpu(). It should be noted, +though, that hvmemul_get_fpu() being the only current handler for the +get_fpu() callback, we don't have an active problem with CR4: Both +CR4.OSFXSR and CR4.OSXSAVE get handled as necessary by that function. + +This is XSA-190. + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +--- +v4: Only raise #NM on FWAIT when CR0.TS and CR0.MP are set. +v3: Correct which exception to raise upon set CR0.EM. +v2: Require the read_cr hook to be set, which then requires a change to + the test code too. +--- +The change to xen/arch/x86/hvm/emulate.c isn't strictly needed for +fixing the security issue, but the patch would be rather incomplete +without. + +--- a/tools/tests/x86_emulator/test_x86_emulator.c ++++ b/tools/tests/x86_emulator/test_x86_emulator.c +@@ -158,6 +158,22 @@ static inline uint64_t xgetbv(uint32_t x + (ebx & (1U << 5)) != 0; \ + }) + ++static int read_cr( ++ unsigned int reg, ++ unsigned long *val, ++ struct x86_emulate_ctxt *ctxt) ++{ ++ /* Fake just enough state for the emulator's _get_fpu() to be happy. */ ++ switch ( reg ) ++ { ++ case 0: ++ *val = 0x00000001; /* PE */ ++ return X86EMUL_OKAY; ++ } ++ ++ return X86EMUL_UNHANDLEABLE; ++} ++ + int get_fpu( + void (*exception_callback)(void *, struct cpu_user_regs *), + void *exception_callback_arg, +@@ -189,6 +205,7 @@ static struct x86_emulate_ops emulops = + .write = write, + .cmpxchg = cmpxchg, + .cpuid = cpuid, ++ .read_cr = read_cr, + .get_fpu = get_fpu, + }; + +--- a/xen/arch/x86/hvm/emulate.c ++++ b/xen/arch/x86/hvm/emulate.c +@@ -1628,14 +1628,14 @@ static int hvmemul_get_fpu( + switch ( type ) + { + case X86EMUL_FPU_fpu: ++ case X86EMUL_FPU_wait: + break; + case X86EMUL_FPU_mmx: + if ( !cpu_has_mmx ) + return X86EMUL_UNHANDLEABLE; + break; + case X86EMUL_FPU_xmm: +- if ( (curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_EM) || +- !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) ) ++ if ( !(curr->arch.hvm_vcpu.guest_cr[4] & X86_CR4_OSFXSR) ) + return X86EMUL_UNHANDLEABLE; + break; + case X86EMUL_FPU_ymm: +--- a/xen/arch/x86/x86_emulate/x86_emulate.c ++++ b/xen/arch/x86/x86_emulate/x86_emulate.c +@@ -420,6 +420,9 @@ typedef union { + + /* Control register flags. */ + #define CR0_PE (1<<0) ++#define CR0_MP (1<<1) ++#define CR0_EM (1<<2) ++#define CR0_TS (1<<3) + #define CR4_TSD (1<<2) + + /* EFLAGS bit definitions. */ +@@ -447,6 +450,7 @@ typedef union { + #define EXC_OF 4 + #define EXC_BR 5 + #define EXC_UD 6 ++#define EXC_NM 7 + #define EXC_TS 10 + #define EXC_NP 11 + #define EXC_SS 12 +@@ -746,10 +750,45 @@ static void fpu_handle_exception(void *_ + regs->eip += fic->insn_bytes; + } + ++static int _get_fpu( ++ enum x86_emulate_fpu_type type, ++ struct fpu_insn_ctxt *fic, ++ struct x86_emulate_ctxt *ctxt, ++ const struct x86_emulate_ops *ops) ++{ ++ int rc; ++ ++ fic->exn_raised = 0; ++ ++ fail_if(!ops->get_fpu); ++ rc = ops->get_fpu(fpu_handle_exception, fic, type, ctxt); ++ ++ if ( rc == X86EMUL_OKAY ) ++ { ++ unsigned long cr0; ++ ++ fail_if(!ops->read_cr); ++ rc = ops->read_cr(0, &cr0, ctxt); ++ if ( rc != X86EMUL_OKAY ) ++ return rc; ++ if ( cr0 & CR0_EM ) ++ { ++ generate_exception_if(type == X86EMUL_FPU_fpu, EXC_NM, -1); ++ generate_exception_if(type == X86EMUL_FPU_mmx, EXC_UD, -1); ++ generate_exception_if(type == X86EMUL_FPU_xmm, EXC_UD, -1); ++ } ++ generate_exception_if((cr0 & CR0_TS) && ++ (type != X86EMUL_FPU_wait || (cr0 & CR0_MP)), ++ EXC_NM, -1); ++ } ++ ++ done: ++ return rc; ++} ++ + #define get_fpu(_type, _fic) \ +-do{ (_fic)->exn_raised = 0; \ +- fail_if(ops->get_fpu == NULL); \ +- rc = ops->get_fpu(fpu_handle_exception, _fic, _type, ctxt); \ ++do { \ ++ rc = _get_fpu(_type, _fic, ctxt, ops); \ + if ( rc ) goto done; \ + } while (0) + #define _put_fpu() \ +@@ -2879,8 +2918,14 @@ x86_emulate( + } + + case 0x9b: /* wait/fwait */ +- emulate_fpu_insn("fwait"); ++ { ++ struct fpu_insn_ctxt fic = { .insn_bytes = 1 }; ++ ++ get_fpu(X86EMUL_FPU_wait, &fic); ++ asm volatile ( "fwait" ::: "memory" ); ++ put_fpu(&fic); + break; ++ } + + case 0x9c: /* pushf */ + src.val = _regs.eflags; +--- a/xen/arch/x86/x86_emulate/x86_emulate.h ++++ b/xen/arch/x86/x86_emulate/x86_emulate.h +@@ -115,6 +115,7 @@ struct __packed segment_register { + /* FPU sub-types which may be requested via ->get_fpu(). */ + enum x86_emulate_fpu_type { + X86EMUL_FPU_fpu, /* Standard FPU coprocessor instruction set */ ++ X86EMUL_FPU_wait, /* WAIT/FWAIT instruction */ + X86EMUL_FPU_mmx, /* MMX instruction set (%mm0-%mm7) */ + X86EMUL_FPU_xmm, /* SSE instruction set (%xmm0-%xmm7/15) */ + X86EMUL_FPU_ymm /* AVX/XOP instruction set (%ymm0-%ymm7/15) */