Subject: [alpine-devel] [PATCH] xen: add fixes for XSA-47, XSA-48, XSA-44 and XSA-46

From: Roger Pau Monne <[email-masked]>
Date: Thu, 18 Apr 2013 16:26:26 +0200

---
Please run abuild checksum before committing.
---
 main/xen/APKBUILD                 |    4 +
 main/xen/xsa44-4.2.patch          |   77 ++++++++++
 main/xen/xsa46-4.2.patch          |  293 +++++++++++++++++++++++++++++++++++++
 main/xen/xsa47-4.2-unstable.patch |   31 ++++
 main/xen/xsa48-4.2.patch          |  114 ++++++++++++++
 5 files changed, 519 insertions(+), 0 deletions(-)
 create mode 100644 main/xen/xsa44-4.2.patch
 create mode 100644 main/xen/xsa46-4.2.patch
 create mode 100644 main/xen/xsa47-4.2-unstable.patch
 create mode 100644 main/xen/xsa48-4.2.patch

diff --git a/main/xen/APKBUILD b/main/xen/APKBUILD
index 51ff406..122ac1a 100644
--- a/main/xen/APKBUILD
+++ b/main/xen/APKBUILD
@@ -26,6 +26,10 @@ source="http://bits.xensource.com/oss-xen/release/$pkgver/$pkgname-$pkgver.tar.g
 	xsa35-4.2-with-xsa34.patch
 	xsa36-4.2.patch
 	xsa38.patch
+	xsa47-4.2-unstable.patch
+	xsa48-4.2.patch
+	xsa44-4.2.patch
+	xsa46-4.2.patch
 
 	xenstored.initd
 	xenstored.confd
diff --git a/main/xen/xsa44-4.2.patch b/main/xen/xsa44-4.2.patch
new file mode 100644
index 0000000..07ed938
--- /dev/null
+++ b/main/xen/xsa44-4.2.patch
@@ -0,0 +1,77 @@
+x86: clear EFLAGS.NT in SYSENTER entry path
+
+... as it causes problems if we happen to exit back via IRET: In the
+course of trying to handle the fault, the hypervisor creates a stack
+frame by hand, and uses PUSHFQ to set the respective EFLAGS field, but
+expects to be able to IRET through that stack frame to the second
+portion of the fixup code (which causes a #GP due to the stored EFLAGS
+having NT set).
+
+And even if this worked (e.g if we cleared NT in that path), it would
+then (through the fail safe callback) cause a #GP in the guest with the
+SYSENTER handler's first instruction as the source, which in turn would
+allow guest user mode code to crash the guest kernel.
+
+Inject a #GP on the fake (NULL) address of the SYSENTER instruction
+instead, just like in the case where the guest kernel didn't register
+a corresponding entry point.
+
+On 32-bit we also need to make sure we clear SYSENTER_CS for all CPUs
+(neither #RESET nor #INIT guarantee this).
+
+This is CVE-2013-1917 / XSA-44.
+
+Reported-by: Andrew Cooper <[email-masked]>
+Signed-off-by: Jan Beulich <[email-masked]>
+Tested-by: Andrew Cooper <[email-masked]>
+Acked-by: Andrew Cooper <[email-masked]>
+
+--- a/xen/arch/x86/acpi/suspend.c
++++ b/xen/arch/x86/acpi/suspend.c
+@@ -81,8 +81,12 @@ void restore_rest_processor_state(void)
+     }
+ 
+ #else /* !defined(CONFIG_X86_64) */
+-    if ( supervisor_mode_kernel && cpu_has_sep )
+-        wrmsr(MSR_IA32_SYSENTER_ESP, &this_cpu(init_tss).esp1, 0);
++    if ( cpu_has_sep )
++    {
++        wrmsr(MSR_IA32_SYSENTER_CS, 0, 0);
++        if ( supervisor_mode_kernel )
++            wrmsr(MSR_IA32_SYSENTER_ESP, &this_cpu(init_tss).esp1, 0);
++    }
+ #endif
+ 
+     /* Maybe load the debug registers. */
+--- a/xen/arch/x86/cpu/common.c
++++ b/xen/arch/x86/cpu/common.c
+@@ -655,8 +655,11 @@ void __cpuinit cpu_init(void)
+ #if defined(CONFIG_X86_32)
+ 	t->ss0  = __HYPERVISOR_DS;
+ 	t->esp0 = get_stack_bottom();
+-	if ( supervisor_mode_kernel && cpu_has_sep )
++	if ( cpu_has_sep ) {
++	    wrmsr(MSR_IA32_SYSENTER_CS, 0, 0);
++	    if ( supervisor_mode_kernel )
+ 		wrmsr(MSR_IA32_SYSENTER_ESP, &t->esp1, 0);
++	}
+ #elif defined(CONFIG_X86_64)
+ 	/* Bottom-of-stack must be 16-byte aligned! */
+ 	BUG_ON((get_stack_bottom() & 15) != 0);
+--- a/xen/arch/x86/x86_64/entry.S
++++ b/xen/arch/x86/x86_64/entry.S
+@@ -284,7 +284,14 @@ sysenter_eflags_saved:
+         cmpb  $0,VCPU_sysenter_disables_events(%rbx)
+         movq  VCPU_sysenter_addr(%rbx),%rax
+         setne %cl
++        testl $X86_EFLAGS_NT,UREGS_eflags(%rsp)
+         leaq  VCPU_trap_bounce(%rbx),%rdx
++UNLIKELY_START(nz, sysenter_nt_set)
++        pushfq
++        andl  $~X86_EFLAGS_NT,(%rsp)
++        popfq
++        xorl  %eax,%eax
++UNLIKELY_END(sysenter_nt_set)
+         testq %rax,%rax
+         leal  (,%rcx,TBF_INTERRUPT),%ecx
+ UNLIKELY_START(z, sysenter_gpf)
diff --git a/main/xen/xsa46-4.2.patch b/main/xen/xsa46-4.2.patch
new file mode 100644
index 0000000..9448ea9
--- /dev/null
+++ b/main/xen/xsa46-4.2.patch
@@ -0,0 +1,293 @@
+x86: fix various issues with handling guest IRQs
+
+- properly revoke IRQ access in map_domain_pirq() error path
+- don't permit replacing an in use IRQ
+- don't accept inputs in the GSI range for MAP_PIRQ_TYPE_MSI
+- track IRQ access permission in host IRQ terms, not guest IRQ ones
+  (and with that, also disallow Dom0 access to IRQ0)
+
+This is CVE-2013-1919 / XSA-46.
+
+Signed-off-by: Jan Beulich <[email-masked]>
+Acked-by: Stefano Stabellini <[email-masked]>
+
+--- a/tools/libxl/libxl_create.c
++++ b/tools/libxl/libxl_create.c
+@@ -968,14 +968,16 @@ static void domcreate_launch_dm(libxl__e
+     }
+ 
+     for (i = 0; i < d_config->b_info.num_irqs; i++) {
+-        uint32_t irq = d_config->b_info.irqs[i];
++        int irq = d_config->b_info.irqs[i];
+ 
+-        LOG(DEBUG, "dom%d irq %"PRIx32, domid, irq);
++        LOG(DEBUG, "dom%d irq %d", domid, irq);
+ 
+-        ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
++        ret = irq >= 0 ? xc_physdev_map_pirq(CTX->xch, domid, irq, &irq)
++                       : -EOVERFLOW;
++        if (!ret)
++            ret = xc_domain_irq_permission(CTX->xch, domid, irq, 1);
+         if ( ret<0 ){
+-            LOGE(ERROR,
+-                 "failed give dom%d access to irq %"PRId32, domid, irq);
++            LOGE(ERROR, "failed give dom%d access to irq %d", domid, irq);
+             ret = ERROR_FAIL;
+         }
+     }
+--- a/tools/python/xen/xend/server/irqif.py
++++ b/tools/python/xen/xend/server/irqif.py
+@@ -73,6 +73,12 @@ class IRQController(DevController):
+        
+         pirq = get_param('irq')
+ 
++        rc = xc.physdev_map_pirq(domid = self.getDomid(),
++                                 index = pirq,
++                                 pirq  = pirq)
++        if rc < 0:
++            raise VmError('irq: Failed to map irq %x' % (pirq))
++
+         rc = xc.domain_irq_permission(domid        = self.getDomid(),
+                                       pirq         = pirq,
+                                       allow_access = True)
+@@ -81,12 +87,6 @@ class IRQController(DevController):
+             #todo non-fatal
+             raise VmError(
+                 'irq: Failed to configure irq: %d' % (pirq))
+-        rc = xc.physdev_map_pirq(domid = self.getDomid(),
+-                                index = pirq,
+-                                pirq  = pirq)
+-        if rc < 0:
+-            raise VmError(
+-                'irq: Failed to map irq %x' % (pirq))
+         back = dict([(k, config[k]) for k in self.valid_cfg if k in config])
+         return (self.allocateDeviceID(), back, {})
+ 
+--- a/xen/arch/x86/domain_build.c
++++ b/xen/arch/x86/domain_build.c
+@@ -1219,7 +1219,7 @@ int __init construct_dom0(
+     /* DOM0 is permitted full I/O capabilities. */
+     rc |= ioports_permit_access(dom0, 0, 0xFFFF);
+     rc |= iomem_permit_access(dom0, 0UL, ~0UL);
+-    rc |= irqs_permit_access(dom0, 0, d->nr_pirqs - 1);
++    rc |= irqs_permit_access(dom0, 1, nr_irqs_gsi - 1);
+ 
+     /*
+      * Modify I/O port access permissions.
+--- a/xen/arch/x86/domctl.c
++++ b/xen/arch/x86/domctl.c
+@@ -772,9 +772,13 @@ long arch_do_domctl(
+             goto bind_out;
+ 
+         ret = -EPERM;
+-        if ( !IS_PRIV(current->domain) &&
+-             !irq_access_permitted(current->domain, bind->machine_irq) )
+-            goto bind_out;
++        if ( !IS_PRIV(current->domain) )
++        {
++            int irq = domain_pirq_to_irq(d, bind->machine_irq);
++
++            if ( irq <= 0 || !irq_access_permitted(current->domain, irq) )
++                goto bind_out;
++        }
+ 
+         ret = -ESRCH;
+         if ( iommu_enabled )
+@@ -803,9 +807,13 @@ long arch_do_domctl(
+         bind = &(domctl->u.bind_pt_irq);
+ 
+         ret = -EPERM;
+-        if ( !IS_PRIV(current->domain) &&
+-             !irq_access_permitted(current->domain, bind->machine_irq) )
+-            goto unbind_out;
++        if ( !IS_PRIV(current->domain) )
++        {
++            int irq = domain_pirq_to_irq(d, bind->machine_irq);
++
++            if ( irq <= 0 || !irq_access_permitted(current->domain, irq) )
++                goto unbind_out;
++        }
+ 
+         if ( iommu_enabled )
+         {
+--- a/xen/arch/x86/irq.c
++++ b/xen/arch/x86/irq.c
+@@ -184,6 +184,14 @@ int create_irq(int node)
+         desc->arch.used = IRQ_UNUSED;
+         irq = ret;
+     }
++    else if ( dom0 )
++    {
++        ret = irq_permit_access(dom0, irq);
++        if ( ret )
++            printk(XENLOG_G_ERR
++                   "Could not grant Dom0 access to IRQ%d (error %d)\n",
++                   irq, ret);
++    }
+ 
+     return irq;
+ }
+@@ -280,6 +288,17 @@ void clear_irq_vector(int irq)
+ void destroy_irq(unsigned int irq)
+ {
+     BUG_ON(!MSI_IRQ(irq));
++
++    if ( dom0 )
++    {
++        int err = irq_deny_access(dom0, irq);
++
++        if ( err )
++            printk(XENLOG_G_ERR
++                   "Could not revoke Dom0 access to IRQ%u (error %d)\n",
++                   irq, err);
++    }
++
+     dynamic_irq_cleanup(irq);
+     clear_irq_vector(irq);
+ }
+@@ -1858,7 +1877,7 @@ int map_domain_pirq(
+ 
+     if ( !IS_PRIV(current->domain) &&
+          !(IS_PRIV_FOR(current->domain, d) &&
+-           irq_access_permitted(current->domain, pirq)))
++           irq_access_permitted(current->domain, irq)))
+         return -EPERM;
+ 
+     if ( pirq < 0 || pirq >= d->nr_pirqs || irq < 0 || irq >= nr_irqs )
+@@ -1887,17 +1906,18 @@ int map_domain_pirq(
+         return ret;
+     }
+ 
+-    ret = irq_permit_access(d, pirq);
++    ret = irq_permit_access(d, irq);
+     if ( ret )
+     {
+-        dprintk(XENLOG_G_ERR, "dom%d: could not permit access to irq %d\n",
+-                d->domain_id, pirq);
++        printk(XENLOG_G_ERR
++               "dom%d: could not permit access to IRQ%d (pirq %d)\n",
++               d->domain_id, irq, pirq);
+         return ret;
+     }
+ 
+     ret = prepare_domain_irq_pirq(d, irq, pirq, &info);
+     if ( ret )
+-        return ret;
++        goto revoke;
+ 
+     desc = irq_to_desc(irq);
+ 
+@@ -1921,8 +1941,14 @@ int map_domain_pirq(
+         spin_lock_irqsave(&desc->lock, flags);
+ 
+         if ( desc->handler != &no_irq_type )
++        {
++            spin_unlock_irqrestore(&desc->lock, flags);
+             dprintk(XENLOG_G_ERR, "dom%d: irq %d in use\n",
+                     d->domain_id, irq);
++            pci_disable_msi(msi_desc);
++            ret = -EBUSY;
++            goto done;
++        }
+         setup_msi_handler(desc, msi_desc);
+ 
+         if ( opt_irq_vector_map == OPT_IRQ_VECTOR_MAP_PERDEV
+@@ -1951,7 +1977,14 @@ int map_domain_pirq(
+ 
+ done:
+     if ( ret )
++    {
+         cleanup_domain_irq_pirq(d, irq, info);
++ revoke:
++        if ( irq_deny_access(d, irq) )
++            printk(XENLOG_G_ERR
++                   "dom%d: could not revoke access to IRQ%d (pirq %d)\n",
++                   d->domain_id, irq, pirq);
++    }
+     return ret;
+ }
+ 
+@@ -2017,10 +2050,11 @@ int unmap_domain_pirq(struct domain *d, 
+     if ( !forced_unbind )
+         cleanup_domain_irq_pirq(d, irq, info);
+ 
+-    ret = irq_deny_access(d, pirq);
++    ret = irq_deny_access(d, irq);
+     if ( ret )
+-        dprintk(XENLOG_G_ERR, "dom%d: could not deny access to irq %d\n",
+-                d->domain_id, pirq);
++        printk(XENLOG_G_ERR
++               "dom%d: could not deny access to IRQ%d (pirq %d)\n",
++               d->domain_id, irq, pirq);
+ 
+  done:
+     return ret;
+--- a/xen/arch/x86/physdev.c
++++ b/xen/arch/x86/physdev.c
+@@ -147,7 +147,7 @@ int physdev_map_pirq(domid_t domid, int 
+         if ( irq == -1 )
+             irq = create_irq(NUMA_NO_NODE);
+ 
+-        if ( irq < 0 || irq >= nr_irqs )
++        if ( irq < nr_irqs_gsi || irq >= nr_irqs )
+         {
+             dprintk(XENLOG_G_ERR, "dom%d: can't create irq for msi!\n",
+                     d->domain_id);
+--- a/xen/common/domctl.c
++++ b/xen/common/domctl.c
+@@ -25,6 +25,7 @@
+ #include <xen/paging.h>
+ #include <xen/hypercall.h>
+ #include <asm/current.h>
++#include <asm/irq.h>
+ #include <asm/page.h>
+ #include <public/domctl.h>
+ #include <xsm/xsm.h>
+@@ -897,9 +898,9 @@ long do_domctl(XEN_GUEST_HANDLE(xen_domc
+         else if ( xsm_irq_permission(d, pirq, allow) )
+             ret = -EPERM;
+         else if ( allow )
+-            ret = irq_permit_access(d, pirq);
++            ret = pirq_permit_access(d, pirq);
+         else
+-            ret = irq_deny_access(d, pirq);
++            ret = pirq_deny_access(d, pirq);
+ 
+         rcu_unlock_domain(d);
+     }
+--- a/xen/common/event_channel.c
++++ b/xen/common/event_channel.c
+@@ -369,7 +369,7 @@ static long evtchn_bind_pirq(evtchn_bind
+     if ( (pirq < 0) || (pirq >= d->nr_pirqs) )
+         return -EINVAL;
+ 
+-    if ( !is_hvm_domain(d) && !irq_access_permitted(d, pirq) )
++    if ( !is_hvm_domain(d) && !pirq_access_permitted(d, pirq) )
+         return -EPERM;
+ 
+     spin_lock(&d->event_lock);
+--- a/xen/include/xen/iocap.h
++++ b/xen/include/xen/iocap.h
+@@ -28,4 +28,22 @@
+ #define irq_access_permitted(d, i)                      \
+     rangeset_contains_singleton((d)->irq_caps, i)
+ 
++#define pirq_permit_access(d, i) ({                     \
++    struct domain *d__ = (d);                           \
++    int i__ = domain_pirq_to_irq(d__, i);               \
++    i__ > 0 ? rangeset_add_singleton(d__->irq_caps, i__)\
++            : -EINVAL;                                  \
++})
++#define pirq_deny_access(d, i) ({                       \
++    struct domain *d__ = (d);                           \
++    int i__ = domain_pirq_to_irq(d__, i);               \
++    i__ > 0 ? rangeset_remove_singleton(d__->irq_caps, i__)\
++            : -EINVAL;                                  \
++})
++#define pirq_access_permitted(d, i) ({                  \
++    struct domain *d__ = (d);                           \
++    rangeset_contains_singleton(d__->irq_caps,          \
++                                domain_pirq_to_irq(d__, i));\
++})
++
+ #endif /* __XEN_IOCAP_H__ */
diff --git a/main/xen/xsa47-4.2-unstable.patch b/main/xen/xsa47-4.2-unstable.patch
new file mode 100644
index 0000000..7ebb8c8
--- /dev/null
+++ b/main/xen/xsa47-4.2-unstable.patch
@@ -0,0 +1,31 @@
+defer event channel bucket pointer store until after XSM checks
+
+Otherwise a dangling pointer can be left, which would cause subsequent
+memory corruption as soon as the space got re-allocated for some other
+purpose.
+
+This is CVE-2013-1920 / XSA-47.
+
+Reported-by: Wei Liu <[email-masked]>
+Signed-off-by: Jan Beulich <[email-masked]>
+Reviewed-by: Tim Deegan <[email-masked]>
+
+--- a/xen/common/event_channel.c
++++ b/xen/common/event_channel.c
+@@ -140,7 +140,6 @@ static int get_free_port(struct domain *
+     chn = xzalloc_array(struct evtchn, EVTCHNS_PER_BUCKET);
+     if ( unlikely(chn == NULL) )
+         return -ENOMEM;
+-    bucket_from_port(d, port) = chn;
+ 
+     for ( i = 0; i < EVTCHNS_PER_BUCKET; i++ )
+     {
+@@ -153,6 +152,8 @@ static int get_free_port(struct domain *
+         }
+     }
+ 
++    bucket_from_port(d, port) = chn;
++
+     return port;
+ }
+ 
diff --git a/main/xen/xsa48-4.2.patch b/main/xen/xsa48-4.2.patch
new file mode 100644
index 0000000..c44806e
--- /dev/null
+++ b/main/xen/xsa48-4.2.patch
@@ -0,0 +1,114 @@
+Add -f FMT  / --format FMT arg to qemu-nbd
+
+From: "Daniel P. Berrange" <[email-masked]>
+
+Currently the qemu-nbd program will auto-detect the format of
+any disk it is given. This behaviour is known to be insecure.
+For example, if qemu-nbd initially exposes a 'raw' file to an
+unprivileged app, and that app runs
+
+   'qemu-img create -f qcow2 -o backing_file=/etc/shadow /dev/nbd0'
+
+then the next time the app is started, the qemu-nbd will now
+detect it as a 'qcow2' file and expose /etc/shadow to the
+unprivileged app.
+
+The only way to avoid this is to explicitly tell qemu-nbd what
+disk format to use on the command line, completely disabling
+auto-detection. This patch adds a '-f' / '--format' arg for
+this purpose, mirroring what is already available via qemu-img
+and qemu commands.
+
+  qemu-nbd --format raw -p 9000 evil.img
+
+will now always use raw, regardless of what format 'evil.img'
+looks like it contains
+
+Signed-off-by: Daniel P. Berrange <[email-masked]>
+[Use errx, not err. - Paolo]
+Signed-off-by: Paolo Bonzini <[email-masked]>
+Signed-off-by: Stefano Stabellini <[email-masked]>
+
+[ This is a security issue, CVE-2013-1922 / XSA-48. ]
+
+diff --git a/qemu-nbd.c b/qemu-nbd.c
+index 291cba2..8fbe2cf 100644
+--- a/qemu-nbd.c
++++ b/qemu-nbd.c
+@@ -247,6 +247,7 @@ out:
+ int main(int argc, char **argv)
+ {
+     BlockDriverState *bs;
++    BlockDriver *drv;
+     off_t dev_offset = 0;
+     off_t offset = 0;
+     uint32_t nbdflags = 0;
+@@ -256,7 +257,7 @@ int main(int argc, char **argv)
+     struct sockaddr_in addr;
+     socklen_t addr_len = sizeof(addr);
+     off_t fd_size;
+-    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:t";
++    const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:t";
+     struct option lopt[] = {
+         { "help", 0, NULL, 'h' },
+         { "version", 0, NULL, 'V' },
+@@ -271,6 +272,7 @@ int main(int argc, char **argv)
+         { "snapshot", 0, NULL, 's' },
+         { "nocache", 0, NULL, 'n' },
+         { "shared", 1, NULL, 'e' },
++        { "format", 1, NULL, 'f' },
+         { "persistent", 0, NULL, 't' },
+         { "verbose", 0, NULL, 'v' },
+         { NULL, 0, NULL, 0 }
+@@ -292,6 +294,7 @@ int main(int argc, char **argv)
+     int max_fd;
+     int persistent = 0;
+     pthread_t client_thread;
++    const char *fmt = NULL;
+ 
+     /* The client thread uses SIGTERM to interrupt the server.  A signal
+      * handler ensures that "qemu-nbd -v -c" exits with a nice status code.
+@@ -368,6 +371,9 @@ int main(int argc, char **argv)
+                 errx(EXIT_FAILURE, "Shared device number must be greater than 0\n");
+             }
+             break;
++        case 'f':
++            fmt = optarg;
++            break;
+ 	case 't':
+ 	    persistent = 1;
+ 	    break;
+@@ -478,9 +484,19 @@ int main(int argc, char **argv)
+     bdrv_init();
+     atexit(bdrv_close_all);
+ 
++    if (fmt) {
++        drv = bdrv_find_format(fmt);
++        if (!drv) {
++            errx(EXIT_FAILURE, "Unknown file format '%s'", fmt);
++        }
++    } else {
++        drv = NULL;
++    }
++
+     bs = bdrv_new("hda");
+     srcpath = argv[optind];
+-    if ((ret = bdrv_open(bs, srcpath, flags, NULL)) < 0) {
++    ret = bdrv_open(bs, srcpath, flags, drv);
++    if (ret < 0) {
+         errno = -ret;
+         err(EXIT_FAILURE, "Failed to bdrv_open '%s'", argv[optind]);
+     }
+diff --git a/qemu-nbd.texi b/qemu-nbd.texi
+index 44996cc..f56c68e 100644
+--- a/qemu-nbd.texi
++++ b/qemu-nbd.texi
+@@ -36,6 +36,8 @@ Export Qemu disk image using NBD protocol.
+   disconnect the specified device
+ @item -e, --shared=@var{num}
+   device can be shared by @var{num} clients (default @samp{1})
++@item -f, --format=@var{fmt}
++  force block driver for format @var{fmt} instead of auto-detecting
+ @item -t, --persistent
+   don't exit on the last connection
+ @item -v, --verbose
-- 
1.7.7.5 (Apple Git-26)

---
Unsubscribe:  [email-masked]
Help:         [email-masked]
---