From 22d02a70398309a0042a7296e6a0a38296537e61 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 25 Aug 2020 15:27:58 +0100 Subject: [PATCH 01/15] QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) --- xen.spec | 10 +++++- xsa335-qemu.patch | 84 +++++++++++++++++++++++++++++++++++++++++++++++ xsa335-trad.patch | 45 +++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 xsa335-qemu.patch create mode 100644 xsa335-trad.patch diff --git a/xen.spec b/xen.spec index 60b3c61..086ca83 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.1 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -128,6 +128,8 @@ Patch56: xsa321-4.13-5.patch Patch57: xsa321-4.13-6.patch Patch58: xsa321-4.13-7.patch Patch59: xsa327.patch +Patch60: xsa335-qemu.patch +Patch61: xsa335-trad.patch %if %build_qemutrad @@ -348,6 +350,7 @@ manage Xen virtual machines. %patch57 -p1 %patch58 -p1 %patch59 -p1 +%patch61 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -364,6 +367,7 @@ popd # qemu-xen patches pushd tools/qemu-xen +%patch60 -p1 popd # stubdom sources @@ -940,6 +944,10 @@ fi %endif %changelog +* Tue Aug 25 2020 Michael Young - 4.13.1-5 +- QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] + (#1871850) + * Tue Jul 07 2020 Michael Young - 4.13.1-4 - incorrect error handling in event channel port allocation leads to DoS [XSA-317, CVE-2020-15566] (#1854465) diff --git a/xsa335-qemu.patch b/xsa335-qemu.patch new file mode 100644 index 0000000..5617502 --- /dev/null +++ b/xsa335-qemu.patch @@ -0,0 +1,84 @@ +From c5bd2924c6d6a5bcbffb8b5e7798a88970131c07 Mon Sep 17 00:00:00 2001 +From: Gerd Hoffmann +Date: Mon, 17 Aug 2020 08:34:22 +0200 +Subject: [PATCH] usb: fix setup_len init (CVE-2020-14364) + +Store calculated setup_len in a local variable, verify it, and only +write it to the struct (USBDevice->setup_len) in case it passed the +sanity checks. + +This prevents other code (do_token_{in,out} functions specifically) +from working with invalid USBDevice->setup_len values and overrunning +the USBDevice->setup_buf[] buffer. + +Fixes: CVE-2020-14364 +Signed-off-by: Gerd Hoffmann +--- + hw/usb/core.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/hw/usb/core.c b/hw/usb/core.c +index 5abd128b6bc5..5234dcc73fea 100644 +--- a/hw/usb/core.c ++++ b/hw/usb/core.c +@@ -129,6 +129,7 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) + static void do_token_setup(USBDevice *s, USBPacket *p) + { + int request, value, index; ++ unsigned int setup_len; + + if (p->iov.size != 8) { + p->status = USB_RET_STALL; +@@ -138,14 +139,15 @@ static void do_token_setup(USBDevice *s, USBPacket *p) + usb_packet_copy(p, s->setup_buf, p->iov.size); + s->setup_index = 0; + p->actual_length = 0; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; +@@ -259,26 +261,28 @@ static void do_token_out(USBDevice *s, USBPacket *p) + static void do_parameter(USBDevice *s, USBPacket *p) + { + int i, request, value, index; ++ unsigned int setup_len; + + for (i = 0; i < 8; i++) { + s->setup_buf[i] = p->parameter >> (i*8); + } + + s->setup_state = SETUP_STATE_PARAM; +- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; + s->setup_index = 0; + + request = (s->setup_buf[0] << 8) | s->setup_buf[1]; + value = (s->setup_buf[3] << 8) | s->setup_buf[2]; + index = (s->setup_buf[5] << 8) | s->setup_buf[4]; + +- if (s->setup_len > sizeof(s->data_buf)) { ++ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; ++ if (setup_len > sizeof(s->data_buf)) { + fprintf(stderr, + "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", +- s->setup_len, sizeof(s->data_buf)); ++ setup_len, sizeof(s->data_buf)); + p->status = USB_RET_STALL; + return; + } ++ s->setup_len = setup_len; + + if (p->pid == USB_TOKEN_OUT) { + usb_packet_copy(p, s->data_buf, s->setup_len); +-- +2.18.4 diff --git a/xsa335-trad.patch b/xsa335-trad.patch new file mode 100644 index 0000000..1310b84 --- /dev/null +++ b/xsa335-trad.patch @@ -0,0 +1,45 @@ +From a62cdd675bc6a8053f6797b6add29b2853b081e3 Mon Sep 17 00:00:00 2001 +From: Ian Jackson +Date: Wed, 19 Aug 2020 18:31:45 +0100 +Subject: [PATCH] SUPPORT.md: Desupport qemu trad except stub dm + +While investigating XSA-335 we discovered that many upstream security +fixes were missing. It is not practical to backport them. There is +no good reason to be running this very ancient version of qemu, except +that it is the only way to run a stub dm which is currently supported +by upstream. + +Signed-off-by: Ian Jackson +--- + SUPPORT.md | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/SUPPORT.md b/SUPPORT.md +index 1479055c45..b0939052e2 100644 +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -758,6 +758,21 @@ See the section **Blkback** for image formats supported by QEMU. + + Status: Supported, not security supported + ++### qemu-xen-traditional ### ++ ++The Xen Project provides an old version of qemu with modifications ++which enable use as a device model stub domain. The old version is ++normally selected by default only in a stub dm configuration, but it ++can be requested explicitly in other configurations, for example in ++`xl` with `device_model_version="QEMU_XEN_TRADITIONAL"`. ++ ++ Status, Device Model Stub Domains: Supported, with caveats ++ Status, as host process device model: No security support, not recommended ++ ++qemu-xen-traditional is security supported only for those available ++devices which are supported for mainstream QEMU (see above), with ++trusted driver domains (see Device Model Stub Domains). ++ + ## Virtual Firmware + + ### x86/HVM iPXE +-- +2.20.1 + From 39685fac181879d7921bb02521da82a6eaa01376 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 22 Sep 2020 21:05:24 +0100 Subject: [PATCH 02/15] 10 security fixes x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) Missing unlock in XENMEM_acquire_resource error path [XSA-334, CVE-2020-25598] (#1881616) race when migrating timers between x86 HVM vCPU-s [XSA-336, CVE-2020-25604] (#1881618) PCI passthrough code reading back hardware registers [XSA-337, CVE-2020-25595] (#1881587) once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] (#1881588) x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] (#1881617) Missing memory barriers when accessing/allocating an event channel [XSA-340, CVE-2020-25603] (#1881583) out of bounds event channels available to 32-bit x86 domains [XSA-342, CVE-2020-25600] (#1881582) races with evtchn_reset() [XSA-343, CVE-2020-25599] (#1881581) lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, CVE-2020-25601] (#1881586) --- xen.spec | 51 +++++- xsa333.patch | 39 +++++ xsa334.patch | 51 ++++++ xsa336.patch | 283 ++++++++++++++++++++++++++++++++ xsa337-4.13-1.patch | 87 ++++++++++ xsa337-4.13-2.patch | 181 ++++++++++++++++++++ xsa338.patch | 42 +++++ xsa339.patch | 76 +++++++++ xsa340.patch | 65 ++++++++ xsa342-4.13.patch | 145 ++++++++++++++++ xsa343-1.patch | 199 ++++++++++++++++++++++ xsa343-2.patch | 295 +++++++++++++++++++++++++++++++++ xsa343-3.patch | 392 ++++++++++++++++++++++++++++++++++++++++++++ xsa344-4.13-1.patch | 130 +++++++++++++++ xsa344-4.13-2.patch | 203 +++++++++++++++++++++++ 15 files changed, 2238 insertions(+), 1 deletion(-) create mode 100644 xsa333.patch create mode 100644 xsa334.patch create mode 100644 xsa336.patch create mode 100644 xsa337-4.13-1.patch create mode 100644 xsa337-4.13-2.patch create mode 100644 xsa338.patch create mode 100644 xsa339.patch create mode 100644 xsa340.patch create mode 100644 xsa342-4.13.patch create mode 100644 xsa343-1.patch create mode 100644 xsa343-2.patch create mode 100644 xsa343-3.patch create mode 100644 xsa344-4.13-1.patch create mode 100644 xsa344-4.13-2.patch diff --git a/xen.spec b/xen.spec index 086ca83..15a6b19 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.1 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -130,6 +130,20 @@ Patch58: xsa321-4.13-7.patch Patch59: xsa327.patch Patch60: xsa335-qemu.patch Patch61: xsa335-trad.patch +Patch62: xsa333.patch +Patch63: xsa334.patch +Patch64: xsa336.patch +Patch65: xsa337-4.13-1.patch +Patch66: xsa337-4.13-2.patch +Patch67: xsa338.patch +Patch68: xsa339.patch +Patch69: xsa340.patch +Patch70: xsa342-4.13.patch +Patch71: xsa343-1.patch +Patch72: xsa343-2.patch +Patch73: xsa343-3.patch +Patch74: xsa344-4.13-1.patch +Patch75: xsa344-4.13-2.patch %if %build_qemutrad @@ -351,6 +365,20 @@ manage Xen virtual machines. %patch58 -p1 %patch59 -p1 %patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 +%patch65 -p1 +%patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -944,6 +972,27 @@ fi %endif %changelog +* Tue Sep 22 2020 Michael Young - 4.13.1-6 +- x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, + CVE-2020-25602] (#1881619) +- Missing unlock in XENMEM_acquire_resource error path [XSA-334, + CVE-2020-25598] (#1881616) +- race when migrating timers between x86 HVM vCPU-s [XSA-336, + CVE-2020-25604] (#1881618) +- PCI passthrough code reading back hardware registers [XSA-337, + CVE-2020-25595] (#1881587) +- once valid event channels may not turn invalid [XSA-338, CVE-2020-25597] + (#1881588) +- x86 pv guest kernel DoS via SYSENTER [XSA-339, CVE-2020-25596] + (#1881617) +- Missing memory barriers when accessing/allocating an event channel [XSA-340, + CVE-2020-25603] (#1881583) +- out of bounds event channels available to 32-bit x86 domains [XSA-342, + CVE-2020-25600] (#1881582) +- races with evtchn_reset() [XSA-343, CVE-2020-25599] (#1881581) +- lack of preemption in evtchn_reset() / evtchn_destroy() [XSA-344, + CVE-2020-25601] (#1881586) + * Tue Aug 25 2020 Michael Young - 4.13.1-5 - QEMU: usb: out-of-bounds r/w access issue [XSA-335, CVE-2020-14364] (#1871850) diff --git a/xsa333.patch b/xsa333.patch new file mode 100644 index 0000000..6b86c94 --- /dev/null +++ b/xsa333.patch @@ -0,0 +1,39 @@ +From: Andrew Cooper +Subject: x86/pv: Handle the Intel-specific MSR_MISC_ENABLE correctly + +This MSR doesn't exist on AMD hardware, and switching away from the safe +functions in the common MSR path was an erroneous change. + +Partially revert the change. + +This is XSA-333. + +Fixes: 4fdc932b3cc ("x86/Intel: drop another 32-bit leftover") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich +Reviewed-by: Wei Liu + +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index efeb2a727e..6332c74b80 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -924,7 +924,8 @@ static int read_msr(unsigned int reg, uint64_t *val, + return X86EMUL_OKAY; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, *val); ++ if ( rdmsr_safe(reg, *val) ) ++ break; + *val = guest_misc_enable(*val); + return X86EMUL_OKAY; + +@@ -1059,7 +1060,8 @@ static int write_msr(unsigned int reg, uint64_t val, + break; + + case MSR_IA32_MISC_ENABLE: +- rdmsrl(reg, temp); ++ if ( rdmsr_safe(reg, temp) ) ++ break; + if ( val != guest_misc_enable(temp) ) + goto invalid; + return X86EMUL_OKAY; diff --git a/xsa334.patch b/xsa334.patch new file mode 100644 index 0000000..4260cdb --- /dev/null +++ b/xsa334.patch @@ -0,0 +1,51 @@ +From: Andrew Cooper +Subject: xen/memory: Don't skip the RCU unlock path in acquire_resource() + +In the case that an HVM Stubdomain makes an XENMEM_acquire_resource hypercall, +the FIXME path will bypass rcu_unlock_domain() on the way out of the function. + +Move the check to the start of the function. This does change the behaviour +of the get-size path for HVM Stubdomains, but that functionality is currently +broken and unused anyway, as well as being quite useless to entities which +can't actually map the resource anyway. + +This is XSA-334. + +Fixes: 83fa6552ce ("common: add a new mappable resource type: XENMEM_resource_grant_table") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/common/memory.c b/xen/common/memory.c +index 1a3c9ffb30..29741d8904 100644 +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -1058,6 +1058,14 @@ static int acquire_resource( + xen_pfn_t mfn_list[32]; + int rc; + ++ /* ++ * FIXME: Until foreign pages inserted into the P2M are properly ++ * reference counted, it is unsafe to allow mapping of ++ * resource pages unless the caller is the hardware domain. ++ */ ++ if ( paging_mode_translate(currd) && !is_hardware_domain(currd) ) ++ return -EACCES; ++ + if ( copy_from_guest(&xmar, arg, 1) ) + return -EFAULT; + +@@ -1114,14 +1122,6 @@ static int acquire_resource( + xen_pfn_t gfn_list[ARRAY_SIZE(mfn_list)]; + unsigned int i; + +- /* +- * FIXME: Until foreign pages inserted into the P2M are properly +- * reference counted, it is unsafe to allow mapping of +- * resource pages unless the caller is the hardware domain. +- */ +- if ( !is_hardware_domain(currd) ) +- return -EACCES; +- + if ( copy_from_guest(gfn_list, xmar.frame_list, xmar.nr_frames) ) + rc = -EFAULT; + diff --git a/xsa336.patch b/xsa336.patch new file mode 100644 index 0000000..b44c298 --- /dev/null +++ b/xsa336.patch @@ -0,0 +1,283 @@ +From: Roger Pau Monné +Subject: x86/vpt: fix race when migrating timers between vCPUs + +The current vPT code will migrate the emulated timers between vCPUs +(change the pt->vcpu field) while just holding the destination lock, +either from create_periodic_time or pt_adjust_global_vcpu_target if +the global target is adjusted. Changing the periodic_timer vCPU field +in this way creates a race where a third party could grab the lock in +the unlocked region of pt_adjust_global_vcpu_target (or before +create_periodic_time performs the vcpu change) and then release the +lock from a different vCPU, creating a locking imbalance. + +Introduce a per-domain rwlock in order to protect periodic_time +migration between vCPU lists. Taking the lock in read mode prevents +any timer from being migrated to a different vCPU, while taking it in +write mode allows performing migration of timers across vCPUs. The +per-vcpu locks are still used to protect all the other fields from the +periodic_timer struct. + +Note that such migration shouldn't happen frequently, and hence +there's no performance drop as a result of such locking. + +This is XSA-336. + +Reported-by: Igor Druzhinin +Tested-by: Igor Druzhinin +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich +--- +Changes since v2: + - Re-order pt_adjust_vcpu to remove one if. + - Fix pt_lock to not call pt_vcpu_lock, as we might end up using a + stale value of pt->vcpu when taking the per-vcpu lock. + +Changes since v1: + - Use a per-domain rwlock to protect timer vCPU migration. + +--- a/xen/arch/x86/hvm/hvm.c ++++ b/xen/arch/x86/hvm/hvm.c +@@ -658,6 +658,8 @@ int hvm_domain_initialise(struct domain + /* need link to containing domain */ + d->arch.hvm.pl_time->domain = d; + ++ rwlock_init(&d->arch.hvm.pl_time->pt_migrate); ++ + /* Set the default IO Bitmap. */ + if ( is_hardware_domain(d) ) + { +--- a/xen/arch/x86/hvm/vpt.c ++++ b/xen/arch/x86/hvm/vpt.c +@@ -153,23 +153,32 @@ static int pt_irq_masked(struct periodic + return 1; + } + +-static void pt_lock(struct periodic_time *pt) ++static void pt_vcpu_lock(struct vcpu *v) + { +- struct vcpu *v; ++ read_lock(&v->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&v->arch.hvm.tm_lock); ++} + +- for ( ; ; ) +- { +- v = pt->vcpu; +- spin_lock(&v->arch.hvm.tm_lock); +- if ( likely(pt->vcpu == v) ) +- break; +- spin_unlock(&v->arch.hvm.tm_lock); +- } ++static void pt_vcpu_unlock(struct vcpu *v) ++{ ++ spin_unlock(&v->arch.hvm.tm_lock); ++ read_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); ++} ++ ++static void pt_lock(struct periodic_time *pt) ++{ ++ /* ++ * We cannot use pt_vcpu_lock here, because we need to acquire the ++ * per-domain lock first and then (re-)fetch the value of pt->vcpu, or ++ * else we might be using a stale value of pt->vcpu. ++ */ ++ read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); ++ spin_lock(&pt->vcpu->arch.hvm.tm_lock); + } + + static void pt_unlock(struct periodic_time *pt) + { +- spin_unlock(&pt->vcpu->arch.hvm.tm_lock); ++ pt_vcpu_unlock(pt->vcpu); + } + + static void pt_process_missed_ticks(struct periodic_time *pt) +@@ -219,7 +228,7 @@ void pt_save_timer(struct vcpu *v) + if ( v->pause_flags & VPF_blocked ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + if ( !pt->do_not_freeze ) +@@ -227,7 +236,7 @@ void pt_save_timer(struct vcpu *v) + + pt_freeze_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void pt_restore_timer(struct vcpu *v) +@@ -235,7 +244,7 @@ void pt_restore_timer(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + { +@@ -248,7 +257,7 @@ void pt_restore_timer(struct vcpu *v) + + pt_thaw_time(v); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + static void pt_timer_fn(void *data) +@@ -309,7 +318,7 @@ int pt_update_irq(struct vcpu *v) + int irq, pt_vector = -1; + bool level; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + earliest_pt = NULL; + max_lag = -1ULL; +@@ -339,7 +348,7 @@ int pt_update_irq(struct vcpu *v) + + if ( earliest_pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return -1; + } + +@@ -347,7 +356,7 @@ int pt_update_irq(struct vcpu *v) + irq = earliest_pt->irq; + level = earliest_pt->level; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + switch ( earliest_pt->source ) + { +@@ -394,7 +403,7 @@ int pt_update_irq(struct vcpu *v) + time_cb *cb = NULL; + void *cb_priv; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + /* Make sure the timer is still on the list. */ + list_for_each_entry ( pt, &v->arch.hvm.tm_list, list ) + if ( pt == earliest_pt ) +@@ -404,7 +413,7 @@ int pt_update_irq(struct vcpu *v) + cb_priv = pt->priv; + break; + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -441,12 +450,12 @@ void pt_intr_post(struct vcpu *v, struct + if ( intack.source == hvm_intsrc_vector ) + return; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + pt = is_pt_irq(v, intack); + if ( pt == NULL ) + { +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + return; + } + +@@ -455,7 +464,7 @@ void pt_intr_post(struct vcpu *v, struct + cb = pt->cb; + cb_priv = pt->priv; + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + + if ( cb != NULL ) + cb(v, cb_priv); +@@ -466,12 +475,12 @@ void pt_migrate(struct vcpu *v) + struct list_head *head = &v->arch.hvm.tm_list; + struct periodic_time *pt; + +- spin_lock(&v->arch.hvm.tm_lock); ++ pt_vcpu_lock(v); + + list_for_each_entry ( pt, head, list ) + migrate_timer(&pt->timer, v->processor); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ pt_vcpu_unlock(v); + } + + void create_periodic_time( +@@ -490,7 +499,7 @@ void create_periodic_time( + + destroy_periodic_time(pt); + +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&v->domain->arch.hvm.pl_time->pt_migrate); + + pt->pending_intr_nr = 0; + pt->do_not_freeze = 0; +@@ -540,7 +549,7 @@ void create_periodic_time( + init_timer(&pt->timer, pt_timer_fn, pt, v->processor); + set_timer(&pt->timer, pt->scheduled); + +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); + } + + void destroy_periodic_time(struct periodic_time *pt) +@@ -565,30 +574,20 @@ void destroy_periodic_time(struct period + + static void pt_adjust_vcpu(struct periodic_time *pt, struct vcpu *v) + { +- int on_list; +- + ASSERT(pt->source == PTSRC_isa || pt->source == PTSRC_ioapic); + + if ( pt->vcpu == NULL ) + return; + +- pt_lock(pt); +- on_list = pt->on_list; +- if ( pt->on_list ) +- list_del(&pt->list); +- pt->on_list = 0; +- pt_unlock(pt); +- +- spin_lock(&v->arch.hvm.tm_lock); ++ write_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + pt->vcpu = v; +- if ( on_list ) ++ if ( pt->on_list ) + { +- pt->on_list = 1; ++ list_del(&pt->list); + list_add(&pt->list, &v->arch.hvm.tm_list); +- + migrate_timer(&pt->timer, v->processor); + } +- spin_unlock(&v->arch.hvm.tm_lock); ++ write_unlock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); + } + + void pt_adjust_global_vcpu_target(struct vcpu *v) +--- a/xen/include/asm-x86/hvm/vpt.h ++++ b/xen/include/asm-x86/hvm/vpt.h +@@ -128,6 +128,13 @@ struct pl_time { /* platform time */ + struct RTCState vrtc; + struct HPETState vhpet; + struct PMTState vpmt; ++ /* ++ * rwlock to prevent periodic_time vCPU migration. Take the lock in read ++ * mode in order to prevent the vcpu field of periodic_time from changing. ++ * Lock must be taken in write mode when changes to the vcpu field are ++ * performed, as it allows exclusive access to all the timers of a domain. ++ */ ++ rwlock_t pt_migrate; + /* guest_time = Xen sys time + stime_offset */ + int64_t stime_offset; + /* Ensures monotonicity in appropriate timer modes. */ diff --git a/xsa337-4.13-1.patch b/xsa337-4.13-1.patch new file mode 100644 index 0000000..2091626 --- /dev/null +++ b/xsa337-4.13-1.patch @@ -0,0 +1,87 @@ +From: Roger Pau Monné +Subject: x86/msi: get rid of read_msi_msg + +It's safer and faster to just use the cached last written +(untranslated) MSI message stored in msi_desc for the single user that +calls read_msi_msg. + +This also prevents relying on the data read from the device MSI +registers in order to figure out the index into the IOMMU interrupt +remapping table, which is not safe. + +This is part of XSA-337. + +Reported-by: Andrew Cooper +Requested-by: Andrew Cooper +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -183,54 +183,6 @@ void msi_compose_msg(unsigned vector, co + MSI_DATA_VECTOR(vector); + } + +-static bool read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) +-{ +- switch ( entry->msi_attrib.type ) +- { +- case PCI_CAP_ID_MSI: +- { +- struct pci_dev *dev = entry->dev; +- int pos = entry->msi_attrib.pos; +- uint16_t data; +- +- msg->address_lo = pci_conf_read32(dev->sbdf, +- msi_lower_address_reg(pos)); +- if ( entry->msi_attrib.is_64 ) +- { +- msg->address_hi = pci_conf_read32(dev->sbdf, +- msi_upper_address_reg(pos)); +- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 1)); +- } +- else +- { +- msg->address_hi = 0; +- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 0)); +- } +- msg->data = data; +- break; +- } +- case PCI_CAP_ID_MSIX: +- { +- void __iomem *base = entry->mask_base; +- +- if ( unlikely(!msix_memory_decoded(entry->dev, +- entry->msi_attrib.pos)) ) +- return false; +- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); +- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); +- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); +- break; +- } +- default: +- BUG(); +- } +- +- if ( iommu_intremap ) +- iommu_read_msi_from_ire(entry, msg); +- +- return true; +-} +- + static int write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) + { + entry->msg = *msg; +@@ -302,10 +254,7 @@ void set_msi_affinity(struct irq_desc *d + + ASSERT(spin_is_locked(&desc->lock)); + +- memset(&msg, 0, sizeof(msg)); +- if ( !read_msi_msg(msi_desc, &msg) ) +- return; +- ++ msg = msi_desc->msg; + msg.data &= ~MSI_DATA_VECTOR_MASK; + msg.data |= MSI_DATA_VECTOR(desc->arch.vector); + msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; diff --git a/xsa337-4.13-2.patch b/xsa337-4.13-2.patch new file mode 100644 index 0000000..bdefd37 --- /dev/null +++ b/xsa337-4.13-2.patch @@ -0,0 +1,181 @@ +From: Jan Beulich +Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs + +When assigned to less trusted or un-trusted guests, devices may change +state behind our backs (they may e.g. get reset by means we may not know +about). Therefore we should avoid reading BARs from hardware once a +device is no longer owned by Dom0. Furthermore when we can't read a BAR, +or when we read zero, we shouldn't instead use the caller provided +address unless that caller can be trusted. + +Re-arrange the logic in msix_capability_init() such that only Dom0 (and +only if the device isn't DomU-owned yet) or calls through +PHYSDEVOP_prepare_msix will actually result in the reading of the +respective BAR register(s). Additionally do so only as long as in-use +table entries are known (note that invocation of PHYSDEVOP_prepare_msix +counts as a "pseudo" entry). In all other uses the value already +recorded will get used instead. + +Clear the recorded values in _pci_cleanup_msix() as well as on the one +affected error path. (Adjust this error path to also avoid blindly +disabling MSI-X when it was enabled on entry to the function.) + +While moving around variable declarations (in many cases to reduce their +scopes), also adjust some of their types. + +This is part of XSA-337. + +Signed-off-by: Jan Beulich +Reviewed-by: Roger Pau Monné + +--- a/xen/arch/x86/msi.c ++++ b/xen/arch/x86/msi.c +@@ -769,16 +769,14 @@ static int msix_capability_init(struct p + { + struct arch_msix *msix = dev->msix; + struct msi_desc *entry = NULL; +- int vf; + u16 control; + u64 table_paddr; + u32 table_offset; +- u8 bir, pbus, pslot, pfunc; + u16 seg = dev->seg; + u8 bus = dev->bus; + u8 slot = PCI_SLOT(dev->devfn); + u8 func = PCI_FUNC(dev->devfn); +- bool maskall = msix->host_maskall; ++ bool maskall = msix->host_maskall, zap_on_error = false; + unsigned int pos = pci_find_cap_offset(seg, bus, slot, func, + PCI_CAP_ID_MSIX); + +@@ -820,43 +818,45 @@ static int msix_capability_init(struct p + + /* Locate MSI-X table region */ + table_offset = pci_conf_read32(dev->sbdf, msix_table_offset_reg(pos)); +- bir = (u8)(table_offset & PCI_MSIX_BIRMASK); +- table_offset &= ~PCI_MSIX_BIRMASK; ++ if ( !msix->used_entries && ++ (!msi || ++ (is_hardware_domain(current->domain) && ++ (dev->domain == current->domain || dev->domain == dom_io))) ) ++ { ++ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc; ++ int vf; ++ paddr_t pba_paddr; ++ unsigned int pba_offset; + +- if ( !dev->info.is_virtfn ) +- { +- pbus = bus; +- pslot = slot; +- pfunc = func; +- vf = -1; +- } +- else +- { +- pbus = dev->info.physfn.bus; +- pslot = PCI_SLOT(dev->info.physfn.devfn); +- pfunc = PCI_FUNC(dev->info.physfn.devfn); +- vf = PCI_BDF2(dev->bus, dev->devfn); +- } +- +- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); +- WARN_ON(msi && msi->table_base != table_paddr); +- if ( !table_paddr ) +- { +- if ( !msi || !msi->table_base ) ++ if ( !dev->info.is_virtfn ) + { +- pci_conf_write16(dev->sbdf, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); +- xfree(entry); +- return -ENXIO; ++ pbus = bus; ++ pslot = slot; ++ pfunc = func; ++ vf = -1; ++ } ++ else ++ { ++ pbus = dev->info.physfn.bus; ++ pslot = PCI_SLOT(dev->info.physfn.devfn); ++ pfunc = PCI_FUNC(dev->info.physfn.devfn); ++ vf = PCI_BDF2(dev->bus, dev->devfn); + } +- table_paddr = msi->table_base; +- } +- table_paddr += table_offset; + +- if ( !msix->used_entries ) +- { +- u64 pba_paddr; +- u32 pba_offset; ++ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); ++ WARN_ON(msi && msi->table_base != table_paddr); ++ if ( !table_paddr ) ++ { ++ if ( !msi || !msi->table_base ) ++ { ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), ++ control & ~PCI_MSIX_FLAGS_ENABLE); ++ xfree(entry); ++ return -ENXIO; ++ } ++ table_paddr = msi->table_base; ++ } ++ table_paddr += table_offset & ~PCI_MSIX_BIRMASK; + + msix->table.first = PFN_DOWN(table_paddr); + msix->table.last = PFN_DOWN(table_paddr + +@@ -875,7 +875,18 @@ static int msix_capability_init(struct p + BITS_TO_LONGS(msix->nr_entries) - 1); + WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last)); ++ ++ zap_on_error = true; ++ } ++ else if ( !msix->table.first ) ++ { ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); ++ xfree(entry); ++ return -ENODATA; + } ++ else ++ table_paddr = (msix->table.first << PAGE_SHIFT) + ++ (table_offset & ~PCI_MSIX_BIRMASK & ~PAGE_MASK); + + if ( entry ) + { +@@ -886,8 +897,15 @@ static int msix_capability_init(struct p + + if ( idx < 0 ) + { +- pci_conf_write16(dev->sbdf, msix_control_reg(pos), +- control & ~PCI_MSIX_FLAGS_ENABLE); ++ if ( zap_on_error ) ++ { ++ msix->table.first = 0; ++ msix->pba.first = 0; ++ ++ control &= ~PCI_MSIX_FLAGS_ENABLE; ++ } ++ ++ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); + xfree(entry); + return idx; + } +@@ -1076,9 +1094,14 @@ static void _pci_cleanup_msix(struct arc + if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first, + msix->table.last) ) + WARN(); ++ msix->table.first = 0; ++ msix->table.last = 0; ++ + if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first, + msix->pba.last) ) + WARN(); ++ msix->pba.first = 0; ++ msix->pba.last = 0; + } + } + diff --git a/xsa338.patch b/xsa338.patch new file mode 100644 index 0000000..7765219 --- /dev/null +++ b/xsa338.patch @@ -0,0 +1,42 @@ +From: Jan Beulich +Subject: evtchn: relax port_is_valid() + +To avoid ports potentially becoming invalid behind the back of certain +other functions (due to ->max_evtchn shrinking) because of +- a guest invoking evtchn_reset() and from a 2nd vCPU opening new + channels in parallel (see also XSA-343), +- alloc_unbound_xen_event_channel() produced channels living above the + 2-level range (see also XSA-342), +drop the max_evtchns check from port_is_valid(). For a port for which +the function once returned "true", the returned value may not turn into +"false" later on. The function's result may only depend on bounds which +can only ever grow (which is the case for d->valid_evtchns). + +This also eliminates a false sense of safety, utilized by some of the +users (see again XSA-343): Without a suitable lock held, d->max_evtchns +may change at any time, and hence deducing that certain other operations +are safe when port_is_valid() returned true is not legitimate. The +opportunities to abuse this may get widened by the change here +(depending on guest and host configuration), but will be taken care of +by the other XSA. + +This is XSA-338. + +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v5: New, split from larger patch. + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,8 +107,6 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- if ( p >= d->max_evtchns ) +- return 0; + return p < read_atomic(&d->valid_evtchns); + } + diff --git a/xsa339.patch b/xsa339.patch new file mode 100644 index 0000000..3311ae0 --- /dev/null +++ b/xsa339.patch @@ -0,0 +1,76 @@ +From: Andrew Cooper +Subject: x86/pv: Avoid double exception injection + +There is at least one path (SYSENTER with NT set, Xen converts to #GP) which +ends up injecting the #GP fault twice, first in compat_sysenter(), and then a +second time in compat_test_all_events(), due to the stale TBF_EXCEPTION left +in TRAPBOUNCE_flags. + +The guest kernel sees the second fault first, which is a kernel level #GP +pointing at the head of the #GP handler, and is therefore a userspace +trigger-able DoS. + +This particular bug has bitten us several times before, so rearrange +{compat_,}create_bounce_frame() to clobber TRAPBOUNCE on success, rather than +leaving this task to one area of code which isn't used uniformly. + +Other scenarios which might result in a double injection (e.g. two calls +directly to compat_create_bounce_frame) will now crash the guest, which is far +more obvious than letting the kernel run with corrupt state. + +This is XSA-339 + +Fixes: fdac9515607b ("x86: clear EFLAGS.NT in SYSENTER entry path") +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S +index c3e62f8734..73619f57ca 100644 +--- a/xen/arch/x86/x86_64/compat/entry.S ++++ b/xen/arch/x86/x86_64/compat/entry.S +@@ -78,7 +78,6 @@ compat_process_softirqs: + sti + .Lcompat_bounce_exception: + call compat_create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp compat_test_all_events + + ALIGN +@@ -352,7 +351,13 @@ __UNLIKELY_END(compat_bounce_null_selector) + movl %eax,UREGS_cs+8(%rsp) + movl TRAPBOUNCE_eip(%rdx),%eax + movl %eax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %ax, TRAPBOUNCE_cs(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret ++ + .section .fixup,"ax" + .Lfx13: + xorl %edi,%edi +diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S +index 1e880eb9f6..71a00e846b 100644 +--- a/xen/arch/x86/x86_64/entry.S ++++ b/xen/arch/x86/x86_64/entry.S +@@ -90,7 +90,6 @@ process_softirqs: + sti + .Lbounce_exception: + call create_bounce_frame +- movb $0, TRAPBOUNCE_flags(%rdx) + jmp test_all_events + + ALIGN +@@ -512,6 +511,11 @@ UNLIKELY_START(z, create_bounce_frame_bad_bounce_ip) + jmp asm_domain_crash_synchronous /* Does not return */ + __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) + movq %rax,UREGS_rip+8(%rsp) ++ ++ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ ++ xor %eax, %eax ++ mov %rax, TRAPBOUNCE_eip(%rdx) ++ mov %al, TRAPBOUNCE_flags(%rdx) + ret + + .pushsection .fixup, "ax", @progbits diff --git a/xsa340.patch b/xsa340.patch new file mode 100644 index 0000000..38d04da --- /dev/null +++ b/xsa340.patch @@ -0,0 +1,65 @@ +From: Julien Grall +Subject: xen/evtchn: Add missing barriers when accessing/allocating an event channel + +While the allocation of a bucket is always performed with the per-domain +lock, the bucket may be accessed without the lock taken (for instance, see +evtchn_send()). + +Instead such sites relies on port_is_valid() to return a non-zero value +when the port has a struct evtchn associated to it. The function will +mostly check whether the port is less than d->valid_evtchns as all the +buckets/event channels should be allocated up to that point. + +Unfortunately a compiler is free to re-order the assignment in +evtchn_allocate_port() so it would be possible to have d->valid_evtchns +updated before the new bucket has finish to allocate. + +Additionally on Arm, even if this was compiled "correctly", the +processor can still re-order the memory access. + +Add a write memory barrier in the allocation side and a read memory +barrier when the port is valid to prevent any re-ordering issue. + +This is XSA-340. + +Reported-by: Julien Grall +Signed-off-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -178,6 +178,13 @@ int evtchn_allocate_port(struct domain * + return -ENOMEM; + bucket_from_port(d, port) = chn; + ++ /* ++ * d->valid_evtchns is used to check whether the bucket can be ++ * accessed without the per-domain lock. Therefore, ++ * d->valid_evtchns should be seen *after* the new bucket has ++ * been setup. ++ */ ++ smp_wmb(); + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -107,7 +107,17 @@ void notify_via_xen_event_channel(struct + + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { +- return p < read_atomic(&d->valid_evtchns); ++ if ( p >= read_atomic(&d->valid_evtchns) ) ++ return false; ++ ++ /* ++ * The caller will usually access the event channel afterwards and ++ * may be done without taking the per-domain lock. The barrier is ++ * going in pair the smp_wmb() barrier in evtchn_allocate_port(). ++ */ ++ smp_rmb(); ++ ++ return true; + } + + static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p) diff --git a/xsa342-4.13.patch b/xsa342-4.13.patch new file mode 100644 index 0000000..334baf1 --- /dev/null +++ b/xsa342-4.13.patch @@ -0,0 +1,145 @@ +From: Jan Beulich +Subject: evtchn/x86: enforce correct upper limit for 32-bit guests + +The recording of d->max_evtchns in evtchn_2l_init(), in particular with +the limited set of callers of the function, is insufficient. Neither for +PV nor for HVM guests the bitness is known at domain_create() time, yet +the upper bound in 2-level mode depends upon guest bitness. Recording +too high a limit "allows" x86 32-bit domains to open not properly usable +event channels, management of which (inside Xen) would then result in +corruption of the shared info and vCPU info structures. + +Keep the upper limit dynamic for the 2-level case, introducing a helper +function to retrieve the effective limit. This helper is now supposed to +be private to the event channel code. The used in do_poll() and +domain_dump_evtchn_info() weren't consistent with port uses elsewhere +and hence get switched to port_is_valid(). + +Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to +the prior ABI limit, rather than all the way up to the new one. + +Finally a word on the change to do_poll(): Accessing ->max_evtchns +without holding a suitable lock was never safe, as it as well as +->evtchn_port_ops may change behind do_poll()'s back. Using +port_is_valid() instead widens some the window for potential abuse, +until we've dealt with the race altogether (see XSA-343). + +This is XSA-342. + +Reported-by: Julien Grall +Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtc + void evtchn_2l_init(struct domain *d) + { + d->evtchn_port_ops = &evtchn_port_ops_2l; +- d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + } + + /* +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct do + + int evtchn_allocate_port(struct domain *d, evtchn_port_t port) + { +- if ( port > d->max_evtchn_port || port >= d->max_evtchns ) ++ if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) + return -ENOSPC; + + if ( port_is_valid(d, port) ) +@@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(stru + + spin_lock(&d->event_lock); + +- for ( port = 1; port < d->max_evtchns; ++port ) ++ for ( port = 1; port_is_valid(d, port); ++port ) + { + const struct evtchn *chn; + char *ssid; + +- if ( !port_is_valid(d, port) ) +- continue; + chn = evtchn_from_port(d, port); + if ( chn->state == ECS_FREE ) + continue; +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -478,7 +478,7 @@ static void cleanup_event_array(struct d + d->evtchn_fifo = NULL; + } + +-static void setup_ports(struct domain *d) ++static void setup_ports(struct domain *d, unsigned int prev_evtchns) + { + unsigned int port; + +@@ -488,7 +488,7 @@ static void setup_ports(struct domain *d + * - save its pending state. + * - set default priority. + */ +- for ( port = 1; port < d->max_evtchns; port++ ) ++ for ( port = 1; port < prev_evtchns; port++ ) + { + struct evtchn *evtchn; + +@@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtc + if ( !d->evtchn_fifo ) + { + struct vcpu *vcb; ++ /* Latch the value before it changes during setup_event_array(). */ ++ unsigned int prev_evtchns = max_evtchns(d); + + for_each_vcpu ( d, vcb ) { + rc = setup_control_block(vcb); +@@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtc + goto error; + + d->evtchn_port_ops = &evtchn_port_ops_fifo; +- d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; +- setup_ports(d); ++ setup_ports(d, prev_evtchns); + } + else + rc = map_control_block(v, gfn, offset); +--- a/xen/common/schedule.c ++++ b/xen/common/schedule.c +@@ -1434,7 +1434,7 @@ static long do_poll(struct sched_poll *s + goto out; + + rc = -EINVAL; +- if ( port >= d->max_evtchns ) ++ if ( !port_is_valid(d, port) ) + goto out; + + rc = 0; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct + #define bucket_from_port(d, p) \ + ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) + ++static inline unsigned int max_evtchns(const struct domain *d) ++{ ++ return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS ++ : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); ++} ++ + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { + if ( p >= read_atomic(&d->valid_evtchns) ) +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -382,7 +382,6 @@ struct domain + /* Event channel information. */ + struct evtchn *evtchn; /* first bucket only */ + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ +- unsigned int max_evtchns; /* number supported by ABI */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ + spinlock_t event_lock; diff --git a/xsa343-1.patch b/xsa343-1.patch new file mode 100644 index 0000000..0abbc03 --- /dev/null +++ b/xsa343-1.patch @@ -0,0 +1,199 @@ +From: Jan Beulich +Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports + +While the function closes all ports, it does so without holding any +lock, and hence racing requests may be issued causing new ports to get +opened. This would have been problematic in particular if such a newly +opened port had a port number above the new implementation limit (i.e. +when switching from FIFO to 2-level) after the reset, as prior to +"evtchn: relax port_is_valid()" this could have led to e.g. +evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. + +Introduce a counter of active ports and check that it's (still) no +larger then the number of Xen internally used ones after obtaining the +necessary lock in evtchn_reset(). + +As to the access model of the new {active,xen}_evtchns fields - while +all writes get done using write_atomic(), reads ought to use +read_atomic() only when outside of a suitably locked region. + +Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have +a need to call check_free_port(). + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Reviewed-by: Stefano Stabellini +Reviewed-by: Julien Grall +--- +v7: Drop optimization from evtchn_reset(). +v6: Fix loop exit condition in evtchn_reset(). Use {read,write}_atomic() + also for xen_evtchns. +v5: Move increment in alloc_unbound_xen_event_channel() out of the inner + locked region. +v4: Account for Xen internal ports. +v3: Document intended access next to new struct field. +v2: Add comment to check_free_port(). Drop commented out calls. + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -188,6 +188,8 @@ int evtchn_allocate_port(struct domain * + write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); + } + ++ write_atomic(&d->active_evtchns, d->active_evtchns + 1); ++ + return 0; + } + +@@ -211,11 +213,26 @@ static int get_free_port(struct domain * + return -ENOSPC; + } + ++/* ++ * Check whether a port is still marked free, and if so update the domain ++ * counter accordingly. To be used on function exit paths. ++ */ ++static void check_free_port(struct domain *d, evtchn_port_t port) ++{ ++ if ( port_is_valid(d, port) && ++ evtchn_from_port(d, port)->state == ECS_FREE ) ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++} ++ + void evtchn_free(struct domain *d, struct evtchn *chn) + { + /* Clear pending event to avoid unexpected behavior on re-bind. */ + evtchn_port_clear_pending(d, chn); + ++ if ( consumer_is_xen(chn) ) ++ write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); ++ write_atomic(&d->active_evtchns, d->active_evtchns - 1); ++ + /* Reset binding to vcpu0 when the channel is freed. */ + chn->state = ECS_FREE; + chn->notify_vcpu_id = 0; +@@ -258,6 +275,7 @@ static long evtchn_alloc_unbound(evtchn_ + alloc->port = port; + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + rcu_unlock_domain(d); + +@@ -351,6 +369,7 @@ static long evtchn_bind_interdomain(evtc + bind->local_port = lport; + + out: ++ check_free_port(ld, lport); + spin_unlock(&ld->event_lock); + if ( ld != rd ) + spin_unlock(&rd->event_lock); +@@ -488,7 +507,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct domain *d = current->domain; + struct vcpu *v = d->vcpu[0]; + struct pirq *info; +- int port, pirq = bind->pirq; ++ int port = 0, pirq = bind->pirq; + long rc; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) +@@ -536,6 +555,7 @@ static long evtchn_bind_pirq(evtchn_bind + arch_evtchn_bind_pirq(d, pirq); + + out: ++ check_free_port(d, port); + spin_unlock(&d->event_lock); + + return rc; +@@ -1011,10 +1031,10 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +- + int evtchn_reset(struct domain *d) + { + unsigned int i; ++ int rc = 0; + + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; +@@ -1024,7 +1044,9 @@ int evtchn_reset(struct domain *d) + + spin_lock(&d->event_lock); + +- if ( d->evtchn_fifo ) ++ if ( d->active_evtchns > d->xen_evtchns ) ++ rc = -EAGAIN; ++ else if ( d->evtchn_fifo ) + { + /* Switching back to 2-level ABI. */ + evtchn_fifo_destroy(d); +@@ -1033,7 +1055,7 @@ int evtchn_reset(struct domain *d) + + spin_unlock(&d->event_lock); + +- return 0; ++ return rc; + } + + static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) +@@ -1219,10 +1241,9 @@ int alloc_unbound_xen_event_channel( + + spin_lock(&ld->event_lock); + +- rc = get_free_port(ld); ++ port = rc = get_free_port(ld); + if ( rc < 0 ) + goto out; +- port = rc; + chn = evtchn_from_port(ld, port); + + rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); +@@ -1238,7 +1259,10 @@ int alloc_unbound_xen_event_channel( + + spin_unlock(&chn->lock); + ++ write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); ++ + out: ++ check_free_port(ld, port); + spin_unlock(&ld->event_lock); + + return rc < 0 ? rc : port; +@@ -1314,6 +1338,7 @@ int evtchn_init(struct domain *d, unsign + return -EINVAL; + } + evtchn_from_port(d, 0)->state = ECS_RESERVED; ++ write_atomic(&d->active_evtchns, 0); + + #if MAX_VIRT_CPUS > BITS_PER_LONG + d->poll_mask = xzalloc_array(unsigned long, BITS_TO_LONGS(d->max_vcpus)); +@@ -1340,6 +1365,8 @@ void evtchn_destroy(struct domain *d) + for ( i = 0; port_is_valid(d, i); i++ ) + evtchn_close(d, i, 0); + ++ ASSERT(!d->active_evtchns); ++ + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -361,6 +361,16 @@ struct domain + struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ + unsigned int max_evtchn_port; /* max permitted port number */ + unsigned int valid_evtchns; /* number of allocated event channels */ ++ /* ++ * Number of in-use event channels. Writers should use write_atomic(). ++ * Readers need to use read_atomic() only when not holding event_lock. ++ */ ++ unsigned int active_evtchns; ++ /* ++ * Number of event channels used internally by Xen (not subject to ++ * EVTCHNOP_reset). Read/write access like for active_evtchns. ++ */ ++ unsigned int xen_evtchns; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; diff --git a/xsa343-2.patch b/xsa343-2.patch new file mode 100644 index 0000000..b8eb499 --- /dev/null +++ b/xsa343-2.patch @@ -0,0 +1,295 @@ +From: Jan Beulich +Subject: evtchn: convert per-channel lock to be IRQ-safe + +... in order for send_guest_{global,vcpu}_virq() to be able to make use +of it. + +This is part of XSA-343. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +v6: New. +--- +TBD: This is the "dumb" conversion variant. In a couple of cases the + slightly simpler spin_{,un}lock_irq() could apparently be used. + +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ + int port; + domid_t dom = alloc->dom; + long rc; ++ unsigned long flags; + + d = rcu_lock_domain_by_any_id(dom); + if ( d == NULL ) +@@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) + chn->u.unbound.remote_domid = current->domain->domain_id; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + alloc->port = port; + +@@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ + } + + +-static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) ++static unsigned long double_evtchn_lock(struct evtchn *lchn, ++ struct evtchn *rchn) + { +- if ( lchn < rchn ) ++ unsigned long flags; ++ ++ if ( lchn <= rchn ) + { +- spin_lock(&lchn->lock); +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); ++ if ( lchn != rchn ) ++ spin_lock(&rchn->lock); + } + else + { +- if ( lchn != rchn ) +- spin_lock(&rchn->lock); ++ spin_lock_irqsave(&rchn->lock, flags); + spin_lock(&lchn->lock); + } ++ ++ return flags; + } + +-static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) ++static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, ++ unsigned long flags) + { +- spin_unlock(&lchn->lock); + if ( lchn != rchn ) +- spin_unlock(&rchn->lock); ++ spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&rchn->lock, flags); + } + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) +@@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc + int lport, rport = bind->remote_port; + domid_t rdom = bind->remote_dom; + long rc; ++ unsigned long flags; + + if ( rdom == DOMID_SELF ) + rdom = current->domain->domain_id; +@@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc + if ( rc ) + goto out; + +- double_evtchn_lock(lchn, rchn); ++ flags = double_evtchn_lock(lchn, rchn); + + lchn->u.interdomain.remote_dom = rd; + lchn->u.interdomain.remote_port = rport; +@@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc + */ + evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); + +- double_evtchn_unlock(lchn, rchn); ++ double_evtchn_unlock(lchn, rchn, flags); + + bind->local_port = lport; + +@@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t + struct domain *d = current->domain; + int virq = bind->virq, vcpu = bind->vcpu; + int rc = 0; ++ unsigned long flags; + + if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) + return -EINVAL; +@@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_VIRQ; + chn->notify_vcpu_id = vcpu; + chn->u.virq = virq; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + v->virq_to_evtchn[virq] = bind->port = port; + +@@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ + struct domain *d = current->domain; + int port, vcpu = bind->vcpu; + long rc = 0; ++ unsigned long flags; + + if ( domain_vcpu(d, vcpu) == NULL ) + return -ENOENT; +@@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ + + chn = evtchn_from_port(d, port); + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_IPI; + chn->notify_vcpu_id = vcpu; + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind + struct pirq *info; + int port = 0, pirq = bind->pirq; + long rc; ++ unsigned long flags; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) + return -EINVAL; +@@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind + goto out; + } + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_PIRQ; + chn->u.pirq.irq = pirq; + link_pirq_port(port, chn, v); + evtchn_port_init(d, chn); + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + bind->port = port; + +@@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int + struct evtchn *chn1, *chn2; + int port2; + long rc = 0; ++ unsigned long flags; + + again: + spin_lock(&d1->event_lock); +@@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int + BUG_ON(chn2->state != ECS_INTERDOMAIN); + BUG_ON(chn2->u.interdomain.remote_dom != d1); + +- double_evtchn_lock(chn1, chn2); ++ flags = double_evtchn_lock(chn1, chn2); + + evtchn_free(d1, chn1); + + chn2->state = ECS_UNBOUND; + chn2->u.unbound.remote_domid = d1->domain_id; + +- double_evtchn_unlock(chn1, chn2); ++ double_evtchn_unlock(chn1, chn2, flags); + + goto out; + +@@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int + BUG(); + } + +- spin_lock(&chn1->lock); ++ spin_lock_irqsave(&chn1->lock, flags); + evtchn_free(d1, chn1); +- spin_unlock(&chn1->lock); ++ spin_unlock_irqrestore(&chn1->lock, flags); + + out: + if ( d2 != NULL ) +@@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig + struct evtchn *lchn, *rchn; + struct domain *rd; + int rport, ret = 0; ++ unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + return -EINVAL; + + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + /* Guest cannot send via a Xen-attached event channel. */ + if ( unlikely(consumer_is_xen(lchn)) ) +@@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig + } + + out: +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + + return ret; + } +@@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( + { + struct evtchn *chn; + int port, rc; ++ unsigned long flags; + + spin_lock(&ld->event_lock); + +@@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( + if ( rc ) + goto out; + +- spin_lock(&chn->lock); ++ spin_lock_irqsave(&chn->lock, flags); + + chn->state = ECS_UNBOUND; + chn->xen_consumer = get_xen_consumer(notification_fn); + chn->notify_vcpu_id = lvcpu; + chn->u.unbound.remote_domid = remote_domid; + +- spin_unlock(&chn->lock); ++ spin_unlock_irqrestore(&chn->lock, flags); + + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + +@@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct + { + struct evtchn *lchn, *rchn; + struct domain *rd; ++ unsigned long flags; + + ASSERT(port_is_valid(ld, lport)); + lchn = evtchn_from_port(ld, lport); + +- spin_lock(&lchn->lock); ++ spin_lock_irqsave(&lchn->lock, flags); + + if ( likely(lchn->state == ECS_INTERDOMAIN) ) + { +@@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct + evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); + } + +- spin_unlock(&lchn->lock); ++ spin_unlock_irqrestore(&lchn->lock, flags); + } + + void evtchn_check_pollers(struct domain *d, unsigned int port) diff --git a/xsa343-3.patch b/xsa343-3.patch new file mode 100644 index 0000000..e513e30 --- /dev/null +++ b/xsa343-3.patch @@ -0,0 +1,392 @@ +From: Jan Beulich +Subject: evtchn: address races with evtchn_reset() + +Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely +lock-less manner, as both may change by a racing evtchn_reset(). In the +common case, at least one of the domain's event lock or the per-channel +lock needs to be held. In the specific case of the inter-domain sending +by evtchn_send() and notify_via_xen_event_channel() holding the other +side's per-channel lock is sufficient, as the channel can't change state +without both per-channel locks held. Without such a channel changing +state, evtchn_reset() can't complete successfully. + +Lock-free accesses continue to be permitted for the shim (calling some +otherwise internal event channel functions), as this happens while the +domain is in effectively single-threaded mode. Special care also needs +taking for the shim's marking of in-use ports as ECS_RESERVED (allowing +use of such ports in the shim case is okay because switching into and +hence also out of FIFO mode is impossihble there). + +As a side effect, certain operations on Xen bound event channels which +were mistakenly permitted so far (e.g. unmask or poll) will be refused +now. + +This is part of XSA-343. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +--- +v9: Add arch_evtchn_is_special() to fix PV shim. +v8: Add BUILD_BUG_ON() in evtchn_usable(). +v7: Add locking related comment ahead of struct evtchn_port_ops. +v6: New. +--- +TBD: I've been considering to move some of the wrappers from xen/event.h + into event_channel.c (or even drop them altogether), when they + require external locking (e.g. evtchn_port_init() or + evtchn_port_set_priority()). Does anyone have a strong opinion + either way? + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2488,14 +2488,24 @@ static void dump_irqs(unsigned char key) + + for ( i = 0; i < action->nr_guests; ) + { ++ struct evtchn *evtchn; ++ unsigned int pending = 2, masked = 2; ++ + d = action->guest[i++]; + pirq = domain_irq_to_pirq(d, irq); + info = pirq_info(d, pirq); ++ evtchn = evtchn_from_port(d, info->evtchn); ++ local_irq_disable(); ++ if ( spin_trylock(&evtchn->lock) ) ++ { ++ pending = evtchn_is_pending(d, evtchn); ++ masked = evtchn_is_masked(d, evtchn); ++ spin_unlock(&evtchn->lock); ++ } ++ local_irq_enable(); + printk("d%d:%3d(%c%c%c)%c", +- d->domain_id, pirq, +- evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', +- evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', +- info->masked ? 'M' : '-', ++ d->domain_id, pirq, "-P?"[pending], ++ "-M?"[masked], info->masked ? 'M' : '-', + i < action->nr_guests ? ',' : '\n'); + } + } +--- a/xen/arch/x86/pv/shim.c ++++ b/xen/arch/x86/pv/shim.c +@@ -660,8 +660,11 @@ void pv_shim_inject_evtchn(unsigned int + if ( port_is_valid(guest, port) ) + { + struct evtchn *chn = evtchn_from_port(guest, port); ++ unsigned long flags; + ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + } + +--- a/xen/common/event_2l.c ++++ b/xen/common/event_2l.c +@@ -63,8 +63,10 @@ static void evtchn_2l_unmask(struct doma + } + } + +-static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +@@ -72,8 +74,10 @@ static bool evtchn_2l_is_pending(const s + guest_test_bit(d, port, &shared_info(d, evtchn_pending))); + } + +-static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) ++static bool evtchn_2l_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { ++ evtchn_port_t port = evtchn->port; + unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + + ASSERT(port < max_ports); +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -156,8 +156,9 @@ int evtchn_allocate_port(struct domain * + + if ( port_is_valid(d, port) ) + { +- if ( evtchn_from_port(d, port)->state != ECS_FREE || +- evtchn_port_is_busy(d, port) ) ++ const struct evtchn *chn = evtchn_from_port(d, port); ++ ++ if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) + return -EBUSY; + } + else +@@ -774,6 +775,7 @@ void send_guest_vcpu_virq(struct vcpu *v + unsigned long flags; + int port; + struct domain *d; ++ struct evtchn *chn; + + ASSERT(!virq_is_global(virq)); + +@@ -784,7 +786,10 @@ void send_guest_vcpu_virq(struct vcpu *v + goto out; + + d = v->domain; +- evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); ++ chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); ++ evtchn_port_set_pending(d, v->vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -813,7 +818,9 @@ void send_guest_global_virq(struct domai + goto out; + + chn = evtchn_from_port(d, port); ++ spin_lock(&chn->lock); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock(&chn->lock); + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -823,6 +830,7 @@ void send_guest_pirq(struct domain *d, c + { + int port; + struct evtchn *chn; ++ unsigned long flags; + + /* + * PV guests: It should not be possible to race with __evtchn_close(). The +@@ -837,7 +845,9 @@ void send_guest_pirq(struct domain *d, c + } + + chn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&chn->lock, flags); + evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ spin_unlock_irqrestore(&chn->lock, flags); + } + + static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; +@@ -1034,12 +1044,15 @@ int evtchn_unmask(unsigned int port) + { + struct domain *d = current->domain; + struct evtchn *evtchn; ++ unsigned long flags; + + if ( unlikely(!port_is_valid(d, port)) ) + return -EINVAL; + + evtchn = evtchn_from_port(d, port); ++ spin_lock_irqsave(&evtchn->lock, flags); + evtchn_port_unmask(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); + + return 0; + } +@@ -1449,8 +1462,8 @@ static void domain_dump_evtchn_info(stru + + printk(" %4u [%d/%d/", + port, +- evtchn_port_is_pending(d, port), +- evtchn_port_is_masked(d, port)); ++ evtchn_is_pending(d, chn), ++ evtchn_is_masked(d, chn)); + evtchn_port_print_state(d, chn); + printk("]: s=%d n=%d x=%d", + chn->state, chn->notify_vcpu_id, chn->xen_consumer); +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -296,23 +296,26 @@ static void evtchn_fifo_unmask(struct do + evtchn_fifo_set_pending(v, evtchn); + } + +-static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) ++static bool evtchn_fifo_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); + } + +-static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); + } + +-static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) ++static bool_t evtchn_fifo_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { +- const event_word_t *word = evtchn_fifo_word_from_port(d, port); ++ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); + + return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); + } +--- a/xen/include/asm-x86/event.h ++++ b/xen/include/asm-x86/event.h +@@ -47,4 +47,10 @@ static inline bool arch_virq_is_global(u + return true; + } + ++#ifdef CONFIG_PV_SHIM ++# include ++# define arch_evtchn_is_special(chn) \ ++ (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) ++#endif ++ + #endif +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -133,6 +133,24 @@ static inline struct evtchn *evtchn_from + return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); + } + ++/* ++ * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be ++ * taken care of separately where used for Xen's internal purposes. ++ */ ++static bool evtchn_usable(const struct evtchn *evtchn) ++{ ++ if ( evtchn->xen_consumer ) ++ return false; ++ ++#ifdef arch_evtchn_is_special ++ if ( arch_evtchn_is_special(evtchn) ) ++ return true; ++#endif ++ ++ BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); ++ return evtchn->state > ECS_RESERVED; ++} ++ + /* Wait on a Xen-attached event channel. */ + #define wait_on_xen_event_channel(port, condition) \ + do { \ +@@ -165,19 +183,24 @@ int evtchn_reset(struct domain *d); + + /* + * Low-level event channel port ops. ++ * ++ * All hooks have to be called with a lock held which prevents the channel ++ * from changing state. This may be the domain event lock, the per-channel ++ * lock, or in the case of sending interdomain events also the other side's ++ * per-channel lock. Exceptions apply in certain cases for the PV shim. + */ + struct evtchn_port_ops { + void (*init)(struct domain *d, struct evtchn *evtchn); + void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); + void (*clear_pending)(struct domain *d, struct evtchn *evtchn); + void (*unmask)(struct domain *d, struct evtchn *evtchn); +- bool (*is_pending)(const struct domain *d, evtchn_port_t port); +- bool (*is_masked)(const struct domain *d, evtchn_port_t port); ++ bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); ++ bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); + /* + * Is the port unavailable because it's still being cleaned up + * after being closed? + */ +- bool (*is_busy)(const struct domain *d, evtchn_port_t port); ++ bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); + int (*set_priority)(struct domain *d, struct evtchn *evtchn, + unsigned int priority); + void (*print_state)(struct domain *d, const struct evtchn *evtchn); +@@ -193,38 +216,67 @@ static inline void evtchn_port_set_pendi + unsigned int vcpu_id, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); + } + + static inline void evtchn_port_clear_pending(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->clear_pending(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->clear_pending(d, evtchn); + } + + static inline void evtchn_port_unmask(struct domain *d, + struct evtchn *evtchn) + { +- d->evtchn_port_ops->unmask(d, evtchn); ++ if ( evtchn_usable(evtchn) ) ++ d->evtchn_port_ops->unmask(d, evtchn); + } + +-static inline bool evtchn_port_is_pending(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_pending(const struct domain *d, ++ const struct evtchn *evtchn) + { +- return d->evtchn_port_ops->is_pending(d, port); ++ return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); + } + +-static inline bool evtchn_port_is_masked(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) + { +- return d->evtchn_port_ops->is_masked(d, port); ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_pending(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; ++} ++ ++static inline bool evtchn_is_masked(const struct domain *d, ++ const struct evtchn *evtchn) ++{ ++ return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); ++} ++ ++static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) ++{ ++ struct evtchn *evtchn = evtchn_from_port(d, port); ++ bool rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&evtchn->lock, flags); ++ rc = evtchn_is_masked(d, evtchn); ++ spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ return rc; + } + +-static inline bool evtchn_port_is_busy(const struct domain *d, +- evtchn_port_t port) ++static inline bool evtchn_is_busy(const struct domain *d, ++ const struct evtchn *evtchn) + { + return d->evtchn_port_ops->is_busy && +- d->evtchn_port_ops->is_busy(d, port); ++ d->evtchn_port_ops->is_busy(d, evtchn); + } + + static inline int evtchn_port_set_priority(struct domain *d, +@@ -233,6 +285,8 @@ static inline int evtchn_port_set_priori + { + if ( !d->evtchn_port_ops->set_priority ) + return -ENOSYS; ++ if ( !evtchn_usable(evtchn) ) ++ return -EACCES; + return d->evtchn_port_ops->set_priority(d, evtchn, priority); + } + diff --git a/xsa344-4.13-1.patch b/xsa344-4.13-1.patch new file mode 100644 index 0000000..d8e9b3f --- /dev/null +++ b/xsa344-4.13-1.patch @@ -0,0 +1,130 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_destroy() + +Especially closing of fully established interdomain channels can take +quite some time, due to the locking involved. Therefore we shouldn't +assume we can clean up still active ports all in one go. Besides adding +the necessary preemption check, also avoid pointlessly starting from +(or now really ending at) 0; 1 is the lowest numbered port which may +need closing. + +Since we're now reducing ->valid_evtchns, free_xen_event_channel(), +and (at least to be on the safe side) notify_via_xen_event_channel() +need to cope with attempts to close / unbind from / send through already +closed (and no longer valid, as per port_is_valid()) ports. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -770,12 +770,14 @@ int domain_kill(struct domain *d) + return domain_kill(d); + d->is_dying = DOMDYING_dying; + argo_destroy(d); +- evtchn_destroy(d); + gnttab_release_mappings(d); + vnuma_destroy(d->vnuma); + domain_set_outstanding_pages(d, 0); + /* fallthrough */ + case DOMDYING_dying: ++ rc = evtchn_destroy(d); ++ if ( rc ) ++ break; + rc = domain_relinquish_resources(d); + if ( rc != 0 ) + break; +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1297,7 +1297,16 @@ int alloc_unbound_xen_event_channel( + + void free_xen_event_channel(struct domain *d, int port) + { +- BUG_ON(!port_is_valid(d, port)); ++ if ( !port_is_valid(d, port) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ BUG_ON(!d->is_dying); ++ return; ++ } + + evtchn_close(d, port, 0); + } +@@ -1309,7 +1318,17 @@ void notify_via_xen_event_channel(struct + struct domain *rd; + unsigned long flags; + +- ASSERT(port_is_valid(ld, lport)); ++ if ( !port_is_valid(ld, lport) ) ++ { ++ /* ++ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing ++ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). ++ */ ++ smp_rmb(); ++ ASSERT(ld->is_dying); ++ return; ++ } ++ + lchn = evtchn_from_port(ld, lport); + + spin_lock_irqsave(&lchn->lock, flags); +@@ -1380,8 +1399,7 @@ int evtchn_init(struct domain *d, unsign + return 0; + } + +- +-void evtchn_destroy(struct domain *d) ++int evtchn_destroy(struct domain *d) + { + unsigned int i; + +@@ -1390,14 +1408,29 @@ void evtchn_destroy(struct domain *d) + spin_barrier(&d->event_lock); + + /* Close all existing event channels. */ +- for ( i = 0; port_is_valid(d, i); i++ ) ++ for ( i = d->valid_evtchns; --i; ) ++ { + evtchn_close(d, i, 0); + ++ /* ++ * Avoid preempting when called from domain_create()'s error path, ++ * and don't check too often (choice of frequency is arbitrary). ++ */ ++ if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead && ++ hypercall_preempt_check() ) ++ { ++ write_atomic(&d->valid_evtchns, i); ++ return -ERESTART; ++ } ++ } ++ + ASSERT(!d->active_evtchns); + + clear_global_virq_handlers(d); + + evtchn_fifo_destroy(d); ++ ++ return 0; + } + + +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -136,7 +136,7 @@ struct evtchn + } __attribute__((aligned(64))); + + int evtchn_init(struct domain *d, unsigned int max_port); +-void evtchn_destroy(struct domain *d); /* from domain_kill */ ++int evtchn_destroy(struct domain *d); /* from domain_kill */ + void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */ + + struct waitqueue_vcpu; diff --git a/xsa344-4.13-2.patch b/xsa344-4.13-2.patch new file mode 100644 index 0000000..3f03394 --- /dev/null +++ b/xsa344-4.13-2.patch @@ -0,0 +1,203 @@ +From: Jan Beulich +Subject: evtchn: arrange for preemption in evtchn_reset() + +Like for evtchn_destroy() looping over all possible event channels to +close them can take a significant amount of time. Unlike done there, we +can't alter domain properties (i.e. d->valid_evtchns) here. Borrow, in a +lightweight form, the paging domctl continuation concept, redirecting +the continuations to different sub-ops. Just like there this is to be +able to allow for predictable overall results of the involved sub-ops: +Racing requests should either complete or be refused. + +Note that a domain can't interfere with an already started (by a remote +domain) reset, due to being paused. It can prevent a remote reset from +happening by leaving a reset unfinished, but that's only going to affect +itself. + +This is part of XSA-344. + +Signed-off-by: Jan Beulich +Acked-by: Julien Grall +Reviewed-by: Stefano Stabellini + +--- a/xen/common/domain.c ++++ b/xen/common/domain.c +@@ -1214,7 +1214,7 @@ void domain_unpause_except_self(struct d + domain_unpause(d); + } + +-int domain_soft_reset(struct domain *d) ++int domain_soft_reset(struct domain *d, bool resuming) + { + struct vcpu *v; + int rc; +@@ -1228,7 +1228,7 @@ int domain_soft_reset(struct domain *d) + } + spin_unlock(&d->shutdown_lock); + +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, resuming); + if ( rc ) + return rc; + +--- a/xen/common/domctl.c ++++ b/xen/common/domctl.c +@@ -572,12 +572,22 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe + } + + case XEN_DOMCTL_soft_reset: ++ case XEN_DOMCTL_soft_reset_cont: + if ( d == current->domain ) /* no domain_pause() */ + { + ret = -EINVAL; + break; + } +- ret = domain_soft_reset(d); ++ ret = domain_soft_reset(d, op->cmd == XEN_DOMCTL_soft_reset_cont); ++ if ( ret == -ERESTART ) ++ { ++ op->cmd = XEN_DOMCTL_soft_reset_cont; ++ if ( !__copy_field_to_guest(u_domctl, op, cmd) ) ++ ret = hypercall_create_continuation(__HYPERVISOR_domctl, ++ "h", u_domctl); ++ else ++ ret = -EFAULT; ++ } + break; + + case XEN_DOMCTL_destroydomain: +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -1057,7 +1057,7 @@ int evtchn_unmask(unsigned int port) + return 0; + } + +-int evtchn_reset(struct domain *d) ++int evtchn_reset(struct domain *d, bool resuming) + { + unsigned int i; + int rc = 0; +@@ -1065,11 +1065,40 @@ int evtchn_reset(struct domain *d) + if ( d != current->domain && !d->controller_pause_count ) + return -EINVAL; + +- for ( i = 0; port_is_valid(d, i); i++ ) ++ spin_lock(&d->event_lock); ++ ++ /* ++ * If we are resuming, then start where we stopped. Otherwise, check ++ * that a reset operation is not already in progress, and if none is, ++ * record that this is now the case. ++ */ ++ i = resuming ? d->next_evtchn : !d->next_evtchn; ++ if ( i > d->next_evtchn ) ++ d->next_evtchn = i; ++ ++ spin_unlock(&d->event_lock); ++ ++ if ( !i ) ++ return -EBUSY; ++ ++ for ( ; port_is_valid(d, i); i++ ) ++ { + evtchn_close(d, i, 1); + ++ /* NB: Choice of frequency is arbitrary. */ ++ if ( !(i & 0x3f) && hypercall_preempt_check() ) ++ { ++ spin_lock(&d->event_lock); ++ d->next_evtchn = i; ++ spin_unlock(&d->event_lock); ++ return -ERESTART; ++ } ++ } ++ + spin_lock(&d->event_lock); + ++ d->next_evtchn = 0; ++ + if ( d->active_evtchns > d->xen_evtchns ) + rc = -EAGAIN; + else if ( d->evtchn_fifo ) +@@ -1204,7 +1233,8 @@ long do_event_channel_op(int cmd, XEN_GU + break; + } + +- case EVTCHNOP_reset: { ++ case EVTCHNOP_reset: ++ case EVTCHNOP_reset_cont: { + struct evtchn_reset reset; + struct domain *d; + +@@ -1217,9 +1247,13 @@ long do_event_channel_op(int cmd, XEN_GU + + rc = xsm_evtchn_reset(XSM_TARGET, current->domain, d); + if ( !rc ) +- rc = evtchn_reset(d); ++ rc = evtchn_reset(d, cmd == EVTCHNOP_reset_cont); + + rcu_unlock_domain(d); ++ ++ if ( rc == -ERESTART ) ++ rc = hypercall_create_continuation(__HYPERVISOR_event_channel_op, ++ "ih", EVTCHNOP_reset_cont, arg); + break; + } + +--- a/xen/include/public/domctl.h ++++ b/xen/include/public/domctl.h +@@ -1152,7 +1152,10 @@ struct xen_domctl { + #define XEN_DOMCTL_iomem_permission 20 + #define XEN_DOMCTL_ioport_permission 21 + #define XEN_DOMCTL_hypercall_init 22 +-#define XEN_DOMCTL_arch_setup 23 /* Obsolete IA64 only */ ++#ifdef __XEN__ ++/* #define XEN_DOMCTL_arch_setup 23 Obsolete IA64 only */ ++#define XEN_DOMCTL_soft_reset_cont 23 ++#endif + #define XEN_DOMCTL_settimeoffset 24 + #define XEN_DOMCTL_getvcpuaffinity 25 + #define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ +--- a/xen/include/public/event_channel.h ++++ b/xen/include/public/event_channel.h +@@ -74,6 +74,9 @@ + #define EVTCHNOP_init_control 11 + #define EVTCHNOP_expand_array 12 + #define EVTCHNOP_set_priority 13 ++#ifdef __XEN__ ++#define EVTCHNOP_reset_cont 14 ++#endif + /* ` } */ + + typedef uint32_t evtchn_port_t; +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -171,7 +171,7 @@ void evtchn_check_pollers(struct domain + void evtchn_2l_init(struct domain *d); + + /* Close all event channels and reset to 2-level ABI. */ +-int evtchn_reset(struct domain *d); ++int evtchn_reset(struct domain *d, bool resuming); + + /* + * Low-level event channel port ops. +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -394,6 +394,8 @@ struct domain + * EVTCHNOP_reset). Read/write access like for active_evtchns. + */ + unsigned int xen_evtchns; ++ /* Port to resume from in evtchn_reset(), when in a continuation. */ ++ unsigned int next_evtchn; + spinlock_t event_lock; + const struct evtchn_port_ops *evtchn_port_ops; + struct evtchn_fifo_domain *evtchn_fifo; +@@ -663,7 +665,7 @@ int domain_shutdown(struct domain *d, u8 + void domain_resume(struct domain *d); + void domain_pause_for_debugger(void); + +-int domain_soft_reset(struct domain *d); ++int domain_soft_reset(struct domain *d, bool resuming); + + int vcpu_start_shutdown_deferral(struct vcpu *v); + void vcpu_end_shutdown_deferral(struct vcpu *v); From 298b801f9a76081d244dfc5ca7aad053f0970132 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 20 Oct 2020 20:13:39 +0100 Subject: [PATCH 03/15] 3 security updates x86: Race condition in Xen mapping code [XSA-345] undue deferral of IOMMU TLB flushes [XSA-346] unsafe AMD IOMMU page table updates [XSA-347] --- xen.spec | 23 +- ...map_pages_to_xen-to-have-only-a-sing.patch | 94 +++++++ ...modify_xen_mappings-to-have-one-exit.patch | 68 +++++ ...ome-races-in-hypervisor-mapping-upda.patch | 249 ++++++++++++++++++ xsa346-4.13-1.patch | 50 ++++ xsa346-4.13-2.patch | 204 ++++++++++++++ xsa347-4.13-1.patch | 149 +++++++++++ xsa347-4.13-2.patch | 72 +++++ xsa347-4.13-3.patch | 59 +++++ 9 files changed, 967 insertions(+), 1 deletion(-) create mode 100644 xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch create mode 100644 xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch create mode 100644 xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch create mode 100644 xsa346-4.13-1.patch create mode 100644 xsa346-4.13-2.patch create mode 100644 xsa347-4.13-1.patch create mode 100644 xsa347-4.13-2.patch create mode 100644 xsa347-4.13-3.patch diff --git a/xen.spec b/xen.spec index 15a6b19..ef97c0a 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.1 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -144,6 +144,14 @@ Patch72: xsa343-2.patch Patch73: xsa343-3.patch Patch74: xsa344-4.13-1.patch Patch75: xsa344-4.13-2.patch +Patch76: xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch +Patch77: xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch +Patch78: xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch +Patch79: xsa346-4.13-1.patch +Patch80: xsa346-4.13-2.patch +Patch81: xsa347-4.13-1.patch +Patch82: xsa347-4.13-2.patch +Patch83: xsa347-4.13-3.patch %if %build_qemutrad @@ -379,6 +387,14 @@ manage Xen virtual machines. %patch73 -p1 %patch74 -p1 %patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -972,6 +988,11 @@ fi %endif %changelog +* Tue Oct 20 2020 Michael Young - 4.13.1-7 +- x86: Race condition in Xen mapping code [XSA-345] +- undue deferral of IOMMU TLB flushes [XSA-346] +- unsafe AMD IOMMU page table updates [XSA-347] + * Tue Sep 22 2020 Michael Young - 4.13.1-6 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, CVE-2020-25602] (#1881619) diff --git a/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch b/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch new file mode 100644 index 0000000..d325385 --- /dev/null +++ b/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch @@ -0,0 +1,94 @@ +From b3e0d4e37b7902533a463812374947d4d6d2e463 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:41 +0000 +Subject: [PATCH 1/3] x86/mm: Refactor map_pages_to_xen to have only a single + exit path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Acked-by: Jan Beulich +--- + xen/arch/x86/mm.c | 17 +++++++++++------ + 1 file changed, 11 insertions(+), 6 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 30dffb68e8..133a393875 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5187,6 +5187,7 @@ int map_pages_to_xen( + l2_pgentry_t *pl2e, ol2e; + l1_pgentry_t *pl1e, ol1e; + unsigned int i; ++ int rc = -ENOMEM; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5207,7 +5208,8 @@ int map_pages_to_xen( + l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); + + if ( !pl3e ) +- return -ENOMEM; ++ goto out; ++ + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5295,7 +5297,7 @@ int map_pages_to_xen( + + pl2e = alloc_xen_pagetable(); + if ( pl2e == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(pl2e + i, +@@ -5324,7 +5326,7 @@ int map_pages_to_xen( + + pl2e = virt_to_xen_l2e(virt); + if ( !pl2e ) +- return -ENOMEM; ++ goto out; + + if ( ((((virt >> PAGE_SHIFT) | mfn_x(mfn)) & + ((1u << PAGETABLE_ORDER) - 1)) == 0) && +@@ -5367,7 +5369,7 @@ int map_pages_to_xen( + { + pl1e = virt_to_xen_l1e(virt); + if ( pl1e == NULL ) +- return -ENOMEM; ++ goto out; + } + else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) + { +@@ -5394,7 +5396,7 @@ int map_pages_to_xen( + + pl1e = alloc_xen_pagetable(); + if ( pl1e == NULL ) +- return -ENOMEM; ++ goto out; + + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&pl1e[i], +@@ -5538,7 +5540,10 @@ int map_pages_to_xen( + + #undef flush_flags + +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + int populate_pt_range(unsigned long virt, unsigned long nr_mfns) +-- +2.25.1 + diff --git a/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch b/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch new file mode 100644 index 0000000..836bed6 --- /dev/null +++ b/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch @@ -0,0 +1,68 @@ +From 9f6f35b833d295acaaa2d8ff8cf309bf688cfd50 Mon Sep 17 00:00:00 2001 +From: Wei Liu +Date: Sat, 11 Jan 2020 21:57:42 +0000 +Subject: [PATCH 2/3] x86/mm: Refactor modify_xen_mappings to have one exit + path + +We will soon need to perform clean-ups before returning. + +No functional change. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Wei Liu +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Acked-by: Jan Beulich +--- + xen/arch/x86/mm.c | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 133a393875..af726d3274 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -5570,6 +5570,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + l1_pgentry_t *pl1e; + unsigned int i; + unsigned long v = s; ++ int rc = -ENOMEM; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) +@@ -5611,7 +5612,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PAGE1GB: shatter the superpage and fall through. */ + pl2e = alloc_xen_pagetable(); + if ( !pl2e ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) + l2e_write(pl2e + i, + l2e_from_pfn(l3e_get_pfn(*pl3e) + +@@ -5666,7 +5668,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + /* PSE: shatter the superpage and try again. */ + pl1e = alloc_xen_pagetable(); + if ( !pl1e ) +- return -ENOMEM; ++ goto out; ++ + for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) + l1e_write(&pl1e[i], + l1e_from_pfn(l2e_get_pfn(*pl2e) + i, +@@ -5795,7 +5798,10 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + flush_area(NULL, FLUSH_TLB_GLOBAL); + + #undef FLAGS_MASK +- return 0; ++ rc = 0; ++ ++ out: ++ return rc; + } + + #undef flush_area +-- +2.25.1 + diff --git a/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch b/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch new file mode 100644 index 0000000..db40741 --- /dev/null +++ b/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch @@ -0,0 +1,249 @@ +From 0ff9a8453dc47cd47eee9659d5916afb5094e871 Mon Sep 17 00:00:00 2001 +From: Hongyan Xia +Date: Sat, 11 Jan 2020 21:57:43 +0000 +Subject: [PATCH 3/3] x86/mm: Prevent some races in hypervisor mapping updates + +map_pages_to_xen will attempt to coalesce mappings into 2MiB and 1GiB +superpages if possible, to maximize TLB efficiency. This means both +replacing superpage entries with smaller entries, and replacing +smaller entries with superpages. + +Unfortunately, while some potential races are handled correctly, +others are not. These include: + +1. When one processor modifies a sub-superpage mapping while another +processor replaces the entire range with a superpage. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and +B. + +* A walks the pagetables, get a pointer to L2. +* B replaces L3[N] with a 1GiB mapping. +* B Frees L2 +* A writes L2[M] # + +This is race exacerbated by the fact that virt_to_xen_l[21]e doesn't +handle higher-level superpages properly: If you call virt_xen_to_l2e +on a virtual address within an L3 superpage, you'll either hit a BUG() +(most likely), or get a pointer into the middle of a data page; same +with virt_xen_to_l1 on a virtual address within either an L3 or L2 +superpage. + +So take the following example: + +* A reads pl3e and discovers it to point to an L2. +* B replaces L3[N] with a 1GiB mapping +* A calls virt_to_xen_l2e() and hits the BUG_ON() # + +2. When two processors simultaneously try to replace a sub-superpage +mapping with a superpage mapping. + +Take the following example: + +Suppose L3[N] points to L2. And suppose we have two processors, A and B, +both trying to replace L3[N] with a superpage. + +* A walks the pagetables, get a pointer to pl3e, and takes a copy ol3e pointing to L2. +* B walks the pagetables, gets a pointre to pl3e, and takes a copy ol3e pointing to L2. +* A writes the new value into L3[N] +* B writes the new value into L3[N] +* A recursively frees all the L1's under L2, then frees L2 +* B recursively double-frees all the L1's under L2, then double-frees L2 # + +Fix this by grabbing a lock for the entirety of the mapping update +operation. + +Rather than grabbing map_pgdir_lock for the entire operation, however, +repurpose the PGT_locked bit from L3's page->type_info as a lock. +This means that rather than locking the entire address space, we +"only" lock a single 512GiB chunk of hypervisor address space at a +time. + +There was a proposal for a lock-and-reverify approach, where we walk +the pagetables to the point where we decide what to do; then grab the +map_pgdir_lock, re-verify the information we collected without the +lock, and finally make the change (starting over again if anything had +changed). Without being able to guarantee that the L2 table wasn't +freed, however, that means every read would need to be considered +potentially unsafe. Thinking carefully about that is probably +something that wants to be done on public, not under time pressure. + +This is part of XSA-345. + +Reported-by: Hongyan Xia +Signed-off-by: Hongyan Xia +Signed-off-by: George Dunlap +Reviewed-by: Jan Beulich +--- + xen/arch/x86/mm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 89 insertions(+), 3 deletions(-) + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index af726d3274..d6a0761f43 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -2167,6 +2167,50 @@ void page_unlock(struct page_info *page) + current_locked_page_set(NULL); + } + ++/* ++ * L3 table locks: ++ * ++ * Used for serialization in map_pages_to_xen() and modify_xen_mappings(). ++ * ++ * For Xen PT pages, the page->u.inuse.type_info is unused and it is safe to ++ * reuse the PGT_locked flag. This lock is taken only when we move down to L3 ++ * tables and below, since L4 (and above, for 5-level paging) is still globally ++ * protected by map_pgdir_lock. ++ * ++ * PV MMU update hypercalls call map_pages_to_xen while holding a page's page_lock(). ++ * This has two implications: ++ * - We cannot reuse reuse current_locked_page_* for debugging ++ * - To avoid the chance of deadlock, even for different pages, we ++ * must never grab page_lock() after grabbing l3t_lock(). This ++ * includes any page_lock()-based locks, such as ++ * mem_sharing_page_lock(). ++ * ++ * Also note that we grab the map_pgdir_lock while holding the ++ * l3t_lock(), so to avoid deadlock we must avoid grabbing them in ++ * reverse order. ++ */ ++static void l3t_lock(struct page_info *page) ++{ ++ unsigned long x, nx; ++ ++ do { ++ while ( (x = page->u.inuse.type_info) & PGT_locked ) ++ cpu_relax(); ++ nx = x | PGT_locked; ++ } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); ++} ++ ++static void l3t_unlock(struct page_info *page) ++{ ++ unsigned long x, nx, y = page->u.inuse.type_info; ++ ++ do { ++ x = y; ++ BUG_ON(!(x & PGT_locked)); ++ nx = x & ~PGT_locked; ++ } while ( (y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x ); ++} ++ + #ifdef CONFIG_PV + /* + * PTE flags that a guest may change without re-validating the PTE. +@@ -5177,6 +5221,23 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) + flush_area_local((const void *)v, f) : \ + flush_area_all((const void *)v, f)) + ++#define L3T_INIT(page) (page) = ZERO_BLOCK_PTR ++ ++#define L3T_LOCK(page) \ ++ do { \ ++ if ( locking ) \ ++ l3t_lock(page); \ ++ } while ( false ) ++ ++#define L3T_UNLOCK(page) \ ++ do { \ ++ if ( locking && (page) != ZERO_BLOCK_PTR ) \ ++ { \ ++ l3t_unlock(page); \ ++ (page) = ZERO_BLOCK_PTR; \ ++ } \ ++ } while ( false ) ++ + int map_pages_to_xen( + unsigned long virt, + mfn_t mfn, +@@ -5188,6 +5249,7 @@ int map_pages_to_xen( + l1_pgentry_t *pl1e, ol1e; + unsigned int i; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + #define flush_flags(oldf) do { \ + unsigned int o_ = (oldf); \ +@@ -5203,13 +5265,20 @@ int map_pages_to_xen( + } \ + } while (0) + ++ L3T_INIT(current_l3page); ++ + while ( nr_mfns != 0 ) + { +- l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); ++ l3_pgentry_t *pl3e, ol3e; + ++ L3T_UNLOCK(current_l3page); ++ ++ pl3e = virt_to_xen_l3e(virt); + if ( !pl3e ) + goto out; + ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); + ol3e = *pl3e; + + if ( cpu_has_page1gb && +@@ -5543,6 +5612,7 @@ int map_pages_to_xen( + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + +@@ -5571,6 +5641,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + unsigned int i; + unsigned long v = s; + int rc = -ENOMEM; ++ struct page_info *current_l3page; + + /* Set of valid PTE bits which may be altered. */ + #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) +@@ -5579,11 +5650,22 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + ASSERT(IS_ALIGNED(s, PAGE_SIZE)); + ASSERT(IS_ALIGNED(e, PAGE_SIZE)); + ++ L3T_INIT(current_l3page); ++ + while ( v < e ) + { +- l3_pgentry_t *pl3e = virt_to_xen_l3e(v); ++ l3_pgentry_t *pl3e; ++ ++ L3T_UNLOCK(current_l3page); + +- if ( !pl3e || !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) ++ pl3e = virt_to_xen_l3e(v); ++ if ( !pl3e ) ++ goto out; ++ ++ current_l3page = virt_to_page(pl3e); ++ L3T_LOCK(current_l3page); ++ ++ if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) + { + /* Confirm the caller isn't trying to create new mappings. */ + ASSERT(!(nf & _PAGE_PRESENT)); +@@ -5801,9 +5883,13 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) + rc = 0; + + out: ++ L3T_UNLOCK(current_l3page); + return rc; + } + ++#undef L3T_LOCK ++#undef L3T_UNLOCK ++ + #undef flush_area + + int destroy_xen_mappings(unsigned long s, unsigned long e) +-- +2.25.1 + diff --git a/xsa346-4.13-1.patch b/xsa346-4.13-1.patch new file mode 100644 index 0000000..a32e658 --- /dev/null +++ b/xsa346-4.13-1.patch @@ -0,0 +1,50 @@ +From: Jan Beulich +Subject: IOMMU: suppress "iommu_dont_flush_iotlb" when about to free a page + +Deferring flushes to a single, wide range one - as is done when +handling XENMAPSPACE_gmfn_range - is okay only as long as +pages don't get freed ahead of the eventual flush. While the only +function setting the flag (xenmem_add_to_physmap()) suggests by its name +that it's only mapping new entries, in reality the way +xenmem_add_to_physmap_one() works means an unmap would happen not only +for the page being moved (but not freed) but, if the destination GFN is +populated, also for the page being displaced from that GFN. Collapsing +the two flushes for this GFN into just one (end even more so deferring +it to a batched invocation) is not correct. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant +Acked-by: Julien Grall + +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -292,6 +292,7 @@ int guest_remove_page(struct domain *d, + p2m_type_t p2mt; + #endif + mfn_t mfn; ++ bool *dont_flush_p, dont_flush; + int rc; + + #ifdef CONFIG_X86 +@@ -378,8 +379,18 @@ int guest_remove_page(struct domain *d, + return -ENXIO; + } + ++ /* ++ * Since we're likely to free the page below, we need to suspend ++ * xenmem_add_to_physmap()'s suppressing of IOMMU TLB flushes. ++ */ ++ dont_flush_p = &this_cpu(iommu_dont_flush_iotlb); ++ dont_flush = *dont_flush_p; ++ *dont_flush_p = false; ++ + rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0); + ++ *dont_flush_p = dont_flush; ++ + /* + * With the lack of an IOMMU on some platforms, domains with DMA-capable + * device must retrieve the same pfn when the hypercall populate_physmap diff --git a/xsa346-4.13-2.patch b/xsa346-4.13-2.patch new file mode 100644 index 0000000..6371b5c --- /dev/null +++ b/xsa346-4.13-2.patch @@ -0,0 +1,204 @@ +From: Jan Beulich +Subject: IOMMU: hold page ref until after deferred TLB flush + +When moving around a page via XENMAPSPACE_gmfn_range, deferring the TLB +flush for the "from" GFN range requires that the page remains allocated +to the guest until the TLB flush has actually occurred. Otherwise a +parallel hypercall to remove the page would only flush the TLB for the +GFN it has been moved to, but not the one is was mapped at originally. + +This is part of XSA-346. + +Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/arch/arm/mm.c ++++ b/xen/arch/arm/mm.c +@@ -1407,7 +1407,7 @@ void share_xen_page_with_guest(struct pa + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gfn) + { +@@ -1480,10 +1480,6 @@ int xenmem_add_to_physmap_one( + break; + } + case XENMAPSPACE_dev_mmio: +- /* extra should be 0. Reserved for future use. */ +- if ( extra.res0 ) +- return -EOPNOTSUPP; +- + rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); + return rc; + +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -4617,7 +4617,7 @@ static int handle_iomem_range(unsigned l + int xenmem_add_to_physmap_one( + struct domain *d, + unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, + gfn_t gpfn) + { +@@ -4701,9 +4701,20 @@ int xenmem_add_to_physmap_one( + rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); + + put_both: +- /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ ++ /* ++ * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. ++ * We also may need to transfer ownership of the page reference to our ++ * caller. ++ */ + if ( space == XENMAPSPACE_gmfn ) ++ { + put_gfn(d, gfn); ++ if ( !rc && extra.ppage ) ++ { ++ *extra.ppage = page; ++ page = NULL; ++ } ++ } + + if ( page ) + put_page(page); +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -814,13 +814,12 @@ int xenmem_add_to_physmap(struct domain + { + unsigned int done = 0; + long rc = 0; +- union xen_add_to_physmap_batch_extra extra; ++ union add_to_physmap_extra extra = {}; ++ struct page_info *pages[16]; + + ASSERT(paging_mode_translate(d)); + +- if ( xatp->space != XENMAPSPACE_gmfn_foreign ) +- extra.res0 = 0; +- else ++ if ( xatp->space == XENMAPSPACE_gmfn_foreign ) + extra.foreign_domid = DOMID_INVALID; + + if ( xatp->space != XENMAPSPACE_gmfn_range ) +@@ -835,7 +834,10 @@ int xenmem_add_to_physmap(struct domain + xatp->size -= start; + + if ( is_iommu_enabled(d) ) ++ { + this_cpu(iommu_dont_flush_iotlb) = 1; ++ extra.ppage = &pages[0]; ++ } + + while ( xatp->size > done ) + { +@@ -847,8 +849,12 @@ int xenmem_add_to_physmap(struct domain + xatp->idx++; + xatp->gpfn++; + ++ if ( extra.ppage ) ++ ++extra.ppage; ++ + /* Check for continuation if it's not the last iteration. */ +- if ( xatp->size > ++done && hypercall_preempt_check() ) ++ if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || ++ (xatp->size > done && hypercall_preempt_check()) ) + { + rc = start + done; + break; +@@ -858,6 +864,7 @@ int xenmem_add_to_physmap(struct domain + if ( is_iommu_enabled(d) ) + { + int ret; ++ unsigned int i; + + this_cpu(iommu_dont_flush_iotlb) = 0; + +@@ -866,6 +873,15 @@ int xenmem_add_to_physmap(struct domain + if ( unlikely(ret) && rc >= 0 ) + rc = ret; + ++ /* ++ * Now that the IOMMU TLB flush was done for the original GFN, drop ++ * the page references. The 2nd flush below is fine to make later, as ++ * whoever removes the page again from its new GFN will have to do ++ * another flush anyway. ++ */ ++ for ( i = 0; i < done; ++i ) ++ put_page(pages[i]); ++ + ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, + IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); + if ( unlikely(ret) && rc >= 0 ) +@@ -879,6 +895,8 @@ static int xenmem_add_to_physmap_batch(s + struct xen_add_to_physmap_batch *xatpb, + unsigned int extent) + { ++ union add_to_physmap_extra extra = {}; ++ + if ( unlikely(xatpb->size < extent) ) + return -EILSEQ; + +@@ -890,6 +908,19 @@ static int xenmem_add_to_physmap_batch(s + !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) + return -EFAULT; + ++ switch ( xatpb->space ) ++ { ++ case XENMAPSPACE_dev_mmio: ++ /* res0 is reserved for future use. */ ++ if ( xatpb->u.res0 ) ++ return -EOPNOTSUPP; ++ break; ++ ++ case XENMAPSPACE_gmfn_foreign: ++ extra.foreign_domid = xatpb->u.foreign_domid; ++ break; ++ } ++ + while ( xatpb->size > extent ) + { + xen_ulong_t idx; +@@ -902,8 +933,7 @@ static int xenmem_add_to_physmap_batch(s + extent, 1)) ) + return -EFAULT; + +- rc = xenmem_add_to_physmap_one(d, xatpb->space, +- xatpb->u, ++ rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, + idx, _gfn(gpfn)); + + if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) +--- a/xen/include/xen/mm.h ++++ b/xen/include/xen/mm.h +@@ -588,8 +588,22 @@ void scrub_one_page(struct page_info *); + &(d)->xenpage_list : &(d)->page_list) + #endif + ++union add_to_physmap_extra { ++ /* ++ * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs ++ * to be kept until after the flush, so the page can't get removed from ++ * the domain (and re-used for another purpose) beforehand. By passing ++ * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants ++ * to have ownership of such a reference transferred in the success case. ++ */ ++ struct page_info **ppage; ++ ++ /* XENMAPSPACE_gmfn_foreign */ ++ domid_t foreign_domid; ++}; ++ + int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, +- union xen_add_to_physmap_batch_extra extra, ++ union add_to_physmap_extra extra, + unsigned long idx, gfn_t gfn); + + int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, diff --git a/xsa347-4.13-1.patch b/xsa347-4.13-1.patch new file mode 100644 index 0000000..e9f31a1 --- /dev/null +++ b/xsa347-4.13-1.patch @@ -0,0 +1,149 @@ +From: Jan Beulich +Subject: AMD/IOMMU: convert amd_iommu_pte from struct to union + +This is to add a "raw" counterpart to the bitfield equivalent. Take the +opportunity and + - convert fields to bool / unsigned int, + - drop the naming of the reserved field, + - shorten the names of the ignored ones. + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Andrew Cooper +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -38,7 +38,7 @@ static unsigned int pfn_to_pde_idx(unsig + static unsigned int clear_iommu_pte_present(unsigned long l1_mfn, + unsigned long dfn) + { +- struct amd_iommu_pte *table, *pte; ++ union amd_iommu_pte *table, *pte; + unsigned int flush_flags; + + table = map_domain_page(_mfn(l1_mfn)); +@@ -52,7 +52,7 @@ static unsigned int clear_iommu_pte_pres + return flush_flags; + } + +-static unsigned int set_iommu_pde_present(struct amd_iommu_pte *pte, ++static unsigned int set_iommu_pde_present(union amd_iommu_pte *pte, + unsigned long next_mfn, + unsigned int next_level, bool iw, + bool ir) +@@ -87,7 +87,7 @@ static unsigned int set_iommu_pte_presen + int pde_level, + bool iw, bool ir) + { +- struct amd_iommu_pte *table, *pde; ++ union amd_iommu_pte *table, *pde; + unsigned int flush_flags; + + table = map_domain_page(_mfn(pt_mfn)); +@@ -178,7 +178,7 @@ void iommu_dte_set_guest_cr3(struct amd_ + static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, + unsigned long pt_mfn[], bool map) + { +- struct amd_iommu_pte *pde, *next_table_vaddr; ++ union amd_iommu_pte *pde, *next_table_vaddr; + unsigned long next_table_mfn; + unsigned int level; + struct page_info *table; +@@ -458,7 +458,7 @@ int __init amd_iommu_quarantine_init(str + unsigned long end_gfn = + 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); + unsigned int level = amd_iommu_get_paging_mode(end_gfn); +- struct amd_iommu_pte *table; ++ union amd_iommu_pte *table; + + if ( hd->arch.root_table ) + { +@@ -489,7 +489,7 @@ int __init amd_iommu_quarantine_init(str + + for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) + { +- struct amd_iommu_pte *pde = &table[i]; ++ union amd_iommu_pte *pde = &table[i]; + + /* + * PDEs are essentially a subset of PTEs, so this function +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -390,7 +390,7 @@ static void deallocate_next_page_table(s + + static void deallocate_page_table(struct page_info *pg) + { +- struct amd_iommu_pte *table_vaddr; ++ union amd_iommu_pte *table_vaddr; + unsigned int index, level = PFN_ORDER(pg); + + PFN_ORDER(pg) = 0; +@@ -405,7 +405,7 @@ static void deallocate_page_table(struct + + for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) + { +- struct amd_iommu_pte *pde = &table_vaddr[index]; ++ union amd_iommu_pte *pde = &table_vaddr[index]; + + if ( pde->mfn && pde->next_level && pde->pr ) + { +@@ -557,7 +557,7 @@ static void amd_dump_p2m_table_level(str + paddr_t gpa, int indent) + { + paddr_t address; +- struct amd_iommu_pte *table_vaddr; ++ const union amd_iommu_pte *table_vaddr; + int index; + + if ( level < 1 ) +@@ -573,7 +573,7 @@ static void amd_dump_p2m_table_level(str + + for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) + { +- struct amd_iommu_pte *pde = &table_vaddr[index]; ++ const union amd_iommu_pte *pde = &table_vaddr[index]; + + if ( !(index % 2) ) + process_pending_softirqs(); +--- a/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h ++++ b/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h +@@ -465,20 +465,23 @@ union amd_iommu_x2apic_control { + #define IOMMU_PAGE_TABLE_U32_PER_ENTRY (IOMMU_PAGE_TABLE_ENTRY_SIZE / 4) + #define IOMMU_PAGE_TABLE_ALIGNMENT 4096 + +-struct amd_iommu_pte { +- uint64_t pr:1; +- uint64_t ignored0:4; +- uint64_t a:1; +- uint64_t d:1; +- uint64_t ignored1:2; +- uint64_t next_level:3; +- uint64_t mfn:40; +- uint64_t reserved:7; +- uint64_t u:1; +- uint64_t fc:1; +- uint64_t ir:1; +- uint64_t iw:1; +- uint64_t ignored2:1; ++union amd_iommu_pte { ++ uint64_t raw; ++ struct { ++ bool pr:1; ++ unsigned int ign0:4; ++ bool a:1; ++ bool d:1; ++ unsigned int ign1:2; ++ unsigned int next_level:3; ++ uint64_t mfn:40; ++ unsigned int :7; ++ bool u:1; ++ bool fc:1; ++ bool ir:1; ++ bool iw:1; ++ unsigned int ign2:1; ++ }; + }; + + /* Paging modes */ diff --git a/xsa347-4.13-2.patch b/xsa347-4.13-2.patch new file mode 100644 index 0000000..fbe7461 --- /dev/null +++ b/xsa347-4.13-2.patch @@ -0,0 +1,72 @@ +From: Jan Beulich +Subject: AMD/IOMMU: update live PTEs atomically + +Updating a live PTE bitfield by bitfield risks the compiler re-ordering +the individual updates as well as splitting individual updates into +multiple memory writes. Construct the new entry fully in a local +variable, do the check to determine the flushing needs on the thus +established new entry, and then write the new entry by a single insn. + +Similarly using memset() to clear a PTE is unsafe, as the order of +writes the function does is, at least in principle, undefined. + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -45,7 +45,7 @@ static unsigned int clear_iommu_pte_pres + pte = &table[pfn_to_pde_idx(dfn, 1)]; + + flush_flags = pte->pr ? IOMMU_FLUSHF_modified : 0; +- memset(pte, 0, sizeof(*pte)); ++ write_atomic(&pte->raw, 0); + + unmap_domain_page(table); + +@@ -57,26 +57,30 @@ static unsigned int set_iommu_pde_presen + unsigned int next_level, bool iw, + bool ir) + { ++ union amd_iommu_pte new = {}, old; + unsigned int flush_flags = IOMMU_FLUSHF_added; + +- if ( pte->pr && +- (pte->mfn != next_mfn || +- pte->iw != iw || +- pte->ir != ir || +- pte->next_level != next_level) ) +- flush_flags |= IOMMU_FLUSHF_modified; +- + /* + * FC bit should be enabled in PTE, this helps to solve potential + * issues with ATS devices + */ +- pte->fc = !next_level; ++ new.fc = !next_level; ++ ++ new.mfn = next_mfn; ++ new.iw = iw; ++ new.ir = ir; ++ new.next_level = next_level; ++ new.pr = true; ++ ++ old.raw = read_atomic(&pte->raw); ++ old.ign0 = 0; ++ old.ign1 = 0; ++ old.ign2 = 0; ++ ++ if ( old.pr && old.raw != new.raw ) ++ flush_flags |= IOMMU_FLUSHF_modified; + +- pte->mfn = next_mfn; +- pte->iw = iw; +- pte->ir = ir; +- pte->next_level = next_level; +- pte->pr = 1; ++ write_atomic(&pte->raw, new.raw); + + return flush_flags; + } diff --git a/xsa347-4.13-3.patch b/xsa347-4.13-3.patch new file mode 100644 index 0000000..90c8e66 --- /dev/null +++ b/xsa347-4.13-3.patch @@ -0,0 +1,59 @@ +From: Jan Beulich +Subject: AMD/IOMMU: ensure suitable ordering of DTE modifications + +DMA and interrupt translation should be enabled only after other +applicable DTE fields have been written. Similarly when disabling +translation or when moving a device between domains, translation should +first be disabled, before other entry fields get modified. Note however +that the "moving" aspect doesn't apply to the interrupt remapping side, +as domain specifics are maintained in the IRTEs here, not the DTE. We +also never disable interrupt remapping once it got enabled for a device +(the respective argument passed is always the immutable iommu_intremap). + +This is part of XSA-347. + +Signed-off-by: Jan Beulich +Reviewed-by: Paul Durrant + +--- a/xen/drivers/passthrough/amd/iommu_map.c ++++ b/xen/drivers/passthrough/amd/iommu_map.c +@@ -107,11 +107,18 @@ void amd_iommu_set_root_page_table(struc + uint64_t root_ptr, uint16_t domain_id, + uint8_t paging_mode, bool valid) + { ++ if ( valid || dte->v ) ++ { ++ dte->tv = false; ++ dte->v = true; ++ smp_wmb(); ++ } + dte->domain_id = domain_id; + dte->pt_root = paddr_to_pfn(root_ptr); + dte->iw = true; + dte->ir = true; + dte->paging_mode = paging_mode; ++ smp_wmb(); + dte->tv = true; + dte->v = valid; + } +@@ -134,6 +141,7 @@ void amd_iommu_set_intremap_table( + } + + dte->ig = false; /* unmapped interrupts result in i/o page faults */ ++ smp_wmb(); + dte->iv = valid; + } + +--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c ++++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c +@@ -120,7 +120,10 @@ static void amd_iommu_setup_domain_devic + /* Undo what amd_iommu_disable_domain_device() may have done. */ + ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; + if ( dte->it_root ) ++ { + dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED; ++ smp_wmb(); ++ } + dte->iv = iommu_intremap; + dte->ex = ivrs_dev->dte_allow_exclusion; + dte->sys_mgt = MASK_EXTR(ivrs_dev->device_flags, ACPI_IVHD_SYSTEM_MGMT); From 0af4083600bdfebe9a113522ace364c97666de66 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 28 Oct 2020 22:37:41 +0000 Subject: [PATCH 04/15] x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] (#1891092) --- xen.spec | 18 +- ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 ++++++++ ...-mm-check-page-types-in-do_page_walk.patch | 170 +++++++++++++++++ ...ng-linear-page-tables-in-map_guest_l.patch | 92 ++++++++++ ...ng-linear-page-tables-in-guest_get_e.patch | 172 ++++++++++++++++++ ...ng-top-level-linear-page-tables-in-u.patch | 101 ++++++++++ ...use-of-linear-page-tables-to-shadow-.patch | 106 +++++++++++ 7 files changed, 731 insertions(+), 1 deletion(-) create mode 100644 xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch create mode 100644 xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch create mode 100644 xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch create mode 100644 xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch create mode 100644 xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch create mode 100644 xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch diff --git a/xen.spec b/xen.spec index ef97c0a..6289068 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.1 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -152,6 +152,12 @@ Patch80: xsa346-4.13-2.patch Patch81: xsa347-4.13-1.patch Patch82: xsa347-4.13-2.patch Patch83: xsa347-4.13-3.patch +Patch84: xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +Patch85: xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch +Patch86: xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +Patch87: xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +Patch88: xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +Patch89: xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch %if %build_qemutrad @@ -395,6 +401,12 @@ manage Xen virtual machines. %patch81 -p1 %patch82 -p1 %patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -988,6 +1000,10 @@ fi %endif %changelog +* Wed Oct 28 2020 Michael Young - 4.13.1-8 +- x86 PV guest INVLPG-like flushes may leave stale TLB entries + [XSA-286, CVE-2020-27674] (#1891092) + * Tue Oct 20 2020 Michael Young - 4.13.1-7 - x86: Race condition in Xen mapping code [XSA-345] - undue deferral of IOMMU TLB flushes [XSA-346] diff --git a/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch new file mode 100644 index 0000000..0d39c48 --- /dev/null +++ b/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch @@ -0,0 +1,73 @@ +From: Jan Beulich +Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() + +The L3 one at least is going to be re-used by a subsequent patch, and +splitting the L4 one then as well seems only natural. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index db4f035d8d..b1582b56fb 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; + + l2_pgentry_t *compat_idle_pg_table_l2; + +-void *do_page_walk(struct vcpu *v, unsigned long addr) ++static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); +- l4_pgentry_t l4e, *l4t; +- l3_pgentry_t l3e, *l3t; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; ++ unsigned long mfn = pagetable_get_pfn(root); ++ l4_pgentry_t *l4t, l4e; + +- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) +- return NULL; ++ if ( !is_canonical_address(addr) ) ++ return l4e_empty(); + + l4t = map_domain_page(_mfn(mfn)); + l4e = l4t[l4_table_offset(addr)]; + unmap_domain_page(l4t); ++ ++ return l4e; ++} ++ ++static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) ++{ ++ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); ++ l3_pgentry_t *l3t, l3e; ++ + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) +- return NULL; ++ return l3e_empty(); + + l3t = map_l3t_from_l4e(l4e); + l3e = l3t[l3_table_offset(addr)]; + unmap_domain_page(l3t); ++ ++ return l3e; ++} ++ ++void *do_page_walk(struct vcpu *v, unsigned long addr) ++{ ++ l3_pgentry_t l3e; ++ l2_pgentry_t l2e, *l2t; ++ l1_pgentry_t l1e, *l1t; ++ unsigned long mfn; ++ ++ if ( !is_pv_vcpu(v) ) ++ return NULL; ++ ++ l3e = page_walk_get_l3e(v->arch.guest_table, addr); + mfn = l3e_get_pfn(l3e); + if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) + return NULL; diff --git a/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch new file mode 100644 index 0000000..ea923d4 --- /dev/null +++ b/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch @@ -0,0 +1,170 @@ +From: Jan Beulich +Subject: x86/mm: check page types in do_page_walk() + +For page table entries read to be guaranteed valid, transiently locking +the pages and validating their types is necessary. Note that guest use +of linear page tables is intentionally not taken into account here, as +ordinary data (guest stacks) can't possibly live inside page tables. + +This is part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index b1582b56fb..7d439639b7 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; + + static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + { +- unsigned long mfn = pagetable_get_pfn(root); +- l4_pgentry_t *l4t, l4e; ++ mfn_t mfn = pagetable_get_mfn(root); ++ /* current's root page table can't disappear under our feet. */ ++ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); ++ struct page_info *pg; ++ l4_pgentry_t l4e = l4e_empty(); + + if ( !is_canonical_address(addr) ) + return l4e_empty(); + +- l4t = map_domain_page(_mfn(mfn)); +- l4e = l4t[l4_table_offset(addr)]; +- unmap_domain_page(l4t); ++ pg = mfn_to_page(mfn); ++ if ( need_lock && !page_lock(pg) ) ++ return l4e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) ++ { ++ l4_pgentry_t *l4t = map_domain_page(mfn); ++ ++ l4e = l4t[l4_table_offset(addr)]; ++ unmap_domain_page(l4t); ++ } ++ ++ if ( need_lock ) ++ page_unlock(pg); + + return l4e; + } +@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) + static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + { + l4_pgentry_t l4e = page_walk_get_l4e(root, addr); +- l3_pgentry_t *l3t, l3e; ++ mfn_t mfn = l4e_get_mfn(l4e); ++ struct page_info *pg; ++ l3_pgentry_t l3e = l3e_empty(); + + if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) + return l3e_empty(); + +- l3t = map_l3t_from_l4e(l4e); +- l3e = l3t[l3_table_offset(addr)]; +- unmap_domain_page(l3t); ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l3e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) ++ { ++ l3_pgentry_t *l3t = map_domain_page(mfn); ++ ++ l3e = l3t[l3_table_offset(addr)]; ++ unmap_domain_page(l3t); ++ } ++ ++ page_unlock(pg); + + return l3e; + } +@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +- l2_pgentry_t l2e, *l2t; +- l1_pgentry_t l1e, *l1t; +- unsigned long mfn; ++ l2_pgentry_t l2e = l2e_empty(); ++ l1_pgentry_t l1e = l1e_empty(); ++ mfn_t mfn; ++ struct page_info *pg; + + if ( !is_pv_vcpu(v) ) + return NULL; + + l3e = page_walk_get_l3e(v->arch.guest_table, addr); +- mfn = l3e_get_pfn(l3e); +- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ mfn = l3e_get_mfn(l3e); ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l2t = map_domain_page(_mfn(mfn)); +- l2e = l2t[l2_table_offset(addr)]; +- unmap_domain_page(l2t); +- mfn = l2e_get_pfn(l2e); +- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ const l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l2e_get_mfn(l2e); ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) + { +- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); ++ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); + goto ret; + } + +- l1t = map_domain_page(_mfn(mfn)); +- l1e = l1t[l1_table_offset(addr)]; +- unmap_domain_page(l1t); +- mfn = l1e_get_pfn(l1e); +- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return NULL; ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ const l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ mfn = l1e_get_mfn(l1e); ++ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) + return NULL; + + ret: +- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); ++ return map_domain_page(mfn) + (addr & ~PAGE_MASK); + } + + /* diff --git a/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch new file mode 100644 index 0000000..dcf3367 --- /dev/null +++ b/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch @@ -0,0 +1,92 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in map_guest_l1e() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Replace the linear L2 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Signed-off-by: Roger Pau Monné +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index 2b0dadc8da..acebf9e957 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + if ( unlikely(!__addr_ok(linear)) ) + return NULL; + +- /* Find this l1e and its enclosing l1mfn in the linear map. */ +- if ( __copy_from_user(&l2e, +- &__linear_l2_table[l2_linear_offset(linear)], +- sizeof(l2_pgentry_t)) ) ++ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) ++ { ++ ASSERT_UNREACHABLE(); + return NULL; ++ } ++ ++ /* Find this l1e and its enclosing l1mfn. */ ++ l2e = page_walk_get_l2e(current->arch.guest_table, linear); + + /* Check flags that it will be safe to read the l1e. */ + if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 7d439639b7..670aa3f892 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) + return l3e; + } + ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) ++{ ++ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); ++ mfn_t mfn = l3e_get_mfn(l3e); ++ struct page_info *pg; ++ l2_pgentry_t l2e = l2e_empty(); ++ ++ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || ++ (l3e_get_flags(l3e) & _PAGE_PSE) ) ++ return l2e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l2e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) ++ { ++ l2_pgentry_t *l2t = map_domain_page(mfn); ++ ++ l2e = l2t[l2_table_offset(addr)]; ++ unmap_domain_page(l2t); ++ } ++ ++ page_unlock(pg); ++ ++ return l2e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index 320c6cd196..cd3e7ec501 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -577,7 +577,9 @@ void audit_domains(void); + void make_cr3(struct vcpu *v, mfn_t mfn); + void update_cr3(struct vcpu *v); + int vcpu_destroy_pagetables(struct vcpu *); ++ + void *do_page_walk(struct vcpu *v, unsigned long addr); ++l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); + + int __sync_local_execstate(void); + diff --git a/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch new file mode 100644 index 0000000..b15efaf --- /dev/null +++ b/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch @@ -0,0 +1,172 @@ +From: Jan Beulich +Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() + +First of all drop guest_get_eff_l1e() entirely - there's no actual user +of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around +its only call site. + +Then replace the linear L1 table access by an actual page walk. + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c +index acebf9e957..7624447246 100644 +--- a/xen/arch/x86/pv/mm.c ++++ b/xen/arch/x86/pv/mm.c +@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) + } + + /* +- * Read the guest's l1e that maps this address, from the kernel-mode +- * page tables. +- */ +-static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) +-{ +- struct vcpu *curr = current; +- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); +- l1_pgentry_t l1e; +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- l1e = guest_get_eff_l1e(linear); +- +- if ( user_mode ) +- toggle_guest_pt(curr); +- +- return l1e; +-} +- +-/* + * Map a guest's LDT page (covering the byte at @offset from start of the LDT) + * into Xen's virtual range. Returns true if the mapping changed, false + * otherwise. +diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h +index a1bd473b29..43d33a1fd1 100644 +--- a/xen/arch/x86/pv/mm.h ++++ b/xen/arch/x86/pv/mm.h +@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); + + int new_guest_cr3(mfn_t mfn); + +-/* Read a PV guest's l1e that maps this linear address. */ +-static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) ++/* ++ * Read the guest's l1e that maps this address, from the kernel-mode ++ * page tables. ++ */ ++static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) + { +- l1_pgentry_t l1e; ++ l1_pgentry_t l1e = l1e_empty(); + + ASSERT(!paging_mode_translate(current->domain)); + ASSERT(!paging_mode_external(current->domain)); + +- if ( unlikely(!__addr_ok(linear)) || +- __copy_from_user(&l1e, +- &__linear_l1_table[l1_linear_offset(linear)], +- sizeof(l1_pgentry_t)) ) +- l1e = l1e_empty(); ++ if ( likely(__addr_ok(linear)) ) ++ l1e = page_walk_get_l1e(current->arch.guest_table, linear); + + return l1e; + } +diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c +index a920fb5e15..2bf4497a16 100644 +--- a/xen/arch/x86/pv/ro-page-fault.c ++++ b/xen/arch/x86/pv/ro-page-fault.c +@@ -357,7 +357,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) + bool mmio_ro; + + /* Attempt to read the PTE that maps the VA being accessed. */ +- pte = guest_get_eff_l1e(addr); ++ pte = guest_get_eff_kern_l1e(addr); + + /* We are only looking for read-only mappings */ + if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index 670aa3f892..c5686e0d25 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) + return l2e; + } + ++/* ++ * For now no "set_accessed" parameter, as all callers want it set to true. ++ * For now also no "set_dirty" parameter, as all callers deal with r/o ++ * mappings, and we don't want to set the dirty bit there (conflicts with ++ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o ++ * PTEs, the logic below tolerates the bit becoming set "behind our backs". ++ */ ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) ++{ ++ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); ++ mfn_t mfn = l2e_get_mfn(l2e); ++ struct page_info *pg; ++ l1_pgentry_t l1e = l1e_empty(); ++ ++ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || ++ (l2e_get_flags(l2e) & _PAGE_PSE) ) ++ return l1e_empty(); ++ ++ pg = mfn_to_page(mfn); ++ if ( !page_lock(pg) ) ++ return l1e_empty(); ++ ++ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) ++ { ++ l1_pgentry_t *l1t = map_domain_page(mfn); ++ ++ l1e = l1t[l1_table_offset(addr)]; ++ ++ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == ++ _PAGE_PRESENT ) ++ { ++ l1_pgentry_t ol1e = l1e; ++ ++ l1e_add_flags(l1e, _PAGE_ACCESSED); ++ /* ++ * Best effort only; with the lock held the page shouldn't ++ * change anyway, except for the dirty bit to perhaps become set. ++ */ ++ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), ++ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != ++ l1e_get_intpte(ol1e) && ++ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) ++ { ++ l1e_add_flags(ol1e, _PAGE_DIRTY); ++ l1e_add_flags(l1e, _PAGE_DIRTY); ++ } ++ } ++ ++ unmap_domain_page(l1t); ++ } ++ ++ page_unlock(pg); ++ ++ return l1e; ++} ++ + void *do_page_walk(struct vcpu *v, unsigned long addr) + { + l3_pgentry_t l3e; +diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h +index cd3e7ec501..865db999c1 100644 +--- a/xen/include/asm-x86/mm.h ++++ b/xen/include/asm-x86/mm.h +@@ -580,6 +580,7 @@ int vcpu_destroy_pagetables(struct vcpu *); + + void *do_page_walk(struct vcpu *v, unsigned long addr); + l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); ++l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); + + int __sync_local_execstate(void); + diff --git a/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch new file mode 100644 index 0000000..21506b5 --- /dev/null +++ b/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch @@ -0,0 +1,101 @@ +From: Jan Beulich +Subject: x86/mm: avoid using top level linear page tables in + {,un}map_domain_page() + +Move the page table recursion two levels down. This entails avoiding +to free the recursive mapping prematurely in free_perdomain_mappings(). + +This is part of XSA-286. + +Reported-by: Jann Horn +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c +index 4a07cfb18e..660bd06aaf 100644 +--- a/xen/arch/x86/domain_page.c ++++ b/xen/arch/x86/domain_page.c +@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) + #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) + #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) + #define MAPCACHE_L1ENT(idx) \ +- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] ++ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ ++ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] + + void *map_domain_page(mfn_t mfn) + { +@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) + { + struct mapcache_domain *dcache = &d->arch.pv.mapcache; + unsigned int bitmap_pages; ++ int rc; + + ASSERT(is_pv_domain(d)); + +@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) + return 0; + #endif + ++ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); + BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + +- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > ++ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + ++ (1U << L2_PAGETABLE_SHIFT) > + MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); + bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); + dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; +@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) + + spin_lock_init(&dcache->lock); + +- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, +- 2 * bitmap_pages + 1, +- NIL(l1_pgentry_t *), NULL); ++ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, ++ 2 * bitmap_pages + 1, ++ NIL(l1_pgentry_t *), NULL); ++ if ( !rc ) ++ { ++ /* ++ * Install mapping of our L2 table into its own last slot, for easy ++ * access to the L1 entries via MAPCACHE_L1ENT(). ++ */ ++ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); ++ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; ++ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); ++ ++ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); ++ unmap_domain_page(l2t); ++ unmap_domain_page(l3t); ++ } ++ ++ return rc; + } + + int mapcache_vcpu_init(struct vcpu *v) +@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) + else + { + ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); +- pl1e = &__linear_l1_table[l1_linear_offset(va)]; ++ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); + } + + return l1e_get_mfn(*pl1e); +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 30dffb68e8..279664a83e 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -6031,6 +6031,10 @@ void free_perdomain_mappings(struct domain *d) + { + struct page_info *l1pg = l2e_get_page(l2tab[j]); + ++ /* mapcache_domain_init() installs a recursive entry. */ ++ if ( l1pg == l2pg ) ++ continue; ++ + if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) + { + l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch new file mode 100644 index 0000000..6c9d393 --- /dev/null +++ b/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch @@ -0,0 +1,106 @@ +From: Jan Beulich +Subject: x86/mm: restrict use of linear page tables to shadow mode code + +Other code does not require them to be set up anymore, so restrict when +to populate the respective L4 slot and reduce visibility of the +accessors. + +While with the removal of all uses the vulnerability is actually fixed, +removing the creation of the linear mapping adds an extra layer of +protection. Similarly reducing visibility of the accessors mostly +eliminates the risk of undue re-introduction of uses of the linear +mappings. + +This is (not strictly) part of XSA-286. + +Signed-off-by: Jan Beulich +Reviewed-by: George Dunlap +Reviewed-by: Andrew Cooper + +diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c +index 279664a83e..fa0f813d29 100644 +--- a/xen/arch/x86/mm.c ++++ b/xen/arch/x86/mm.c +@@ -1757,9 +1757,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, + l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = + idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; + +- /* Slot 258: Self linear mappings. */ ++ /* Slot 258: Self linear mappings (shadow pt only). */ + ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); + l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = ++ !shadow_mode_external(d) ? l4e_empty() : + l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); + + /* Slot 259: Shadow linear mappings (if applicable) .*/ +diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h +index 3217777921..b214087194 100644 +--- a/xen/arch/x86/mm/shadow/private.h ++++ b/xen/arch/x86/mm/shadow/private.h +@@ -135,6 +135,15 @@ enum { + # define GUEST_PTE_SIZE 4 + #endif + ++/* Where to find each level of the linear mapping */ ++#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) ++#define __linear_l2_table \ ++ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l3_table \ ++ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) ++#define __linear_l4_table \ ++ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) ++ + /****************************************************************************** + * Auditing routines + */ +diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c +index c5686e0d25..dcb20d1d9d 100644 +--- a/xen/arch/x86/x86_64/mm.c ++++ b/xen/arch/x86/x86_64/mm.c +@@ -833,9 +833,6 @@ void __init paging_init(void) + + machine_to_phys_mapping_valid = 1; + +- /* Set up linear page table mapping. */ +- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], +- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); + return; + + nomem: +diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h +index 8d79a71398..9d587a076a 100644 +--- a/xen/include/asm-x86/config.h ++++ b/xen/include/asm-x86/config.h +@@ -193,7 +193,7 @@ extern unsigned char boot_edid_info[128]; + */ + #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) + #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) +-/* Slot 258: linear page table (guest table). */ ++/* Slot 258: linear page table (monitor table, HVM only). */ + #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) + #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) + /* Slot 259: linear page table (shadow table). */ +diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h +index c1e92937c0..e72c277b9f 100644 +--- a/xen/include/asm-x86/page.h ++++ b/xen/include/asm-x86/page.h +@@ -274,19 +274,6 @@ void copy_page_sse2(void *, const void *); + #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) + #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) + +-#endif /* !defined(__ASSEMBLY__) */ +- +-/* Where to find each level of the linear mapping */ +-#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) +-#define __linear_l2_table \ +- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l3_table \ +- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) +-#define __linear_l4_table \ +- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) +- +- +-#ifndef __ASSEMBLY__ + extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; + extern l2_pgentry_t *compat_idle_pg_table_l2; + extern unsigned int m2p_compat_vstart; From e7122f27e1838dac5966793726a5840d7841e689 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 28 Oct 2020 22:42:19 +0000 Subject: [PATCH 05/15] add some CVE/bug references --- xen.spec | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/xen.spec b/xen.spec index 6289068..a56f408 100644 --- a/xen.spec +++ b/xen.spec @@ -1005,9 +1005,12 @@ fi [XSA-286, CVE-2020-27674] (#1891092) * Tue Oct 20 2020 Michael Young - 4.13.1-7 -- x86: Race condition in Xen mapping code [XSA-345] -- undue deferral of IOMMU TLB flushes [XSA-346] -- unsafe AMD IOMMU page table updates [XSA-347] +- x86: Race condition in Xen mapping code [XSA-345, CVE-2020-27672] + (#1891097) +- undue deferral of IOMMU TLB flushes [XSA-346, CVE-2020-27671] + (#1891093) +- unsafe AMD IOMMU page table updates [XSA-347 CVE-2020-27670] + (#1891088) * Tue Sep 22 2020 Michael Young - 4.13.1-6 - x86 pv: Crash when handling guest access to MSR_MISC_ENABLE [XSA-333, From 5f155647b3ab1b3477541fd50d9a050925b7f142 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 3 Nov 2020 21:49:13 +0000 Subject: [PATCH 06/15] update to xen-4.13.2 --- .gitignore | 2 +- sources | 2 +- xen.spec | 93 +---- ...and-L3-parts-of-the-walk-out-of-do_p.patch | 73 ---- ...-mm-check-page-types-in-do_page_walk.patch | 170 -------- ...ng-linear-page-tables-in-map_guest_l.patch | 92 ---- ...ng-linear-page-tables-in-guest_get_e.patch | 172 -------- ...ng-top-level-linear-page-tables-in-u.patch | 101 ----- ...use-of-linear-page-tables-to-shadow-.patch | 106 ----- xsa317.patch | 50 --- xsa319.patch | 27 -- xsa320-4.13-1.patch | 117 ------ xsa320-4.13-2.patch | 179 -------- xsa321-4.13-1.patch | 31 -- xsa321-4.13-2.patch | 175 -------- xsa321-4.13-3.patch | 82 ---- xsa321-4.13-4.patch | 36 -- xsa321-4.13-5.patch | 24 -- xsa321-4.13-6.patch | 91 ---- xsa321-4.13-7.patch | 153 ------- xsa327.patch | 63 --- xsa328-4.13-1.patch | 118 ------ xsa328-4.13-2.patch | 48 --- xsa333.patch | 39 -- xsa334.patch | 51 --- xsa336.patch | 283 ------------- xsa337-4.13-1.patch | 87 ---- xsa337-4.13-2.patch | 181 -------- xsa338.patch | 42 -- xsa339.patch | 76 ---- xsa340.patch | 65 --- xsa342-4.13.patch | 145 ------- xsa343-1.patch | 199 --------- xsa343-2.patch | 295 ------------- xsa343-3.patch | 392 ------------------ xsa344-4.13-1.patch | 130 ------ xsa344-4.13-2.patch | 203 --------- ...map_pages_to_xen-to-have-only-a-sing.patch | 94 ----- ...modify_xen_mappings-to-have-one-exit.patch | 68 --- ...ome-races-in-hypervisor-mapping-upda.patch | 249 ----------- xsa346-4.13-1.patch | 50 --- xsa346-4.13-2.patch | 204 --------- xsa347-4.13-1.patch | 149 ------- xsa347-4.13-2.patch | 72 ---- xsa347-4.13-3.patch | 59 --- 45 files changed, 8 insertions(+), 5130 deletions(-) delete mode 100644 xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch delete mode 100644 xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch delete mode 100644 xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch delete mode 100644 xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch delete mode 100644 xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch delete mode 100644 xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch delete mode 100644 xsa317.patch delete mode 100644 xsa319.patch delete mode 100644 xsa320-4.13-1.patch delete mode 100644 xsa320-4.13-2.patch delete mode 100644 xsa321-4.13-1.patch delete mode 100644 xsa321-4.13-2.patch delete mode 100644 xsa321-4.13-3.patch delete mode 100644 xsa321-4.13-4.patch delete mode 100644 xsa321-4.13-5.patch delete mode 100644 xsa321-4.13-6.patch delete mode 100644 xsa321-4.13-7.patch delete mode 100644 xsa327.patch delete mode 100644 xsa328-4.13-1.patch delete mode 100644 xsa328-4.13-2.patch delete mode 100644 xsa333.patch delete mode 100644 xsa334.patch delete mode 100644 xsa336.patch delete mode 100644 xsa337-4.13-1.patch delete mode 100644 xsa337-4.13-2.patch delete mode 100644 xsa338.patch delete mode 100644 xsa339.patch delete mode 100644 xsa340.patch delete mode 100644 xsa342-4.13.patch delete mode 100644 xsa343-1.patch delete mode 100644 xsa343-2.patch delete mode 100644 xsa343-3.patch delete mode 100644 xsa344-4.13-1.patch delete mode 100644 xsa344-4.13-2.patch delete mode 100644 xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch delete mode 100644 xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch delete mode 100644 xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch delete mode 100644 xsa346-4.13-1.patch delete mode 100644 xsa346-4.13-2.patch delete mode 100644 xsa347-4.13-1.patch delete mode 100644 xsa347-4.13-2.patch delete mode 100644 xsa347-4.13-3.patch diff --git a/.gitignore b/.gitignore index c542b51..21b852b 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.13.1.tar.gz +/xen-4.13.2.tar.gz diff --git a/sources b/sources index 0fe92bd..ea02e58 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.13.1.tar.gz) = b56d20704155d98d803496cba83eb928e0f986a750831cd5600fc88d0ae772fe1456571654375054043d2da8daca255cc98385ebf08b1b1a75ecf7f4b7a0ee90 +SHA512 (xen-4.13.2.tar.gz) = cd3092281c97e9421e303aa288aac04dcccd5536ba7c0ff4d51fbf3d07b5ffacfe3456ba06f5cf63577dafbf8cf3a5d9825ceb5e9ef8ca1427900cc3e57b50a3 diff --git a/xen.spec b/xen.spec index a56f408..770f620 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.13.1 -Release: 8%{?dist} +Version: 4.13.2 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -114,51 +114,8 @@ Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch -Patch46: xsa320-4.13-1.patch -Patch47: xsa320-4.13-2.patch -Patch48: xsa317.patch -Patch49: xsa319.patch -Patch50: xsa328-4.13-1.patch -Patch51: xsa328-4.13-2.patch -Patch52: xsa321-4.13-1.patch -Patch53: xsa321-4.13-2.patch -Patch54: xsa321-4.13-3.patch -Patch55: xsa321-4.13-4.patch -Patch56: xsa321-4.13-5.patch -Patch57: xsa321-4.13-6.patch -Patch58: xsa321-4.13-7.patch -Patch59: xsa327.patch Patch60: xsa335-qemu.patch Patch61: xsa335-trad.patch -Patch62: xsa333.patch -Patch63: xsa334.patch -Patch64: xsa336.patch -Patch65: xsa337-4.13-1.patch -Patch66: xsa337-4.13-2.patch -Patch67: xsa338.patch -Patch68: xsa339.patch -Patch69: xsa340.patch -Patch70: xsa342-4.13.patch -Patch71: xsa343-1.patch -Patch72: xsa343-2.patch -Patch73: xsa343-3.patch -Patch74: xsa344-4.13-1.patch -Patch75: xsa344-4.13-2.patch -Patch76: xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch -Patch77: xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch -Patch78: xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch -Patch79: xsa346-4.13-1.patch -Patch80: xsa346-4.13-2.patch -Patch81: xsa347-4.13-1.patch -Patch82: xsa347-4.13-2.patch -Patch83: xsa347-4.13-3.patch -Patch84: xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch -Patch85: xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch -Patch86: xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch -Patch87: xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch -Patch88: xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch -Patch89: xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch - %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -364,49 +321,7 @@ manage Xen virtual machines. %patch42 -p1 %patch44 -p1 %patch45 -p1 -%patch46 -p1 -%patch47 -p1 -%patch48 -p1 -%patch49 -p1 -%patch50 -p1 -%patch51 -p1 -%patch52 -p1 -%patch53 -p1 -%patch54 -p1 -%patch55 -p1 -%patch56 -p1 -%patch57 -p1 -%patch58 -p1 -%patch59 -p1 %patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 -%patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 -%patch88 -p1 -%patch89 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -1000,6 +915,10 @@ fi %endif %changelog +* Tue Nov 03 2020 Michael Young - 4.13.2-1 +- update to 4.13.2 + remove patches now included or superceded upstream + * Wed Oct 28 2020 Michael Young - 4.13.1-8 - x86 PV guest INVLPG-like flushes may leave stale TLB entries [XSA-286, CVE-2020-27674] (#1891092) diff --git a/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch b/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch deleted file mode 100644 index 0d39c48..0000000 --- a/xsa286-4.13-0001-x86-mm-split-L4-and-L3-parts-of-the-walk-out-of-do_p.patch +++ /dev/null @@ -1,73 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: split L4 and L3 parts of the walk out of do_page_walk() - -The L3 one at least is going to be re-used by a subsequent patch, and -splitting the L4 one then as well seems only natural. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index db4f035d8d..b1582b56fb 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -44,26 +44,47 @@ unsigned int __read_mostly m2p_compat_vstart = __HYPERVISOR_COMPAT_VIRT_START; - - l2_pgentry_t *compat_idle_pg_table_l2; - --void *do_page_walk(struct vcpu *v, unsigned long addr) -+static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(v->arch.guest_table); -- l4_pgentry_t l4e, *l4t; -- l3_pgentry_t l3e, *l3t; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -+ unsigned long mfn = pagetable_get_pfn(root); -+ l4_pgentry_t *l4t, l4e; - -- if ( !is_pv_vcpu(v) || !is_canonical_address(addr) ) -- return NULL; -+ if ( !is_canonical_address(addr) ) -+ return l4e_empty(); - - l4t = map_domain_page(_mfn(mfn)); - l4e = l4t[l4_table_offset(addr)]; - unmap_domain_page(l4t); -+ -+ return l4e; -+} -+ -+static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) -+{ -+ l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -+ l3_pgentry_t *l3t, l3e; -+ - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) -- return NULL; -+ return l3e_empty(); - - l3t = map_l3t_from_l4e(l4e); - l3e = l3t[l3_table_offset(addr)]; - unmap_domain_page(l3t); -+ -+ return l3e; -+} -+ -+void *do_page_walk(struct vcpu *v, unsigned long addr) -+{ -+ l3_pgentry_t l3e; -+ l2_pgentry_t l2e, *l2t; -+ l1_pgentry_t l1e, *l1t; -+ unsigned long mfn; -+ -+ if ( !is_pv_vcpu(v) ) -+ return NULL; -+ -+ l3e = page_walk_get_l3e(v->arch.guest_table, addr); - mfn = l3e_get_pfn(l3e); - if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) - return NULL; diff --git a/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch b/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch deleted file mode 100644 index ea923d4..0000000 --- a/xsa286-4.13-0002-x86-mm-check-page-types-in-do_page_walk.patch +++ /dev/null @@ -1,170 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: check page types in do_page_walk() - -For page table entries read to be guaranteed valid, transiently locking -the pages and validating their types is necessary. Note that guest use -of linear page tables is intentionally not taken into account here, as -ordinary data (guest stacks) can't possibly live inside page tables. - -This is part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index b1582b56fb..7d439639b7 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -46,15 +46,29 @@ l2_pgentry_t *compat_idle_pg_table_l2; - - static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - { -- unsigned long mfn = pagetable_get_pfn(root); -- l4_pgentry_t *l4t, l4e; -+ mfn_t mfn = pagetable_get_mfn(root); -+ /* current's root page table can't disappear under our feet. */ -+ bool need_lock = !mfn_eq(mfn, pagetable_get_mfn(current->arch.guest_table)); -+ struct page_info *pg; -+ l4_pgentry_t l4e = l4e_empty(); - - if ( !is_canonical_address(addr) ) - return l4e_empty(); - -- l4t = map_domain_page(_mfn(mfn)); -- l4e = l4t[l4_table_offset(addr)]; -- unmap_domain_page(l4t); -+ pg = mfn_to_page(mfn); -+ if ( need_lock && !page_lock(pg) ) -+ return l4e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l4_page_table ) -+ { -+ l4_pgentry_t *l4t = map_domain_page(mfn); -+ -+ l4e = l4t[l4_table_offset(addr)]; -+ unmap_domain_page(l4t); -+ } -+ -+ if ( need_lock ) -+ page_unlock(pg); - - return l4e; - } -@@ -62,14 +76,26 @@ static l4_pgentry_t page_walk_get_l4e(pagetable_t root, unsigned long addr) - static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - { - l4_pgentry_t l4e = page_walk_get_l4e(root, addr); -- l3_pgentry_t *l3t, l3e; -+ mfn_t mfn = l4e_get_mfn(l4e); -+ struct page_info *pg; -+ l3_pgentry_t l3e = l3e_empty(); - - if ( !(l4e_get_flags(l4e) & _PAGE_PRESENT) ) - return l3e_empty(); - -- l3t = map_l3t_from_l4e(l4e); -- l3e = l3t[l3_table_offset(addr)]; -- unmap_domain_page(l3t); -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l3e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l3_page_table ) -+ { -+ l3_pgentry_t *l3t = map_domain_page(mfn); -+ -+ l3e = l3t[l3_table_offset(addr)]; -+ unmap_domain_page(l3t); -+ } -+ -+ page_unlock(pg); - - return l3e; - } -@@ -77,44 +103,67 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -- l2_pgentry_t l2e, *l2t; -- l1_pgentry_t l1e, *l1t; -- unsigned long mfn; -+ l2_pgentry_t l2e = l2e_empty(); -+ l1_pgentry_t l1e = l1e_empty(); -+ mfn_t mfn; -+ struct page_info *pg; - - if ( !is_pv_vcpu(v) ) - return NULL; - - l3e = page_walk_get_l3e(v->arch.guest_table, addr); -- mfn = l3e_get_pfn(l3e); -- if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ mfn = l3e_get_mfn(l3e); -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l3e_get_flags(l3e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L3_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l2t = map_domain_page(_mfn(mfn)); -- l2e = l2t[l2_table_offset(addr)]; -- unmap_domain_page(l2t); -- mfn = l2e_get_pfn(l2e); -- if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ const l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l2e_get_mfn(l2e); -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - if ( (l2e_get_flags(l2e) & _PAGE_PSE) ) - { -- mfn += PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1)); -+ mfn = mfn_add(mfn, PFN_DOWN(addr & ((1UL << L2_PAGETABLE_SHIFT) - 1))); - goto ret; - } - -- l1t = map_domain_page(_mfn(mfn)); -- l1e = l1t[l1_table_offset(addr)]; -- unmap_domain_page(l1t); -- mfn = l1e_get_pfn(l1e); -- if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(_mfn(mfn)) ) -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return NULL; -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ const l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ mfn = l1e_get_mfn(l1e); -+ if ( !(l1e_get_flags(l1e) & _PAGE_PRESENT) || !mfn_valid(mfn) ) - return NULL; - - ret: -- return map_domain_page(_mfn(mfn)) + (addr & ~PAGE_MASK); -+ return map_domain_page(mfn) + (addr & ~PAGE_MASK); - } - - /* diff --git a/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch b/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch deleted file mode 100644 index dcf3367..0000000 --- a/xsa286-4.13-0003-x86-mm-avoid-using-linear-page-tables-in-map_guest_l.patch +++ /dev/null @@ -1,92 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in map_guest_l1e() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Replace the linear L2 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Signed-off-by: Roger Pau Monné -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index 2b0dadc8da..acebf9e957 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -40,11 +40,14 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - if ( unlikely(!__addr_ok(linear)) ) - return NULL; - -- /* Find this l1e and its enclosing l1mfn in the linear map. */ -- if ( __copy_from_user(&l2e, -- &__linear_l2_table[l2_linear_offset(linear)], -- sizeof(l2_pgentry_t)) ) -+ if ( unlikely(!(current->arch.flags & TF_kernel_mode)) ) -+ { -+ ASSERT_UNREACHABLE(); - return NULL; -+ } -+ -+ /* Find this l1e and its enclosing l1mfn. */ -+ l2e = page_walk_get_l2e(current->arch.guest_table, linear); - - /* Check flags that it will be safe to read the l1e. */ - if ( (l2e_get_flags(l2e) & (_PAGE_PRESENT | _PAGE_PSE)) != _PAGE_PRESENT ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 7d439639b7..670aa3f892 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -100,6 +100,34 @@ static l3_pgentry_t page_walk_get_l3e(pagetable_t root, unsigned long addr) - return l3e; - } - -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) -+{ -+ l3_pgentry_t l3e = page_walk_get_l3e(root, addr); -+ mfn_t mfn = l3e_get_mfn(l3e); -+ struct page_info *pg; -+ l2_pgentry_t l2e = l2e_empty(); -+ -+ if ( !(l3e_get_flags(l3e) & _PAGE_PRESENT) || -+ (l3e_get_flags(l3e) & _PAGE_PSE) ) -+ return l2e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l2e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l2_page_table ) -+ { -+ l2_pgentry_t *l2t = map_domain_page(mfn); -+ -+ l2e = l2t[l2_table_offset(addr)]; -+ unmap_domain_page(l2t); -+ } -+ -+ page_unlock(pg); -+ -+ return l2e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index 320c6cd196..cd3e7ec501 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -577,7 +577,9 @@ void audit_domains(void); - void make_cr3(struct vcpu *v, mfn_t mfn); - void update_cr3(struct vcpu *v); - int vcpu_destroy_pagetables(struct vcpu *); -+ - void *do_page_walk(struct vcpu *v, unsigned long addr); -+l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); - - int __sync_local_execstate(void); - diff --git a/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch b/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch deleted file mode 100644 index b15efaf..0000000 --- a/xsa286-4.13-0004-x86-mm-avoid-using-linear-page-tables-in-guest_get_e.patch +++ /dev/null @@ -1,172 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using linear page tables in guest_get_eff_kern_l1e() - -First of all drop guest_get_eff_l1e() entirely - there's no actual user -of it: pv_ro_page_fault() has a guest_kernel_mode() conditional around -its only call site. - -Then replace the linear L1 table access by an actual page walk. - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/pv/mm.c b/xen/arch/x86/pv/mm.c -index acebf9e957..7624447246 100644 ---- a/xen/arch/x86/pv/mm.c -+++ b/xen/arch/x86/pv/mm.c -@@ -59,27 +59,6 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn) - } - - /* -- * Read the guest's l1e that maps this address, from the kernel-mode -- * page tables. -- */ --static l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) --{ -- struct vcpu *curr = current; -- const bool user_mode = !(curr->arch.flags & TF_kernel_mode); -- l1_pgentry_t l1e; -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- l1e = guest_get_eff_l1e(linear); -- -- if ( user_mode ) -- toggle_guest_pt(curr); -- -- return l1e; --} -- --/* - * Map a guest's LDT page (covering the byte at @offset from start of the LDT) - * into Xen's virtual range. Returns true if the mapping changed, false - * otherwise. -diff --git a/xen/arch/x86/pv/mm.h b/xen/arch/x86/pv/mm.h -index a1bd473b29..43d33a1fd1 100644 ---- a/xen/arch/x86/pv/mm.h -+++ b/xen/arch/x86/pv/mm.h -@@ -5,19 +5,19 @@ l1_pgentry_t *map_guest_l1e(unsigned long linear, mfn_t *gl1mfn); - - int new_guest_cr3(mfn_t mfn); - --/* Read a PV guest's l1e that maps this linear address. */ --static inline l1_pgentry_t guest_get_eff_l1e(unsigned long linear) -+/* -+ * Read the guest's l1e that maps this address, from the kernel-mode -+ * page tables. -+ */ -+static inline l1_pgentry_t guest_get_eff_kern_l1e(unsigned long linear) - { -- l1_pgentry_t l1e; -+ l1_pgentry_t l1e = l1e_empty(); - - ASSERT(!paging_mode_translate(current->domain)); - ASSERT(!paging_mode_external(current->domain)); - -- if ( unlikely(!__addr_ok(linear)) || -- __copy_from_user(&l1e, -- &__linear_l1_table[l1_linear_offset(linear)], -- sizeof(l1_pgentry_t)) ) -- l1e = l1e_empty(); -+ if ( likely(__addr_ok(linear)) ) -+ l1e = page_walk_get_l1e(current->arch.guest_table, linear); - - return l1e; - } -diff --git a/xen/arch/x86/pv/ro-page-fault.c b/xen/arch/x86/pv/ro-page-fault.c -index a920fb5e15..2bf4497a16 100644 ---- a/xen/arch/x86/pv/ro-page-fault.c -+++ b/xen/arch/x86/pv/ro-page-fault.c -@@ -357,7 +357,7 @@ int pv_ro_page_fault(unsigned long addr, struct cpu_user_regs *regs) - bool mmio_ro; - - /* Attempt to read the PTE that maps the VA being accessed. */ -- pte = guest_get_eff_l1e(addr); -+ pte = guest_get_eff_kern_l1e(addr); - - /* We are only looking for read-only mappings */ - if ( ((l1e_get_flags(pte) & (_PAGE_PRESENT | _PAGE_RW)) != _PAGE_PRESENT) ) -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index 670aa3f892..c5686e0d25 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -128,6 +128,62 @@ l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr) - return l2e; - } - -+/* -+ * For now no "set_accessed" parameter, as all callers want it set to true. -+ * For now also no "set_dirty" parameter, as all callers deal with r/o -+ * mappings, and we don't want to set the dirty bit there (conflicts with -+ * CET-SS). However, as there are CPUs which may set the dirty bit on r/o -+ * PTEs, the logic below tolerates the bit becoming set "behind our backs". -+ */ -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr) -+{ -+ l2_pgentry_t l2e = page_walk_get_l2e(root, addr); -+ mfn_t mfn = l2e_get_mfn(l2e); -+ struct page_info *pg; -+ l1_pgentry_t l1e = l1e_empty(); -+ -+ if ( !(l2e_get_flags(l2e) & _PAGE_PRESENT) || -+ (l2e_get_flags(l2e) & _PAGE_PSE) ) -+ return l1e_empty(); -+ -+ pg = mfn_to_page(mfn); -+ if ( !page_lock(pg) ) -+ return l1e_empty(); -+ -+ if ( (pg->u.inuse.type_info & PGT_type_mask) == PGT_l1_page_table ) -+ { -+ l1_pgentry_t *l1t = map_domain_page(mfn); -+ -+ l1e = l1t[l1_table_offset(addr)]; -+ -+ if ( (l1e_get_flags(l1e) & (_PAGE_ACCESSED | _PAGE_PRESENT)) == -+ _PAGE_PRESENT ) -+ { -+ l1_pgentry_t ol1e = l1e; -+ -+ l1e_add_flags(l1e, _PAGE_ACCESSED); -+ /* -+ * Best effort only; with the lock held the page shouldn't -+ * change anyway, except for the dirty bit to perhaps become set. -+ */ -+ while ( cmpxchg(&l1e_get_intpte(l1t[l1_table_offset(addr)]), -+ l1e_get_intpte(ol1e), l1e_get_intpte(l1e)) != -+ l1e_get_intpte(ol1e) && -+ !(l1e_get_flags(l1e) & _PAGE_DIRTY) ) -+ { -+ l1e_add_flags(ol1e, _PAGE_DIRTY); -+ l1e_add_flags(l1e, _PAGE_DIRTY); -+ } -+ } -+ -+ unmap_domain_page(l1t); -+ } -+ -+ page_unlock(pg); -+ -+ return l1e; -+} -+ - void *do_page_walk(struct vcpu *v, unsigned long addr) - { - l3_pgentry_t l3e; -diff --git a/xen/include/asm-x86/mm.h b/xen/include/asm-x86/mm.h -index cd3e7ec501..865db999c1 100644 ---- a/xen/include/asm-x86/mm.h -+++ b/xen/include/asm-x86/mm.h -@@ -580,6 +580,7 @@ int vcpu_destroy_pagetables(struct vcpu *); - - void *do_page_walk(struct vcpu *v, unsigned long addr); - l2_pgentry_t page_walk_get_l2e(pagetable_t root, unsigned long addr); -+l1_pgentry_t page_walk_get_l1e(pagetable_t root, unsigned long addr); - - int __sync_local_execstate(void); - diff --git a/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch b/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch deleted file mode 100644 index 21506b5..0000000 --- a/xsa286-4.13-0005-x86-mm-avoid-using-top-level-linear-page-tables-in-u.patch +++ /dev/null @@ -1,101 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: avoid using top level linear page tables in - {,un}map_domain_page() - -Move the page table recursion two levels down. This entails avoiding -to free the recursive mapping prematurely in free_perdomain_mappings(). - -This is part of XSA-286. - -Reported-by: Jann Horn -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/domain_page.c b/xen/arch/x86/domain_page.c -index 4a07cfb18e..660bd06aaf 100644 ---- a/xen/arch/x86/domain_page.c -+++ b/xen/arch/x86/domain_page.c -@@ -65,7 +65,8 @@ void __init mapcache_override_current(struct vcpu *v) - #define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER) - #define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1) - #define MAPCACHE_L1ENT(idx) \ -- __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))] -+ ((l1_pgentry_t *)(MAPCACHE_VIRT_START | \ -+ ((L2_PAGETABLE_ENTRIES - 1) << L2_PAGETABLE_SHIFT)))[idx] - - void *map_domain_page(mfn_t mfn) - { -@@ -235,6 +236,7 @@ int mapcache_domain_init(struct domain *d) - { - struct mapcache_domain *dcache = &d->arch.pv.mapcache; - unsigned int bitmap_pages; -+ int rc; - - ASSERT(is_pv_domain(d)); - -@@ -243,8 +245,10 @@ int mapcache_domain_init(struct domain *d) - return 0; - #endif - -+ BUILD_BUG_ON(MAPCACHE_VIRT_START & ((1 << L3_PAGETABLE_SHIFT) - 1)); - BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 + -- 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) > -+ 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) + -+ (1U << L2_PAGETABLE_SHIFT) > - MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20)); - bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long)); - dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE; -@@ -253,9 +257,25 @@ int mapcache_domain_init(struct domain *d) - - spin_lock_init(&dcache->lock); - -- return create_perdomain_mapping(d, (unsigned long)dcache->inuse, -- 2 * bitmap_pages + 1, -- NIL(l1_pgentry_t *), NULL); -+ rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse, -+ 2 * bitmap_pages + 1, -+ NIL(l1_pgentry_t *), NULL); -+ if ( !rc ) -+ { -+ /* -+ * Install mapping of our L2 table into its own last slot, for easy -+ * access to the L1 entries via MAPCACHE_L1ENT(). -+ */ -+ l3_pgentry_t *l3t = __map_domain_page(d->arch.perdomain_l3_pg); -+ l3_pgentry_t l3e = l3t[l3_table_offset(MAPCACHE_VIRT_END)]; -+ l2_pgentry_t *l2t = map_l2t_from_l3e(l3e); -+ -+ l2e_get_intpte(l2t[L2_PAGETABLE_ENTRIES - 1]) = l3e_get_intpte(l3e); -+ unmap_domain_page(l2t); -+ unmap_domain_page(l3t); -+ } -+ -+ return rc; - } - - int mapcache_vcpu_init(struct vcpu *v) -@@ -346,7 +366,7 @@ mfn_t domain_page_map_to_mfn(const void *ptr) - else - { - ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END); -- pl1e = &__linear_l1_table[l1_linear_offset(va)]; -+ pl1e = &MAPCACHE_L1ENT(PFN_DOWN(va - MAPCACHE_VIRT_START)); - } - - return l1e_get_mfn(*pl1e); -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 30dffb68e8..279664a83e 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -6031,6 +6031,10 @@ void free_perdomain_mappings(struct domain *d) - { - struct page_info *l1pg = l2e_get_page(l2tab[j]); - -+ /* mapcache_domain_init() installs a recursive entry. */ -+ if ( l1pg == l2pg ) -+ continue; -+ - if ( l2e_get_flags(l2tab[j]) & _PAGE_AVAIL0 ) - { - l1_pgentry_t *l1tab = __map_domain_page(l1pg); diff --git a/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch b/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch deleted file mode 100644 index 6c9d393..0000000 --- a/xsa286-4.13-0006-x86-mm-restrict-use-of-linear-page-tables-to-shadow-.patch +++ /dev/null @@ -1,106 +0,0 @@ -From: Jan Beulich -Subject: x86/mm: restrict use of linear page tables to shadow mode code - -Other code does not require them to be set up anymore, so restrict when -to populate the respective L4 slot and reduce visibility of the -accessors. - -While with the removal of all uses the vulnerability is actually fixed, -removing the creation of the linear mapping adds an extra layer of -protection. Similarly reducing visibility of the accessors mostly -eliminates the risk of undue re-introduction of uses of the linear -mappings. - -This is (not strictly) part of XSA-286. - -Signed-off-by: Jan Beulich -Reviewed-by: George Dunlap -Reviewed-by: Andrew Cooper - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 279664a83e..fa0f813d29 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -1757,9 +1757,10 @@ void init_xen_l4_slots(l4_pgentry_t *l4t, mfn_t l4mfn, - l4t[l4_table_offset(PCI_MCFG_VIRT_START)] = - idle_pg_table[l4_table_offset(PCI_MCFG_VIRT_START)]; - -- /* Slot 258: Self linear mappings. */ -+ /* Slot 258: Self linear mappings (shadow pt only). */ - ASSERT(!mfn_eq(l4mfn, INVALID_MFN)); - l4t[l4_table_offset(LINEAR_PT_VIRT_START)] = -+ !shadow_mode_external(d) ? l4e_empty() : - l4e_from_mfn(l4mfn, __PAGE_HYPERVISOR_RW); - - /* Slot 259: Shadow linear mappings (if applicable) .*/ -diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h -index 3217777921..b214087194 100644 ---- a/xen/arch/x86/mm/shadow/private.h -+++ b/xen/arch/x86/mm/shadow/private.h -@@ -135,6 +135,15 @@ enum { - # define GUEST_PTE_SIZE 4 - #endif - -+/* Where to find each level of the linear mapping */ -+#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) -+#define __linear_l2_table \ -+ ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l3_table \ -+ ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) -+#define __linear_l4_table \ -+ ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -+ - /****************************************************************************** - * Auditing routines - */ -diff --git a/xen/arch/x86/x86_64/mm.c b/xen/arch/x86/x86_64/mm.c -index c5686e0d25..dcb20d1d9d 100644 ---- a/xen/arch/x86/x86_64/mm.c -+++ b/xen/arch/x86/x86_64/mm.c -@@ -833,9 +833,6 @@ void __init paging_init(void) - - machine_to_phys_mapping_valid = 1; - -- /* Set up linear page table mapping. */ -- l4e_write(&idle_pg_table[l4_table_offset(LINEAR_PT_VIRT_START)], -- l4e_from_paddr(__pa(idle_pg_table), __PAGE_HYPERVISOR_RW)); - return; - - nomem: -diff --git a/xen/include/asm-x86/config.h b/xen/include/asm-x86/config.h -index 8d79a71398..9d587a076a 100644 ---- a/xen/include/asm-x86/config.h -+++ b/xen/include/asm-x86/config.h -@@ -193,7 +193,7 @@ extern unsigned char boot_edid_info[128]; - */ - #define PCI_MCFG_VIRT_START (PML4_ADDR(257)) - #define PCI_MCFG_VIRT_END (PCI_MCFG_VIRT_START + PML4_ENTRY_BYTES) --/* Slot 258: linear page table (guest table). */ -+/* Slot 258: linear page table (monitor table, HVM only). */ - #define LINEAR_PT_VIRT_START (PML4_ADDR(258)) - #define LINEAR_PT_VIRT_END (LINEAR_PT_VIRT_START + PML4_ENTRY_BYTES) - /* Slot 259: linear page table (shadow table). */ -diff --git a/xen/include/asm-x86/page.h b/xen/include/asm-x86/page.h -index c1e92937c0..e72c277b9f 100644 ---- a/xen/include/asm-x86/page.h -+++ b/xen/include/asm-x86/page.h -@@ -274,19 +274,6 @@ void copy_page_sse2(void *, const void *); - #define vmap_to_mfn(va) _mfn(l1e_get_pfn(*virt_to_xen_l1e((unsigned long)(va)))) - #define vmap_to_page(va) mfn_to_page(vmap_to_mfn(va)) - --#endif /* !defined(__ASSEMBLY__) */ -- --/* Where to find each level of the linear mapping */ --#define __linear_l1_table ((l1_pgentry_t *)(LINEAR_PT_VIRT_START)) --#define __linear_l2_table \ -- ((l2_pgentry_t *)(__linear_l1_table + l1_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l3_table \ -- ((l3_pgentry_t *)(__linear_l2_table + l2_linear_offset(LINEAR_PT_VIRT_START))) --#define __linear_l4_table \ -- ((l4_pgentry_t *)(__linear_l3_table + l3_linear_offset(LINEAR_PT_VIRT_START))) -- -- --#ifndef __ASSEMBLY__ - extern root_pgentry_t idle_pg_table[ROOT_PAGETABLE_ENTRIES]; - extern l2_pgentry_t *compat_idle_pg_table_l2; - extern unsigned int m2p_compat_vstart; diff --git a/xsa317.patch b/xsa317.patch deleted file mode 100644 index 20e2c64..0000000 --- a/xsa317.patch +++ /dev/null @@ -1,50 +0,0 @@ -From aeb46e92f915f19a61d5a8a1f4b696793f64e6fb Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 19 Mar 2020 13:17:31 +0000 -Subject: [PATCH] xen/common: event_channel: Don't ignore error in - get_free_port() - -Currently, get_free_port() is assuming that the port has been allocated -when evtchn_allocate_port() is not return -EBUSY. - -However, the function may return an error when: - - We exhausted all the event channels. This can happen if the limit - configured by the administrator for the guest ('max_event_channels' - in xl cfg) is higher than the ABI used by the guest. For instance, - if the guest is using 2L, the limit should not be higher than 4095. - - We cannot allocate memory (e.g Xen has not more memory). - -Users of get_free_port() (such as EVTCHNOP_alloc_unbound) will validly -assuming the port was valid and will next call evtchn_from_port(). This -will result to a crash as the memory backing the event channel structure -is not present. - -Fixes: 368ae9a05fe ("xen/pvshim: forward evtchn ops between L0 Xen and L2 DomU") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/event_channel.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c -index e86e2bfab0..a8d182b584 100644 ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -195,10 +195,10 @@ static int get_free_port(struct domain *d) - { - int rc = evtchn_allocate_port(d, port); - -- if ( rc == -EBUSY ) -- continue; -- -- return port; -+ if ( rc == 0 ) -+ return port; -+ else if ( rc != -EBUSY ) -+ return rc; - } - - return -ENOSPC; --- -2.17.1 - diff --git a/xsa319.patch b/xsa319.patch deleted file mode 100644 index 769443c..0000000 --- a/xsa319.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Jan Beulich -Subject: x86/shadow: correct an inverted conditional in dirty VRAM tracking - -This originally was "mfn_x(mfn) == INVALID_MFN". Make it like this -again, taking the opportunity to also drop the unnecessary nearby -braces. - -This is XSA-319. - -Fixes: 246a5a3377c2 ("xen: Use a typesafe to define INVALID_MFN") -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper - ---- a/xen/arch/x86/mm/shadow/common.c -+++ b/xen/arch/x86/mm/shadow/common.c -@@ -3252,10 +3252,8 @@ int shadow_track_dirty_vram(struct domai - int dirty = 0; - paddr_t sl1ma = dirty_vram->sl1ma[i]; - -- if ( !mfn_eq(mfn, INVALID_MFN) ) -- { -+ if ( mfn_eq(mfn, INVALID_MFN) ) - dirty = 1; -- } - else - { - page = mfn_to_page(mfn); diff --git a/xsa320-4.13-1.patch b/xsa320-4.13-1.patch deleted file mode 100644 index 09eb8ea..0000000 --- a/xsa320-4.13-1.patch +++ /dev/null @@ -1,117 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: CPUID/MSR definitions for Special Register Buffer Data Sampling - -This is part of XSA-320 / CVE-2020-0543 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Acked-by: Wei Liu - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 1d9d816622..9268454297 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -483,10 +483,10 @@ accounting for hardware capabilities as enumerated via CPUID. - - Currently accepted: - --The Speculation Control hardware features `md-clear`, `ibrsb`, `stibp`, `ibpb`, --`l1d-flush` and `ssbd` are used by default if available and applicable. They can --be ignored, e.g. `no-ibrsb`, at which point Xen won't use them itself, and --won't offer them to guests. -+The Speculation Control hardware features `srbds-ctrl`, `md-clear`, `ibrsb`, -+`stibp`, `ibpb`, `l1d-flush` and `ssbd` are used by default if available and -+applicable. They can be ignored, e.g. `no-ibrsb`, at which point Xen won't -+use them itself, and won't offer them to guests. - - ### cpuid_mask_cpu - > `= fam_0f_rev_[cdefg] | fam_10_rev_[bc] | fam_11_rev_b` -diff --git a/tools/libxl/libxl_cpuid.c b/tools/libxl/libxl_cpuid.c -index 6cea4227ba..a78f08b927 100644 ---- a/tools/libxl/libxl_cpuid.c -+++ b/tools/libxl/libxl_cpuid.c -@@ -213,6 +213,7 @@ int libxl_cpuid_parse_config(libxl_cpuid_policy_list *cpuid, const char* str) - - {"avx512-4vnniw",0x00000007, 0, CPUID_REG_EDX, 2, 1}, - {"avx512-4fmaps",0x00000007, 0, CPUID_REG_EDX, 3, 1}, -+ {"srbds-ctrl", 0x00000007, 0, CPUID_REG_EDX, 9, 1}, - {"md-clear", 0x00000007, 0, CPUID_REG_EDX, 10, 1}, - {"cet-ibt", 0x00000007, 0, CPUID_REG_EDX, 20, 1}, - {"ibrsb", 0x00000007, 0, CPUID_REG_EDX, 26, 1}, -diff --git a/tools/misc/xen-cpuid.c b/tools/misc/xen-cpuid.c -index 603e1d65fd..a09440813b 100644 ---- a/tools/misc/xen-cpuid.c -+++ b/tools/misc/xen-cpuid.c -@@ -157,6 +157,7 @@ static const char *const str_7d0[32] = - [ 2] = "avx512_4vnniw", [ 3] = "avx512_4fmaps", - [ 4] = "fsrm", - -+ /* 8 */ [ 9] = "srbds-ctrl", - [10] = "md-clear", - /* 12 */ [13] = "tsx-force-abort", - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 4b12103482..0cded3c0ad 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -134,6 +134,7 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - /* Write-only */ - case MSR_TSX_FORCE_ABORT: - case MSR_TSX_CTRL: -+ case MSR_MCU_OPT_CTRL: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -@@ -288,6 +289,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - /* Read-only */ - case MSR_TSX_FORCE_ABORT: - case MSR_TSX_CTRL: -+ case MSR_MCU_OPT_CTRL: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 6656c44aec..5fc1c6827e 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -312,12 +312,13 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - printk("Speculative mitigation facilities:\n"); - - /* Hardware features which pertain to speculative mitigations. */ -- printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", -+ printk(" Hardware features:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", - (_7d0 & cpufeat_mask(X86_FEATURE_IBRSB)) ? " IBRS/IBPB" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_STIBP)) ? " STIBP" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_L1D_FLUSH)) ? " L1D_FLUSH" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_SSBD)) ? " SSBD" : "", - (_7d0 & cpufeat_mask(X86_FEATURE_MD_CLEAR)) ? " MD_CLEAR" : "", -+ (_7d0 & cpufeat_mask(X86_FEATURE_SRBDS_CTRL)) ? " SRBDS_CTRL" : "", - (e8b & cpufeat_mask(X86_FEATURE_IBPB)) ? " IBPB" : "", - (caps & ARCH_CAPS_IBRS_ALL) ? " IBRS_ALL" : "", - (caps & ARCH_CAPS_RDCL_NO) ? " RDCL_NO" : "", -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 7693c4a71a..91994669e1 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -179,6 +179,9 @@ - #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x490 - #define MSR_IA32_VMX_VMFUNC 0x491 - -+#define MSR_MCU_OPT_CTRL 0x00000123 -+#define MCU_OPT_CTRL_RNGDS_MITG_DIS (_AC(1, ULL) << 0) -+ - #define MSR_U_CET 0x000006a0 - #define MSR_S_CET 0x000006a2 - #define MSR_PL0_SSP 0x000006a4 -diff --git a/xen/include/public/arch-x86/cpufeatureset.h b/xen/include/public/arch-x86/cpufeatureset.h -index 2835688f1c..a2482c3627 100644 ---- a/xen/include/public/arch-x86/cpufeatureset.h -+++ b/xen/include/public/arch-x86/cpufeatureset.h -@@ -252,6 +252,7 @@ XEN_CPUFEATURE(IBPB, 8*32+12) /*A IBPB support only (no IBRS, used by - /* Intel-defined CPU features, CPUID level 0x00000007:0.edx, word 9 */ - XEN_CPUFEATURE(AVX512_4VNNIW, 9*32+ 2) /*A AVX512 Neural Network Instructions */ - XEN_CPUFEATURE(AVX512_4FMAPS, 9*32+ 3) /*A AVX512 Multiply Accumulation Single Precision */ -+XEN_CPUFEATURE(SRBDS_CTRL, 9*32+ 9) /* MSR_MCU_OPT_CTRL and RNGDS_MITG_DIS. */ - XEN_CPUFEATURE(MD_CLEAR, 9*32+10) /*A VERW clears microarchitectural buffers */ - XEN_CPUFEATURE(TSX_FORCE_ABORT, 9*32+13) /* MSR_TSX_FORCE_ABORT.RTM_ABORT */ - XEN_CPUFEATURE(CET_IBT, 9*32+20) /* CET - Indirect Branch Tracking */ diff --git a/xsa320-4.13-2.patch b/xsa320-4.13-2.patch deleted file mode 100644 index 8a8080a..0000000 --- a/xsa320-4.13-2.patch +++ /dev/null @@ -1,179 +0,0 @@ -From: Andrew Cooper -Subject: x86/spec-ctrl: Mitigate the Special Register Buffer Data Sampling sidechannel - -See patch documentation and comments. - -This is part of XSA-320 / CVE-2020-0543 - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/docs/misc/xen-command-line.pandoc b/docs/misc/xen-command-line.pandoc -index 9268454297..c780312531 100644 ---- a/docs/misc/xen-command-line.pandoc -+++ b/docs/misc/xen-command-line.pandoc -@@ -1991,7 +1991,7 @@ By default SSBD will be mitigated at runtime (i.e `ssbd=runtime`). - ### spec-ctrl (x86) - > `= List of [ , xen=, {pv,hvm,msr-sc,rsb,md-clear}=, - > bti-thunk=retpoline|lfence|jmp, {ibrs,ibpb,ssbd,eager-fpu, --> l1d-flush,branch-harden}= ]` -+> l1d-flush,branch-harden,srb-lock}= ]` - - Controls for speculative execution sidechannel mitigations. By default, Xen - will pick the most appropriate mitigations based on compiled in support, -@@ -2068,6 +2068,12 @@ If Xen is compiled with `CONFIG_SPECULATIVE_HARDEN_BRANCH`, the - speculation barriers to protect selected conditional branches. By default, - Xen will enable this mitigation. - -+On hardware supporting SRBDS_CTRL, the `srb-lock=` option can be used to force -+or prevent Xen from protect the Special Register Buffer from leaking stale -+data. By default, Xen will enable this mitigation, except on parts where MDS -+is fixed and TAA is fixed/mitigated (in which case, there is believed to be no -+way for an attacker to obtain the stale data). -+ - ### sync_console - > `= ` - -diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c -index feb0f6ce20..75c6e34164 100644 ---- a/xen/arch/x86/acpi/power.c -+++ b/xen/arch/x86/acpi/power.c -@@ -295,6 +295,9 @@ static int enter_state(u32 state) - ci->spec_ctrl_flags |= (default_spec_ctrl_flags & SCF_ist_wrmsr); - spec_ctrl_exit_idle(ci); - -+ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); -+ - done: - spin_debug_enable(); - local_irq_restore(flags); -diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c -index dc8fdac1a1..b1e51b3aff 100644 ---- a/xen/arch/x86/smpboot.c -+++ b/xen/arch/x86/smpboot.c -@@ -361,12 +361,14 @@ void start_secondary(void *unused) - microcode_update_one(false); - - /* -- * If MSR_SPEC_CTRL is available, apply Xen's default setting and discard -- * any firmware settings. Note: MSR_SPEC_CTRL may only become available -- * after loading microcode. -+ * If any speculative control MSRs are available, apply Xen's default -+ * settings. Note: These MSRs may only become available after loading -+ * microcode. - */ - if ( boot_cpu_has(X86_FEATURE_IBRSB) ) - wrmsrl(MSR_SPEC_CTRL, default_xen_spec_ctrl); -+ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); - - tsx_init(); /* Needs microcode. May change HLE/RTM feature bits. */ - -diff --git a/xen/arch/x86/spec_ctrl.c b/xen/arch/x86/spec_ctrl.c -index 5fc1c6827e..33343062a7 100644 ---- a/xen/arch/x86/spec_ctrl.c -+++ b/xen/arch/x86/spec_ctrl.c -@@ -65,6 +65,9 @@ static unsigned int __initdata l1d_maxphysaddr; - static bool __initdata cpu_has_bug_msbds_only; /* => minimal HT impact. */ - static bool __initdata cpu_has_bug_mds; /* Any other M{LP,SB,FB}DS combination. */ - -+static int8_t __initdata opt_srb_lock = -1; -+uint64_t __read_mostly default_xen_mcu_opt_ctrl; -+ - static int __init parse_spec_ctrl(const char *s) - { - const char *ss; -@@ -112,6 +115,7 @@ static int __init parse_spec_ctrl(const char *s) - opt_ssbd = false; - opt_l1d_flush = 0; - opt_branch_harden = false; -+ opt_srb_lock = 0; - } - else if ( val > 0 ) - rc = -EINVAL; -@@ -178,6 +182,8 @@ static int __init parse_spec_ctrl(const char *s) - opt_l1d_flush = val; - else if ( (val = parse_boolean("branch-harden", s, ss)) >= 0 ) - opt_branch_harden = val; -+ else if ( (val = parse_boolean("srb-lock", s, ss)) >= 0 ) -+ opt_srb_lock = val; - else - rc = -EINVAL; - -@@ -341,7 +347,7 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - "\n"); - - /* Settings for Xen's protection, irrespective of guests. */ -- printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s\n", -+ printk(" Xen settings: BTI-Thunk %s, SPEC_CTRL: %s%s%s, Other:%s%s%s%s%s\n", - thunk == THUNK_NONE ? "N/A" : - thunk == THUNK_RETPOLINE ? "RETPOLINE" : - thunk == THUNK_LFENCE ? "LFENCE" : -@@ -352,6 +358,8 @@ static void __init print_details(enum ind_thunk thunk, uint64_t caps) - (default_xen_spec_ctrl & SPEC_CTRL_SSBD) ? " SSBD+" : " SSBD-", - !(caps & ARCH_CAPS_TSX_CTRL) ? "" : - (opt_tsx & 1) ? " TSX+" : " TSX-", -+ !boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ? "" : -+ opt_srb_lock ? " SRB_LOCK+" : " SRB_LOCK-", - opt_ibpb ? " IBPB" : "", - opt_l1d_flush ? " L1D_FLUSH" : "", - opt_md_clear_pv || opt_md_clear_hvm ? " VERW" : "", -@@ -1149,6 +1157,34 @@ void __init init_speculation_mitigations(void) - tsx_init(); - } - -+ /* Calculate suitable defaults for MSR_MCU_OPT_CTRL */ -+ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ { -+ uint64_t val; -+ -+ rdmsrl(MSR_MCU_OPT_CTRL, val); -+ -+ /* -+ * On some SRBDS-affected hardware, it may be safe to relax srb-lock -+ * by default. -+ * -+ * On parts which enumerate MDS_NO and not TAA_NO, TSX is the only way -+ * to access the Fill Buffer. If TSX isn't available (inc. SKU -+ * reasons on some models), or TSX is explicitly disabled, then there -+ * is no need for the extra overhead to protect RDRAND/RDSEED. -+ */ -+ if ( opt_srb_lock == -1 && -+ (caps & (ARCH_CAPS_MDS_NO|ARCH_CAPS_TAA_NO)) == ARCH_CAPS_MDS_NO && -+ (!cpu_has_hle || ((caps & ARCH_CAPS_TSX_CTRL) && opt_tsx == 0)) ) -+ opt_srb_lock = 0; -+ -+ val &= ~MCU_OPT_CTRL_RNGDS_MITG_DIS; -+ if ( !opt_srb_lock ) -+ val |= MCU_OPT_CTRL_RNGDS_MITG_DIS; -+ -+ default_xen_mcu_opt_ctrl = val; -+ } -+ - print_details(thunk, caps); - - /* -@@ -1180,6 +1216,9 @@ void __init init_speculation_mitigations(void) - - wrmsrl(MSR_SPEC_CTRL, bsp_delay_spec_ctrl ? 0 : default_xen_spec_ctrl); - } -+ -+ if ( boot_cpu_has(X86_FEATURE_SRBDS_CTRL) ) -+ wrmsrl(MSR_MCU_OPT_CTRL, default_xen_mcu_opt_ctrl); - } - - static void __init __maybe_unused build_assertions(void) -diff --git a/xen/include/asm-x86/spec_ctrl.h b/xen/include/asm-x86/spec_ctrl.h -index 9caecddfec..b252bb8631 100644 ---- a/xen/include/asm-x86/spec_ctrl.h -+++ b/xen/include/asm-x86/spec_ctrl.h -@@ -54,6 +54,8 @@ extern int8_t opt_pv_l1tf_hwdom, opt_pv_l1tf_domu; - */ - extern paddr_t l1tf_addr_mask, l1tf_safe_maddr; - -+extern uint64_t default_xen_mcu_opt_ctrl; -+ - static inline void init_shadow_spec_ctrl_state(void) - { - struct cpu_info *info = get_cpu_info(); diff --git a/xsa321-4.13-1.patch b/xsa321-4.13-1.patch deleted file mode 100644 index 9a08ab2..0000000 --- a/xsa321-4.13-1.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Jan Beulich -Subject: vtd: improve IOMMU TLB flush - -Do not limit PSI flushes to order 0 pages, in order to avoid doing a -full TLB flush if the passed in page has an order greater than 0 and -is aligned. Should increase the performance of IOMMU TLB flushes when -dealing with page orders greater than 0. - -This is part of XSA-321. - -Signed-off-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -570,13 +570,14 @@ static int __must_check iommu_flush_iotl - if ( iommu_domid == -1 ) - continue; - -- if ( page_count != 1 || dfn_eq(dfn, INVALID_DFN) ) -+ if ( !page_count || (page_count & (page_count - 1)) || -+ dfn_eq(dfn, INVALID_DFN) || !IS_ALIGNED(dfn_x(dfn), page_count) ) - rc = iommu_flush_iotlb_dsi(iommu, iommu_domid, - 0, flush_dev_iotlb); - else - rc = iommu_flush_iotlb_psi(iommu, iommu_domid, - dfn_to_daddr(dfn), -- PAGE_ORDER_4K, -+ get_order_from_pages(page_count), - !dma_old_pte_present, - flush_dev_iotlb); - diff --git a/xsa321-4.13-2.patch b/xsa321-4.13-2.patch deleted file mode 100644 index 1e48615..0000000 --- a/xsa321-4.13-2.patch +++ /dev/null @@ -1,175 +0,0 @@ -From: -Subject: vtd: prune (and rename) cache flush functions - -Rename __iommu_flush_cache to iommu_sync_cache and remove -iommu_flush_cache_page. Also remove the iommu_flush_cache_entry -wrapper and just use iommu_sync_cache instead. Note the _entry suffix -was meaningless as the wrapper was already taking a size parameter in -bytes. While there also constify the addr parameter. - -No functional change intended. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -43,8 +43,7 @@ void disable_qinval(struct vtd_iommu *io - int enable_intremap(struct vtd_iommu *iommu, int eim); - void disable_intremap(struct vtd_iommu *iommu); - --void iommu_flush_cache_entry(void *addr, unsigned int size); --void iommu_flush_cache_page(void *addr, unsigned long npages); -+void iommu_sync_cache(const void *addr, unsigned int size); - int iommu_alloc(struct acpi_drhd_unit *drhd); - void iommu_free(struct acpi_drhd_unit *drhd); - ---- a/xen/drivers/passthrough/vtd/intremap.c -+++ b/xen/drivers/passthrough/vtd/intremap.c -@@ -230,7 +230,7 @@ static void free_remap_entry(struct vtd_ - iremap_entries, iremap_entry); - - update_irte(iommu, iremap_entry, &new_ire, false); -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); -@@ -406,7 +406,7 @@ static int ioapic_rte_to_remap_entry(str - } - - update_irte(iommu, iremap_entry, &new_ire, !init); -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); -@@ -695,7 +695,7 @@ static int msi_msg_to_remap_entry( - update_irte(iommu, iremap_entry, &new_ire, msi_desc->irte_initialized); - msi_desc->irte_initialized = true; - -- iommu_flush_cache_entry(iremap_entry, sizeof(*iremap_entry)); -+ iommu_sync_cache(iremap_entry, sizeof(*iremap_entry)); - iommu_flush_iec_index(iommu, 0, index); - - unmap_vtd_domain_page(iremap_entries); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -140,7 +140,8 @@ static int context_get_domain_id(struct - } - - static int iommus_incoherent; --static void __iommu_flush_cache(void *addr, unsigned int size) -+ -+void iommu_sync_cache(const void *addr, unsigned int size) - { - int i; - static unsigned int clflush_size = 0; -@@ -155,16 +156,6 @@ static void __iommu_flush_cache(void *ad - cacheline_flush((char *)addr + i); - } - --void iommu_flush_cache_entry(void *addr, unsigned int size) --{ -- __iommu_flush_cache(addr, size); --} -- --void iommu_flush_cache_page(void *addr, unsigned long npages) --{ -- __iommu_flush_cache(addr, PAGE_SIZE * npages); --} -- - /* Allocate page table, return its machine address */ - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node) - { -@@ -183,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo - vaddr = __map_domain_page(cur_pg); - memset(vaddr, 0, PAGE_SIZE); - -- iommu_flush_cache_page(vaddr, 1); -+ iommu_sync_cache(vaddr, PAGE_SIZE); - unmap_domain_page(vaddr); - cur_pg++; - } -@@ -216,7 +207,7 @@ static u64 bus_to_context_maddr(struct v - } - set_root_value(*root, maddr); - set_root_present(*root); -- iommu_flush_cache_entry(root, sizeof(struct root_entry)); -+ iommu_sync_cache(root, sizeof(struct root_entry)); - } - maddr = (u64) get_context_addr(*root); - unmap_vtd_domain_page(root_entries); -@@ -263,7 +254,7 @@ static u64 addr_to_dma_page_maddr(struct - */ - dma_set_pte_readable(*pte); - dma_set_pte_writable(*pte); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - } - - if ( level == 2 ) -@@ -640,7 +631,7 @@ static int __must_check dma_pte_clear_on - *flush_flags |= IOMMU_FLUSHF_modified; - - spin_unlock(&hd->arch.mapping_lock); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - - unmap_vtd_domain_page(page); - -@@ -679,7 +670,7 @@ static void iommu_free_page_table(struct - iommu_free_pagetable(dma_pte_addr(*pte), next_level); - - dma_clear_pte(*pte); -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - } - - unmap_vtd_domain_page(pt_vaddr); -@@ -1400,7 +1391,7 @@ int domain_context_mapping_one( - context_set_address_width(*context, agaw); - context_set_fault_enable(*context); - context_set_present(*context); -- iommu_flush_cache_entry(context, sizeof(struct context_entry)); -+ iommu_sync_cache(context, sizeof(struct context_entry)); - spin_unlock(&iommu->lock); - - /* Context entry was previously non-present (with domid 0). */ -@@ -1564,7 +1555,7 @@ int domain_context_unmap_one( - - context_clear_present(*context); - context_clear_entry(*context); -- iommu_flush_cache_entry(context, sizeof(struct context_entry)); -+ iommu_sync_cache(context, sizeof(struct context_entry)); - - iommu_domid= domain_iommu_domid(domain, iommu); - if ( iommu_domid == -1 ) -@@ -1791,7 +1782,7 @@ static int __must_check intel_iommu_map_ - - *pte = new; - -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - spin_unlock(&hd->arch.mapping_lock); - unmap_vtd_domain_page(page); - -@@ -1866,7 +1857,7 @@ int iommu_pte_flush(struct domain *d, ui - int iommu_domid; - int rc = 0; - -- iommu_flush_cache_entry(pte, sizeof(struct dma_pte)); -+ iommu_sync_cache(pte, sizeof(struct dma_pte)); - - for_each_drhd_unit ( drhd ) - { -@@ -2724,7 +2715,7 @@ static int __init intel_iommu_quarantine - dma_set_pte_addr(*pte, maddr); - dma_set_pte_readable(*pte); - } -- iommu_flush_cache_page(parent, 1); -+ iommu_sync_cache(parent, PAGE_SIZE); - - unmap_vtd_domain_page(parent); - parent = map_vtd_domain_page(maddr); diff --git a/xsa321-4.13-3.patch b/xsa321-4.13-3.patch deleted file mode 100644 index c141c4b..0000000 --- a/xsa321-4.13-3.patch +++ /dev/null @@ -1,82 +0,0 @@ -From: -Subject: x86/iommu: introduce a cache sync hook - -The hook is only implemented for VT-d and it uses the already existing -iommu_sync_cache function present in VT-d code. The new hook is -added so that the cache can be flushed by code outside of VT-d when -using shared page tables. - -Note that alloc_pgtable_maddr must use the now locally defined -sync_cache function, because IOMMU ops are not yet setup the first -time the function gets called during IOMMU initialization. - -No functional change intended. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -43,7 +43,6 @@ void disable_qinval(struct vtd_iommu *io - int enable_intremap(struct vtd_iommu *iommu, int eim); - void disable_intremap(struct vtd_iommu *iommu); - --void iommu_sync_cache(const void *addr, unsigned int size); - int iommu_alloc(struct acpi_drhd_unit *drhd); - void iommu_free(struct acpi_drhd_unit *drhd); - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -141,7 +141,7 @@ static int context_get_domain_id(struct - - static int iommus_incoherent; - --void iommu_sync_cache(const void *addr, unsigned int size) -+static void sync_cache(const void *addr, unsigned int size) - { - int i; - static unsigned int clflush_size = 0; -@@ -174,7 +174,7 @@ uint64_t alloc_pgtable_maddr(unsigned lo - vaddr = __map_domain_page(cur_pg); - memset(vaddr, 0, PAGE_SIZE); - -- iommu_sync_cache(vaddr, PAGE_SIZE); -+ sync_cache(vaddr, PAGE_SIZE); - unmap_domain_page(vaddr); - cur_pg++; - } -@@ -2763,6 +2763,7 @@ const struct iommu_ops __initconstrel in - .iotlb_flush_all = iommu_flush_iotlb_all, - .get_reserved_device_memory = intel_iommu_get_reserved_device_memory, - .dump_p2m_table = vtd_dump_p2m_table, -+ .sync_cache = sync_cache, - }; - - const struct iommu_init_ops __initconstrel intel_iommu_init_ops = { ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -121,6 +121,13 @@ extern bool untrusted_msi; - int pi_update_irte(const struct pi_desc *pi_desc, const struct pirq *pirq, - const uint8_t gvec); - -+#define iommu_sync_cache(addr, size) ({ \ -+ const struct iommu_ops *ops = iommu_get_ops(); \ -+ \ -+ if ( ops->sync_cache ) \ -+ iommu_vcall(ops, sync_cache, addr, size); \ -+}) -+ - #endif /* !__ARCH_X86_IOMMU_H__ */ - /* - * Local variables: ---- a/xen/include/xen/iommu.h -+++ b/xen/include/xen/iommu.h -@@ -250,6 +250,7 @@ struct iommu_ops { - int (*setup_hpet_msi)(struct msi_desc *); - - int (*adjust_irq_affinities)(void); -+ void (*sync_cache)(const void *addr, unsigned int size); - #endif /* CONFIG_X86 */ - - int __must_check (*suspend)(void); diff --git a/xsa321-4.13-4.patch b/xsa321-4.13-4.patch deleted file mode 100644 index 62bbcc7..0000000 --- a/xsa321-4.13-4.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: -Subject: vtd: don't assume addresses are aligned in sync_cache - -Current code in sync_cache assume that the address passed in is -aligned to a cache line size. Fix the code to support passing in -arbitrary addresses not necessarily aligned to a cache line size. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -143,8 +143,8 @@ static int iommus_incoherent; - - static void sync_cache(const void *addr, unsigned int size) - { -- int i; -- static unsigned int clflush_size = 0; -+ static unsigned long clflush_size = 0; -+ const void *end = addr + size; - - if ( !iommus_incoherent ) - return; -@@ -152,8 +152,9 @@ static void sync_cache(const void *addr, - if ( clflush_size == 0 ) - clflush_size = get_cache_line_size(); - -- for ( i = 0; i < size; i += clflush_size ) -- cacheline_flush((char *)addr + i); -+ addr -= (unsigned long)addr & (clflush_size - 1); -+ for ( ; addr < end; addr += clflush_size ) -+ cacheline_flush((char *)addr); - } - - /* Allocate page table, return its machine address */ diff --git a/xsa321-4.13-5.patch b/xsa321-4.13-5.patch deleted file mode 100644 index 60cfe6c..0000000 --- a/xsa321-4.13-5.patch +++ /dev/null @@ -1,24 +0,0 @@ -From: -Subject: x86/alternative: introduce alternative_2 - -It's based on alternative_io_2 without inputs or outputs but with an -added memory clobber. - -This is part of XSA-321. - -Acked-by: Jan Beulich - ---- a/xen/include/asm-x86/alternative.h -+++ b/xen/include/asm-x86/alternative.h -@@ -114,6 +114,11 @@ extern void alternative_branches(void); - #define alternative(oldinstr, newinstr, feature) \ - asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory") - -+#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \ -+ asm volatile (ALTERNATIVE_2(oldinstr, newinstr1, feature1, \ -+ newinstr2, feature2) \ -+ : : : "memory") -+ - /* - * Alternative inline assembly with input. - * diff --git a/xsa321-4.13-6.patch b/xsa321-4.13-6.patch deleted file mode 100644 index 4c5c5ab..0000000 --- a/xsa321-4.13-6.patch +++ /dev/null @@ -1,91 +0,0 @@ -From: -Subject: vtd: optimize CPU cache sync - -Some VT-d IOMMUs are non-coherent, which requires a cache write back -in order for the changes made by the CPU to be visible to the IOMMU. -This cache write back was unconditionally done using clflush, but there are -other more efficient instructions to do so, hence implement support -for them using the alternative framework. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/drivers/passthrough/vtd/extern.h -+++ b/xen/drivers/passthrough/vtd/extern.h -@@ -68,7 +68,6 @@ int __must_check qinval_device_iotlb_syn - u16 did, u16 size, u64 addr); - - unsigned int get_cache_line_size(void); --void cacheline_flush(char *); - void flush_all_cache(void); - - uint64_t alloc_pgtable_maddr(unsigned long npages, nodeid_t node); ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -31,6 +31,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -154,7 +155,42 @@ static void sync_cache(const void *addr, - - addr -= (unsigned long)addr & (clflush_size - 1); - for ( ; addr < end; addr += clflush_size ) -- cacheline_flush((char *)addr); -+/* -+ * The arguments to a macro must not include preprocessor directives. Doing so -+ * results in undefined behavior, so we have to create some defines here in -+ * order to avoid it. -+ */ -+#if defined(HAVE_AS_CLWB) -+# define CLWB_ENCODING "clwb %[p]" -+#elif defined(HAVE_AS_XSAVEOPT) -+# define CLWB_ENCODING "data16 xsaveopt %[p]" /* clwb */ -+#else -+# define CLWB_ENCODING ".byte 0x66, 0x0f, 0xae, 0x30" /* clwb (%%rax) */ -+#endif -+ -+#define BASE_INPUT(addr) [p] "m" (*(const char *)(addr)) -+#if defined(HAVE_AS_CLWB) || defined(HAVE_AS_XSAVEOPT) -+# define INPUT BASE_INPUT -+#else -+# define INPUT(addr) "a" (addr), BASE_INPUT(addr) -+#endif -+ /* -+ * Note regarding the use of NOP_DS_PREFIX: it's faster to do a clflush -+ * + prefix than a clflush + nop, and hence the prefix is added instead -+ * of letting the alternative framework fill the gap by appending nops. -+ */ -+ alternative_io_2(".byte " __stringify(NOP_DS_PREFIX) "; clflush %[p]", -+ "data16 clflush %[p]", /* clflushopt */ -+ X86_FEATURE_CLFLUSHOPT, -+ CLWB_ENCODING, -+ X86_FEATURE_CLWB, /* no outputs */, -+ INPUT(addr)); -+#undef INPUT -+#undef BASE_INPUT -+#undef CLWB_ENCODING -+ -+ alternative_2("", "sfence", X86_FEATURE_CLFLUSHOPT, -+ "sfence", X86_FEATURE_CLWB); - } - - /* Allocate page table, return its machine address */ ---- a/xen/drivers/passthrough/vtd/x86/vtd.c -+++ b/xen/drivers/passthrough/vtd/x86/vtd.c -@@ -51,11 +51,6 @@ unsigned int get_cache_line_size(void) - return ((cpuid_ebx(1) >> 8) & 0xff) * 8; - } - --void cacheline_flush(char * addr) --{ -- clflush(addr); --} -- - void flush_all_cache() - { - wbinvd(); diff --git a/xsa321-4.13-7.patch b/xsa321-4.13-7.patch deleted file mode 100644 index 0bd018f..0000000 --- a/xsa321-4.13-7.patch +++ /dev/null @@ -1,153 +0,0 @@ -From: -Subject: x86/ept: flush cache when modifying PTEs and sharing page tables - -Modifications made to the page tables by EPT code need to be written -to memory when the page tables are shared with the IOMMU, as Intel -IOMMUs can be non-coherent and thus require changes to be written to -memory in order to be visible to the IOMMU. - -In order to achieve this make sure data is written back to memory -after writing an EPT entry when the recalc bit is not set in -atomic_write_ept_entry. If such bit is set, the entry will be -adjusted and atomic_write_ept_entry will be called a second time -without the recalc bit set. Note that when splitting a super page the -new tables resulting of the split should also be written back. - -Failure to do so can allow devices behind the IOMMU access to the -stale super page, or cause coherency issues as changes made by the -processor to the page tables are not visible to the IOMMU. - -This allows to remove the VT-d specific iommu_pte_flush helper, since -the cache write back is now performed by atomic_write_ept_entry, and -hence iommu_iotlb_flush can be used to flush the IOMMU TLB. The newly -used method (iommu_iotlb_flush) can result in less flushes, since it -might sometimes be called rightly with 0 flags, in which case it -becomes a no-op. - -This is part of XSA-321. - -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -58,6 +58,19 @@ static int atomic_write_ept_entry(struct - - write_atomic(&entryptr->epte, new.epte); - -+ /* -+ * The recalc field on the EPT is used to signal either that a -+ * recalculation of the EMT field is required (which doesn't effect the -+ * IOMMU), or a type change. Type changes can only be between ram_rw, -+ * logdirty and ioreq_server: changes to/from logdirty won't work well with -+ * an IOMMU anyway, as IOMMU #PFs are not synchronous and will lead to -+ * aborts, and changes to/from ioreq_server are already fully flushed -+ * before returning to guest context (see -+ * XEN_DMOP_map_mem_type_to_ioreq_server). -+ */ -+ if ( !new.recalc && iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(entryptr, sizeof(*entryptr)); -+ - return 0; - } - -@@ -278,6 +291,9 @@ static bool_t ept_split_super_page(struc - break; - } - -+ if ( iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(table, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); -+ - unmap_domain_page(table); - - /* Even failed we should install the newly allocated ept page. */ -@@ -337,6 +353,9 @@ static int ept_next_level(struct p2m_dom - if ( !next ) - return GUEST_TABLE_MAP_FAILED; - -+ if ( iommu_use_hap_pt(p2m->domain) ) -+ iommu_sync_cache(next, EPT_PAGETABLE_ENTRIES * sizeof(ept_entry_t)); -+ - rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); - ASSERT(rc == 0); - } -@@ -821,7 +840,10 @@ out: - need_modify_vtd_table ) - { - if ( iommu_use_hap_pt(d) ) -- rc = iommu_pte_flush(d, gfn, &ept_entry->epte, order, vtd_pte_present); -+ rc = iommu_iotlb_flush(d, _dfn(gfn), (1u << order), -+ (iommu_flags ? IOMMU_FLUSHF_added : 0) | -+ (vtd_pte_present ? IOMMU_FLUSHF_modified -+ : 0)); - else if ( need_iommu_pt_sync(d) ) - rc = iommu_flags ? - iommu_legacy_map(d, _dfn(gfn), mfn, order, iommu_flags) : ---- a/xen/drivers/passthrough/vtd/iommu.c -+++ b/xen/drivers/passthrough/vtd/iommu.c -@@ -1884,53 +1884,6 @@ static int intel_iommu_lookup_page(struc - return 0; - } - --int iommu_pte_flush(struct domain *d, uint64_t dfn, uint64_t *pte, -- int order, int present) --{ -- struct acpi_drhd_unit *drhd; -- struct vtd_iommu *iommu = NULL; -- struct domain_iommu *hd = dom_iommu(d); -- bool_t flush_dev_iotlb; -- int iommu_domid; -- int rc = 0; -- -- iommu_sync_cache(pte, sizeof(struct dma_pte)); -- -- for_each_drhd_unit ( drhd ) -- { -- iommu = drhd->iommu; -- if ( !test_bit(iommu->index, &hd->arch.iommu_bitmap) ) -- continue; -- -- flush_dev_iotlb = !!find_ats_dev_drhd(iommu); -- iommu_domid= domain_iommu_domid(d, iommu); -- if ( iommu_domid == -1 ) -- continue; -- -- rc = iommu_flush_iotlb_psi(iommu, iommu_domid, -- __dfn_to_daddr(dfn), -- order, !present, flush_dev_iotlb); -- if ( rc > 0 ) -- { -- iommu_flush_write_buffer(iommu); -- rc = 0; -- } -- } -- -- if ( unlikely(rc) ) -- { -- if ( !d->is_shutting_down && printk_ratelimit() ) -- printk(XENLOG_ERR VTDPREFIX -- " d%d: IOMMU pages flush failed: %d\n", -- d->domain_id, rc); -- -- if ( !is_hardware_domain(d) ) -- domain_crash(d); -- } -- -- return rc; --} -- - static int __init vtd_ept_page_compatible(struct vtd_iommu *iommu) - { - u64 ept_cap, vtd_cap = iommu->cap; ---- a/xen/include/asm-x86/iommu.h -+++ b/xen/include/asm-x86/iommu.h -@@ -97,10 +97,6 @@ static inline int iommu_adjust_irq_affin - : 0; - } - --/* While VT-d specific, this must get declared in a generic header. */ --int __must_check iommu_pte_flush(struct domain *d, u64 gfn, u64 *pte, -- int order, int present); -- - static inline bool iommu_supports_x2apic(void) - { - return iommu_init_ops && iommu_init_ops->supports_x2apic diff --git a/xsa327.patch b/xsa327.patch deleted file mode 100644 index 0541cfa..0000000 --- a/xsa327.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 030300ebbb86c40c12db038714479d746167c767 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Tue, 26 May 2020 18:31:33 +0100 -Subject: [PATCH] xen: Check the alignment of the offset pased via - VCPUOP_register_vcpu_info - -Currently a guest is able to register any guest physical address to use -for the vcpu_info structure as long as the structure can fits in the -rest of the frame. - -This means a guest can provide an address that is not aligned to the -natural alignment of the structure. - -On Arm 32-bit, unaligned access are completely forbidden by the -hypervisor. This will result to a data abort which is fatal. - -On Arm 64-bit, unaligned access are only forbidden when used for atomic -access. As the structure contains fields (such as evtchn_pending_self) -that are updated using atomic operations, any unaligned access will be -fatal as well. - -While the misalignment is only fatal on Arm, a generic check is added -as an x86 guest shouldn't sensibly pass an unaligned address (this -would result to a split lock). - -This is XSA-327. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Andrew Cooper -Reviewed-by: Stefano Stabellini ---- - xen/common/domain.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/xen/common/domain.c b/xen/common/domain.c -index 7cc9526139a6..e9be05f1d05f 100644 ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1227,10 +1227,20 @@ int map_vcpu_info(struct vcpu *v, unsigned long gfn, unsigned offset) - void *mapping; - vcpu_info_t *new_info; - struct page_info *page; -+ unsigned int align; - - if ( offset > (PAGE_SIZE - sizeof(vcpu_info_t)) ) - return -EINVAL; - -+#ifdef CONFIG_COMPAT -+ if ( has_32bit_shinfo(d) ) -+ align = alignof(new_info->compat); -+ else -+#endif -+ align = alignof(*new_info); -+ if ( offset & (align - 1) ) -+ return -EINVAL; -+ - if ( !mfn_eq(v->vcpu_info_mfn, INVALID_MFN) ) - return -EINVAL; - --- -2.17.1 - diff --git a/xsa328-4.13-1.patch b/xsa328-4.13-1.patch deleted file mode 100644 index 56e48de..0000000 --- a/xsa328-4.13-1.patch +++ /dev/null @@ -1,118 +0,0 @@ -From: Jan Beulich -Subject: x86/EPT: ept_set_middle_entry() related adjustments - -ept_split_super_page() wants to further modify the newly allocated -table, so have ept_set_middle_entry() return the mapped pointer rather -than tearing it down and then getting re-established right again. - -Similarly ept_next_level() wants to hand back a mapped pointer of -the next level page, so re-use the one established by -ept_set_middle_entry() in case that path was taken. - -Pull the setting of suppress_ve ahead of insertion into the higher level -table, and don't have ept_split_super_page() set the field a 2nd time. - -This is part of XSA-328. - -Signed-off-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -187,8 +187,9 @@ static void ept_p2m_type_to_flags(struct - #define GUEST_TABLE_SUPER_PAGE 2 - #define GUEST_TABLE_POD_PAGE 3 - --/* Fill in middle levels of ept table */ --static int ept_set_middle_entry(struct p2m_domain *p2m, ept_entry_t *ept_entry) -+/* Fill in middle level of ept table; return pointer to mapped new table. */ -+static ept_entry_t *ept_set_middle_entry(struct p2m_domain *p2m, -+ ept_entry_t *ept_entry) - { - mfn_t mfn; - ept_entry_t *table; -@@ -196,7 +197,12 @@ static int ept_set_middle_entry(struct p - - mfn = p2m_alloc_ptp(p2m, 0); - if ( mfn_eq(mfn, INVALID_MFN) ) -- return 0; -+ return NULL; -+ -+ table = map_domain_page(mfn); -+ -+ for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -+ table[i].suppress_ve = 1; - - ept_entry->epte = 0; - ept_entry->mfn = mfn_x(mfn); -@@ -208,14 +214,7 @@ static int ept_set_middle_entry(struct p - - ept_entry->suppress_ve = 1; - -- table = map_domain_page(mfn); -- -- for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -- table[i].suppress_ve = 1; -- -- unmap_domain_page(table); -- -- return 1; -+ return table; - } - - /* free ept sub tree behind an entry */ -@@ -253,10 +252,10 @@ static bool_t ept_split_super_page(struc - - ASSERT(is_epte_superpage(ept_entry)); - -- if ( !ept_set_middle_entry(p2m, &new_ept) ) -+ table = ept_set_middle_entry(p2m, &new_ept); -+ if ( !table ) - return 0; - -- table = map_domain_page(_mfn(new_ept.mfn)); - trunk = 1UL << ((level - 1) * EPT_TABLE_ORDER); - - for ( i = 0; i < EPT_PAGETABLE_ENTRIES; i++ ) -@@ -267,7 +266,6 @@ static bool_t ept_split_super_page(struc - epte->sp = (level > 1); - epte->mfn += i * trunk; - epte->snp = is_iommu_enabled(p2m->domain) && iommu_snoop; -- epte->suppress_ve = 1; - - ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access); - -@@ -306,8 +304,7 @@ static int ept_next_level(struct p2m_dom - ept_entry_t **table, unsigned long *gfn_remainder, - int next_level) - { -- unsigned long mfn; -- ept_entry_t *ept_entry, e; -+ ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - - shift = next_level * EPT_TABLE_ORDER; -@@ -332,19 +329,17 @@ static int ept_next_level(struct p2m_dom - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- if ( !ept_set_middle_entry(p2m, ept_entry) ) -+ next = ept_set_middle_entry(p2m, ept_entry); -+ if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- else -- e = atomic_read_ept_entry(ept_entry); /* Refresh */ -+ /* e is now stale and hence may not be used anymore below. */ - } -- - /* The only time sp would be set here is if we had hit a superpage */ -- if ( is_epte_superpage(&e) ) -+ else if ( is_epte_superpage(&e) ) - return GUEST_TABLE_SUPER_PAGE; - -- mfn = e.mfn; - unmap_domain_page(*table); -- *table = map_domain_page(_mfn(mfn)); -+ *table = next ?: map_domain_page(_mfn(e.mfn)); - *gfn_remainder &= (1UL << shift) - 1; - return GUEST_TABLE_NORMAL_PAGE; - } diff --git a/xsa328-4.13-2.patch b/xsa328-4.13-2.patch deleted file mode 100644 index c4f437f..0000000 --- a/xsa328-4.13-2.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: -Subject: x86/ept: atomically modify entries in ept_next_level - -ept_next_level was passing a live PTE pointer to ept_set_middle_entry, -which was then modified without taking into account that the PTE could -be part of a live EPT table. This wasn't a security issue because the -pages returned by p2m_alloc_ptp are zeroed, so adding such an entry -before actually initializing it didn't allow a guest to access -physical memory addresses it wasn't supposed to access. - -This is part of XSA-328. - -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/mm/p2m-ept.c -+++ b/xen/arch/x86/mm/p2m-ept.c -@@ -307,6 +307,8 @@ static int ept_next_level(struct p2m_dom - ept_entry_t *ept_entry, *next = NULL, e; - u32 shift, index; - -+ ASSERT(next_level); -+ - shift = next_level * EPT_TABLE_ORDER; - - index = *gfn_remainder >> shift; -@@ -323,16 +325,20 @@ static int ept_next_level(struct p2m_dom - - if ( !is_epte_present(&e) ) - { -+ int rc; -+ - if ( e.sa_p2mt == p2m_populate_on_demand ) - return GUEST_TABLE_POD_PAGE; - - if ( read_only ) - return GUEST_TABLE_MAP_FAILED; - -- next = ept_set_middle_entry(p2m, ept_entry); -+ next = ept_set_middle_entry(p2m, &e); - if ( !next ) - return GUEST_TABLE_MAP_FAILED; -- /* e is now stale and hence may not be used anymore below. */ -+ -+ rc = atomic_write_ept_entry(p2m, ept_entry, e, next_level); -+ ASSERT(rc == 0); - } - /* The only time sp would be set here is if we had hit a superpage */ - else if ( is_epte_superpage(&e) ) diff --git a/xsa333.patch b/xsa333.patch deleted file mode 100644 index 6b86c94..0000000 --- a/xsa333.patch +++ /dev/null @@ -1,39 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Handle the Intel-specific MSR_MISC_ENABLE correctly - -This MSR doesn't exist on AMD hardware, and switching away from the safe -functions in the common MSR path was an erroneous change. - -Partially revert the change. - -This is XSA-333. - -Fixes: 4fdc932b3cc ("x86/Intel: drop another 32-bit leftover") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich -Reviewed-by: Wei Liu - -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index efeb2a727e..6332c74b80 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -924,7 +924,8 @@ static int read_msr(unsigned int reg, uint64_t *val, - return X86EMUL_OKAY; - - case MSR_IA32_MISC_ENABLE: -- rdmsrl(reg, *val); -+ if ( rdmsr_safe(reg, *val) ) -+ break; - *val = guest_misc_enable(*val); - return X86EMUL_OKAY; - -@@ -1059,7 +1060,8 @@ static int write_msr(unsigned int reg, uint64_t val, - break; - - case MSR_IA32_MISC_ENABLE: -- rdmsrl(reg, temp); -+ if ( rdmsr_safe(reg, temp) ) -+ break; - if ( val != guest_misc_enable(temp) ) - goto invalid; - return X86EMUL_OKAY; diff --git a/xsa334.patch b/xsa334.patch deleted file mode 100644 index 4260cdb..0000000 --- a/xsa334.patch +++ /dev/null @@ -1,51 +0,0 @@ -From: Andrew Cooper -Subject: xen/memory: Don't skip the RCU unlock path in acquire_resource() - -In the case that an HVM Stubdomain makes an XENMEM_acquire_resource hypercall, -the FIXME path will bypass rcu_unlock_domain() on the way out of the function. - -Move the check to the start of the function. This does change the behaviour -of the get-size path for HVM Stubdomains, but that functionality is currently -broken and unused anyway, as well as being quite useless to entities which -can't actually map the resource anyway. - -This is XSA-334. - -Fixes: 83fa6552ce ("common: add a new mappable resource type: XENMEM_resource_grant_table") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/common/memory.c b/xen/common/memory.c -index 1a3c9ffb30..29741d8904 100644 ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -1058,6 +1058,14 @@ static int acquire_resource( - xen_pfn_t mfn_list[32]; - int rc; - -+ /* -+ * FIXME: Until foreign pages inserted into the P2M are properly -+ * reference counted, it is unsafe to allow mapping of -+ * resource pages unless the caller is the hardware domain. -+ */ -+ if ( paging_mode_translate(currd) && !is_hardware_domain(currd) ) -+ return -EACCES; -+ - if ( copy_from_guest(&xmar, arg, 1) ) - return -EFAULT; - -@@ -1114,14 +1122,6 @@ static int acquire_resource( - xen_pfn_t gfn_list[ARRAY_SIZE(mfn_list)]; - unsigned int i; - -- /* -- * FIXME: Until foreign pages inserted into the P2M are properly -- * reference counted, it is unsafe to allow mapping of -- * resource pages unless the caller is the hardware domain. -- */ -- if ( !is_hardware_domain(currd) ) -- return -EACCES; -- - if ( copy_from_guest(gfn_list, xmar.frame_list, xmar.nr_frames) ) - rc = -EFAULT; - diff --git a/xsa336.patch b/xsa336.patch deleted file mode 100644 index b44c298..0000000 --- a/xsa336.patch +++ /dev/null @@ -1,283 +0,0 @@ -From: Roger Pau Monné -Subject: x86/vpt: fix race when migrating timers between vCPUs - -The current vPT code will migrate the emulated timers between vCPUs -(change the pt->vcpu field) while just holding the destination lock, -either from create_periodic_time or pt_adjust_global_vcpu_target if -the global target is adjusted. Changing the periodic_timer vCPU field -in this way creates a race where a third party could grab the lock in -the unlocked region of pt_adjust_global_vcpu_target (or before -create_periodic_time performs the vcpu change) and then release the -lock from a different vCPU, creating a locking imbalance. - -Introduce a per-domain rwlock in order to protect periodic_time -migration between vCPU lists. Taking the lock in read mode prevents -any timer from being migrated to a different vCPU, while taking it in -write mode allows performing migration of timers across vCPUs. The -per-vcpu locks are still used to protect all the other fields from the -periodic_timer struct. - -Note that such migration shouldn't happen frequently, and hence -there's no performance drop as a result of such locking. - -This is XSA-336. - -Reported-by: Igor Druzhinin -Tested-by: Igor Druzhinin -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich ---- -Changes since v2: - - Re-order pt_adjust_vcpu to remove one if. - - Fix pt_lock to not call pt_vcpu_lock, as we might end up using a - stale value of pt->vcpu when taking the per-vcpu lock. - -Changes since v1: - - Use a per-domain rwlock to protect timer vCPU migration. - ---- a/xen/arch/x86/hvm/hvm.c -+++ b/xen/arch/x86/hvm/hvm.c -@@ -658,6 +658,8 @@ int hvm_domain_initialise(struct domain - /* need link to containing domain */ - d->arch.hvm.pl_time->domain = d; - -+ rwlock_init(&d->arch.hvm.pl_time->pt_migrate); -+ - /* Set the default IO Bitmap. */ - if ( is_hardware_domain(d) ) - { ---- a/xen/arch/x86/hvm/vpt.c -+++ b/xen/arch/x86/hvm/vpt.c -@@ -153,23 +153,32 @@ static int pt_irq_masked(struct periodic - return 1; - } - --static void pt_lock(struct periodic_time *pt) -+static void pt_vcpu_lock(struct vcpu *v) - { -- struct vcpu *v; -+ read_lock(&v->domain->arch.hvm.pl_time->pt_migrate); -+ spin_lock(&v->arch.hvm.tm_lock); -+} - -- for ( ; ; ) -- { -- v = pt->vcpu; -- spin_lock(&v->arch.hvm.tm_lock); -- if ( likely(pt->vcpu == v) ) -- break; -- spin_unlock(&v->arch.hvm.tm_lock); -- } -+static void pt_vcpu_unlock(struct vcpu *v) -+{ -+ spin_unlock(&v->arch.hvm.tm_lock); -+ read_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); -+} -+ -+static void pt_lock(struct periodic_time *pt) -+{ -+ /* -+ * We cannot use pt_vcpu_lock here, because we need to acquire the -+ * per-domain lock first and then (re-)fetch the value of pt->vcpu, or -+ * else we might be using a stale value of pt->vcpu. -+ */ -+ read_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); -+ spin_lock(&pt->vcpu->arch.hvm.tm_lock); - } - - static void pt_unlock(struct periodic_time *pt) - { -- spin_unlock(&pt->vcpu->arch.hvm.tm_lock); -+ pt_vcpu_unlock(pt->vcpu); - } - - static void pt_process_missed_ticks(struct periodic_time *pt) -@@ -219,7 +228,7 @@ void pt_save_timer(struct vcpu *v) - if ( v->pause_flags & VPF_blocked ) - return; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - if ( !pt->do_not_freeze ) -@@ -227,7 +236,7 @@ void pt_save_timer(struct vcpu *v) - - pt_freeze_time(v); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - void pt_restore_timer(struct vcpu *v) -@@ -235,7 +244,7 @@ void pt_restore_timer(struct vcpu *v) - struct list_head *head = &v->arch.hvm.tm_list; - struct periodic_time *pt; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - { -@@ -248,7 +257,7 @@ void pt_restore_timer(struct vcpu *v) - - pt_thaw_time(v); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - static void pt_timer_fn(void *data) -@@ -309,7 +318,7 @@ int pt_update_irq(struct vcpu *v) - int irq, pt_vector = -1; - bool level; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - earliest_pt = NULL; - max_lag = -1ULL; -@@ -339,7 +348,7 @@ int pt_update_irq(struct vcpu *v) - - if ( earliest_pt == NULL ) - { -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - return -1; - } - -@@ -347,7 +356,7 @@ int pt_update_irq(struct vcpu *v) - irq = earliest_pt->irq; - level = earliest_pt->level; - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - switch ( earliest_pt->source ) - { -@@ -394,7 +403,7 @@ int pt_update_irq(struct vcpu *v) - time_cb *cb = NULL; - void *cb_priv; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - /* Make sure the timer is still on the list. */ - list_for_each_entry ( pt, &v->arch.hvm.tm_list, list ) - if ( pt == earliest_pt ) -@@ -404,7 +413,7 @@ int pt_update_irq(struct vcpu *v) - cb_priv = pt->priv; - break; - } -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - if ( cb != NULL ) - cb(v, cb_priv); -@@ -441,12 +450,12 @@ void pt_intr_post(struct vcpu *v, struct - if ( intack.source == hvm_intsrc_vector ) - return; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - pt = is_pt_irq(v, intack); - if ( pt == NULL ) - { -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - return; - } - -@@ -455,7 +464,7 @@ void pt_intr_post(struct vcpu *v, struct - cb = pt->cb; - cb_priv = pt->priv; - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - - if ( cb != NULL ) - cb(v, cb_priv); -@@ -466,12 +475,12 @@ void pt_migrate(struct vcpu *v) - struct list_head *head = &v->arch.hvm.tm_list; - struct periodic_time *pt; - -- spin_lock(&v->arch.hvm.tm_lock); -+ pt_vcpu_lock(v); - - list_for_each_entry ( pt, head, list ) - migrate_timer(&pt->timer, v->processor); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ pt_vcpu_unlock(v); - } - - void create_periodic_time( -@@ -490,7 +499,7 @@ void create_periodic_time( - - destroy_periodic_time(pt); - -- spin_lock(&v->arch.hvm.tm_lock); -+ write_lock(&v->domain->arch.hvm.pl_time->pt_migrate); - - pt->pending_intr_nr = 0; - pt->do_not_freeze = 0; -@@ -540,7 +549,7 @@ void create_periodic_time( - init_timer(&pt->timer, pt_timer_fn, pt, v->processor); - set_timer(&pt->timer, pt->scheduled); - -- spin_unlock(&v->arch.hvm.tm_lock); -+ write_unlock(&v->domain->arch.hvm.pl_time->pt_migrate); - } - - void destroy_periodic_time(struct periodic_time *pt) -@@ -565,30 +574,20 @@ void destroy_periodic_time(struct period - - static void pt_adjust_vcpu(struct periodic_time *pt, struct vcpu *v) - { -- int on_list; -- - ASSERT(pt->source == PTSRC_isa || pt->source == PTSRC_ioapic); - - if ( pt->vcpu == NULL ) - return; - -- pt_lock(pt); -- on_list = pt->on_list; -- if ( pt->on_list ) -- list_del(&pt->list); -- pt->on_list = 0; -- pt_unlock(pt); -- -- spin_lock(&v->arch.hvm.tm_lock); -+ write_lock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); - pt->vcpu = v; -- if ( on_list ) -+ if ( pt->on_list ) - { -- pt->on_list = 1; -+ list_del(&pt->list); - list_add(&pt->list, &v->arch.hvm.tm_list); -- - migrate_timer(&pt->timer, v->processor); - } -- spin_unlock(&v->arch.hvm.tm_lock); -+ write_unlock(&pt->vcpu->domain->arch.hvm.pl_time->pt_migrate); - } - - void pt_adjust_global_vcpu_target(struct vcpu *v) ---- a/xen/include/asm-x86/hvm/vpt.h -+++ b/xen/include/asm-x86/hvm/vpt.h -@@ -128,6 +128,13 @@ struct pl_time { /* platform time */ - struct RTCState vrtc; - struct HPETState vhpet; - struct PMTState vpmt; -+ /* -+ * rwlock to prevent periodic_time vCPU migration. Take the lock in read -+ * mode in order to prevent the vcpu field of periodic_time from changing. -+ * Lock must be taken in write mode when changes to the vcpu field are -+ * performed, as it allows exclusive access to all the timers of a domain. -+ */ -+ rwlock_t pt_migrate; - /* guest_time = Xen sys time + stime_offset */ - int64_t stime_offset; - /* Ensures monotonicity in appropriate timer modes. */ diff --git a/xsa337-4.13-1.patch b/xsa337-4.13-1.patch deleted file mode 100644 index 2091626..0000000 --- a/xsa337-4.13-1.patch +++ /dev/null @@ -1,87 +0,0 @@ -From: Roger Pau Monné -Subject: x86/msi: get rid of read_msi_msg - -It's safer and faster to just use the cached last written -(untranslated) MSI message stored in msi_desc for the single user that -calls read_msi_msg. - -This also prevents relying on the data read from the device MSI -registers in order to figure out the index into the IOMMU interrupt -remapping table, which is not safe. - -This is part of XSA-337. - -Reported-by: Andrew Cooper -Requested-by: Andrew Cooper -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/msi.c -+++ b/xen/arch/x86/msi.c -@@ -183,54 +183,6 @@ void msi_compose_msg(unsigned vector, co - MSI_DATA_VECTOR(vector); - } - --static bool read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) --{ -- switch ( entry->msi_attrib.type ) -- { -- case PCI_CAP_ID_MSI: -- { -- struct pci_dev *dev = entry->dev; -- int pos = entry->msi_attrib.pos; -- uint16_t data; -- -- msg->address_lo = pci_conf_read32(dev->sbdf, -- msi_lower_address_reg(pos)); -- if ( entry->msi_attrib.is_64 ) -- { -- msg->address_hi = pci_conf_read32(dev->sbdf, -- msi_upper_address_reg(pos)); -- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 1)); -- } -- else -- { -- msg->address_hi = 0; -- data = pci_conf_read16(dev->sbdf, msi_data_reg(pos, 0)); -- } -- msg->data = data; -- break; -- } -- case PCI_CAP_ID_MSIX: -- { -- void __iomem *base = entry->mask_base; -- -- if ( unlikely(!msix_memory_decoded(entry->dev, -- entry->msi_attrib.pos)) ) -- return false; -- msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); -- msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); -- msg->data = readl(base + PCI_MSIX_ENTRY_DATA_OFFSET); -- break; -- } -- default: -- BUG(); -- } -- -- if ( iommu_intremap ) -- iommu_read_msi_from_ire(entry, msg); -- -- return true; --} -- - static int write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) - { - entry->msg = *msg; -@@ -302,10 +254,7 @@ void set_msi_affinity(struct irq_desc *d - - ASSERT(spin_is_locked(&desc->lock)); - -- memset(&msg, 0, sizeof(msg)); -- if ( !read_msi_msg(msi_desc, &msg) ) -- return; -- -+ msg = msi_desc->msg; - msg.data &= ~MSI_DATA_VECTOR_MASK; - msg.data |= MSI_DATA_VECTOR(desc->arch.vector); - msg.address_lo &= ~MSI_ADDR_DEST_ID_MASK; diff --git a/xsa337-4.13-2.patch b/xsa337-4.13-2.patch deleted file mode 100644 index bdefd37..0000000 --- a/xsa337-4.13-2.patch +++ /dev/null @@ -1,181 +0,0 @@ -From: Jan Beulich -Subject: x86/MSI-X: restrict reading of table/PBA bases from BARs - -When assigned to less trusted or un-trusted guests, devices may change -state behind our backs (they may e.g. get reset by means we may not know -about). Therefore we should avoid reading BARs from hardware once a -device is no longer owned by Dom0. Furthermore when we can't read a BAR, -or when we read zero, we shouldn't instead use the caller provided -address unless that caller can be trusted. - -Re-arrange the logic in msix_capability_init() such that only Dom0 (and -only if the device isn't DomU-owned yet) or calls through -PHYSDEVOP_prepare_msix will actually result in the reading of the -respective BAR register(s). Additionally do so only as long as in-use -table entries are known (note that invocation of PHYSDEVOP_prepare_msix -counts as a "pseudo" entry). In all other uses the value already -recorded will get used instead. - -Clear the recorded values in _pci_cleanup_msix() as well as on the one -affected error path. (Adjust this error path to also avoid blindly -disabling MSI-X when it was enabled on entry to the function.) - -While moving around variable declarations (in many cases to reduce their -scopes), also adjust some of their types. - -This is part of XSA-337. - -Signed-off-by: Jan Beulich -Reviewed-by: Roger Pau Monné - ---- a/xen/arch/x86/msi.c -+++ b/xen/arch/x86/msi.c -@@ -769,16 +769,14 @@ static int msix_capability_init(struct p - { - struct arch_msix *msix = dev->msix; - struct msi_desc *entry = NULL; -- int vf; - u16 control; - u64 table_paddr; - u32 table_offset; -- u8 bir, pbus, pslot, pfunc; - u16 seg = dev->seg; - u8 bus = dev->bus; - u8 slot = PCI_SLOT(dev->devfn); - u8 func = PCI_FUNC(dev->devfn); -- bool maskall = msix->host_maskall; -+ bool maskall = msix->host_maskall, zap_on_error = false; - unsigned int pos = pci_find_cap_offset(seg, bus, slot, func, - PCI_CAP_ID_MSIX); - -@@ -820,43 +818,45 @@ static int msix_capability_init(struct p - - /* Locate MSI-X table region */ - table_offset = pci_conf_read32(dev->sbdf, msix_table_offset_reg(pos)); -- bir = (u8)(table_offset & PCI_MSIX_BIRMASK); -- table_offset &= ~PCI_MSIX_BIRMASK; -+ if ( !msix->used_entries && -+ (!msi || -+ (is_hardware_domain(current->domain) && -+ (dev->domain == current->domain || dev->domain == dom_io))) ) -+ { -+ unsigned int bir = table_offset & PCI_MSIX_BIRMASK, pbus, pslot, pfunc; -+ int vf; -+ paddr_t pba_paddr; -+ unsigned int pba_offset; - -- if ( !dev->info.is_virtfn ) -- { -- pbus = bus; -- pslot = slot; -- pfunc = func; -- vf = -1; -- } -- else -- { -- pbus = dev->info.physfn.bus; -- pslot = PCI_SLOT(dev->info.physfn.devfn); -- pfunc = PCI_FUNC(dev->info.physfn.devfn); -- vf = PCI_BDF2(dev->bus, dev->devfn); -- } -- -- table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); -- WARN_ON(msi && msi->table_base != table_paddr); -- if ( !table_paddr ) -- { -- if ( !msi || !msi->table_base ) -+ if ( !dev->info.is_virtfn ) - { -- pci_conf_write16(dev->sbdf, msix_control_reg(pos), -- control & ~PCI_MSIX_FLAGS_ENABLE); -- xfree(entry); -- return -ENXIO; -+ pbus = bus; -+ pslot = slot; -+ pfunc = func; -+ vf = -1; -+ } -+ else -+ { -+ pbus = dev->info.physfn.bus; -+ pslot = PCI_SLOT(dev->info.physfn.devfn); -+ pfunc = PCI_FUNC(dev->info.physfn.devfn); -+ vf = PCI_BDF2(dev->bus, dev->devfn); - } -- table_paddr = msi->table_base; -- } -- table_paddr += table_offset; - -- if ( !msix->used_entries ) -- { -- u64 pba_paddr; -- u32 pba_offset; -+ table_paddr = read_pci_mem_bar(seg, pbus, pslot, pfunc, bir, vf); -+ WARN_ON(msi && msi->table_base != table_paddr); -+ if ( !table_paddr ) -+ { -+ if ( !msi || !msi->table_base ) -+ { -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), -+ control & ~PCI_MSIX_FLAGS_ENABLE); -+ xfree(entry); -+ return -ENXIO; -+ } -+ table_paddr = msi->table_base; -+ } -+ table_paddr += table_offset & ~PCI_MSIX_BIRMASK; - - msix->table.first = PFN_DOWN(table_paddr); - msix->table.last = PFN_DOWN(table_paddr + -@@ -875,7 +875,18 @@ static int msix_capability_init(struct p - BITS_TO_LONGS(msix->nr_entries) - 1); - WARN_ON(rangeset_overlaps_range(mmio_ro_ranges, msix->pba.first, - msix->pba.last)); -+ -+ zap_on_error = true; -+ } -+ else if ( !msix->table.first ) -+ { -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); -+ xfree(entry); -+ return -ENODATA; - } -+ else -+ table_paddr = (msix->table.first << PAGE_SHIFT) + -+ (table_offset & ~PCI_MSIX_BIRMASK & ~PAGE_MASK); - - if ( entry ) - { -@@ -886,8 +897,15 @@ static int msix_capability_init(struct p - - if ( idx < 0 ) - { -- pci_conf_write16(dev->sbdf, msix_control_reg(pos), -- control & ~PCI_MSIX_FLAGS_ENABLE); -+ if ( zap_on_error ) -+ { -+ msix->table.first = 0; -+ msix->pba.first = 0; -+ -+ control &= ~PCI_MSIX_FLAGS_ENABLE; -+ } -+ -+ pci_conf_write16(dev->sbdf, msix_control_reg(pos), control); - xfree(entry); - return idx; - } -@@ -1076,9 +1094,14 @@ static void _pci_cleanup_msix(struct arc - if ( rangeset_remove_range(mmio_ro_ranges, msix->table.first, - msix->table.last) ) - WARN(); -+ msix->table.first = 0; -+ msix->table.last = 0; -+ - if ( rangeset_remove_range(mmio_ro_ranges, msix->pba.first, - msix->pba.last) ) - WARN(); -+ msix->pba.first = 0; -+ msix->pba.last = 0; - } - } - diff --git a/xsa338.patch b/xsa338.patch deleted file mode 100644 index 7765219..0000000 --- a/xsa338.patch +++ /dev/null @@ -1,42 +0,0 @@ -From: Jan Beulich -Subject: evtchn: relax port_is_valid() - -To avoid ports potentially becoming invalid behind the back of certain -other functions (due to ->max_evtchn shrinking) because of -- a guest invoking evtchn_reset() and from a 2nd vCPU opening new - channels in parallel (see also XSA-343), -- alloc_unbound_xen_event_channel() produced channels living above the - 2-level range (see also XSA-342), -drop the max_evtchns check from port_is_valid(). For a port for which -the function once returned "true", the returned value may not turn into -"false" later on. The function's result may only depend on bounds which -can only ever grow (which is the case for d->valid_evtchns). - -This also eliminates a false sense of safety, utilized by some of the -users (see again XSA-343): Without a suitable lock held, d->max_evtchns -may change at any time, and hence deducing that certain other operations -are safe when port_is_valid() returned true is not legitimate. The -opportunities to abuse this may get widened by the change here -(depending on guest and host configuration), but will be taken care of -by the other XSA. - -This is XSA-338. - -Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall ---- -v5: New, split from larger patch. - ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -107,8 +107,6 @@ void notify_via_xen_event_channel(struct - - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { -- if ( p >= d->max_evtchns ) -- return 0; - return p < read_atomic(&d->valid_evtchns); - } - diff --git a/xsa339.patch b/xsa339.patch deleted file mode 100644 index 3311ae0..0000000 --- a/xsa339.patch +++ /dev/null @@ -1,76 +0,0 @@ -From: Andrew Cooper -Subject: x86/pv: Avoid double exception injection - -There is at least one path (SYSENTER with NT set, Xen converts to #GP) which -ends up injecting the #GP fault twice, first in compat_sysenter(), and then a -second time in compat_test_all_events(), due to the stale TBF_EXCEPTION left -in TRAPBOUNCE_flags. - -The guest kernel sees the second fault first, which is a kernel level #GP -pointing at the head of the #GP handler, and is therefore a userspace -trigger-able DoS. - -This particular bug has bitten us several times before, so rearrange -{compat_,}create_bounce_frame() to clobber TRAPBOUNCE on success, rather than -leaving this task to one area of code which isn't used uniformly. - -Other scenarios which might result in a double injection (e.g. two calls -directly to compat_create_bounce_frame) will now crash the guest, which is far -more obvious than letting the kernel run with corrupt state. - -This is XSA-339 - -Fixes: fdac9515607b ("x86: clear EFLAGS.NT in SYSENTER entry path") -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/x86_64/compat/entry.S b/xen/arch/x86/x86_64/compat/entry.S -index c3e62f8734..73619f57ca 100644 ---- a/xen/arch/x86/x86_64/compat/entry.S -+++ b/xen/arch/x86/x86_64/compat/entry.S -@@ -78,7 +78,6 @@ compat_process_softirqs: - sti - .Lcompat_bounce_exception: - call compat_create_bounce_frame -- movb $0, TRAPBOUNCE_flags(%rdx) - jmp compat_test_all_events - - ALIGN -@@ -352,7 +351,13 @@ __UNLIKELY_END(compat_bounce_null_selector) - movl %eax,UREGS_cs+8(%rsp) - movl TRAPBOUNCE_eip(%rdx),%eax - movl %eax,UREGS_rip+8(%rsp) -+ -+ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ -+ xor %eax, %eax -+ mov %ax, TRAPBOUNCE_cs(%rdx) -+ mov %al, TRAPBOUNCE_flags(%rdx) - ret -+ - .section .fixup,"ax" - .Lfx13: - xorl %edi,%edi -diff --git a/xen/arch/x86/x86_64/entry.S b/xen/arch/x86/x86_64/entry.S -index 1e880eb9f6..71a00e846b 100644 ---- a/xen/arch/x86/x86_64/entry.S -+++ b/xen/arch/x86/x86_64/entry.S -@@ -90,7 +90,6 @@ process_softirqs: - sti - .Lbounce_exception: - call create_bounce_frame -- movb $0, TRAPBOUNCE_flags(%rdx) - jmp test_all_events - - ALIGN -@@ -512,6 +511,11 @@ UNLIKELY_START(z, create_bounce_frame_bad_bounce_ip) - jmp asm_domain_crash_synchronous /* Does not return */ - __UNLIKELY_END(create_bounce_frame_bad_bounce_ip) - movq %rax,UREGS_rip+8(%rsp) -+ -+ /* Trapbounce complete. Clobber state to avoid an erroneous second injection. */ -+ xor %eax, %eax -+ mov %rax, TRAPBOUNCE_eip(%rdx) -+ mov %al, TRAPBOUNCE_flags(%rdx) - ret - - .pushsection .fixup, "ax", @progbits diff --git a/xsa340.patch b/xsa340.patch deleted file mode 100644 index 38d04da..0000000 --- a/xsa340.patch +++ /dev/null @@ -1,65 +0,0 @@ -From: Julien Grall -Subject: xen/evtchn: Add missing barriers when accessing/allocating an event channel - -While the allocation of a bucket is always performed with the per-domain -lock, the bucket may be accessed without the lock taken (for instance, see -evtchn_send()). - -Instead such sites relies on port_is_valid() to return a non-zero value -when the port has a struct evtchn associated to it. The function will -mostly check whether the port is less than d->valid_evtchns as all the -buckets/event channels should be allocated up to that point. - -Unfortunately a compiler is free to re-order the assignment in -evtchn_allocate_port() so it would be possible to have d->valid_evtchns -updated before the new bucket has finish to allocate. - -Additionally on Arm, even if this was compiled "correctly", the -processor can still re-order the memory access. - -Add a write memory barrier in the allocation side and a read memory -barrier when the port is valid to prevent any re-ordering issue. - -This is XSA-340. - -Reported-by: Julien Grall -Signed-off-by: Julien Grall -Reviewed-by: Stefano Stabellini - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -178,6 +178,13 @@ int evtchn_allocate_port(struct domain * - return -ENOMEM; - bucket_from_port(d, port) = chn; - -+ /* -+ * d->valid_evtchns is used to check whether the bucket can be -+ * accessed without the per-domain lock. Therefore, -+ * d->valid_evtchns should be seen *after* the new bucket has -+ * been setup. -+ */ -+ smp_wmb(); - write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); - } - ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -107,7 +107,17 @@ void notify_via_xen_event_channel(struct - - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { -- return p < read_atomic(&d->valid_evtchns); -+ if ( p >= read_atomic(&d->valid_evtchns) ) -+ return false; -+ -+ /* -+ * The caller will usually access the event channel afterwards and -+ * may be done without taking the per-domain lock. The barrier is -+ * going in pair the smp_wmb() barrier in evtchn_allocate_port(). -+ */ -+ smp_rmb(); -+ -+ return true; - } - - static inline struct evtchn *evtchn_from_port(struct domain *d, unsigned int p) diff --git a/xsa342-4.13.patch b/xsa342-4.13.patch deleted file mode 100644 index 334baf1..0000000 --- a/xsa342-4.13.patch +++ /dev/null @@ -1,145 +0,0 @@ -From: Jan Beulich -Subject: evtchn/x86: enforce correct upper limit for 32-bit guests - -The recording of d->max_evtchns in evtchn_2l_init(), in particular with -the limited set of callers of the function, is insufficient. Neither for -PV nor for HVM guests the bitness is known at domain_create() time, yet -the upper bound in 2-level mode depends upon guest bitness. Recording -too high a limit "allows" x86 32-bit domains to open not properly usable -event channels, management of which (inside Xen) would then result in -corruption of the shared info and vCPU info structures. - -Keep the upper limit dynamic for the 2-level case, introducing a helper -function to retrieve the effective limit. This helper is now supposed to -be private to the event channel code. The used in do_poll() and -domain_dump_evtchn_info() weren't consistent with port uses elsewhere -and hence get switched to port_is_valid(). - -Furthermore FIFO mode's setup_ports() gets adjusted to loop only up to -the prior ABI limit, rather than all the way up to the new one. - -Finally a word on the change to do_poll(): Accessing ->max_evtchns -without holding a suitable lock was never safe, as it as well as -->evtchn_port_ops may change behind do_poll()'s back. Using -port_is_valid() instead widens some the window for potential abuse, -until we've dealt with the race altogether (see XSA-343). - -This is XSA-342. - -Reported-by: Julien Grall -Fixes: 48974e6ce52e ("evtchn: use a per-domain variable for the max number of event channels") -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall - ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -103,7 +103,6 @@ static const struct evtchn_port_ops evtc - void evtchn_2l_init(struct domain *d) - { - d->evtchn_port_ops = &evtchn_port_ops_2l; -- d->max_evtchns = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - } - - /* ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -151,7 +151,7 @@ static void free_evtchn_bucket(struct do - - int evtchn_allocate_port(struct domain *d, evtchn_port_t port) - { -- if ( port > d->max_evtchn_port || port >= d->max_evtchns ) -+ if ( port > d->max_evtchn_port || port >= max_evtchns(d) ) - return -ENOSPC; - - if ( port_is_valid(d, port) ) -@@ -1396,13 +1396,11 @@ static void domain_dump_evtchn_info(stru - - spin_lock(&d->event_lock); - -- for ( port = 1; port < d->max_evtchns; ++port ) -+ for ( port = 1; port_is_valid(d, port); ++port ) - { - const struct evtchn *chn; - char *ssid; - -- if ( !port_is_valid(d, port) ) -- continue; - chn = evtchn_from_port(d, port); - if ( chn->state == ECS_FREE ) - continue; ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -478,7 +478,7 @@ static void cleanup_event_array(struct d - d->evtchn_fifo = NULL; - } - --static void setup_ports(struct domain *d) -+static void setup_ports(struct domain *d, unsigned int prev_evtchns) - { - unsigned int port; - -@@ -488,7 +488,7 @@ static void setup_ports(struct domain *d - * - save its pending state. - * - set default priority. - */ -- for ( port = 1; port < d->max_evtchns; port++ ) -+ for ( port = 1; port < prev_evtchns; port++ ) - { - struct evtchn *evtchn; - -@@ -546,6 +546,8 @@ int evtchn_fifo_init_control(struct evtc - if ( !d->evtchn_fifo ) - { - struct vcpu *vcb; -+ /* Latch the value before it changes during setup_event_array(). */ -+ unsigned int prev_evtchns = max_evtchns(d); - - for_each_vcpu ( d, vcb ) { - rc = setup_control_block(vcb); -@@ -562,8 +564,7 @@ int evtchn_fifo_init_control(struct evtc - goto error; - - d->evtchn_port_ops = &evtchn_port_ops_fifo; -- d->max_evtchns = EVTCHN_FIFO_NR_CHANNELS; -- setup_ports(d); -+ setup_ports(d, prev_evtchns); - } - else - rc = map_control_block(v, gfn, offset); ---- a/xen/common/schedule.c -+++ b/xen/common/schedule.c -@@ -1434,7 +1434,7 @@ static long do_poll(struct sched_poll *s - goto out; - - rc = -EINVAL; -- if ( port >= d->max_evtchns ) -+ if ( !port_is_valid(d, port) ) - goto out; - - rc = 0; ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -105,6 +105,12 @@ void notify_via_xen_event_channel(struct - #define bucket_from_port(d, p) \ - ((group_from_port(d, p))[((p) % EVTCHNS_PER_GROUP) / EVTCHNS_PER_BUCKET]) - -+static inline unsigned int max_evtchns(const struct domain *d) -+{ -+ return d->evtchn_fifo ? EVTCHN_FIFO_NR_CHANNELS -+ : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); -+} -+ - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { - if ( p >= read_atomic(&d->valid_evtchns) ) ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -382,7 +382,6 @@ struct domain - /* Event channel information. */ - struct evtchn *evtchn; /* first bucket only */ - struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ -- unsigned int max_evtchns; /* number supported by ABI */ - unsigned int max_evtchn_port; /* max permitted port number */ - unsigned int valid_evtchns; /* number of allocated event channels */ - spinlock_t event_lock; diff --git a/xsa343-1.patch b/xsa343-1.patch deleted file mode 100644 index 0abbc03..0000000 --- a/xsa343-1.patch +++ /dev/null @@ -1,199 +0,0 @@ -From: Jan Beulich -Subject: evtchn: evtchn_reset() shouldn't succeed with still-open ports - -While the function closes all ports, it does so without holding any -lock, and hence racing requests may be issued causing new ports to get -opened. This would have been problematic in particular if such a newly -opened port had a port number above the new implementation limit (i.e. -when switching from FIFO to 2-level) after the reset, as prior to -"evtchn: relax port_is_valid()" this could have led to e.g. -evtchn_close()'s "BUG_ON(!port_is_valid(d2, port2))" to trigger. - -Introduce a counter of active ports and check that it's (still) no -larger then the number of Xen internally used ones after obtaining the -necessary lock in evtchn_reset(). - -As to the access model of the new {active,xen}_evtchns fields - while -all writes get done using write_atomic(), reads ought to use -read_atomic() only when outside of a suitably locked region. - -Note that as of now evtchn_bind_virq() and evtchn_bind_ipi() don't have -a need to call check_free_port(). - -This is part of XSA-343. - -Signed-off-by: Jan Beulich -Reviewed-by: Stefano Stabellini -Reviewed-by: Julien Grall ---- -v7: Drop optimization from evtchn_reset(). -v6: Fix loop exit condition in evtchn_reset(). Use {read,write}_atomic() - also for xen_evtchns. -v5: Move increment in alloc_unbound_xen_event_channel() out of the inner - locked region. -v4: Account for Xen internal ports. -v3: Document intended access next to new struct field. -v2: Add comment to check_free_port(). Drop commented out calls. - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -188,6 +188,8 @@ int evtchn_allocate_port(struct domain * - write_atomic(&d->valid_evtchns, d->valid_evtchns + EVTCHNS_PER_BUCKET); - } - -+ write_atomic(&d->active_evtchns, d->active_evtchns + 1); -+ - return 0; - } - -@@ -211,11 +213,26 @@ static int get_free_port(struct domain * - return -ENOSPC; - } - -+/* -+ * Check whether a port is still marked free, and if so update the domain -+ * counter accordingly. To be used on function exit paths. -+ */ -+static void check_free_port(struct domain *d, evtchn_port_t port) -+{ -+ if ( port_is_valid(d, port) && -+ evtchn_from_port(d, port)->state == ECS_FREE ) -+ write_atomic(&d->active_evtchns, d->active_evtchns - 1); -+} -+ - void evtchn_free(struct domain *d, struct evtchn *chn) - { - /* Clear pending event to avoid unexpected behavior on re-bind. */ - evtchn_port_clear_pending(d, chn); - -+ if ( consumer_is_xen(chn) ) -+ write_atomic(&d->xen_evtchns, d->xen_evtchns - 1); -+ write_atomic(&d->active_evtchns, d->active_evtchns - 1); -+ - /* Reset binding to vcpu0 when the channel is freed. */ - chn->state = ECS_FREE; - chn->notify_vcpu_id = 0; -@@ -258,6 +275,7 @@ static long evtchn_alloc_unbound(evtchn_ - alloc->port = port; - - out: -+ check_free_port(d, port); - spin_unlock(&d->event_lock); - rcu_unlock_domain(d); - -@@ -351,6 +369,7 @@ static long evtchn_bind_interdomain(evtc - bind->local_port = lport; - - out: -+ check_free_port(ld, lport); - spin_unlock(&ld->event_lock); - if ( ld != rd ) - spin_unlock(&rd->event_lock); -@@ -488,7 +507,7 @@ static long evtchn_bind_pirq(evtchn_bind - struct domain *d = current->domain; - struct vcpu *v = d->vcpu[0]; - struct pirq *info; -- int port, pirq = bind->pirq; -+ int port = 0, pirq = bind->pirq; - long rc; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) -@@ -536,6 +555,7 @@ static long evtchn_bind_pirq(evtchn_bind - arch_evtchn_bind_pirq(d, pirq); - - out: -+ check_free_port(d, port); - spin_unlock(&d->event_lock); - - return rc; -@@ -1011,10 +1031,10 @@ int evtchn_unmask(unsigned int port) - return 0; - } - -- - int evtchn_reset(struct domain *d) - { - unsigned int i; -+ int rc = 0; - - if ( d != current->domain && !d->controller_pause_count ) - return -EINVAL; -@@ -1024,7 +1044,9 @@ int evtchn_reset(struct domain *d) - - spin_lock(&d->event_lock); - -- if ( d->evtchn_fifo ) -+ if ( d->active_evtchns > d->xen_evtchns ) -+ rc = -EAGAIN; -+ else if ( d->evtchn_fifo ) - { - /* Switching back to 2-level ABI. */ - evtchn_fifo_destroy(d); -@@ -1033,7 +1055,7 @@ int evtchn_reset(struct domain *d) - - spin_unlock(&d->event_lock); - -- return 0; -+ return rc; - } - - static long evtchn_set_priority(const struct evtchn_set_priority *set_priority) -@@ -1219,10 +1241,9 @@ int alloc_unbound_xen_event_channel( - - spin_lock(&ld->event_lock); - -- rc = get_free_port(ld); -+ port = rc = get_free_port(ld); - if ( rc < 0 ) - goto out; -- port = rc; - chn = evtchn_from_port(ld, port); - - rc = xsm_evtchn_unbound(XSM_TARGET, ld, chn, remote_domid); -@@ -1238,7 +1259,10 @@ int alloc_unbound_xen_event_channel( - - spin_unlock(&chn->lock); - -+ write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); -+ - out: -+ check_free_port(ld, port); - spin_unlock(&ld->event_lock); - - return rc < 0 ? rc : port; -@@ -1314,6 +1338,7 @@ int evtchn_init(struct domain *d, unsign - return -EINVAL; - } - evtchn_from_port(d, 0)->state = ECS_RESERVED; -+ write_atomic(&d->active_evtchns, 0); - - #if MAX_VIRT_CPUS > BITS_PER_LONG - d->poll_mask = xzalloc_array(unsigned long, BITS_TO_LONGS(d->max_vcpus)); -@@ -1340,6 +1365,8 @@ void evtchn_destroy(struct domain *d) - for ( i = 0; port_is_valid(d, i); i++ ) - evtchn_close(d, i, 0); - -+ ASSERT(!d->active_evtchns); -+ - clear_global_virq_handlers(d); - - evtchn_fifo_destroy(d); ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -361,6 +361,16 @@ struct domain - struct evtchn **evtchn_group[NR_EVTCHN_GROUPS]; /* all other buckets */ - unsigned int max_evtchn_port; /* max permitted port number */ - unsigned int valid_evtchns; /* number of allocated event channels */ -+ /* -+ * Number of in-use event channels. Writers should use write_atomic(). -+ * Readers need to use read_atomic() only when not holding event_lock. -+ */ -+ unsigned int active_evtchns; -+ /* -+ * Number of event channels used internally by Xen (not subject to -+ * EVTCHNOP_reset). Read/write access like for active_evtchns. -+ */ -+ unsigned int xen_evtchns; - spinlock_t event_lock; - const struct evtchn_port_ops *evtchn_port_ops; - struct evtchn_fifo_domain *evtchn_fifo; diff --git a/xsa343-2.patch b/xsa343-2.patch deleted file mode 100644 index b8eb499..0000000 --- a/xsa343-2.patch +++ /dev/null @@ -1,295 +0,0 @@ -From: Jan Beulich -Subject: evtchn: convert per-channel lock to be IRQ-safe - -... in order for send_guest_{global,vcpu}_virq() to be able to make use -of it. - -This is part of XSA-343. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -v6: New. ---- -TBD: This is the "dumb" conversion variant. In a couple of cases the - slightly simpler spin_{,un}lock_irq() could apparently be used. - ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -248,6 +248,7 @@ static long evtchn_alloc_unbound(evtchn_ - int port; - domid_t dom = alloc->dom; - long rc; -+ unsigned long flags; - - d = rcu_lock_domain_by_any_id(dom); - if ( d == NULL ) -@@ -263,14 +264,14 @@ static long evtchn_alloc_unbound(evtchn_ - if ( rc ) - goto out; - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_UNBOUND; - if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) - chn->u.unbound.remote_domid = current->domain->domain_id; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - alloc->port = port; - -@@ -283,26 +284,32 @@ static long evtchn_alloc_unbound(evtchn_ - } - - --static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) -+static unsigned long double_evtchn_lock(struct evtchn *lchn, -+ struct evtchn *rchn) - { -- if ( lchn < rchn ) -+ unsigned long flags; -+ -+ if ( lchn <= rchn ) - { -- spin_lock(&lchn->lock); -- spin_lock(&rchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); -+ if ( lchn != rchn ) -+ spin_lock(&rchn->lock); - } - else - { -- if ( lchn != rchn ) -- spin_lock(&rchn->lock); -+ spin_lock_irqsave(&rchn->lock, flags); - spin_lock(&lchn->lock); - } -+ -+ return flags; - } - --static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) -+static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, -+ unsigned long flags) - { -- spin_unlock(&lchn->lock); - if ( lchn != rchn ) -- spin_unlock(&rchn->lock); -+ spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&rchn->lock, flags); - } - - static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) -@@ -312,6 +319,7 @@ static long evtchn_bind_interdomain(evtc - int lport, rport = bind->remote_port; - domid_t rdom = bind->remote_dom; - long rc; -+ unsigned long flags; - - if ( rdom == DOMID_SELF ) - rdom = current->domain->domain_id; -@@ -347,7 +355,7 @@ static long evtchn_bind_interdomain(evtc - if ( rc ) - goto out; - -- double_evtchn_lock(lchn, rchn); -+ flags = double_evtchn_lock(lchn, rchn); - - lchn->u.interdomain.remote_dom = rd; - lchn->u.interdomain.remote_port = rport; -@@ -364,7 +372,7 @@ static long evtchn_bind_interdomain(evtc - */ - evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - -- double_evtchn_unlock(lchn, rchn); -+ double_evtchn_unlock(lchn, rchn, flags); - - bind->local_port = lport; - -@@ -387,6 +395,7 @@ int evtchn_bind_virq(evtchn_bind_virq_t - struct domain *d = current->domain; - int virq = bind->virq, vcpu = bind->vcpu; - int rc = 0; -+ unsigned long flags; - - if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) - return -EINVAL; -@@ -424,14 +433,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t - - chn = evtchn_from_port(d, port); - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_VIRQ; - chn->notify_vcpu_id = vcpu; - chn->u.virq = virq; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - v->virq_to_evtchn[virq] = bind->port = port; - -@@ -448,6 +457,7 @@ static long evtchn_bind_ipi(evtchn_bind_ - struct domain *d = current->domain; - int port, vcpu = bind->vcpu; - long rc = 0; -+ unsigned long flags; - - if ( domain_vcpu(d, vcpu) == NULL ) - return -ENOENT; -@@ -459,13 +469,13 @@ static long evtchn_bind_ipi(evtchn_bind_ - - chn = evtchn_from_port(d, port); - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_IPI; - chn->notify_vcpu_id = vcpu; - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - bind->port = port; - -@@ -509,6 +519,7 @@ static long evtchn_bind_pirq(evtchn_bind - struct pirq *info; - int port = 0, pirq = bind->pirq; - long rc; -+ unsigned long flags; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) - return -EINVAL; -@@ -541,14 +552,14 @@ static long evtchn_bind_pirq(evtchn_bind - goto out; - } - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_PIRQ; - chn->u.pirq.irq = pirq; - link_pirq_port(port, chn, v); - evtchn_port_init(d, chn); - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - bind->port = port; - -@@ -569,6 +580,7 @@ int evtchn_close(struct domain *d1, int - struct evtchn *chn1, *chn2; - int port2; - long rc = 0; -+ unsigned long flags; - - again: - spin_lock(&d1->event_lock); -@@ -668,14 +680,14 @@ int evtchn_close(struct domain *d1, int - BUG_ON(chn2->state != ECS_INTERDOMAIN); - BUG_ON(chn2->u.interdomain.remote_dom != d1); - -- double_evtchn_lock(chn1, chn2); -+ flags = double_evtchn_lock(chn1, chn2); - - evtchn_free(d1, chn1); - - chn2->state = ECS_UNBOUND; - chn2->u.unbound.remote_domid = d1->domain_id; - -- double_evtchn_unlock(chn1, chn2); -+ double_evtchn_unlock(chn1, chn2, flags); - - goto out; - -@@ -683,9 +695,9 @@ int evtchn_close(struct domain *d1, int - BUG(); - } - -- spin_lock(&chn1->lock); -+ spin_lock_irqsave(&chn1->lock, flags); - evtchn_free(d1, chn1); -- spin_unlock(&chn1->lock); -+ spin_unlock_irqrestore(&chn1->lock, flags); - - out: - if ( d2 != NULL ) -@@ -705,13 +717,14 @@ int evtchn_send(struct domain *ld, unsig - struct evtchn *lchn, *rchn; - struct domain *rd; - int rport, ret = 0; -+ unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - return -EINVAL; - - lchn = evtchn_from_port(ld, lport); - -- spin_lock(&lchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); - - /* Guest cannot send via a Xen-attached event channel. */ - if ( unlikely(consumer_is_xen(lchn)) ) -@@ -746,7 +759,7 @@ int evtchn_send(struct domain *ld, unsig - } - - out: -- spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&lchn->lock, flags); - - return ret; - } -@@ -1238,6 +1251,7 @@ int alloc_unbound_xen_event_channel( - { - struct evtchn *chn; - int port, rc; -+ unsigned long flags; - - spin_lock(&ld->event_lock); - -@@ -1250,14 +1264,14 @@ int alloc_unbound_xen_event_channel( - if ( rc ) - goto out; - -- spin_lock(&chn->lock); -+ spin_lock_irqsave(&chn->lock, flags); - - chn->state = ECS_UNBOUND; - chn->xen_consumer = get_xen_consumer(notification_fn); - chn->notify_vcpu_id = lvcpu; - chn->u.unbound.remote_domid = remote_domid; - -- spin_unlock(&chn->lock); -+ spin_unlock_irqrestore(&chn->lock, flags); - - write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); - -@@ -1280,11 +1294,12 @@ void notify_via_xen_event_channel(struct - { - struct evtchn *lchn, *rchn; - struct domain *rd; -+ unsigned long flags; - - ASSERT(port_is_valid(ld, lport)); - lchn = evtchn_from_port(ld, lport); - -- spin_lock(&lchn->lock); -+ spin_lock_irqsave(&lchn->lock, flags); - - if ( likely(lchn->state == ECS_INTERDOMAIN) ) - { -@@ -1294,7 +1309,7 @@ void notify_via_xen_event_channel(struct - evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); - } - -- spin_unlock(&lchn->lock); -+ spin_unlock_irqrestore(&lchn->lock, flags); - } - - void evtchn_check_pollers(struct domain *d, unsigned int port) diff --git a/xsa343-3.patch b/xsa343-3.patch deleted file mode 100644 index e513e30..0000000 --- a/xsa343-3.patch +++ /dev/null @@ -1,392 +0,0 @@ -From: Jan Beulich -Subject: evtchn: address races with evtchn_reset() - -Neither d->evtchn_port_ops nor max_evtchns(d) may be used in an entirely -lock-less manner, as both may change by a racing evtchn_reset(). In the -common case, at least one of the domain's event lock or the per-channel -lock needs to be held. In the specific case of the inter-domain sending -by evtchn_send() and notify_via_xen_event_channel() holding the other -side's per-channel lock is sufficient, as the channel can't change state -without both per-channel locks held. Without such a channel changing -state, evtchn_reset() can't complete successfully. - -Lock-free accesses continue to be permitted for the shim (calling some -otherwise internal event channel functions), as this happens while the -domain is in effectively single-threaded mode. Special care also needs -taking for the shim's marking of in-use ports as ECS_RESERVED (allowing -use of such ports in the shim case is okay because switching into and -hence also out of FIFO mode is impossihble there). - -As a side effect, certain operations on Xen bound event channels which -were mistakenly permitted so far (e.g. unmask or poll) will be refused -now. - -This is part of XSA-343. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall ---- -v9: Add arch_evtchn_is_special() to fix PV shim. -v8: Add BUILD_BUG_ON() in evtchn_usable(). -v7: Add locking related comment ahead of struct evtchn_port_ops. -v6: New. ---- -TBD: I've been considering to move some of the wrappers from xen/event.h - into event_channel.c (or even drop them altogether), when they - require external locking (e.g. evtchn_port_init() or - evtchn_port_set_priority()). Does anyone have a strong opinion - either way? - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2488,14 +2488,24 @@ static void dump_irqs(unsigned char key) - - for ( i = 0; i < action->nr_guests; ) - { -+ struct evtchn *evtchn; -+ unsigned int pending = 2, masked = 2; -+ - d = action->guest[i++]; - pirq = domain_irq_to_pirq(d, irq); - info = pirq_info(d, pirq); -+ evtchn = evtchn_from_port(d, info->evtchn); -+ local_irq_disable(); -+ if ( spin_trylock(&evtchn->lock) ) -+ { -+ pending = evtchn_is_pending(d, evtchn); -+ masked = evtchn_is_masked(d, evtchn); -+ spin_unlock(&evtchn->lock); -+ } -+ local_irq_enable(); - printk("d%d:%3d(%c%c%c)%c", -- d->domain_id, pirq, -- evtchn_port_is_pending(d, info->evtchn) ? 'P' : '-', -- evtchn_port_is_masked(d, info->evtchn) ? 'M' : '-', -- info->masked ? 'M' : '-', -+ d->domain_id, pirq, "-P?"[pending], -+ "-M?"[masked], info->masked ? 'M' : '-', - i < action->nr_guests ? ',' : '\n'); - } - } ---- a/xen/arch/x86/pv/shim.c -+++ b/xen/arch/x86/pv/shim.c -@@ -660,8 +660,11 @@ void pv_shim_inject_evtchn(unsigned int - if ( port_is_valid(guest, port) ) - { - struct evtchn *chn = evtchn_from_port(guest, port); -+ unsigned long flags; - -+ spin_lock_irqsave(&chn->lock, flags); - evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -+ spin_unlock_irqrestore(&chn->lock, flags); - } - } - ---- a/xen/common/event_2l.c -+++ b/xen/common/event_2l.c -@@ -63,8 +63,10 @@ static void evtchn_2l_unmask(struct doma - } - } - --static bool evtchn_2l_is_pending(const struct domain *d, evtchn_port_t port) -+static bool evtchn_2l_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -+ evtchn_port_t port = evtchn->port; - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); -@@ -72,8 +74,10 @@ static bool evtchn_2l_is_pending(const s - guest_test_bit(d, port, &shared_info(d, evtchn_pending))); - } - --static bool evtchn_2l_is_masked(const struct domain *d, evtchn_port_t port) -+static bool evtchn_2l_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) - { -+ evtchn_port_t port = evtchn->port; - unsigned int max_ports = BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - - ASSERT(port < max_ports); ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -156,8 +156,9 @@ int evtchn_allocate_port(struct domain * - - if ( port_is_valid(d, port) ) - { -- if ( evtchn_from_port(d, port)->state != ECS_FREE || -- evtchn_port_is_busy(d, port) ) -+ const struct evtchn *chn = evtchn_from_port(d, port); -+ -+ if ( chn->state != ECS_FREE || evtchn_is_busy(d, chn) ) - return -EBUSY; - } - else -@@ -774,6 +775,7 @@ void send_guest_vcpu_virq(struct vcpu *v - unsigned long flags; - int port; - struct domain *d; -+ struct evtchn *chn; - - ASSERT(!virq_is_global(virq)); - -@@ -784,7 +786,10 @@ void send_guest_vcpu_virq(struct vcpu *v - goto out; - - d = v->domain; -- evtchn_port_set_pending(d, v->vcpu_id, evtchn_from_port(d, port)); -+ chn = evtchn_from_port(d, port); -+ spin_lock(&chn->lock); -+ evtchn_port_set_pending(d, v->vcpu_id, chn); -+ spin_unlock(&chn->lock); - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -813,7 +818,9 @@ void send_guest_global_virq(struct domai - goto out; - - chn = evtchn_from_port(d, port); -+ spin_lock(&chn->lock); - evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ spin_unlock(&chn->lock); - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -823,6 +830,7 @@ void send_guest_pirq(struct domain *d, c - { - int port; - struct evtchn *chn; -+ unsigned long flags; - - /* - * PV guests: It should not be possible to race with __evtchn_close(). The -@@ -837,7 +845,9 @@ void send_guest_pirq(struct domain *d, c - } - - chn = evtchn_from_port(d, port); -+ spin_lock_irqsave(&chn->lock, flags); - evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ spin_unlock_irqrestore(&chn->lock, flags); - } - - static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; -@@ -1034,12 +1044,15 @@ int evtchn_unmask(unsigned int port) - { - struct domain *d = current->domain; - struct evtchn *evtchn; -+ unsigned long flags; - - if ( unlikely(!port_is_valid(d, port)) ) - return -EINVAL; - - evtchn = evtchn_from_port(d, port); -+ spin_lock_irqsave(&evtchn->lock, flags); - evtchn_port_unmask(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); - - return 0; - } -@@ -1449,8 +1462,8 @@ static void domain_dump_evtchn_info(stru - - printk(" %4u [%d/%d/", - port, -- evtchn_port_is_pending(d, port), -- evtchn_port_is_masked(d, port)); -+ evtchn_is_pending(d, chn), -+ evtchn_is_masked(d, chn)); - evtchn_port_print_state(d, chn); - printk("]: s=%d n=%d x=%d", - chn->state, chn->notify_vcpu_id, chn->xen_consumer); ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -296,23 +296,26 @@ static void evtchn_fifo_unmask(struct do - evtchn_fifo_set_pending(v, evtchn); - } - --static bool evtchn_fifo_is_pending(const struct domain *d, evtchn_port_t port) -+static bool evtchn_fifo_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return word && guest_test_bit(d, EVTCHN_FIFO_PENDING, word); - } - --static bool_t evtchn_fifo_is_masked(const struct domain *d, evtchn_port_t port) -+static bool_t evtchn_fifo_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return !word || guest_test_bit(d, EVTCHN_FIFO_MASKED, word); - } - --static bool_t evtchn_fifo_is_busy(const struct domain *d, evtchn_port_t port) -+static bool_t evtchn_fifo_is_busy(const struct domain *d, -+ const struct evtchn *evtchn) - { -- const event_word_t *word = evtchn_fifo_word_from_port(d, port); -+ const event_word_t *word = evtchn_fifo_word_from_port(d, evtchn->port); - - return word && guest_test_bit(d, EVTCHN_FIFO_LINKED, word); - } ---- a/xen/include/asm-x86/event.h -+++ b/xen/include/asm-x86/event.h -@@ -47,4 +47,10 @@ static inline bool arch_virq_is_global(u - return true; - } - -+#ifdef CONFIG_PV_SHIM -+# include -+# define arch_evtchn_is_special(chn) \ -+ (pv_shim && (chn)->port && (chn)->state == ECS_RESERVED) -+#endif -+ - #endif ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -133,6 +133,24 @@ static inline struct evtchn *evtchn_from - return bucket_from_port(d, p) + (p % EVTCHNS_PER_BUCKET); - } - -+/* -+ * "usable" as in "by a guest", i.e. Xen consumed channels are assumed to be -+ * taken care of separately where used for Xen's internal purposes. -+ */ -+static bool evtchn_usable(const struct evtchn *evtchn) -+{ -+ if ( evtchn->xen_consumer ) -+ return false; -+ -+#ifdef arch_evtchn_is_special -+ if ( arch_evtchn_is_special(evtchn) ) -+ return true; -+#endif -+ -+ BUILD_BUG_ON(ECS_FREE > ECS_RESERVED); -+ return evtchn->state > ECS_RESERVED; -+} -+ - /* Wait on a Xen-attached event channel. */ - #define wait_on_xen_event_channel(port, condition) \ - do { \ -@@ -165,19 +183,24 @@ int evtchn_reset(struct domain *d); - - /* - * Low-level event channel port ops. -+ * -+ * All hooks have to be called with a lock held which prevents the channel -+ * from changing state. This may be the domain event lock, the per-channel -+ * lock, or in the case of sending interdomain events also the other side's -+ * per-channel lock. Exceptions apply in certain cases for the PV shim. - */ - struct evtchn_port_ops { - void (*init)(struct domain *d, struct evtchn *evtchn); - void (*set_pending)(struct vcpu *v, struct evtchn *evtchn); - void (*clear_pending)(struct domain *d, struct evtchn *evtchn); - void (*unmask)(struct domain *d, struct evtchn *evtchn); -- bool (*is_pending)(const struct domain *d, evtchn_port_t port); -- bool (*is_masked)(const struct domain *d, evtchn_port_t port); -+ bool (*is_pending)(const struct domain *d, const struct evtchn *evtchn); -+ bool (*is_masked)(const struct domain *d, const struct evtchn *evtchn); - /* - * Is the port unavailable because it's still being cleaned up - * after being closed? - */ -- bool (*is_busy)(const struct domain *d, evtchn_port_t port); -+ bool (*is_busy)(const struct domain *d, const struct evtchn *evtchn); - int (*set_priority)(struct domain *d, struct evtchn *evtchn, - unsigned int priority); - void (*print_state)(struct domain *d, const struct evtchn *evtchn); -@@ -193,38 +216,67 @@ static inline void evtchn_port_set_pendi - unsigned int vcpu_id, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->set_pending(d->vcpu[vcpu_id], evtchn); - } - - static inline void evtchn_port_clear_pending(struct domain *d, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->clear_pending(d, evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->clear_pending(d, evtchn); - } - - static inline void evtchn_port_unmask(struct domain *d, - struct evtchn *evtchn) - { -- d->evtchn_port_ops->unmask(d, evtchn); -+ if ( evtchn_usable(evtchn) ) -+ d->evtchn_port_ops->unmask(d, evtchn); - } - --static inline bool evtchn_port_is_pending(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_is_pending(const struct domain *d, -+ const struct evtchn *evtchn) - { -- return d->evtchn_port_ops->is_pending(d, port); -+ return evtchn_usable(evtchn) && d->evtchn_port_ops->is_pending(d, evtchn); - } - --static inline bool evtchn_port_is_masked(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) - { -- return d->evtchn_port_ops->is_masked(d, port); -+ struct evtchn *evtchn = evtchn_from_port(d, port); -+ bool rc; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&evtchn->lock, flags); -+ rc = evtchn_is_pending(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ return rc; -+} -+ -+static inline bool evtchn_is_masked(const struct domain *d, -+ const struct evtchn *evtchn) -+{ -+ return !evtchn_usable(evtchn) || d->evtchn_port_ops->is_masked(d, evtchn); -+} -+ -+static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) -+{ -+ struct evtchn *evtchn = evtchn_from_port(d, port); -+ bool rc; -+ unsigned long flags; -+ -+ spin_lock_irqsave(&evtchn->lock, flags); -+ rc = evtchn_is_masked(d, evtchn); -+ spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ return rc; - } - --static inline bool evtchn_port_is_busy(const struct domain *d, -- evtchn_port_t port) -+static inline bool evtchn_is_busy(const struct domain *d, -+ const struct evtchn *evtchn) - { - return d->evtchn_port_ops->is_busy && -- d->evtchn_port_ops->is_busy(d, port); -+ d->evtchn_port_ops->is_busy(d, evtchn); - } - - static inline int evtchn_port_set_priority(struct domain *d, -@@ -233,6 +285,8 @@ static inline int evtchn_port_set_priori - { - if ( !d->evtchn_port_ops->set_priority ) - return -ENOSYS; -+ if ( !evtchn_usable(evtchn) ) -+ return -EACCES; - return d->evtchn_port_ops->set_priority(d, evtchn, priority); - } - diff --git a/xsa344-4.13-1.patch b/xsa344-4.13-1.patch deleted file mode 100644 index d8e9b3f..0000000 --- a/xsa344-4.13-1.patch +++ /dev/null @@ -1,130 +0,0 @@ -From: Jan Beulich -Subject: evtchn: arrange for preemption in evtchn_destroy() - -Especially closing of fully established interdomain channels can take -quite some time, due to the locking involved. Therefore we shouldn't -assume we can clean up still active ports all in one go. Besides adding -the necessary preemption check, also avoid pointlessly starting from -(or now really ending at) 0; 1 is the lowest numbered port which may -need closing. - -Since we're now reducing ->valid_evtchns, free_xen_event_channel(), -and (at least to be on the safe side) notify_via_xen_event_channel() -need to cope with attempts to close / unbind from / send through already -closed (and no longer valid, as per port_is_valid()) ports. - -This is part of XSA-344. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall -Reviewed-by: Stefano Stabellini - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -770,12 +770,14 @@ int domain_kill(struct domain *d) - return domain_kill(d); - d->is_dying = DOMDYING_dying; - argo_destroy(d); -- evtchn_destroy(d); - gnttab_release_mappings(d); - vnuma_destroy(d->vnuma); - domain_set_outstanding_pages(d, 0); - /* fallthrough */ - case DOMDYING_dying: -+ rc = evtchn_destroy(d); -+ if ( rc ) -+ break; - rc = domain_relinquish_resources(d); - if ( rc != 0 ) - break; ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -1297,7 +1297,16 @@ int alloc_unbound_xen_event_channel( - - void free_xen_event_channel(struct domain *d, int port) - { -- BUG_ON(!port_is_valid(d, port)); -+ if ( !port_is_valid(d, port) ) -+ { -+ /* -+ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing -+ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). -+ */ -+ smp_rmb(); -+ BUG_ON(!d->is_dying); -+ return; -+ } - - evtchn_close(d, port, 0); - } -@@ -1309,7 +1318,17 @@ void notify_via_xen_event_channel(struct - struct domain *rd; - unsigned long flags; - -- ASSERT(port_is_valid(ld, lport)); -+ if ( !port_is_valid(ld, lport) ) -+ { -+ /* -+ * Make sure ->is_dying is read /after/ ->valid_evtchns, pairing -+ * with the spin_barrier() and BUG_ON() in evtchn_destroy(). -+ */ -+ smp_rmb(); -+ ASSERT(ld->is_dying); -+ return; -+ } -+ - lchn = evtchn_from_port(ld, lport); - - spin_lock_irqsave(&lchn->lock, flags); -@@ -1380,8 +1399,7 @@ int evtchn_init(struct domain *d, unsign - return 0; - } - -- --void evtchn_destroy(struct domain *d) -+int evtchn_destroy(struct domain *d) - { - unsigned int i; - -@@ -1390,14 +1408,29 @@ void evtchn_destroy(struct domain *d) - spin_barrier(&d->event_lock); - - /* Close all existing event channels. */ -- for ( i = 0; port_is_valid(d, i); i++ ) -+ for ( i = d->valid_evtchns; --i; ) -+ { - evtchn_close(d, i, 0); - -+ /* -+ * Avoid preempting when called from domain_create()'s error path, -+ * and don't check too often (choice of frequency is arbitrary). -+ */ -+ if ( i && !(i & 0x3f) && d->is_dying != DOMDYING_dead && -+ hypercall_preempt_check() ) -+ { -+ write_atomic(&d->valid_evtchns, i); -+ return -ERESTART; -+ } -+ } -+ - ASSERT(!d->active_evtchns); - - clear_global_virq_handlers(d); - - evtchn_fifo_destroy(d); -+ -+ return 0; - } - - ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -136,7 +136,7 @@ struct evtchn - } __attribute__((aligned(64))); - - int evtchn_init(struct domain *d, unsigned int max_port); --void evtchn_destroy(struct domain *d); /* from domain_kill */ -+int evtchn_destroy(struct domain *d); /* from domain_kill */ - void evtchn_destroy_final(struct domain *d); /* from complete_domain_destroy */ - - struct waitqueue_vcpu; diff --git a/xsa344-4.13-2.patch b/xsa344-4.13-2.patch deleted file mode 100644 index 3f03394..0000000 --- a/xsa344-4.13-2.patch +++ /dev/null @@ -1,203 +0,0 @@ -From: Jan Beulich -Subject: evtchn: arrange for preemption in evtchn_reset() - -Like for evtchn_destroy() looping over all possible event channels to -close them can take a significant amount of time. Unlike done there, we -can't alter domain properties (i.e. d->valid_evtchns) here. Borrow, in a -lightweight form, the paging domctl continuation concept, redirecting -the continuations to different sub-ops. Just like there this is to be -able to allow for predictable overall results of the involved sub-ops: -Racing requests should either complete or be refused. - -Note that a domain can't interfere with an already started (by a remote -domain) reset, due to being paused. It can prevent a remote reset from -happening by leaving a reset unfinished, but that's only going to affect -itself. - -This is part of XSA-344. - -Signed-off-by: Jan Beulich -Acked-by: Julien Grall -Reviewed-by: Stefano Stabellini - ---- a/xen/common/domain.c -+++ b/xen/common/domain.c -@@ -1214,7 +1214,7 @@ void domain_unpause_except_self(struct d - domain_unpause(d); - } - --int domain_soft_reset(struct domain *d) -+int domain_soft_reset(struct domain *d, bool resuming) - { - struct vcpu *v; - int rc; -@@ -1228,7 +1228,7 @@ int domain_soft_reset(struct domain *d) - } - spin_unlock(&d->shutdown_lock); - -- rc = evtchn_reset(d); -+ rc = evtchn_reset(d, resuming); - if ( rc ) - return rc; - ---- a/xen/common/domctl.c -+++ b/xen/common/domctl.c -@@ -572,12 +572,22 @@ long do_domctl(XEN_GUEST_HANDLE_PARAM(xe - } - - case XEN_DOMCTL_soft_reset: -+ case XEN_DOMCTL_soft_reset_cont: - if ( d == current->domain ) /* no domain_pause() */ - { - ret = -EINVAL; - break; - } -- ret = domain_soft_reset(d); -+ ret = domain_soft_reset(d, op->cmd == XEN_DOMCTL_soft_reset_cont); -+ if ( ret == -ERESTART ) -+ { -+ op->cmd = XEN_DOMCTL_soft_reset_cont; -+ if ( !__copy_field_to_guest(u_domctl, op, cmd) ) -+ ret = hypercall_create_continuation(__HYPERVISOR_domctl, -+ "h", u_domctl); -+ else -+ ret = -EFAULT; -+ } - break; - - case XEN_DOMCTL_destroydomain: ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -1057,7 +1057,7 @@ int evtchn_unmask(unsigned int port) - return 0; - } - --int evtchn_reset(struct domain *d) -+int evtchn_reset(struct domain *d, bool resuming) - { - unsigned int i; - int rc = 0; -@@ -1065,11 +1065,40 @@ int evtchn_reset(struct domain *d) - if ( d != current->domain && !d->controller_pause_count ) - return -EINVAL; - -- for ( i = 0; port_is_valid(d, i); i++ ) -+ spin_lock(&d->event_lock); -+ -+ /* -+ * If we are resuming, then start where we stopped. Otherwise, check -+ * that a reset operation is not already in progress, and if none is, -+ * record that this is now the case. -+ */ -+ i = resuming ? d->next_evtchn : !d->next_evtchn; -+ if ( i > d->next_evtchn ) -+ d->next_evtchn = i; -+ -+ spin_unlock(&d->event_lock); -+ -+ if ( !i ) -+ return -EBUSY; -+ -+ for ( ; port_is_valid(d, i); i++ ) -+ { - evtchn_close(d, i, 1); - -+ /* NB: Choice of frequency is arbitrary. */ -+ if ( !(i & 0x3f) && hypercall_preempt_check() ) -+ { -+ spin_lock(&d->event_lock); -+ d->next_evtchn = i; -+ spin_unlock(&d->event_lock); -+ return -ERESTART; -+ } -+ } -+ - spin_lock(&d->event_lock); - -+ d->next_evtchn = 0; -+ - if ( d->active_evtchns > d->xen_evtchns ) - rc = -EAGAIN; - else if ( d->evtchn_fifo ) -@@ -1204,7 +1233,8 @@ long do_event_channel_op(int cmd, XEN_GU - break; - } - -- case EVTCHNOP_reset: { -+ case EVTCHNOP_reset: -+ case EVTCHNOP_reset_cont: { - struct evtchn_reset reset; - struct domain *d; - -@@ -1217,9 +1247,13 @@ long do_event_channel_op(int cmd, XEN_GU - - rc = xsm_evtchn_reset(XSM_TARGET, current->domain, d); - if ( !rc ) -- rc = evtchn_reset(d); -+ rc = evtchn_reset(d, cmd == EVTCHNOP_reset_cont); - - rcu_unlock_domain(d); -+ -+ if ( rc == -ERESTART ) -+ rc = hypercall_create_continuation(__HYPERVISOR_event_channel_op, -+ "ih", EVTCHNOP_reset_cont, arg); - break; - } - ---- a/xen/include/public/domctl.h -+++ b/xen/include/public/domctl.h -@@ -1152,7 +1152,10 @@ struct xen_domctl { - #define XEN_DOMCTL_iomem_permission 20 - #define XEN_DOMCTL_ioport_permission 21 - #define XEN_DOMCTL_hypercall_init 22 --#define XEN_DOMCTL_arch_setup 23 /* Obsolete IA64 only */ -+#ifdef __XEN__ -+/* #define XEN_DOMCTL_arch_setup 23 Obsolete IA64 only */ -+#define XEN_DOMCTL_soft_reset_cont 23 -+#endif - #define XEN_DOMCTL_settimeoffset 24 - #define XEN_DOMCTL_getvcpuaffinity 25 - #define XEN_DOMCTL_real_mode_area 26 /* Obsolete PPC only */ ---- a/xen/include/public/event_channel.h -+++ b/xen/include/public/event_channel.h -@@ -74,6 +74,9 @@ - #define EVTCHNOP_init_control 11 - #define EVTCHNOP_expand_array 12 - #define EVTCHNOP_set_priority 13 -+#ifdef __XEN__ -+#define EVTCHNOP_reset_cont 14 -+#endif - /* ` } */ - - typedef uint32_t evtchn_port_t; ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -171,7 +171,7 @@ void evtchn_check_pollers(struct domain - void evtchn_2l_init(struct domain *d); - - /* Close all event channels and reset to 2-level ABI. */ --int evtchn_reset(struct domain *d); -+int evtchn_reset(struct domain *d, bool resuming); - - /* - * Low-level event channel port ops. ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -394,6 +394,8 @@ struct domain - * EVTCHNOP_reset). Read/write access like for active_evtchns. - */ - unsigned int xen_evtchns; -+ /* Port to resume from in evtchn_reset(), when in a continuation. */ -+ unsigned int next_evtchn; - spinlock_t event_lock; - const struct evtchn_port_ops *evtchn_port_ops; - struct evtchn_fifo_domain *evtchn_fifo; -@@ -663,7 +665,7 @@ int domain_shutdown(struct domain *d, u8 - void domain_resume(struct domain *d); - void domain_pause_for_debugger(void); - --int domain_soft_reset(struct domain *d); -+int domain_soft_reset(struct domain *d, bool resuming); - - int vcpu_start_shutdown_deferral(struct vcpu *v); - void vcpu_end_shutdown_deferral(struct vcpu *v); diff --git a/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch b/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch deleted file mode 100644 index d325385..0000000 --- a/xsa345-4.13-0001-x86-mm-Refactor-map_pages_to_xen-to-have-only-a-sing.patch +++ /dev/null @@ -1,94 +0,0 @@ -From b3e0d4e37b7902533a463812374947d4d6d2e463 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Sat, 11 Jan 2020 21:57:41 +0000 -Subject: [PATCH 1/3] x86/mm: Refactor map_pages_to_xen to have only a single - exit path - -We will soon need to perform clean-ups before returning. - -No functional change. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Wei Liu -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- - xen/arch/x86/mm.c | 17 +++++++++++------ - 1 file changed, 11 insertions(+), 6 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 30dffb68e8..133a393875 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5187,6 +5187,7 @@ int map_pages_to_xen( - l2_pgentry_t *pl2e, ol2e; - l1_pgentry_t *pl1e, ol1e; - unsigned int i; -+ int rc = -ENOMEM; - - #define flush_flags(oldf) do { \ - unsigned int o_ = (oldf); \ -@@ -5207,7 +5208,8 @@ int map_pages_to_xen( - l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); - - if ( !pl3e ) -- return -ENOMEM; -+ goto out; -+ - ol3e = *pl3e; - - if ( cpu_has_page1gb && -@@ -5295,7 +5297,7 @@ int map_pages_to_xen( - - pl2e = alloc_xen_pagetable(); - if ( pl2e == NULL ) -- return -ENOMEM; -+ goto out; - - for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) - l2e_write(pl2e + i, -@@ -5324,7 +5326,7 @@ int map_pages_to_xen( - - pl2e = virt_to_xen_l2e(virt); - if ( !pl2e ) -- return -ENOMEM; -+ goto out; - - if ( ((((virt >> PAGE_SHIFT) | mfn_x(mfn)) & - ((1u << PAGETABLE_ORDER) - 1)) == 0) && -@@ -5367,7 +5369,7 @@ int map_pages_to_xen( - { - pl1e = virt_to_xen_l1e(virt); - if ( pl1e == NULL ) -- return -ENOMEM; -+ goto out; - } - else if ( l2e_get_flags(*pl2e) & _PAGE_PSE ) - { -@@ -5394,7 +5396,7 @@ int map_pages_to_xen( - - pl1e = alloc_xen_pagetable(); - if ( pl1e == NULL ) -- return -ENOMEM; -+ goto out; - - for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - l1e_write(&pl1e[i], -@@ -5538,7 +5540,10 @@ int map_pages_to_xen( - - #undef flush_flags - -- return 0; -+ rc = 0; -+ -+ out: -+ return rc; - } - - int populate_pt_range(unsigned long virt, unsigned long nr_mfns) --- -2.25.1 - diff --git a/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch b/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch deleted file mode 100644 index 836bed6..0000000 --- a/xsa345-4.13-0002-x86-mm-Refactor-modify_xen_mappings-to-have-one-exit.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 9f6f35b833d295acaaa2d8ff8cf309bf688cfd50 Mon Sep 17 00:00:00 2001 -From: Wei Liu -Date: Sat, 11 Jan 2020 21:57:42 +0000 -Subject: [PATCH 2/3] x86/mm: Refactor modify_xen_mappings to have one exit - path - -We will soon need to perform clean-ups before returning. - -No functional change. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Wei Liu -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Acked-by: Jan Beulich ---- - xen/arch/x86/mm.c | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index 133a393875..af726d3274 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -5570,6 +5570,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - l1_pgentry_t *pl1e; - unsigned int i; - unsigned long v = s; -+ int rc = -ENOMEM; - - /* Set of valid PTE bits which may be altered. */ - #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) -@@ -5611,7 +5612,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - /* PAGE1GB: shatter the superpage and fall through. */ - pl2e = alloc_xen_pagetable(); - if ( !pl2e ) -- return -ENOMEM; -+ goto out; -+ - for ( i = 0; i < L2_PAGETABLE_ENTRIES; i++ ) - l2e_write(pl2e + i, - l2e_from_pfn(l3e_get_pfn(*pl3e) + -@@ -5666,7 +5668,8 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - /* PSE: shatter the superpage and try again. */ - pl1e = alloc_xen_pagetable(); - if ( !pl1e ) -- return -ENOMEM; -+ goto out; -+ - for ( i = 0; i < L1_PAGETABLE_ENTRIES; i++ ) - l1e_write(&pl1e[i], - l1e_from_pfn(l2e_get_pfn(*pl2e) + i, -@@ -5795,7 +5798,10 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - flush_area(NULL, FLUSH_TLB_GLOBAL); - - #undef FLAGS_MASK -- return 0; -+ rc = 0; -+ -+ out: -+ return rc; - } - - #undef flush_area --- -2.25.1 - diff --git a/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch b/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch deleted file mode 100644 index db40741..0000000 --- a/xsa345-4.13-0003-x86-mm-Prevent-some-races-in-hypervisor-mapping-upda.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 0ff9a8453dc47cd47eee9659d5916afb5094e871 Mon Sep 17 00:00:00 2001 -From: Hongyan Xia -Date: Sat, 11 Jan 2020 21:57:43 +0000 -Subject: [PATCH 3/3] x86/mm: Prevent some races in hypervisor mapping updates - -map_pages_to_xen will attempt to coalesce mappings into 2MiB and 1GiB -superpages if possible, to maximize TLB efficiency. This means both -replacing superpage entries with smaller entries, and replacing -smaller entries with superpages. - -Unfortunately, while some potential races are handled correctly, -others are not. These include: - -1. When one processor modifies a sub-superpage mapping while another -processor replaces the entire range with a superpage. - -Take the following example: - -Suppose L3[N] points to L2. And suppose we have two processors, A and -B. - -* A walks the pagetables, get a pointer to L2. -* B replaces L3[N] with a 1GiB mapping. -* B Frees L2 -* A writes L2[M] # - -This is race exacerbated by the fact that virt_to_xen_l[21]e doesn't -handle higher-level superpages properly: If you call virt_xen_to_l2e -on a virtual address within an L3 superpage, you'll either hit a BUG() -(most likely), or get a pointer into the middle of a data page; same -with virt_xen_to_l1 on a virtual address within either an L3 or L2 -superpage. - -So take the following example: - -* A reads pl3e and discovers it to point to an L2. -* B replaces L3[N] with a 1GiB mapping -* A calls virt_to_xen_l2e() and hits the BUG_ON() # - -2. When two processors simultaneously try to replace a sub-superpage -mapping with a superpage mapping. - -Take the following example: - -Suppose L3[N] points to L2. And suppose we have two processors, A and B, -both trying to replace L3[N] with a superpage. - -* A walks the pagetables, get a pointer to pl3e, and takes a copy ol3e pointing to L2. -* B walks the pagetables, gets a pointre to pl3e, and takes a copy ol3e pointing to L2. -* A writes the new value into L3[N] -* B writes the new value into L3[N] -* A recursively frees all the L1's under L2, then frees L2 -* B recursively double-frees all the L1's under L2, then double-frees L2 # - -Fix this by grabbing a lock for the entirety of the mapping update -operation. - -Rather than grabbing map_pgdir_lock for the entire operation, however, -repurpose the PGT_locked bit from L3's page->type_info as a lock. -This means that rather than locking the entire address space, we -"only" lock a single 512GiB chunk of hypervisor address space at a -time. - -There was a proposal for a lock-and-reverify approach, where we walk -the pagetables to the point where we decide what to do; then grab the -map_pgdir_lock, re-verify the information we collected without the -lock, and finally make the change (starting over again if anything had -changed). Without being able to guarantee that the L2 table wasn't -freed, however, that means every read would need to be considered -potentially unsafe. Thinking carefully about that is probably -something that wants to be done on public, not under time pressure. - -This is part of XSA-345. - -Reported-by: Hongyan Xia -Signed-off-by: Hongyan Xia -Signed-off-by: George Dunlap -Reviewed-by: Jan Beulich ---- - xen/arch/x86/mm.c | 92 +++++++++++++++++++++++++++++++++++++++++++++-- - 1 file changed, 89 insertions(+), 3 deletions(-) - -diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c -index af726d3274..d6a0761f43 100644 ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -2167,6 +2167,50 @@ void page_unlock(struct page_info *page) - current_locked_page_set(NULL); - } - -+/* -+ * L3 table locks: -+ * -+ * Used for serialization in map_pages_to_xen() and modify_xen_mappings(). -+ * -+ * For Xen PT pages, the page->u.inuse.type_info is unused and it is safe to -+ * reuse the PGT_locked flag. This lock is taken only when we move down to L3 -+ * tables and below, since L4 (and above, for 5-level paging) is still globally -+ * protected by map_pgdir_lock. -+ * -+ * PV MMU update hypercalls call map_pages_to_xen while holding a page's page_lock(). -+ * This has two implications: -+ * - We cannot reuse reuse current_locked_page_* for debugging -+ * - To avoid the chance of deadlock, even for different pages, we -+ * must never grab page_lock() after grabbing l3t_lock(). This -+ * includes any page_lock()-based locks, such as -+ * mem_sharing_page_lock(). -+ * -+ * Also note that we grab the map_pgdir_lock while holding the -+ * l3t_lock(), so to avoid deadlock we must avoid grabbing them in -+ * reverse order. -+ */ -+static void l3t_lock(struct page_info *page) -+{ -+ unsigned long x, nx; -+ -+ do { -+ while ( (x = page->u.inuse.type_info) & PGT_locked ) -+ cpu_relax(); -+ nx = x | PGT_locked; -+ } while ( cmpxchg(&page->u.inuse.type_info, x, nx) != x ); -+} -+ -+static void l3t_unlock(struct page_info *page) -+{ -+ unsigned long x, nx, y = page->u.inuse.type_info; -+ -+ do { -+ x = y; -+ BUG_ON(!(x & PGT_locked)); -+ nx = x & ~PGT_locked; -+ } while ( (y = cmpxchg(&page->u.inuse.type_info, x, nx)) != x ); -+} -+ - #ifdef CONFIG_PV - /* - * PTE flags that a guest may change without re-validating the PTE. -@@ -5177,6 +5221,23 @@ l1_pgentry_t *virt_to_xen_l1e(unsigned long v) - flush_area_local((const void *)v, f) : \ - flush_area_all((const void *)v, f)) - -+#define L3T_INIT(page) (page) = ZERO_BLOCK_PTR -+ -+#define L3T_LOCK(page) \ -+ do { \ -+ if ( locking ) \ -+ l3t_lock(page); \ -+ } while ( false ) -+ -+#define L3T_UNLOCK(page) \ -+ do { \ -+ if ( locking && (page) != ZERO_BLOCK_PTR ) \ -+ { \ -+ l3t_unlock(page); \ -+ (page) = ZERO_BLOCK_PTR; \ -+ } \ -+ } while ( false ) -+ - int map_pages_to_xen( - unsigned long virt, - mfn_t mfn, -@@ -5188,6 +5249,7 @@ int map_pages_to_xen( - l1_pgentry_t *pl1e, ol1e; - unsigned int i; - int rc = -ENOMEM; -+ struct page_info *current_l3page; - - #define flush_flags(oldf) do { \ - unsigned int o_ = (oldf); \ -@@ -5203,13 +5265,20 @@ int map_pages_to_xen( - } \ - } while (0) - -+ L3T_INIT(current_l3page); -+ - while ( nr_mfns != 0 ) - { -- l3_pgentry_t ol3e, *pl3e = virt_to_xen_l3e(virt); -+ l3_pgentry_t *pl3e, ol3e; - -+ L3T_UNLOCK(current_l3page); -+ -+ pl3e = virt_to_xen_l3e(virt); - if ( !pl3e ) - goto out; - -+ current_l3page = virt_to_page(pl3e); -+ L3T_LOCK(current_l3page); - ol3e = *pl3e; - - if ( cpu_has_page1gb && -@@ -5543,6 +5612,7 @@ int map_pages_to_xen( - rc = 0; - - out: -+ L3T_UNLOCK(current_l3page); - return rc; - } - -@@ -5571,6 +5641,7 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - unsigned int i; - unsigned long v = s; - int rc = -ENOMEM; -+ struct page_info *current_l3page; - - /* Set of valid PTE bits which may be altered. */ - #define FLAGS_MASK (_PAGE_NX|_PAGE_RW|_PAGE_PRESENT) -@@ -5579,11 +5650,22 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - ASSERT(IS_ALIGNED(s, PAGE_SIZE)); - ASSERT(IS_ALIGNED(e, PAGE_SIZE)); - -+ L3T_INIT(current_l3page); -+ - while ( v < e ) - { -- l3_pgentry_t *pl3e = virt_to_xen_l3e(v); -+ l3_pgentry_t *pl3e; -+ -+ L3T_UNLOCK(current_l3page); - -- if ( !pl3e || !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) -+ pl3e = virt_to_xen_l3e(v); -+ if ( !pl3e ) -+ goto out; -+ -+ current_l3page = virt_to_page(pl3e); -+ L3T_LOCK(current_l3page); -+ -+ if ( !(l3e_get_flags(*pl3e) & _PAGE_PRESENT) ) - { - /* Confirm the caller isn't trying to create new mappings. */ - ASSERT(!(nf & _PAGE_PRESENT)); -@@ -5801,9 +5883,13 @@ int modify_xen_mappings(unsigned long s, unsigned long e, unsigned int nf) - rc = 0; - - out: -+ L3T_UNLOCK(current_l3page); - return rc; - } - -+#undef L3T_LOCK -+#undef L3T_UNLOCK -+ - #undef flush_area - - int destroy_xen_mappings(unsigned long s, unsigned long e) --- -2.25.1 - diff --git a/xsa346-4.13-1.patch b/xsa346-4.13-1.patch deleted file mode 100644 index a32e658..0000000 --- a/xsa346-4.13-1.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: suppress "iommu_dont_flush_iotlb" when about to free a page - -Deferring flushes to a single, wide range one - as is done when -handling XENMAPSPACE_gmfn_range - is okay only as long as -pages don't get freed ahead of the eventual flush. While the only -function setting the flag (xenmem_add_to_physmap()) suggests by its name -that it's only mapping new entries, in reality the way -xenmem_add_to_physmap_one() works means an unmap would happen not only -for the page being moved (but not freed) but, if the destination GFN is -populated, also for the page being displaced from that GFN. Collapsing -the two flushes for this GFN into just one (end even more so deferring -it to a batched invocation) is not correct. - -This is part of XSA-346. - -Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant -Acked-by: Julien Grall - ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -292,6 +292,7 @@ int guest_remove_page(struct domain *d, - p2m_type_t p2mt; - #endif - mfn_t mfn; -+ bool *dont_flush_p, dont_flush; - int rc; - - #ifdef CONFIG_X86 -@@ -378,8 +379,18 @@ int guest_remove_page(struct domain *d, - return -ENXIO; - } - -+ /* -+ * Since we're likely to free the page below, we need to suspend -+ * xenmem_add_to_physmap()'s suppressing of IOMMU TLB flushes. -+ */ -+ dont_flush_p = &this_cpu(iommu_dont_flush_iotlb); -+ dont_flush = *dont_flush_p; -+ *dont_flush_p = false; -+ - rc = guest_physmap_remove_page(d, _gfn(gmfn), mfn, 0); - -+ *dont_flush_p = dont_flush; -+ - /* - * With the lack of an IOMMU on some platforms, domains with DMA-capable - * device must retrieve the same pfn when the hypercall populate_physmap diff --git a/xsa346-4.13-2.patch b/xsa346-4.13-2.patch deleted file mode 100644 index 6371b5c..0000000 --- a/xsa346-4.13-2.patch +++ /dev/null @@ -1,204 +0,0 @@ -From: Jan Beulich -Subject: IOMMU: hold page ref until after deferred TLB flush - -When moving around a page via XENMAPSPACE_gmfn_range, deferring the TLB -flush for the "from" GFN range requires that the page remains allocated -to the guest until the TLB flush has actually occurred. Otherwise a -parallel hypercall to remove the page would only flush the TLB for the -GFN it has been moved to, but not the one is was mapped at originally. - -This is part of XSA-346. - -Fixes: cf95b2a9fd5a ("iommu: Introduce per cpu flag (iommu_dont_flush_iotlb) to avoid unnecessary iotlb... ") -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall - ---- a/xen/arch/arm/mm.c -+++ b/xen/arch/arm/mm.c -@@ -1407,7 +1407,7 @@ void share_xen_page_with_guest(struct pa - int xenmem_add_to_physmap_one( - struct domain *d, - unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, - gfn_t gfn) - { -@@ -1480,10 +1480,6 @@ int xenmem_add_to_physmap_one( - break; - } - case XENMAPSPACE_dev_mmio: -- /* extra should be 0. Reserved for future use. */ -- if ( extra.res0 ) -- return -EOPNOTSUPP; -- - rc = map_dev_mmio_region(d, gfn, 1, _mfn(idx)); - return rc; - ---- a/xen/arch/x86/mm.c -+++ b/xen/arch/x86/mm.c -@@ -4617,7 +4617,7 @@ static int handle_iomem_range(unsigned l - int xenmem_add_to_physmap_one( - struct domain *d, - unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, - gfn_t gpfn) - { -@@ -4701,9 +4701,20 @@ int xenmem_add_to_physmap_one( - rc = guest_physmap_add_page(d, gpfn, mfn, PAGE_ORDER_4K); - - put_both: -- /* In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. */ -+ /* -+ * In the XENMAPSPACE_gmfn case, we took a ref of the gfn at the top. -+ * We also may need to transfer ownership of the page reference to our -+ * caller. -+ */ - if ( space == XENMAPSPACE_gmfn ) -+ { - put_gfn(d, gfn); -+ if ( !rc && extra.ppage ) -+ { -+ *extra.ppage = page; -+ page = NULL; -+ } -+ } - - if ( page ) - put_page(page); ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -814,13 +814,12 @@ int xenmem_add_to_physmap(struct domain - { - unsigned int done = 0; - long rc = 0; -- union xen_add_to_physmap_batch_extra extra; -+ union add_to_physmap_extra extra = {}; -+ struct page_info *pages[16]; - - ASSERT(paging_mode_translate(d)); - -- if ( xatp->space != XENMAPSPACE_gmfn_foreign ) -- extra.res0 = 0; -- else -+ if ( xatp->space == XENMAPSPACE_gmfn_foreign ) - extra.foreign_domid = DOMID_INVALID; - - if ( xatp->space != XENMAPSPACE_gmfn_range ) -@@ -835,7 +834,10 @@ int xenmem_add_to_physmap(struct domain - xatp->size -= start; - - if ( is_iommu_enabled(d) ) -+ { - this_cpu(iommu_dont_flush_iotlb) = 1; -+ extra.ppage = &pages[0]; -+ } - - while ( xatp->size > done ) - { -@@ -847,8 +849,12 @@ int xenmem_add_to_physmap(struct domain - xatp->idx++; - xatp->gpfn++; - -+ if ( extra.ppage ) -+ ++extra.ppage; -+ - /* Check for continuation if it's not the last iteration. */ -- if ( xatp->size > ++done && hypercall_preempt_check() ) -+ if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || -+ (xatp->size > done && hypercall_preempt_check()) ) - { - rc = start + done; - break; -@@ -858,6 +864,7 @@ int xenmem_add_to_physmap(struct domain - if ( is_iommu_enabled(d) ) - { - int ret; -+ unsigned int i; - - this_cpu(iommu_dont_flush_iotlb) = 0; - -@@ -866,6 +873,15 @@ int xenmem_add_to_physmap(struct domain - if ( unlikely(ret) && rc >= 0 ) - rc = ret; - -+ /* -+ * Now that the IOMMU TLB flush was done for the original GFN, drop -+ * the page references. The 2nd flush below is fine to make later, as -+ * whoever removes the page again from its new GFN will have to do -+ * another flush anyway. -+ */ -+ for ( i = 0; i < done; ++i ) -+ put_page(pages[i]); -+ - ret = iommu_iotlb_flush(d, _dfn(xatp->gpfn - done), done, - IOMMU_FLUSHF_added | IOMMU_FLUSHF_modified); - if ( unlikely(ret) && rc >= 0 ) -@@ -879,6 +895,8 @@ static int xenmem_add_to_physmap_batch(s - struct xen_add_to_physmap_batch *xatpb, - unsigned int extent) - { -+ union add_to_physmap_extra extra = {}; -+ - if ( unlikely(xatpb->size < extent) ) - return -EILSEQ; - -@@ -890,6 +908,19 @@ static int xenmem_add_to_physmap_batch(s - !guest_handle_subrange_okay(xatpb->errs, extent, xatpb->size - 1) ) - return -EFAULT; - -+ switch ( xatpb->space ) -+ { -+ case XENMAPSPACE_dev_mmio: -+ /* res0 is reserved for future use. */ -+ if ( xatpb->u.res0 ) -+ return -EOPNOTSUPP; -+ break; -+ -+ case XENMAPSPACE_gmfn_foreign: -+ extra.foreign_domid = xatpb->u.foreign_domid; -+ break; -+ } -+ - while ( xatpb->size > extent ) - { - xen_ulong_t idx; -@@ -902,8 +933,7 @@ static int xenmem_add_to_physmap_batch(s - extent, 1)) ) - return -EFAULT; - -- rc = xenmem_add_to_physmap_one(d, xatpb->space, -- xatpb->u, -+ rc = xenmem_add_to_physmap_one(d, xatpb->space, extra, - idx, _gfn(gpfn)); - - if ( unlikely(__copy_to_guest_offset(xatpb->errs, extent, &rc, 1)) ) ---- a/xen/include/xen/mm.h -+++ b/xen/include/xen/mm.h -@@ -588,8 +588,22 @@ void scrub_one_page(struct page_info *); - &(d)->xenpage_list : &(d)->page_list) - #endif - -+union add_to_physmap_extra { -+ /* -+ * XENMAPSPACE_gmfn: When deferring TLB flushes, a page reference needs -+ * to be kept until after the flush, so the page can't get removed from -+ * the domain (and re-used for another purpose) beforehand. By passing -+ * non-NULL, the caller of xenmem_add_to_physmap_one() indicates it wants -+ * to have ownership of such a reference transferred in the success case. -+ */ -+ struct page_info **ppage; -+ -+ /* XENMAPSPACE_gmfn_foreign */ -+ domid_t foreign_domid; -+}; -+ - int xenmem_add_to_physmap_one(struct domain *d, unsigned int space, -- union xen_add_to_physmap_batch_extra extra, -+ union add_to_physmap_extra extra, - unsigned long idx, gfn_t gfn); - - int xenmem_add_to_physmap(struct domain *d, struct xen_add_to_physmap *xatp, diff --git a/xsa347-4.13-1.patch b/xsa347-4.13-1.patch deleted file mode 100644 index e9f31a1..0000000 --- a/xsa347-4.13-1.patch +++ /dev/null @@ -1,149 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: convert amd_iommu_pte from struct to union - -This is to add a "raw" counterpart to the bitfield equivalent. Take the -opportunity and - - convert fields to bool / unsigned int, - - drop the naming of the reserved field, - - shorten the names of the ignored ones. - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Andrew Cooper -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -38,7 +38,7 @@ static unsigned int pfn_to_pde_idx(unsig - static unsigned int clear_iommu_pte_present(unsigned long l1_mfn, - unsigned long dfn) - { -- struct amd_iommu_pte *table, *pte; -+ union amd_iommu_pte *table, *pte; - unsigned int flush_flags; - - table = map_domain_page(_mfn(l1_mfn)); -@@ -52,7 +52,7 @@ static unsigned int clear_iommu_pte_pres - return flush_flags; - } - --static unsigned int set_iommu_pde_present(struct amd_iommu_pte *pte, -+static unsigned int set_iommu_pde_present(union amd_iommu_pte *pte, - unsigned long next_mfn, - unsigned int next_level, bool iw, - bool ir) -@@ -87,7 +87,7 @@ static unsigned int set_iommu_pte_presen - int pde_level, - bool iw, bool ir) - { -- struct amd_iommu_pte *table, *pde; -+ union amd_iommu_pte *table, *pde; - unsigned int flush_flags; - - table = map_domain_page(_mfn(pt_mfn)); -@@ -178,7 +178,7 @@ void iommu_dte_set_guest_cr3(struct amd_ - static int iommu_pde_from_dfn(struct domain *d, unsigned long dfn, - unsigned long pt_mfn[], bool map) - { -- struct amd_iommu_pte *pde, *next_table_vaddr; -+ union amd_iommu_pte *pde, *next_table_vaddr; - unsigned long next_table_mfn; - unsigned int level; - struct page_info *table; -@@ -458,7 +458,7 @@ int __init amd_iommu_quarantine_init(str - unsigned long end_gfn = - 1ul << (DEFAULT_DOMAIN_ADDRESS_WIDTH - PAGE_SHIFT); - unsigned int level = amd_iommu_get_paging_mode(end_gfn); -- struct amd_iommu_pte *table; -+ union amd_iommu_pte *table; - - if ( hd->arch.root_table ) - { -@@ -489,7 +489,7 @@ int __init amd_iommu_quarantine_init(str - - for ( i = 0; i < PTE_PER_TABLE_SIZE; i++ ) - { -- struct amd_iommu_pte *pde = &table[i]; -+ union amd_iommu_pte *pde = &table[i]; - - /* - * PDEs are essentially a subset of PTEs, so this function ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -390,7 +390,7 @@ static void deallocate_next_page_table(s - - static void deallocate_page_table(struct page_info *pg) - { -- struct amd_iommu_pte *table_vaddr; -+ union amd_iommu_pte *table_vaddr; - unsigned int index, level = PFN_ORDER(pg); - - PFN_ORDER(pg) = 0; -@@ -405,7 +405,7 @@ static void deallocate_page_table(struct - - for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) - { -- struct amd_iommu_pte *pde = &table_vaddr[index]; -+ union amd_iommu_pte *pde = &table_vaddr[index]; - - if ( pde->mfn && pde->next_level && pde->pr ) - { -@@ -557,7 +557,7 @@ static void amd_dump_p2m_table_level(str - paddr_t gpa, int indent) - { - paddr_t address; -- struct amd_iommu_pte *table_vaddr; -+ const union amd_iommu_pte *table_vaddr; - int index; - - if ( level < 1 ) -@@ -573,7 +573,7 @@ static void amd_dump_p2m_table_level(str - - for ( index = 0; index < PTE_PER_TABLE_SIZE; index++ ) - { -- struct amd_iommu_pte *pde = &table_vaddr[index]; -+ const union amd_iommu_pte *pde = &table_vaddr[index]; - - if ( !(index % 2) ) - process_pending_softirqs(); ---- a/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h -+++ b/xen/include/asm-x86/hvm/svm/amd-iommu-defs.h -@@ -465,20 +465,23 @@ union amd_iommu_x2apic_control { - #define IOMMU_PAGE_TABLE_U32_PER_ENTRY (IOMMU_PAGE_TABLE_ENTRY_SIZE / 4) - #define IOMMU_PAGE_TABLE_ALIGNMENT 4096 - --struct amd_iommu_pte { -- uint64_t pr:1; -- uint64_t ignored0:4; -- uint64_t a:1; -- uint64_t d:1; -- uint64_t ignored1:2; -- uint64_t next_level:3; -- uint64_t mfn:40; -- uint64_t reserved:7; -- uint64_t u:1; -- uint64_t fc:1; -- uint64_t ir:1; -- uint64_t iw:1; -- uint64_t ignored2:1; -+union amd_iommu_pte { -+ uint64_t raw; -+ struct { -+ bool pr:1; -+ unsigned int ign0:4; -+ bool a:1; -+ bool d:1; -+ unsigned int ign1:2; -+ unsigned int next_level:3; -+ uint64_t mfn:40; -+ unsigned int :7; -+ bool u:1; -+ bool fc:1; -+ bool ir:1; -+ bool iw:1; -+ unsigned int ign2:1; -+ }; - }; - - /* Paging modes */ diff --git a/xsa347-4.13-2.patch b/xsa347-4.13-2.patch deleted file mode 100644 index fbe7461..0000000 --- a/xsa347-4.13-2.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: update live PTEs atomically - -Updating a live PTE bitfield by bitfield risks the compiler re-ordering -the individual updates as well as splitting individual updates into -multiple memory writes. Construct the new entry fully in a local -variable, do the check to determine the flushing needs on the thus -established new entry, and then write the new entry by a single insn. - -Similarly using memset() to clear a PTE is unsafe, as the order of -writes the function does is, at least in principle, undefined. - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -45,7 +45,7 @@ static unsigned int clear_iommu_pte_pres - pte = &table[pfn_to_pde_idx(dfn, 1)]; - - flush_flags = pte->pr ? IOMMU_FLUSHF_modified : 0; -- memset(pte, 0, sizeof(*pte)); -+ write_atomic(&pte->raw, 0); - - unmap_domain_page(table); - -@@ -57,26 +57,30 @@ static unsigned int set_iommu_pde_presen - unsigned int next_level, bool iw, - bool ir) - { -+ union amd_iommu_pte new = {}, old; - unsigned int flush_flags = IOMMU_FLUSHF_added; - -- if ( pte->pr && -- (pte->mfn != next_mfn || -- pte->iw != iw || -- pte->ir != ir || -- pte->next_level != next_level) ) -- flush_flags |= IOMMU_FLUSHF_modified; -- - /* - * FC bit should be enabled in PTE, this helps to solve potential - * issues with ATS devices - */ -- pte->fc = !next_level; -+ new.fc = !next_level; -+ -+ new.mfn = next_mfn; -+ new.iw = iw; -+ new.ir = ir; -+ new.next_level = next_level; -+ new.pr = true; -+ -+ old.raw = read_atomic(&pte->raw); -+ old.ign0 = 0; -+ old.ign1 = 0; -+ old.ign2 = 0; -+ -+ if ( old.pr && old.raw != new.raw ) -+ flush_flags |= IOMMU_FLUSHF_modified; - -- pte->mfn = next_mfn; -- pte->iw = iw; -- pte->ir = ir; -- pte->next_level = next_level; -- pte->pr = 1; -+ write_atomic(&pte->raw, new.raw); - - return flush_flags; - } diff --git a/xsa347-4.13-3.patch b/xsa347-4.13-3.patch deleted file mode 100644 index 90c8e66..0000000 --- a/xsa347-4.13-3.patch +++ /dev/null @@ -1,59 +0,0 @@ -From: Jan Beulich -Subject: AMD/IOMMU: ensure suitable ordering of DTE modifications - -DMA and interrupt translation should be enabled only after other -applicable DTE fields have been written. Similarly when disabling -translation or when moving a device between domains, translation should -first be disabled, before other entry fields get modified. Note however -that the "moving" aspect doesn't apply to the interrupt remapping side, -as domain specifics are maintained in the IRTEs here, not the DTE. We -also never disable interrupt remapping once it got enabled for a device -(the respective argument passed is always the immutable iommu_intremap). - -This is part of XSA-347. - -Signed-off-by: Jan Beulich -Reviewed-by: Paul Durrant - ---- a/xen/drivers/passthrough/amd/iommu_map.c -+++ b/xen/drivers/passthrough/amd/iommu_map.c -@@ -107,11 +107,18 @@ void amd_iommu_set_root_page_table(struc - uint64_t root_ptr, uint16_t domain_id, - uint8_t paging_mode, bool valid) - { -+ if ( valid || dte->v ) -+ { -+ dte->tv = false; -+ dte->v = true; -+ smp_wmb(); -+ } - dte->domain_id = domain_id; - dte->pt_root = paddr_to_pfn(root_ptr); - dte->iw = true; - dte->ir = true; - dte->paging_mode = paging_mode; -+ smp_wmb(); - dte->tv = true; - dte->v = valid; - } -@@ -134,6 +141,7 @@ void amd_iommu_set_intremap_table( - } - - dte->ig = false; /* unmapped interrupts result in i/o page faults */ -+ smp_wmb(); - dte->iv = valid; - } - ---- a/xen/drivers/passthrough/amd/pci_amd_iommu.c -+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c -@@ -120,7 +120,10 @@ static void amd_iommu_setup_domain_devic - /* Undo what amd_iommu_disable_domain_device() may have done. */ - ivrs_dev = &get_ivrs_mappings(iommu->seg)[req_id]; - if ( dte->it_root ) -+ { - dte->int_ctl = IOMMU_DEV_TABLE_INT_CONTROL_TRANSLATED; -+ smp_wmb(); -+ } - dte->iv = iommu_intremap; - dte->ex = ivrs_dev->dte_allow_exclusion; - dte->sys_mgt = MASK_EXTR(ivrs_dev->device_flags, ACPI_IVHD_SYSTEM_MGMT); From fc3d63c3dc890718676ff772a1ee612c7afbe779 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 10 Nov 2020 22:31:53 +0000 Subject: [PATCH 07/15] Information leak via power sidechannel [XSA-351] --- xen.spec | 11 ++- xsa351-arm.patch | 58 +++++++++++++++ xsa351-x86-4.13-1.patch | 155 ++++++++++++++++++++++++++++++++++++++++ xsa351-x86-4.13-2.patch | 128 +++++++++++++++++++++++++++++++++ 4 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 xsa351-arm.patch create mode 100644 xsa351-x86-4.13-1.patch create mode 100644 xsa351-x86-4.13-2.patch diff --git a/xen.spec b/xen.spec index 770f620..39a911d 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 1%{?dist} +Release: 2%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -116,6 +116,9 @@ Patch44: xen.ocaml.4.10.patch Patch45: xen.gcc10.fixes.patch Patch60: xsa335-qemu.patch Patch61: xsa335-trad.patch +Patch62: xsa351-arm.patch +Patch63: xsa351-x86-4.13-1.patch +Patch64: xsa351-x86-4.13-2.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -322,6 +325,9 @@ manage Xen virtual machines. %patch44 -p1 %patch45 -p1 %patch61 -p1 +%patch62 -p1 +%patch63 -p1 +%patch64 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -915,6 +921,9 @@ fi %endif %changelog +* Tue Nov 10 2020 Michael Young - 4.13.2-2 +- Information leak via power sidechannel [XSA-351] + * Tue Nov 03 2020 Michael Young - 4.13.2-1 - update to 4.13.2 remove patches now included or superceded upstream diff --git a/xsa351-arm.patch b/xsa351-arm.patch new file mode 100644 index 0000000..d0d1941 --- /dev/null +++ b/xsa351-arm.patch @@ -0,0 +1,58 @@ +From: Julien Grall +Subject: xen/arm: Always trap AMU system registers + +The Activity Monitors Unit (AMU) has been introduced by ARMv8.4. It is +considered to be unsafe to be expose to guests as they might expose +information about code executed by other guests or the host. + +Arm provided a way to trap all the AMU system registers by setting +CPTR_EL2.TAM to 1. + +Unfortunately, on older revision of the specification, the bit 30 (now +CPTR_EL1.TAM) was RES0. Because of that, Xen is setting it to 0 and +therefore the system registers would be exposed to the guest when it is +run on processors with AMU. + +As the bit is mark as UNKNOWN at boot in Armv8.4, the only safe solution +for us is to always set CPTR_EL1.TAM to 1. + +Guest trying to access the AMU system registers will now receive an +undefined instruction. Unfortunately, this means that even well-behaved +guest may fail to boot because we don't sanitize the ID registers. + +This is a known issues with other Armv8.0+ features (e.g. SVE, Pointer +Auth). This will taken care separately. + +This is part of XSA-351 (or XSA-93 re-born). + +Signed-off-by: Julien Grall +Reviewed-by: Andre Przywara +Reviewed-by: Stefano Stabellini +Reviewed-by: Bertrand Marquis + +diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c +index a36f145e67..22bd1bd4c6 100644 +--- a/xen/arch/arm/traps.c ++++ b/xen/arch/arm/traps.c +@@ -151,7 +151,8 @@ void init_traps(void) + * On ARM64 the TCPx bits which we set here (0..9,12,13) are all + * RES1, i.e. they would trap whether we did this write or not. + */ +- WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | HCPTR_TTA, ++ WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | ++ HCPTR_TTA | HCPTR_TAM, + CPTR_EL2); + + /* +diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h +index 3ca67f8157..d3d12a9d19 100644 +--- a/xen/include/asm-arm/processor.h ++++ b/xen/include/asm-arm/processor.h +@@ -351,6 +351,7 @@ + #define VTCR_RES1 (_AC(1,UL)<<31) + + /* HCPTR Hyp. Coprocessor Trap Register */ ++#define HCPTR_TAM ((_AC(1,U)<<30)) + #define HCPTR_TTA ((_AC(1,U)<<20)) /* Trap trace registers */ + #define HCPTR_CP(x) ((_AC(1,U)<<(x))) /* Trap Coprocessor x */ + #define HCPTR_CP_MASK ((_AC(1,U)<<14)-1) diff --git a/xsa351-x86-4.13-1.patch b/xsa351-x86-4.13-1.patch new file mode 100644 index 0000000..b1fa25e --- /dev/null +++ b/xsa351-x86-4.13-1.patch @@ -0,0 +1,155 @@ +From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= +Subject: x86/msr: fix handling of MSR_IA32_PERF_{STATUS/CTL} +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently a PV hardware domain can also be given control over the CPU +frequency, and such guest is allowed to write to MSR_IA32_PERF_CTL. +However since commit 322ec7c89f6 the default behavior has been changed +to reject accesses to not explicitly handled MSRs, preventing PV +guests that manage CPU frequency from reading +MSR_IA32_PERF_{STATUS/CTL}. + +Additionally some HVM guests (Windows at least) will attempt to read +MSR_IA32_PERF_CTL and will panic if given back a #GP fault: + + vmx.c:3035:d8v0 RDMSR 0x00000199 unimplemented + d8v0 VIRIDIAN CRASH: 3b c0000096 fffff806871c1651 ffffda0253683720 0 + +Move the handling of MSR_IA32_PERF_{STATUS/CTL} to the common MSR +handling shared between HVM and PV guests, and add an explicit case +for reads to MSR_IA32_PERF_{STATUS/CTL}. + +Restore previous behavior and allow PV guests with the required +permissions to read the contents of the mentioned MSRs. Non privileged +guests will get 0 when trying to read those registers, as writes to +MSR_IA32_PERF_CTL by such guest will already be silently dropped. + +Fixes: 322ec7c89f6 ('x86/pv: disallow access to unknown MSRs') +Fixes: 84e848fd7a1 ('x86/hvm: disallow access to unknown MSRs') +Signed-off-by: Roger Pau Monné +Signed-off-by: Andrew Cooper +Reviewed-by: Roger Pau Monné +Reviewed-by: Jan Beulich +(cherry picked from commit 3059178798a23ba870ff86ff54d442a07e6651fc) + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 875ac39d30..8c969197aa 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -208,6 +208,25 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + *val = msrs->misc_features_enables.raw; + break; + ++ /* ++ * These MSRs are not enumerated in CPUID. They have been around ++ * since the Pentium 4, and implemented by other vendors. ++ * ++ * Some versions of Windows try reading these before setting up a #GP ++ * handler, and Linux has several unguarded reads as well. Provide ++ * RAZ semantics, in general, but permit a cpufreq controller dom0 to ++ * have full access. ++ */ ++ case MSR_IA32_PERF_STATUS: ++ case MSR_IA32_PERF_CTL: ++ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) ++ goto gp_fault; ++ ++ *val = 0; ++ if ( likely(!is_cpufreq_controller(d)) || rdmsr_safe(msr, *val) == 0 ) ++ break; ++ goto gp_fault; ++ + case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: + if ( !is_hvm_domain(d) || v != curr ) + goto gp_fault; +@@ -305,6 +324,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_INTEL_CORE_THREAD_COUNT: + case MSR_INTEL_PLATFORM_INFO: + case MSR_ARCH_CAPABILITIES: ++ case MSR_IA32_PERF_STATUS: + /* Read-only */ + case MSR_TSX_FORCE_ABORT: + case MSR_TSX_CTRL: +@@ -411,6 +431,21 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + break; + } + ++ /* ++ * This MSR is not enumerated in CPUID. It has been around since the ++ * Pentium 4, and implemented by other vendors. ++ * ++ * To match the RAZ semantics, implement as write-discard, except for ++ * a cpufreq controller dom0 which has full access. ++ */ ++ case MSR_IA32_PERF_CTL: ++ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) ++ goto gp_fault; ++ ++ if ( likely(!is_cpufreq_controller(d)) || wrmsr_safe(msr, val) == 0 ) ++ break; ++ goto gp_fault; ++ + case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: + if ( !is_hvm_domain(d) || v != curr ) + goto gp_fault; +diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c +index 42258c6bf1..6dc4f92a84 100644 +--- a/xen/arch/x86/pv/emul-priv-op.c ++++ b/xen/arch/x86/pv/emul-priv-op.c +@@ -776,12 +776,6 @@ static inline uint64_t guest_misc_enable(uint64_t val) + return val; + } + +-static inline bool is_cpufreq_controller(const struct domain *d) +-{ +- return ((cpufreq_controller == FREQCTL_dom0_kernel) && +- is_hardware_domain(d)); +-} +- + static int read_msr(unsigned int reg, uint64_t *val, + struct x86_emulate_ctxt *ctxt) + { +@@ -1026,14 +1020,6 @@ static int write_msr(unsigned int reg, uint64_t val, + return X86EMUL_OKAY; + break; + +- case MSR_IA32_PERF_CTL: +- if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) +- break; +- if ( likely(!is_cpufreq_controller(currd)) || +- wrmsr_safe(reg, val) == 0 ) +- return X86EMUL_OKAY; +- break; +- + case MSR_IA32_THERM_CONTROL: + case MSR_IA32_ENERGY_PERF_BIAS: + if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index d6e27fc4b8..8bb5bd7b38 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -1057,6 +1057,22 @@ extern enum cpufreq_controller { + FREQCTL_none, FREQCTL_dom0_kernel, FREQCTL_xen + } cpufreq_controller; + ++static always_inline bool is_cpufreq_controller(const struct domain *d) ++{ ++ /* ++ * A PV dom0 can be nominated as the cpufreq controller, instead of using ++ * Xen's cpufreq driver, at which point dom0 gets direct access to certain ++ * MSRs. ++ * ++ * This interface only works when dom0 is identity pinned and has the same ++ * number of vCPUs as pCPUs on the system. ++ * ++ * It would be far better to paravirtualise the interface. ++ */ ++ return (is_pv_domain(d) && is_hardware_domain(d) && ++ cpufreq_controller == FREQCTL_dom0_kernel); ++} ++ + #define CPUPOOLID_NONE -1 + + struct cpupool *cpupool_get_by_id(int poolid); diff --git a/xsa351-x86-4.13-2.patch b/xsa351-x86-4.13-2.patch new file mode 100644 index 0000000..fee25fb --- /dev/null +++ b/xsa351-x86-4.13-2.patch @@ -0,0 +1,128 @@ +From: Andrew Cooper +Subject: x86/msr: Disallow guest access to the RAPL MSRs + +Researchers have demonstrated using the RAPL interface to perform a +differential power analysis attack to recover AES keys used by other cores in +the system. + +Furthermore, even privileged guests cannot use this interface correctly, due +to MSR scope and vcpu scheduling issues. The interface would want to be +paravirtualised to be used sensibly. + +Disallow access to the RAPL MSRs completely, as well as other MSRs which +potentially access fine grain power information. + +This is part of XSA-351. + +Signed-off-by: Andrew Cooper +Reviewed-by: Jan Beulich + +diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c +index 8c969197aa..8ab6949a8e 100644 +--- a/xen/arch/x86/msr.c ++++ b/xen/arch/x86/msr.c +@@ -152,11 +152,20 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) + case MSR_TSX_CTRL: + case MSR_MCU_OPT_CTRL: + case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): ++ case MSR_RAPL_POWER_UNIT: ++ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: ++ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: ++ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: ++ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: ++ case MSR_PLATFORM_ENERGY_COUNTER: ++ case MSR_PLATFORM_POWER_LIMIT: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: + case MSR_AMD64_LWP_CFG: + case MSR_AMD64_LWP_CBADDR: ++ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: ++ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: + /* Not offered to guests. */ + goto gp_fault; + +@@ -330,11 +339,20 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) + case MSR_TSX_CTRL: + case MSR_MCU_OPT_CTRL: + case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): ++ case MSR_RAPL_POWER_UNIT: ++ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: ++ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: ++ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: ++ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: ++ case MSR_PLATFORM_ENERGY_COUNTER: ++ case MSR_PLATFORM_POWER_LIMIT: + case MSR_U_CET: + case MSR_S_CET: + case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: + case MSR_AMD64_LWP_CFG: + case MSR_AMD64_LWP_CBADDR: ++ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: ++ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: + /* Not offered to guests. */ + goto gp_fault; + +diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h +index 0eb6855614..ba9e90af21 100644 +--- a/xen/include/asm-x86/msr-index.h ++++ b/xen/include/asm-x86/msr-index.h +@@ -96,6 +96,38 @@ + /* Lower 6 bits define the format of the address in the LBR stack */ + #define MSR_IA32_PERF_CAP_LBR_FORMAT 0x3f + ++/* ++ * Intel Runtime Average Power Limiting (RAPL) interface. Power plane base ++ * addresses (MSR_*_POWER_LIMIT) are model specific, but have so-far been ++ * consistent since their introduction in SandyBridge. ++ * ++ * Offsets of functionality from the power plane base is architectural, but ++ * not all power planes support all functionality. ++ */ ++#define MSR_RAPL_POWER_UNIT 0x00000606 ++ ++#define MSR_PKG_POWER_LIMIT 0x00000610 ++#define MSR_PKG_ENERGY_STATUS 0x00000611 ++#define MSR_PKG_PERF_STATUS 0x00000613 ++#define MSR_PKG_POWER_INFO 0x00000614 ++ ++#define MSR_DRAM_POWER_LIMIT 0x00000618 ++#define MSR_DRAM_ENERGY_STATUS 0x00000619 ++#define MSR_DRAM_PERF_STATUS 0x0000061b ++#define MSR_DRAM_POWER_INFO 0x0000061c ++ ++#define MSR_PP0_POWER_LIMIT 0x00000638 ++#define MSR_PP0_ENERGY_STATUS 0x00000639 ++#define MSR_PP0_POLICY 0x0000063a ++ ++#define MSR_PP1_POWER_LIMIT 0x00000640 ++#define MSR_PP1_ENERGY_STATUS 0x00000641 ++#define MSR_PP1_POLICY 0x00000642 ++ ++/* Intel Platform-wide power interface. */ ++#define MSR_PLATFORM_ENERGY_COUNTER 0x0000064d ++#define MSR_PLATFORM_POWER_LIMIT 0x0000065c ++ + #define MSR_IA32_BNDCFGS 0x00000d90 + #define IA32_BNDCFGS_ENABLE 0x00000001 + #define IA32_BNDCFGS_PRESERVE 0x00000002 +@@ -236,6 +268,8 @@ + #define MSR_K8_VM_CR 0xc0010114 + #define MSR_K8_VM_HSAVE_PA 0xc0010117 + ++#define MSR_F15H_CU_POWER 0xc001007a ++#define MSR_F15H_CU_MAX_POWER 0xc001007b + #define MSR_AMD_FAM15H_EVNTSEL0 0xc0010200 + #define MSR_AMD_FAM15H_PERFCTR0 0xc0010201 + #define MSR_AMD_FAM15H_EVNTSEL1 0xc0010202 +@@ -249,6 +283,10 @@ + #define MSR_AMD_FAM15H_EVNTSEL5 0xc001020a + #define MSR_AMD_FAM15H_PERFCTR5 0xc001020b + ++#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299 ++#define MSR_AMD_CORE_ENERGY_STATUS 0xc001029a ++#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b ++ + #define MSR_AMD_L7S0_FEATURE_MASK 0xc0011002 + #define MSR_AMD_THRM_FEATURE_MASK 0xc0011003 + #define MSR_K8_FEATURE_MASK 0xc0011004 From 6f9b8129057d65c94eeaf232270108e027e96123 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 12 Nov 2020 20:26:12 +0000 Subject: [PATCH 08/15] add CVE and bug reference --- xen.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xen.spec b/xen.spec index 39a911d..f7878fd 100644 --- a/xen.spec +++ b/xen.spec @@ -922,7 +922,8 @@ fi %changelog * Tue Nov 10 2020 Michael Young - 4.13.2-2 -- Information leak via power sidechannel [XSA-351] +- Information leak via power sidechannel [XSA-351, CVE-2020-28368] + (#1897146) * Tue Nov 03 2020 Michael Young - 4.13.2-1 - update to 4.13.2 From aa6e17cc272d05f1e2907b12295b84851be4b177 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 23 Nov 2020 23:07:44 +0000 Subject: [PATCH 09/15] support zstd compressed kernels (dom0 only) based on linux kernel code --- xen.spec | 7 +- zstd-dom0.patch | 9214 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 9220 insertions(+), 1 deletion(-) create mode 100644 zstd-dom0.patch diff --git a/xen.spec b/xen.spec index f7878fd..214d461 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -119,6 +119,7 @@ Patch61: xsa335-trad.patch Patch62: xsa351-arm.patch Patch63: xsa351-x86-4.13-1.patch Patch64: xsa351-x86-4.13-2.patch +Patch65: zstd-dom0.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -328,6 +329,7 @@ manage Xen virtual machines. %patch62 -p1 %patch63 -p1 %patch64 -p1 +%patch65 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -921,6 +923,9 @@ fi %endif %changelog +* Mon Nov 23 2020 Michael Young - 4.13.2-3 +- support zstd compressed kernels (dom0 only) based on linux kernel code + * Tue Nov 10 2020 Michael Young - 4.13.2-2 - Information leak via power sidechannel [XSA-351, CVE-2020-28368] (#1897146) diff --git a/zstd-dom0.patch b/zstd-dom0.patch new file mode 100644 index 0000000..57b7f76 --- /dev/null +++ b/zstd-dom0.patch @@ -0,0 +1,9214 @@ +diff --git a/xen/common/Makefile b/xen/common/Makefile +index d109f279a4..5ba09f04ac 100644 +--- a/xen/common/Makefile ++++ b/xen/common/Makefile +@@ -59,7 +59,7 @@ obj-bin-y += warning.init.o + obj-$(CONFIG_XENOPROF) += xenoprof.o + obj-y += xmalloc_tlsf.o + +-obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 earlycpio,$(n).init.o) ++obj-bin-$(CONFIG_X86) += $(foreach n,decompress bunzip2 unxz unlzma lzo unlzo unlz4 unzstd earlycpio,$(n).init.o) + + obj-$(CONFIG_COMPAT) += $(addprefix compat/,domain.o kernel.o memory.o multicall.o xlat.o) + +diff --git a/xen/common/decompress.c b/xen/common/decompress.c +index 9d6e0c4ab0..0da27b0ab6 100644 +--- a/xen/common/decompress.c ++++ b/xen/common/decompress.c +@@ -31,5 +31,8 @@ int __init decompress(void *inbuf, unsigned int len, void *outbuf) + if ( len >= 2 && !memcmp(inbuf, "\x02\x21", 2) ) + return unlz4(inbuf, len, NULL, NULL, outbuf, NULL, error); + ++ if ( len >= 4 && !memcmp(inbuf, "\050\265\057\375", 4) ) ++ return unzstd(inbuf, len, NULL, NULL, outbuf, NULL, error); ++ + return 1; + } +diff --git a/xen/common/unzstd.c b/xen/common/unzstd.c +new file mode 100644 +index 0000000000..a2c382fddc +--- /dev/null ++++ b/xen/common/unzstd.c +@@ -0,0 +1,332 @@ ++/* ++ * Important notes about in-place decompression ++ * ++ * At least on x86, the kernel is decompressed in place: the compressed data ++ * is placed to the end of the output buffer, and the decompressor overwrites ++ * most of the compressed data. There must be enough safety margin to ++ * guarantee that the write position is always behind the read position. ++ * ++ * The safety margin for ZSTD with a 128 KB block size is calculated below. ++ * Note that the margin with ZSTD is bigger than with GZIP or XZ! ++ * ++ * The worst case for in-place decompression is that the beginning of ++ * the file is compressed extremely well, and the rest of the file is ++ * uncompressible. Thus, we must look for worst-case expansion when the ++ * compressor is encoding uncompressible data. ++ * ++ * The structure of the .zst file in case of a compresed kernel is as follows. ++ * Maximum sizes (as bytes) of the fields are in parenthesis. ++ * ++ * Frame Header: (18) ++ * Blocks: (N) ++ * Checksum: (4) ++ * ++ * The frame header and checksum overhead is at most 22 bytes. ++ * ++ * ZSTD stores the data in blocks. Each block has a header whose size is ++ * a 3 bytes. After the block header, there is up to 128 KB of payload. ++ * The maximum uncompressed size of the payload is 128 KB. The minimum ++ * uncompressed size of the payload is never less than the payload size ++ * (excluding the block header). ++ * ++ * The assumption, that the uncompressed size of the payload is never ++ * smaller than the payload itself, is valid only when talking about ++ * the payload as a whole. It is possible that the payload has parts where ++ * the decompressor consumes more input than it produces output. Calculating ++ * the worst case for this would be tricky. Instead of trying to do that, ++ * let's simply make sure that the decompressor never overwrites any bytes ++ * of the payload which it is currently reading. ++ * ++ * Now we have enough information to calculate the safety margin. We need ++ * - 22 bytes for the .zst file format headers; ++ * - 3 bytes per every 128 KiB of uncompressed size (one block header per ++ * block); and ++ * - 128 KiB (biggest possible zstd block size) to make sure that the ++ * decompressor never overwrites anything from the block it is currently ++ * reading. ++ * ++ * We get the following formula: ++ * ++ * safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072 ++ * <= 22 + (uncompressed_size >> 15) + 131072 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/* ++ * Preboot environments #include "path/to/decompress_unzstd.c". ++ * All of the source files we depend on must be #included. ++ * zstd's only source dependeny is xxhash, which has no source ++ * dependencies. ++ * ++ * When UNZSTD_PREBOOT is defined we declare __decompress(), which is ++ * used for kernel decompression, instead of unzstd(). ++ * ++ * Define __DISABLE_EXPORTS in preboot environments to prevent symbols ++ * from xxhash and zstd from being exported by the EXPORT_SYMBOL macro. ++ */ ++ ++#include "decompress.h" ++#include "xxhash.c" ++#include "zstd/entropy_common.c" ++#include "zstd/fse_decompress.c" ++#include "zstd/huf_decompress.c" ++#include "zstd/zstd_common.c" ++#include "zstd/decompress.c" ++ ++#include ++ ++/* 128MB is the maximum window size supported by zstd. */ ++#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX) ++/* ++ * Size of the input and output buffers in multi-call mode. ++ * Pick a larger size because it isn't used during kernel decompression, ++ * since that is single pass, and we have to allocate a large buffer for ++ * zstd's window anyway. The larger size speeds up initramfs decompression. ++ */ ++#define ZSTD_IOBUF_SIZE (1 << 17) ++ ++static int INIT handle_zstd_error(size_t ret, void (*error)(const char *x)) ++{ ++ const int err = ZSTD_getErrorCode(ret); ++ ++ if (!ZSTD_isError(ret)) ++ return 0; ++ ++ switch (err) { ++ case ZSTD_error_memory_allocation: ++ error("ZSTD decompressor ran out of memory"); ++ break; ++ case ZSTD_error_prefix_unknown: ++ error("Input is not in the ZSTD format (wrong magic bytes)"); ++ break; ++ case ZSTD_error_dstSize_tooSmall: ++ case ZSTD_error_corruption_detected: ++ case ZSTD_error_checksum_wrong: ++ error("ZSTD-compressed data is corrupt"); ++ break; ++ default: ++ error("ZSTD-compressed data is probably corrupt"); ++ break; ++ } ++ return -1; ++} ++ ++/* ++ * Handle the case where we have the entire input and output in one segment. ++ * We can allocate less memory (no circular buffer for the sliding window), ++ * and avoid some memcpy() calls. ++ */ ++static int INIT decompress_single(const u8 *in_buf, unsigned int in_len, u8 *out_buf, ++ long out_len, unsigned int *in_pos, ++ void (*error)(const char *x)) ++{ ++ const size_t wksp_size = ZSTD_DCtxWorkspaceBound(); ++ void *wksp = large_malloc(wksp_size); ++ ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size); ++ int err; ++ size_t ret; ++ ++ if (dctx == NULL) { ++ error("Out of memory while allocating ZSTD_DCtx"); ++ err = -1; ++ goto out; ++ } ++ /* ++ * Find out how large the frame actually is, there may be junk at ++ * the end of the frame that ZSTD_decompressDCtx() can't handle. ++ */ ++ ret = ZSTD_findFrameCompressedSize(in_buf, in_len); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ in_len = (long)ret; ++ ++ ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ ++ if (in_pos != NULL) ++ *in_pos = in_len; ++ ++ err = 0; ++out: ++ if (wksp != NULL) ++ large_free(wksp); ++ return err; ++} ++ ++static int INIT __unzstd(unsigned char *in_buf, unsigned int in_len, ++ int (*fill)(void*, unsigned int), ++ int (*flush)(void*, unsigned int), ++ unsigned char *out_buf, long out_len, ++ unsigned int *in_pos, ++ void (*error)(const char *x)) ++{ ++ ZSTD_inBuffer in; ++ ZSTD_outBuffer out; ++ ZSTD_frameParams params; ++ void *in_allocated = NULL; ++ void *out_allocated = NULL; ++ void *wksp = NULL; ++ size_t wksp_size; ++ ZSTD_DStream *dstream; ++ int err; ++ size_t ret; ++ ++ if (out_len == 0) ++ out_len = INT_MAX; /* no limit */ ++ ++ if (fill == NULL && flush == NULL) ++ /* ++ * We can decompress faster and with less memory when we have a ++ * single chunk. ++ */ ++ return decompress_single(in_buf, in_len, out_buf, out_len, ++ in_pos, error); ++ ++ /* ++ * If in_buf is not provided, we must be using fill(), so allocate ++ * a large enough buffer. If it is provided, it must be at least ++ * ZSTD_IOBUF_SIZE large. ++ */ ++ if (in_buf == NULL) { ++ in_allocated = large_malloc(ZSTD_IOBUF_SIZE); ++ if (in_allocated == NULL) { ++ error("Out of memory while allocating input buffer"); ++ err = -1; ++ goto out; ++ } ++ in_buf = in_allocated; ++ in_len = 0; ++ } ++ /* Read the first chunk, since we need to decode the frame header. */ ++ if (fill != NULL) ++ in_len = fill(in_buf, ZSTD_IOBUF_SIZE); ++ if (in_len < 0) { ++ error("ZSTD-compressed data is truncated"); ++ err = -1; ++ goto out; ++ } ++ /* Set the first non-empty input buffer. */ ++ in.src = in_buf; ++ in.pos = 0; ++ in.size = in_len; ++ /* Allocate the output buffer if we are using flush(). */ ++ if (flush != NULL) { ++ out_allocated = large_malloc(ZSTD_IOBUF_SIZE); ++ if (out_allocated == NULL) { ++ error("Out of memory while allocating output buffer"); ++ err = -1; ++ goto out; ++ } ++ out_buf = out_allocated; ++ out_len = ZSTD_IOBUF_SIZE; ++ } ++ /* Set the output buffer. */ ++ out.dst = out_buf; ++ out.pos = 0; ++ out.size = out_len; ++ ++ /* ++ * We need to know the window size to allocate the ZSTD_DStream. ++ * Since we are streaming, we need to allocate a buffer for the sliding ++ * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX ++ * (8 MB), so it is important to use the actual value so as not to ++ * waste memory when it is smaller. ++ */ ++ ret = ZSTD_getFrameParams(¶ms, in.src, in.size); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ if (ret != 0) { ++ error("ZSTD-compressed data has an incomplete frame header"); ++ err = -1; ++ goto out; ++ } ++ if (params.windowSize > ZSTD_WINDOWSIZE_MAX) { ++ error("ZSTD-compressed data has too large a window size"); ++ err = -1; ++ goto out; ++ } ++ ++ /* ++ * Allocate the ZSTD_DStream now that we know how much memory is ++ * required. ++ */ ++ wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize); ++ wksp = large_malloc(wksp_size); ++ dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size); ++ if (dstream == NULL) { ++ error("Out of memory while allocating ZSTD_DStream"); ++ err = -1; ++ goto out; ++ } ++ ++ /* ++ * Decompression loop: ++ * Read more data if necessary (error if no more data can be read). ++ * Call the decompression function, which returns 0 when finished. ++ * Flush any data produced if using flush(). ++ */ ++ if (in_pos != NULL) ++ *in_pos = 0; ++ do { ++ /* ++ * If we need to reload data, either we have fill() and can ++ * try to get more data, or we don't and the input is truncated. ++ */ ++ if (in.pos == in.size) { ++ if (in_pos != NULL) ++ *in_pos += in.pos; ++ in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1; ++ if (in_len < 0) { ++ error("ZSTD-compressed data is truncated"); ++ err = -1; ++ goto out; ++ } ++ in.pos = 0; ++ in.size = in_len; ++ } ++ /* Returns zero when the frame is complete. */ ++ ret = ZSTD_decompressStream(dstream, &out, &in); ++ err = handle_zstd_error(ret, error); ++ if (err) ++ goto out; ++ /* Flush all of the data produced if using flush(). */ ++ if (flush != NULL && out.pos > 0) { ++ if (out.pos != flush(out.dst, out.pos)) { ++ error("Failed to flush()"); ++ err = -1; ++ goto out; ++ } ++ out.pos = 0; ++ } ++ } while (ret != 0); ++ ++ if (in_pos != NULL) ++ *in_pos += in.pos; ++ ++ err = 0; ++out: ++ if (in_allocated != NULL) ++ large_free(in_allocated); ++ if (out_allocated != NULL) ++ large_free(out_allocated); ++ if (wksp != NULL) ++ large_free(wksp); ++ return err; ++} ++ ++STATIC int INIT unzstd(unsigned char *buf, unsigned int len, ++ int (*fill)(void*, unsigned int), ++ int (*flush)(void*, unsigned int), ++ unsigned char *out_buf, ++ unsigned int *pos, ++ void (*error)(const char *x)) ++{ ++ return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error); ++} +diff --git a/xen/common/xxhash.c b/xen/common/xxhash.c +new file mode 100644 +index 0000000000..3ab3e01859 +--- /dev/null ++++ b/xen/common/xxhash.c +@@ -0,0 +1,484 @@ ++/* ++ * xxHash - Extremely Fast Hash algorithm ++ * Copyright (C) 2012-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at: ++ * - xxHash homepage: https://cyan4973.github.io/xxHash/ ++ * - xxHash source repository: https://github.com/Cyan4973/xxHash ++ */ ++ ++#include ++#include ++#include ++#include "zstd/private.h" ++ ++/*-************************************* ++ * Macros ++ **************************************/ ++#define xxh_rotl32(x, r) ((x << r) | (x >> (32 - r))) ++#define xxh_rotl64(x, r) ((x << r) | (x >> (64 - r))) ++ ++#ifdef __LITTLE_ENDIAN ++# define XXH_CPU_LITTLE_ENDIAN 1 ++#else ++# define XXH_CPU_LITTLE_ENDIAN 0 ++#endif ++ ++/*-************************************* ++ * Constants ++ **************************************/ ++static const uint32_t PRIME32_1 = 2654435761U; ++static const uint32_t PRIME32_2 = 2246822519U; ++static const uint32_t PRIME32_3 = 3266489917U; ++static const uint32_t PRIME32_4 = 668265263U; ++static const uint32_t PRIME32_5 = 374761393U; ++ ++static const uint64_t PRIME64_1 = 11400714785074694791ULL; ++static const uint64_t PRIME64_2 = 14029467366897019727ULL; ++static const uint64_t PRIME64_3 = 1609587929392839161ULL; ++static const uint64_t PRIME64_4 = 9650029242287828579ULL; ++static const uint64_t PRIME64_5 = 2870177450012600261ULL; ++ ++/*-************************** ++ * Utils ++ ***************************/ ++void INIT xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++void INIT xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src) ++{ ++ memcpy(dst, src, sizeof(*dst)); ++} ++ ++/*-*************************** ++ * Simple Hash Functions ++ ****************************/ ++static uint32_t INIT xxh32_round(uint32_t seed, const uint32_t input) ++{ ++ seed += input * PRIME32_2; ++ seed = xxh_rotl32(seed, 13); ++ seed *= PRIME32_1; ++ return seed; ++} ++ ++uint32_t INIT xxh32(const void *input, const size_t len, const uint32_t seed) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *b_end = p + len; ++ uint32_t h32; ++ ++ if (len >= 16) { ++ const uint8_t *const limit = b_end - 16; ++ uint32_t v1 = seed + PRIME32_1 + PRIME32_2; ++ uint32_t v2 = seed + PRIME32_2; ++ uint32_t v3 = seed + 0; ++ uint32_t v4 = seed - PRIME32_1; ++ ++ do { ++ v1 = xxh32_round(v1, get_unaligned_le32(p)); ++ p += 4; ++ v2 = xxh32_round(v2, get_unaligned_le32(p)); ++ p += 4; ++ v3 = xxh32_round(v3, get_unaligned_le32(p)); ++ p += 4; ++ v4 = xxh32_round(v4, get_unaligned_le32(p)); ++ p += 4; ++ } while (p <= limit); ++ ++ h32 = xxh_rotl32(v1, 1) + xxh_rotl32(v2, 7) + ++ xxh_rotl32(v3, 12) + xxh_rotl32(v4, 18); ++ } else { ++ h32 = seed + PRIME32_5; ++ } ++ ++ h32 += (uint32_t)len; ++ ++ while (p + 4 <= b_end) { ++ h32 += get_unaligned_le32(p) * PRIME32_3; ++ h32 = xxh_rotl32(h32, 17) * PRIME32_4; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h32 += (*p) * PRIME32_5; ++ h32 = xxh_rotl32(h32, 11) * PRIME32_1; ++ p++; ++ } ++ ++ h32 ^= h32 >> 15; ++ h32 *= PRIME32_2; ++ h32 ^= h32 >> 13; ++ h32 *= PRIME32_3; ++ h32 ^= h32 >> 16; ++ ++ return h32; ++} ++ ++static uint64_t INIT xxh64_round(uint64_t acc, const uint64_t input) ++{ ++ acc += input * PRIME64_2; ++ acc = xxh_rotl64(acc, 31); ++ acc *= PRIME64_1; ++ return acc; ++} ++ ++static uint64_t INIT xxh64_merge_round(uint64_t acc, uint64_t val) ++{ ++ val = xxh64_round(0, val); ++ acc ^= val; ++ acc = acc * PRIME64_1 + PRIME64_4; ++ return acc; ++} ++ ++uint64_t INIT xxh64(const void *input, const size_t len, const uint64_t seed) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ uint64_t h64; ++ ++ if (len >= 32) { ++ const uint8_t *const limit = b_end - 32; ++ uint64_t v1 = seed + PRIME64_1 + PRIME64_2; ++ uint64_t v2 = seed + PRIME64_2; ++ uint64_t v3 = seed + 0; ++ uint64_t v4 = seed - PRIME64_1; ++ ++ do { ++ v1 = xxh64_round(v1, get_unaligned_le64(p)); ++ p += 8; ++ v2 = xxh64_round(v2, get_unaligned_le64(p)); ++ p += 8; ++ v3 = xxh64_round(v3, get_unaligned_le64(p)); ++ p += 8; ++ v4 = xxh64_round(v4, get_unaligned_le64(p)); ++ p += 8; ++ } while (p <= limit); ++ ++ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + ++ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); ++ h64 = xxh64_merge_round(h64, v1); ++ h64 = xxh64_merge_round(h64, v2); ++ h64 = xxh64_merge_round(h64, v3); ++ h64 = xxh64_merge_round(h64, v4); ++ ++ } else { ++ h64 = seed + PRIME64_5; ++ } ++ ++ h64 += (uint64_t)len; ++ ++ while (p + 8 <= b_end) { ++ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); ++ ++ h64 ^= k1; ++ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; ++ p += 8; ++ } ++ ++ if (p + 4 <= b_end) { ++ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; ++ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h64 ^= (*p) * PRIME64_5; ++ h64 = xxh_rotl64(h64, 11) * PRIME64_1; ++ p++; ++ } ++ ++ h64 ^= h64 >> 33; ++ h64 *= PRIME64_2; ++ h64 ^= h64 >> 29; ++ h64 *= PRIME64_3; ++ h64 ^= h64 >> 32; ++ ++ return h64; ++} ++ ++/*-************************************************** ++ * Advanced Hash Functions ++ ***************************************************/ ++void INIT xxh32_reset(struct xxh32_state *statePtr, const uint32_t seed) ++{ ++ /* use a local state for memcpy() to avoid strict-aliasing warnings */ ++ struct xxh32_state state; ++ ++ memset(&state, 0, sizeof(state)); ++ state.v1 = seed + PRIME32_1 + PRIME32_2; ++ state.v2 = seed + PRIME32_2; ++ state.v3 = seed + 0; ++ state.v4 = seed - PRIME32_1; ++ memcpy(statePtr, &state, sizeof(state)); ++} ++ ++void INIT xxh64_reset(struct xxh64_state *statePtr, const uint64_t seed) ++{ ++ /* use a local state for memcpy() to avoid strict-aliasing warnings */ ++ struct xxh64_state state; ++ ++ memset(&state, 0, sizeof(state)); ++ state.v1 = seed + PRIME64_1 + PRIME64_2; ++ state.v2 = seed + PRIME64_2; ++ state.v3 = seed + 0; ++ state.v4 = seed - PRIME64_1; ++ memcpy(statePtr, &state, sizeof(state)); ++} ++ ++int INIT xxh32_update(struct xxh32_state *state, const void *input, const size_t len) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ ++ if (input == NULL) ++ return -EINVAL; ++ ++ state->total_len_32 += (uint32_t)len; ++ state->large_len |= (len >= 16) | (state->total_len_32 >= 16); ++ ++ if (state->memsize + len < 16) { /* fill in tmp buffer */ ++ memcpy((uint8_t *)(state->mem32) + state->memsize, input, len); ++ state->memsize += (uint32_t)len; ++ return 0; ++ } ++ ++ if (state->memsize) { /* some data left from previous update */ ++ const uint32_t *p32 = state->mem32; ++ ++ memcpy((uint8_t *)(state->mem32) + state->memsize, input, ++ 16 - state->memsize); ++ ++ state->v1 = xxh32_round(state->v1, get_unaligned_le32(p32)); ++ p32++; ++ state->v2 = xxh32_round(state->v2, get_unaligned_le32(p32)); ++ p32++; ++ state->v3 = xxh32_round(state->v3, get_unaligned_le32(p32)); ++ p32++; ++ state->v4 = xxh32_round(state->v4, get_unaligned_le32(p32)); ++ p32++; ++ ++ p += 16-state->memsize; ++ state->memsize = 0; ++ } ++ ++ if (p <= b_end - 16) { ++ const uint8_t *const limit = b_end - 16; ++ uint32_t v1 = state->v1; ++ uint32_t v2 = state->v2; ++ uint32_t v3 = state->v3; ++ uint32_t v4 = state->v4; ++ ++ do { ++ v1 = xxh32_round(v1, get_unaligned_le32(p)); ++ p += 4; ++ v2 = xxh32_round(v2, get_unaligned_le32(p)); ++ p += 4; ++ v3 = xxh32_round(v3, get_unaligned_le32(p)); ++ p += 4; ++ v4 = xxh32_round(v4, get_unaligned_le32(p)); ++ p += 4; ++ } while (p <= limit); ++ ++ state->v1 = v1; ++ state->v2 = v2; ++ state->v3 = v3; ++ state->v4 = v4; ++ } ++ ++ if (p < b_end) { ++ memcpy(state->mem32, p, (size_t)(b_end-p)); ++ state->memsize = (uint32_t)(b_end-p); ++ } ++ ++ return 0; ++} ++ ++uint32_t INIT xxh32_digest(const struct xxh32_state *state) ++{ ++ const uint8_t *p = (const uint8_t *)state->mem32; ++ const uint8_t *const b_end = (const uint8_t *)(state->mem32) + ++ state->memsize; ++ uint32_t h32; ++ ++ if (state->large_len) { ++ h32 = xxh_rotl32(state->v1, 1) + xxh_rotl32(state->v2, 7) + ++ xxh_rotl32(state->v3, 12) + xxh_rotl32(state->v4, 18); ++ } else { ++ h32 = state->v3 /* == seed */ + PRIME32_5; ++ } ++ ++ h32 += state->total_len_32; ++ ++ while (p + 4 <= b_end) { ++ h32 += get_unaligned_le32(p) * PRIME32_3; ++ h32 = xxh_rotl32(h32, 17) * PRIME32_4; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h32 += (*p) * PRIME32_5; ++ h32 = xxh_rotl32(h32, 11) * PRIME32_1; ++ p++; ++ } ++ ++ h32 ^= h32 >> 15; ++ h32 *= PRIME32_2; ++ h32 ^= h32 >> 13; ++ h32 *= PRIME32_3; ++ h32 ^= h32 >> 16; ++ ++ return h32; ++} ++ ++int INIT xxh64_update(struct xxh64_state *state, const void *input, const size_t len) ++{ ++ const uint8_t *p = (const uint8_t *)input; ++ const uint8_t *const b_end = p + len; ++ ++ if (input == NULL) ++ return -EINVAL; ++ ++ state->total_len += len; ++ ++ if (state->memsize + len < 32) { /* fill in tmp buffer */ ++ memcpy(((uint8_t *)state->mem64) + state->memsize, input, len); ++ state->memsize += (uint32_t)len; ++ return 0; ++ } ++ ++ if (state->memsize) { /* tmp buffer is full */ ++ uint64_t *p64 = state->mem64; ++ ++ memcpy(((uint8_t *)p64) + state->memsize, input, ++ 32 - state->memsize); ++ ++ state->v1 = xxh64_round(state->v1, get_unaligned_le64(p64)); ++ p64++; ++ state->v2 = xxh64_round(state->v2, get_unaligned_le64(p64)); ++ p64++; ++ state->v3 = xxh64_round(state->v3, get_unaligned_le64(p64)); ++ p64++; ++ state->v4 = xxh64_round(state->v4, get_unaligned_le64(p64)); ++ ++ p += 32 - state->memsize; ++ state->memsize = 0; ++ } ++ ++ if (p + 32 <= b_end) { ++ const uint8_t *const limit = b_end - 32; ++ uint64_t v1 = state->v1; ++ uint64_t v2 = state->v2; ++ uint64_t v3 = state->v3; ++ uint64_t v4 = state->v4; ++ ++ do { ++ v1 = xxh64_round(v1, get_unaligned_le64(p)); ++ p += 8; ++ v2 = xxh64_round(v2, get_unaligned_le64(p)); ++ p += 8; ++ v3 = xxh64_round(v3, get_unaligned_le64(p)); ++ p += 8; ++ v4 = xxh64_round(v4, get_unaligned_le64(p)); ++ p += 8; ++ } while (p <= limit); ++ ++ state->v1 = v1; ++ state->v2 = v2; ++ state->v3 = v3; ++ state->v4 = v4; ++ } ++ ++ if (p < b_end) { ++ memcpy(state->mem64, p, (size_t)(b_end-p)); ++ state->memsize = (uint32_t)(b_end - p); ++ } ++ ++ return 0; ++} ++ ++uint64_t INIT xxh64_digest(const struct xxh64_state *state) ++{ ++ const uint8_t *p = (const uint8_t *)state->mem64; ++ const uint8_t *const b_end = (const uint8_t *)state->mem64 + ++ state->memsize; ++ uint64_t h64; ++ ++ if (state->total_len >= 32) { ++ const uint64_t v1 = state->v1; ++ const uint64_t v2 = state->v2; ++ const uint64_t v3 = state->v3; ++ const uint64_t v4 = state->v4; ++ ++ h64 = xxh_rotl64(v1, 1) + xxh_rotl64(v2, 7) + ++ xxh_rotl64(v3, 12) + xxh_rotl64(v4, 18); ++ h64 = xxh64_merge_round(h64, v1); ++ h64 = xxh64_merge_round(h64, v2); ++ h64 = xxh64_merge_round(h64, v3); ++ h64 = xxh64_merge_round(h64, v4); ++ } else { ++ h64 = state->v3 + PRIME64_5; ++ } ++ ++ h64 += (uint64_t)state->total_len; ++ ++ while (p + 8 <= b_end) { ++ const uint64_t k1 = xxh64_round(0, get_unaligned_le64(p)); ++ ++ h64 ^= k1; ++ h64 = xxh_rotl64(h64, 27) * PRIME64_1 + PRIME64_4; ++ p += 8; ++ } ++ ++ if (p + 4 <= b_end) { ++ h64 ^= (uint64_t)(get_unaligned_le32(p)) * PRIME64_1; ++ h64 = xxh_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; ++ p += 4; ++ } ++ ++ while (p < b_end) { ++ h64 ^= (*p) * PRIME64_5; ++ h64 = xxh_rotl64(h64, 11) * PRIME64_1; ++ p++; ++ } ++ ++ h64 ^= h64 >> 33; ++ h64 *= PRIME64_2; ++ h64 ^= h64 >> 29; ++ h64 *= PRIME64_3; ++ h64 ^= h64 >> 32; ++ ++ return h64; ++} +diff --git a/xen/common/zstd/bitstream.h b/xen/common/zstd/bitstream.h +new file mode 100644 +index 0000000000..3a49784d5c +--- /dev/null ++++ b/xen/common/zstd/bitstream.h +@@ -0,0 +1,379 @@ ++/* ++ * bitstream ++ * Part of FSE library ++ * header file (to include) ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef BITSTREAM_H_MODULE ++#define BITSTREAM_H_MODULE ++ ++/* ++* This API consists of small unitary functions, which must be inlined for best performance. ++* Since link-time-optimization is not available for all compilers, ++* these functions are defined into a .h to be included. ++*/ ++ ++/*-**************************************** ++* Dependencies ++******************************************/ ++#include "error_private.h" /* error codes and messages */ ++#include "mem.h" /* unaligned access routines */ ++ ++/*========================================= ++* Target specific ++=========================================*/ ++#define STREAM_ACCUMULATOR_MIN_32 25 ++#define STREAM_ACCUMULATOR_MIN_64 57 ++#define STREAM_ACCUMULATOR_MIN ((U32)(ZSTD_32bits() ? STREAM_ACCUMULATOR_MIN_32 : STREAM_ACCUMULATOR_MIN_64)) ++ ++/*-****************************************** ++* bitStream encoding API (write forward) ++********************************************/ ++/* bitStream can mix input from multiple sources. ++* A critical property of these streams is that they encode and decode in **reverse** direction. ++* So the first bit sequence you add will be the last to be read, like a LIFO stack. ++*/ ++typedef struct { ++ size_t bitContainer; ++ int bitPos; ++ char *startPtr; ++ char *ptr; ++ char *endPtr; ++} BIT_CStream_t; ++ ++ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *dstBuffer, size_t dstCapacity); ++ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits); ++ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC); ++ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC); ++ ++/* Start with initCStream, providing the size of buffer to write into. ++* bitStream will never write outside of this buffer. ++* `dstCapacity` must be >= sizeof(bitD->bitContainer), otherwise @return will be an error code. ++* ++* bits are first added to a local register. ++* Local register is size_t, hence 64-bits on 64-bits systems, or 32-bits on 32-bits systems. ++* Writing data into memory is an explicit operation, performed by the flushBits function. ++* Hence keep track how many bits are potentially stored into local register to avoid register overflow. ++* After a flushBits, a maximum of 7 bits might still be stored into local register. ++* ++* Avoid storing elements of more than 24 bits if you want compatibility with 32-bits bitstream readers. ++* ++* Last operation is to close the bitStream. ++* The function returns the final size of CStream in bytes. ++* If data couldn't fit into `dstBuffer`, it will return a 0 ( == not storable) ++*/ ++ ++/*-******************************************** ++* bitStream decoding API (read backward) ++**********************************************/ ++typedef struct { ++ size_t bitContainer; ++ unsigned bitsConsumed; ++ const char *ptr; ++ const char *start; ++} BIT_DStream_t; ++ ++typedef enum { ++ BIT_DStream_unfinished = 0, ++ BIT_DStream_endOfBuffer = 1, ++ BIT_DStream_completed = 2, ++ BIT_DStream_overflow = 3 ++} BIT_DStream_status; /* result of BIT_reloadDStream() */ ++/* 1,2,4,8 would be better for bitmap combinations, but slows down performance a bit ... :( */ ++ ++ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize); ++ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, unsigned nbBits); ++ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD); ++ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *bitD); ++ ++/* Start by invoking BIT_initDStream(). ++* A chunk of the bitStream is then stored into a local register. ++* Local register size is 64-bits on 64-bits systems, 32-bits on 32-bits systems (size_t). ++* You can then retrieve bitFields stored into the local register, **in reverse order**. ++* Local register is explicitly reloaded from memory by the BIT_reloadDStream() method. ++* A reload guarantee a minimum of ((8*sizeof(bitD->bitContainer))-7) bits when its result is BIT_DStream_unfinished. ++* Otherwise, it can be less than that, so proceed accordingly. ++* Checking if DStream has reached its end can be performed with BIT_endOfDStream(). ++*/ ++ ++/*-**************************************** ++* unsafe API ++******************************************/ ++ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits); ++/* faster, but works only if value is "clean", meaning all high bits above nbBits are 0 */ ++ ++ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC); ++/* unsafe version; does not check buffer overflow */ ++ ++ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, unsigned nbBits); ++/* faster, but works only if nbBits >= 1 */ ++ ++/*-************************************************************** ++* Internal functions ++****************************************************************/ ++ZSTD_STATIC unsigned BIT_highbit32(register U32 val) { return 31 - __builtin_clz(val); } ++ ++/*===== Local Constants =====*/ ++static const unsigned BIT_mask[] = {0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, ++ 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, 0x1FFFF, ++ 0x3FFFF, 0x7FFFF, 0xFFFFF, 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, 0x1FFFFFF, 0x3FFFFFF}; /* up to 26 bits */ ++ ++/*-************************************************************** ++* bitStream encoding ++****************************************************************/ ++/*! BIT_initCStream() : ++ * `dstCapacity` must be > sizeof(void*) ++ * @return : 0 if success, ++ otherwise an error code (can be tested using ERR_isError() ) */ ++ZSTD_STATIC size_t BIT_initCStream(BIT_CStream_t *bitC, void *startPtr, size_t dstCapacity) ++{ ++ bitC->bitContainer = 0; ++ bitC->bitPos = 0; ++ bitC->startPtr = (char *)startPtr; ++ bitC->ptr = bitC->startPtr; ++ bitC->endPtr = bitC->startPtr + dstCapacity - sizeof(bitC->ptr); ++ if (dstCapacity <= sizeof(bitC->ptr)) ++ return ERROR(dstSize_tooSmall); ++ return 0; ++} ++ ++/*! BIT_addBits() : ++ can add up to 26 bits into `bitC`. ++ Does not check for register overflow ! */ ++ZSTD_STATIC void BIT_addBits(BIT_CStream_t *bitC, size_t value, unsigned nbBits) ++{ ++ bitC->bitContainer |= (value & BIT_mask[nbBits]) << bitC->bitPos; ++ bitC->bitPos += nbBits; ++} ++ ++/*! BIT_addBitsFast() : ++ * works only if `value` is _clean_, meaning all high bits above nbBits are 0 */ ++ZSTD_STATIC void BIT_addBitsFast(BIT_CStream_t *bitC, size_t value, unsigned nbBits) ++{ ++ bitC->bitContainer |= value << bitC->bitPos; ++ bitC->bitPos += nbBits; ++} ++ ++/*! BIT_flushBitsFast() : ++ * unsafe version; does not check buffer overflow */ ++ZSTD_STATIC void BIT_flushBitsFast(BIT_CStream_t *bitC) ++{ ++ size_t const nbBytes = bitC->bitPos >> 3; ++ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); ++ bitC->ptr += nbBytes; ++ bitC->bitPos &= 7; ++ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ ++} ++ ++/*! BIT_flushBits() : ++ * safe version; check for buffer overflow, and prevents it. ++ * note : does not signal buffer overflow. This will be revealed later on using BIT_closeCStream() */ ++ZSTD_STATIC void BIT_flushBits(BIT_CStream_t *bitC) ++{ ++ size_t const nbBytes = bitC->bitPos >> 3; ++ ZSTD_writeLEST(bitC->ptr, bitC->bitContainer); ++ bitC->ptr += nbBytes; ++ if (bitC->ptr > bitC->endPtr) ++ bitC->ptr = bitC->endPtr; ++ bitC->bitPos &= 7; ++ bitC->bitContainer >>= nbBytes * 8; /* if bitPos >= sizeof(bitContainer)*8 --> undefined behavior */ ++} ++ ++/*! BIT_closeCStream() : ++ * @return : size of CStream, in bytes, ++ or 0 if it could not fit into dstBuffer */ ++ZSTD_STATIC size_t BIT_closeCStream(BIT_CStream_t *bitC) ++{ ++ BIT_addBitsFast(bitC, 1, 1); /* endMark */ ++ BIT_flushBits(bitC); ++ ++ if (bitC->ptr >= bitC->endPtr) ++ return 0; /* doesn't fit within authorized budget : cancel */ ++ ++ return (bitC->ptr - bitC->startPtr) + (bitC->bitPos > 0); ++} ++ ++/*-******************************************************** ++* bitStream decoding ++**********************************************************/ ++/*! BIT_initDStream() : ++* Initialize a BIT_DStream_t. ++* `bitD` : a pointer to an already allocated BIT_DStream_t structure. ++* `srcSize` must be the *exact* size of the bitStream, in bytes. ++* @return : size of stream (== srcSize) or an errorCode if a problem is detected ++*/ ++ZSTD_STATIC size_t BIT_initDStream(BIT_DStream_t *bitD, const void *srcBuffer, size_t srcSize) ++{ ++ if (srcSize < 1) { ++ memset(bitD, 0, sizeof(*bitD)); ++ return ERROR(srcSize_wrong); ++ } ++ ++ if (srcSize >= sizeof(bitD->bitContainer)) { /* normal case */ ++ bitD->start = (const char *)srcBuffer; ++ bitD->ptr = (const char *)srcBuffer + srcSize - sizeof(bitD->bitContainer); ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); ++ { ++ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; ++ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; /* ensures bitsConsumed is always set */ ++ if (lastByte == 0) ++ return ERROR(GENERIC); /* endMark not present */ ++ } ++ } else { ++ bitD->start = (const char *)srcBuffer; ++ bitD->ptr = bitD->start; ++ bitD->bitContainer = *(const BYTE *)(bitD->start); ++ switch (srcSize) { ++ case 7: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[6]) << (sizeof(bitD->bitContainer) * 8 - 16); ++ /* fall through */ ++ case 6: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[5]) << (sizeof(bitD->bitContainer) * 8 - 24); ++ /* fall through */ ++ case 5: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[4]) << (sizeof(bitD->bitContainer) * 8 - 32); ++ /* fall through */ ++ case 4: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[3]) << 24; ++ /* fall through */ ++ case 3: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[2]) << 16; ++ /* fall through */ ++ case 2: bitD->bitContainer += (size_t)(((const BYTE *)(srcBuffer))[1]) << 8; ++ default:; ++ } ++ { ++ BYTE const lastByte = ((const BYTE *)srcBuffer)[srcSize - 1]; ++ bitD->bitsConsumed = lastByte ? 8 - BIT_highbit32(lastByte) : 0; ++ if (lastByte == 0) ++ return ERROR(GENERIC); /* endMark not present */ ++ } ++ bitD->bitsConsumed += (U32)(sizeof(bitD->bitContainer) - srcSize) * 8; ++ } ++ ++ return srcSize; ++} ++ ++ZSTD_STATIC size_t BIT_getUpperBits(size_t bitContainer, U32 const start) { return bitContainer >> start; } ++ ++ZSTD_STATIC size_t BIT_getMiddleBits(size_t bitContainer, U32 const start, U32 const nbBits) { return (bitContainer >> start) & BIT_mask[nbBits]; } ++ ++ZSTD_STATIC size_t BIT_getLowerBits(size_t bitContainer, U32 const nbBits) { return bitContainer & BIT_mask[nbBits]; } ++ ++/*! BIT_lookBits() : ++ * Provides next n bits from local register. ++ * local register is not modified. ++ * On 32-bits, maxNbBits==24. ++ * On 64-bits, maxNbBits==56. ++ * @return : value extracted ++ */ ++ZSTD_STATIC size_t BIT_lookBits(const BIT_DStream_t *bitD, U32 nbBits) ++{ ++ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; ++ return ((bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> 1) >> ((bitMask - nbBits) & bitMask); ++} ++ ++/*! BIT_lookBitsFast() : ++* unsafe version; only works only if nbBits >= 1 */ ++ZSTD_STATIC size_t BIT_lookBitsFast(const BIT_DStream_t *bitD, U32 nbBits) ++{ ++ U32 const bitMask = sizeof(bitD->bitContainer) * 8 - 1; ++ return (bitD->bitContainer << (bitD->bitsConsumed & bitMask)) >> (((bitMask + 1) - nbBits) & bitMask); ++} ++ ++ZSTD_STATIC void BIT_skipBits(BIT_DStream_t *bitD, U32 nbBits) { bitD->bitsConsumed += nbBits; } ++ ++/*! BIT_readBits() : ++ * Read (consume) next n bits from local register and update. ++ * Pay attention to not read more than nbBits contained into local register. ++ * @return : extracted value. ++ */ ++ZSTD_STATIC size_t BIT_readBits(BIT_DStream_t *bitD, U32 nbBits) ++{ ++ size_t const value = BIT_lookBits(bitD, nbBits); ++ BIT_skipBits(bitD, nbBits); ++ return value; ++} ++ ++/*! BIT_readBitsFast() : ++* unsafe version; only works only if nbBits >= 1 */ ++ZSTD_STATIC size_t BIT_readBitsFast(BIT_DStream_t *bitD, U32 nbBits) ++{ ++ size_t const value = BIT_lookBitsFast(bitD, nbBits); ++ BIT_skipBits(bitD, nbBits); ++ return value; ++} ++ ++/*! BIT_reloadDStream() : ++* Refill `bitD` from buffer previously set in BIT_initDStream() . ++* This function is safe, it guarantees it will not read beyond src buffer. ++* @return : status of `BIT_DStream_t` internal register. ++ if status == BIT_DStream_unfinished, internal register is filled with >= (sizeof(bitD->bitContainer)*8 - 7) bits */ ++ZSTD_STATIC BIT_DStream_status BIT_reloadDStream(BIT_DStream_t *bitD) ++{ ++ if (bitD->bitsConsumed > (sizeof(bitD->bitContainer) * 8)) /* should not happen => corruption detected */ ++ return BIT_DStream_overflow; ++ ++ if (bitD->ptr >= bitD->start + sizeof(bitD->bitContainer)) { ++ bitD->ptr -= bitD->bitsConsumed >> 3; ++ bitD->bitsConsumed &= 7; ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); ++ return BIT_DStream_unfinished; ++ } ++ if (bitD->ptr == bitD->start) { ++ if (bitD->bitsConsumed < sizeof(bitD->bitContainer) * 8) ++ return BIT_DStream_endOfBuffer; ++ return BIT_DStream_completed; ++ } ++ { ++ U32 nbBytes = bitD->bitsConsumed >> 3; ++ BIT_DStream_status result = BIT_DStream_unfinished; ++ if (bitD->ptr - nbBytes < bitD->start) { ++ nbBytes = (U32)(bitD->ptr - bitD->start); /* ptr > start */ ++ result = BIT_DStream_endOfBuffer; ++ } ++ bitD->ptr -= nbBytes; ++ bitD->bitsConsumed -= nbBytes * 8; ++ bitD->bitContainer = ZSTD_readLEST(bitD->ptr); /* reminder : srcSize > sizeof(bitD) */ ++ return result; ++ } ++} ++ ++/*! BIT_endOfDStream() : ++* @return Tells if DStream has exactly reached its end (all bits consumed). ++*/ ++ZSTD_STATIC unsigned BIT_endOfDStream(const BIT_DStream_t *DStream) ++{ ++ return ((DStream->ptr == DStream->start) && (DStream->bitsConsumed == sizeof(DStream->bitContainer) * 8)); ++} ++ ++#endif /* BITSTREAM_H_MODULE */ +diff --git a/xen/common/zstd/decompress.c b/xen/common/zstd/decompress.c +new file mode 100644 +index 0000000000..8e627d881a +--- /dev/null ++++ b/xen/common/zstd/decompress.c +@@ -0,0 +1,2489 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* *************************************************************** ++* Tuning parameters ++*****************************************************************/ ++/*! ++* MAXWINDOWSIZE_DEFAULT : ++* maximum window size accepted by DStream, by default. ++* Frames requiring more memory will be rejected. ++*/ ++#ifndef ZSTD_MAXWINDOWSIZE_DEFAULT ++#define ZSTD_MAXWINDOWSIZE_DEFAULT ((1 << ZSTD_WINDOWLOG_MAX) + 1) /* defined within zstd.h */ ++#endif ++ ++/*-******************************************************* ++* Dependencies ++*********************************************************/ ++#include "fse.h" ++#include "huf.h" ++#include "mem.h" /* low level memory routines */ ++#include "zstd_internal.h" ++#include /* memcpy, memmove, memset */ ++ ++#define ZSTD_PREFETCH(ptr) __builtin_prefetch(ptr, 0, 0) ++ ++/*-************************************* ++* Macros ++***************************************/ ++#define ZSTD_isError ERR_isError /* for inlining */ ++#define FSE_isError ERR_isError ++#define HUF_isError ERR_isError ++ ++/*_******************************************************* ++* Memory operations ++**********************************************************/ ++static void INIT ZSTD_copy4(void *dst, const void *src) { memcpy(dst, src, 4); } ++ ++/*-************************************************************* ++* Context management ++***************************************************************/ ++typedef enum { ++ ZSTDds_getFrameHeaderSize, ++ ZSTDds_decodeFrameHeader, ++ ZSTDds_decodeBlockHeader, ++ ZSTDds_decompressBlock, ++ ZSTDds_decompressLastBlock, ++ ZSTDds_checkChecksum, ++ ZSTDds_decodeSkippableHeader, ++ ZSTDds_skipFrame ++} ZSTD_dStage; ++ ++typedef struct { ++ FSE_DTable LLTable[FSE_DTABLE_SIZE_U32(LLFSELog)]; ++ FSE_DTable OFTable[FSE_DTABLE_SIZE_U32(OffFSELog)]; ++ FSE_DTable MLTable[FSE_DTABLE_SIZE_U32(MLFSELog)]; ++ HUF_DTable hufTable[HUF_DTABLE_SIZE(HufLog)]; /* can accommodate HUF_decompress4X */ ++ U64 workspace[HUF_DECOMPRESS_WORKSPACE_SIZE_U32 / 2]; ++ U32 rep[ZSTD_REP_NUM]; ++} ZSTD_entropyTables_t; ++ ++struct ZSTD_DCtx_s { ++ const FSE_DTable *LLTptr; ++ const FSE_DTable *MLTptr; ++ const FSE_DTable *OFTptr; ++ const HUF_DTable *HUFptr; ++ ZSTD_entropyTables_t entropy; ++ const void *previousDstEnd; /* detect continuity */ ++ const void *base; /* start of curr segment */ ++ const void *vBase; /* virtual start of previous segment if it was just before curr one */ ++ const void *dictEnd; /* end of previous segment */ ++ size_t expected; ++ ZSTD_frameParams fParams; ++ blockType_e bType; /* used in ZSTD_decompressContinue(), to transfer blockType between header decoding and block decoding stages */ ++ ZSTD_dStage stage; ++ U32 litEntropy; ++ U32 fseEntropy; ++ struct xxh64_state xxhState; ++ size_t headerSize; ++ U32 dictID; ++ const BYTE *litPtr; ++ ZSTD_customMem customMem; ++ size_t litSize; ++ size_t rleSize; ++ BYTE litBuffer[ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH]; ++ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; ++}; /* typedef'd to ZSTD_DCtx within "zstd.h" */ ++ ++size_t INIT ZSTD_DCtxWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DCtx)); } ++ ++size_t INIT ZSTD_decompressBegin(ZSTD_DCtx *dctx) ++{ ++ dctx->expected = ZSTD_frameHeaderSize_prefix; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ dctx->previousDstEnd = NULL; ++ dctx->base = NULL; ++ dctx->vBase = NULL; ++ dctx->dictEnd = NULL; ++ dctx->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ ++ dctx->litEntropy = dctx->fseEntropy = 0; ++ dctx->dictID = 0; ++ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.rep) == sizeof(repStartValue)); ++ memcpy(dctx->entropy.rep, repStartValue, sizeof(repStartValue)); /* initial repcodes */ ++ dctx->LLTptr = dctx->entropy.LLTable; ++ dctx->MLTptr = dctx->entropy.MLTable; ++ dctx->OFTptr = dctx->entropy.OFTable; ++ dctx->HUFptr = dctx->entropy.hufTable; ++ return 0; ++} ++ ++ZSTD_DCtx INIT *ZSTD_createDCtx_advanced(ZSTD_customMem customMem) ++{ ++ ZSTD_DCtx *dctx; ++ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ dctx = (ZSTD_DCtx *)ZSTD_malloc(sizeof(ZSTD_DCtx), customMem); ++ if (!dctx) ++ return NULL; ++ memcpy(&dctx->customMem, &customMem, sizeof(customMem)); ++ ZSTD_decompressBegin(dctx); ++ return dctx; ++} ++ ++ZSTD_DCtx INIT *ZSTD_initDCtx(void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ return ZSTD_createDCtx_advanced(stackMem); ++} ++ ++size_t INIT ZSTD_freeDCtx(ZSTD_DCtx *dctx) ++{ ++ if (dctx == NULL) ++ return 0; /* support free on NULL */ ++ ZSTD_free(dctx, dctx->customMem); ++ return 0; /* reserved as a potential error code in the future */ ++} ++ ++void INIT ZSTD_copyDCtx(ZSTD_DCtx *dstDCtx, const ZSTD_DCtx *srcDCtx) ++{ ++ size_t const workSpaceSize = (ZSTD_BLOCKSIZE_ABSOLUTEMAX + WILDCOPY_OVERLENGTH) + ZSTD_frameHeaderSize_max; ++ memcpy(dstDCtx, srcDCtx, sizeof(ZSTD_DCtx) - workSpaceSize); /* no need to copy workspace */ ++} ++ ++static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict); ++ ++/*-************************************************************* ++* Decompression section ++***************************************************************/ ++ ++/*! ZSTD_isFrame() : ++ * Tells if the content of `buffer` starts with a valid Frame Identifier. ++ * Note : Frame Identifier is 4 bytes. If `size < 4`, @return will always be 0. ++ * Note 2 : Legacy Frame Identifiers are considered valid only if Legacy Support is enabled. ++ * Note 3 : Skippable Frame Identifiers are considered valid. */ ++unsigned INIT ZSTD_isFrame(const void *buffer, size_t size) ++{ ++ if (size < 4) ++ return 0; ++ { ++ U32 const magic = ZSTD_readLE32(buffer); ++ if (magic == ZSTD_MAGICNUMBER) ++ return 1; ++ if ((magic & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) ++ return 1; ++ } ++ return 0; ++} ++ ++/** ZSTD_frameHeaderSize() : ++* srcSize must be >= ZSTD_frameHeaderSize_prefix. ++* @return : size of the Frame Header */ ++static size_t INIT ZSTD_frameHeaderSize(const void *src, size_t srcSize) ++{ ++ if (srcSize < ZSTD_frameHeaderSize_prefix) ++ return ERROR(srcSize_wrong); ++ { ++ BYTE const fhd = ((const BYTE *)src)[4]; ++ U32 const dictID = fhd & 3; ++ U32 const singleSegment = (fhd >> 5) & 1; ++ U32 const fcsId = fhd >> 6; ++ return ZSTD_frameHeaderSize_prefix + !singleSegment + ZSTD_did_fieldSize[dictID] + ZSTD_fcs_fieldSize[fcsId] + (singleSegment && !fcsId); ++ } ++} ++ ++/** ZSTD_getFrameParams() : ++* decode Frame Header, or require larger `srcSize`. ++* @return : 0, `fparamsPtr` is correctly filled, ++* >0, `srcSize` is too small, result is expected `srcSize`, ++* or an error code, which can be tested using ZSTD_isError() */ ++size_t INIT ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, size_t srcSize) ++{ ++ const BYTE *ip = (const BYTE *)src; ++ ++ if (srcSize < ZSTD_frameHeaderSize_prefix) ++ return ZSTD_frameHeaderSize_prefix; ++ if (ZSTD_readLE32(src) != ZSTD_MAGICNUMBER) { ++ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ZSTD_skippableHeaderSize; /* magic number + skippable frame length */ ++ memset(fparamsPtr, 0, sizeof(*fparamsPtr)); ++ fparamsPtr->frameContentSize = ZSTD_readLE32((const char *)src + 4); ++ fparamsPtr->windowSize = 0; /* windowSize==0 means a frame is skippable */ ++ return 0; ++ } ++ return ERROR(prefix_unknown); ++ } ++ ++ /* ensure there is enough `srcSize` to fully read/decode frame header */ ++ { ++ size_t const fhsize = ZSTD_frameHeaderSize(src, srcSize); ++ if (srcSize < fhsize) ++ return fhsize; ++ } ++ ++ { ++ BYTE const fhdByte = ip[4]; ++ size_t pos = 5; ++ U32 const dictIDSizeCode = fhdByte & 3; ++ U32 const checksumFlag = (fhdByte >> 2) & 1; ++ U32 const singleSegment = (fhdByte >> 5) & 1; ++ U32 const fcsID = fhdByte >> 6; ++ U32 const windowSizeMax = 1U << ZSTD_WINDOWLOG_MAX; ++ U32 windowSize = 0; ++ U32 dictID = 0; ++ U64 frameContentSize = 0; ++ if ((fhdByte & 0x08) != 0) ++ return ERROR(frameParameter_unsupported); /* reserved bits, which must be zero */ ++ if (!singleSegment) { ++ BYTE const wlByte = ip[pos++]; ++ U32 const windowLog = (wlByte >> 3) + ZSTD_WINDOWLOG_ABSOLUTEMIN; ++ if (windowLog > ZSTD_WINDOWLOG_MAX) ++ return ERROR(frameParameter_windowTooLarge); /* avoids issue with 1 << windowLog */ ++ windowSize = (1U << windowLog); ++ windowSize += (windowSize >> 3) * (wlByte & 7); ++ } ++ ++ switch (dictIDSizeCode) { ++ default: /* impossible */ ++ case 0: break; ++ case 1: ++ dictID = ip[pos]; ++ pos++; ++ break; ++ case 2: ++ dictID = ZSTD_readLE16(ip + pos); ++ pos += 2; ++ break; ++ case 3: ++ dictID = ZSTD_readLE32(ip + pos); ++ pos += 4; ++ break; ++ } ++ switch (fcsID) { ++ default: /* impossible */ ++ case 0: ++ if (singleSegment) ++ frameContentSize = ip[pos]; ++ break; ++ case 1: frameContentSize = ZSTD_readLE16(ip + pos) + 256; break; ++ case 2: frameContentSize = ZSTD_readLE32(ip + pos); break; ++ case 3: frameContentSize = ZSTD_readLE64(ip + pos); break; ++ } ++ if (!windowSize) ++ windowSize = (U32)frameContentSize; ++ if (windowSize > windowSizeMax) ++ return ERROR(frameParameter_windowTooLarge); ++ fparamsPtr->frameContentSize = frameContentSize; ++ fparamsPtr->windowSize = windowSize; ++ fparamsPtr->dictID = dictID; ++ fparamsPtr->checksumFlag = checksumFlag; ++ } ++ return 0; ++} ++ ++/** ZSTD_getFrameContentSize() : ++* compatible with legacy mode ++* @return : decompressed size of the single frame pointed to be `src` if known, otherwise ++* - ZSTD_CONTENTSIZE_UNKNOWN if the size cannot be determined ++* - ZSTD_CONTENTSIZE_ERROR if an error occurred (e.g. invalid magic number, srcSize too small) */ ++unsigned long long INIT ZSTD_getFrameContentSize(const void *src, size_t srcSize) ++{ ++ { ++ ZSTD_frameParams fParams; ++ if (ZSTD_getFrameParams(&fParams, src, srcSize) != 0) ++ return ZSTD_CONTENTSIZE_ERROR; ++ if (fParams.windowSize == 0) { ++ /* Either skippable or empty frame, size == 0 either way */ ++ return 0; ++ } else if (fParams.frameContentSize != 0) { ++ return fParams.frameContentSize; ++ } else { ++ return ZSTD_CONTENTSIZE_UNKNOWN; ++ } ++ } ++} ++ ++/** ZSTD_findDecompressedSize() : ++ * compatible with legacy mode ++ * `srcSize` must be the exact length of some number of ZSTD compressed and/or ++ * skippable frames ++ * @return : decompressed size of the frames contained */ ++unsigned long long INIT ZSTD_findDecompressedSize(const void *src, size_t srcSize) ++{ ++ { ++ unsigned long long totalDstSize = 0; ++ while (srcSize >= ZSTD_frameHeaderSize_prefix) { ++ const U32 magicNumber = ZSTD_readLE32(src); ++ ++ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ size_t skippableSize; ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ERROR(srcSize_wrong); ++ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; ++ if (srcSize < skippableSize) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ src = (const BYTE *)src + skippableSize; ++ srcSize -= skippableSize; ++ continue; ++ } ++ ++ { ++ unsigned long long const ret = ZSTD_getFrameContentSize(src, srcSize); ++ if (ret >= ZSTD_CONTENTSIZE_ERROR) ++ return ret; ++ ++ /* check for overflow */ ++ if (totalDstSize + ret < totalDstSize) ++ return ZSTD_CONTENTSIZE_ERROR; ++ totalDstSize += ret; ++ } ++ { ++ size_t const frameSrcSize = ZSTD_findFrameCompressedSize(src, srcSize); ++ if (ZSTD_isError(frameSrcSize)) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ src = (const BYTE *)src + frameSrcSize; ++ srcSize -= frameSrcSize; ++ } ++ } ++ ++ if (srcSize) { ++ return ZSTD_CONTENTSIZE_ERROR; ++ } ++ ++ return totalDstSize; ++ } ++} ++ ++/** ZSTD_decodeFrameHeader() : ++* `headerSize` must be the size provided by ZSTD_frameHeaderSize(). ++* @return : 0 if success, or an error code, which can be tested using ZSTD_isError() */ ++static size_t INIT ZSTD_decodeFrameHeader(ZSTD_DCtx *dctx, const void *src, size_t headerSize) ++{ ++ size_t const result = ZSTD_getFrameParams(&(dctx->fParams), src, headerSize); ++ if (ZSTD_isError(result)) ++ return result; /* invalid header */ ++ if (result > 0) ++ return ERROR(srcSize_wrong); /* headerSize too small */ ++ if (dctx->fParams.dictID && (dctx->dictID != dctx->fParams.dictID)) ++ return ERROR(dictionary_wrong); ++ if (dctx->fParams.checksumFlag) ++ xxh64_reset(&dctx->xxhState, 0); ++ return 0; ++} ++ ++typedef struct { ++ blockType_e blockType; ++ U32 lastBlock; ++ U32 origSize; ++} blockProperties_t; ++ ++/*! ZSTD_getcBlockSize() : ++* Provides the size of compressed block from block header `src` */ ++size_t INIT ZSTD_getcBlockSize(const void *src, size_t srcSize, blockProperties_t *bpPtr) ++{ ++ if (srcSize < ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ { ++ U32 const cBlockHeader = ZSTD_readLE24(src); ++ U32 const cSize = cBlockHeader >> 3; ++ bpPtr->lastBlock = cBlockHeader & 1; ++ bpPtr->blockType = (blockType_e)((cBlockHeader >> 1) & 3); ++ bpPtr->origSize = cSize; /* only useful for RLE */ ++ if (bpPtr->blockType == bt_rle) ++ return 1; ++ if (bpPtr->blockType == bt_reserved) ++ return ERROR(corruption_detected); ++ return cSize; ++ } ++} ++ ++static size_t INIT ZSTD_copyRawBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ if (srcSize > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memcpy(dst, src, srcSize); ++ return srcSize; ++} ++ ++static size_t INIT ZSTD_setRleBlock(void *dst, size_t dstCapacity, const void *src, size_t srcSize, size_t regenSize) ++{ ++ if (srcSize != 1) ++ return ERROR(srcSize_wrong); ++ if (regenSize > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memset(dst, *(const BYTE *)src, regenSize); ++ return regenSize; ++} ++ ++/*! ZSTD_decodeLiteralsBlock() : ++ @return : nb of bytes read from src (< srcSize ) */ ++size_t INIT ZSTD_decodeLiteralsBlock(ZSTD_DCtx *dctx, const void *src, size_t srcSize) /* note : srcSize < BLOCKSIZE */ ++{ ++ if (srcSize < MIN_CBLOCK_SIZE) ++ return ERROR(corruption_detected); ++ ++ { ++ const BYTE *const istart = (const BYTE *)src; ++ symbolEncodingType_e const litEncType = (symbolEncodingType_e)(istart[0] & 3); ++ ++ switch (litEncType) { ++ case set_repeat: ++ if (dctx->litEntropy == 0) ++ return ERROR(dictionary_corrupted); ++ /* fall through */ ++ case set_compressed: ++ if (srcSize < 5) ++ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need up to 5 for case 3 */ ++ { ++ size_t lhSize, litSize, litCSize; ++ U32 singleStream = 0; ++ U32 const lhlCode = (istart[0] >> 2) & 3; ++ U32 const lhc = ZSTD_readLE32(istart); ++ switch (lhlCode) { ++ case 0: ++ case 1: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ /* 2 - 2 - 10 - 10 */ ++ singleStream = !lhlCode; ++ lhSize = 3; ++ litSize = (lhc >> 4) & 0x3FF; ++ litCSize = (lhc >> 14) & 0x3FF; ++ break; ++ case 2: ++ /* 2 - 2 - 14 - 14 */ ++ lhSize = 4; ++ litSize = (lhc >> 4) & 0x3FFF; ++ litCSize = lhc >> 18; ++ break; ++ case 3: ++ /* 2 - 2 - 18 - 18 */ ++ lhSize = 5; ++ litSize = (lhc >> 4) & 0x3FFFF; ++ litCSize = (lhc >> 22) + (istart[4] << 10); ++ break; ++ } ++ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(corruption_detected); ++ if (litCSize + lhSize > srcSize) ++ return ERROR(corruption_detected); ++ ++ if (HUF_isError( ++ (litEncType == set_repeat) ++ ? (singleStream ? HUF_decompress1X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr) ++ : HUF_decompress4X_usingDTable(dctx->litBuffer, litSize, istart + lhSize, litCSize, dctx->HUFptr)) ++ : (singleStream ++ ? HUF_decompress1X2_DCtx_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, ++ dctx->entropy.workspace, sizeof(dctx->entropy.workspace)) ++ : HUF_decompress4X_hufOnly_wksp(dctx->entropy.hufTable, dctx->litBuffer, litSize, istart + lhSize, litCSize, ++ dctx->entropy.workspace, sizeof(dctx->entropy.workspace))))) ++ return ERROR(corruption_detected); ++ ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ dctx->litEntropy = 1; ++ if (litEncType == set_compressed) ++ dctx->HUFptr = dctx->entropy.hufTable; ++ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); ++ return litCSize + lhSize; ++ } ++ ++ case set_basic: { ++ size_t litSize, lhSize; ++ U32 const lhlCode = ((istart[0]) >> 2) & 3; ++ switch (lhlCode) { ++ case 0: ++ case 2: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ lhSize = 1; ++ litSize = istart[0] >> 3; ++ break; ++ case 1: ++ lhSize = 2; ++ litSize = ZSTD_readLE16(istart) >> 4; ++ break; ++ case 3: ++ lhSize = 3; ++ litSize = ZSTD_readLE24(istart) >> 4; ++ break; ++ } ++ ++ if (lhSize + litSize + WILDCOPY_OVERLENGTH > srcSize) { /* risk reading beyond src buffer with wildcopy */ ++ if (litSize + lhSize > srcSize) ++ return ERROR(corruption_detected); ++ memcpy(dctx->litBuffer, istart + lhSize, litSize); ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ memset(dctx->litBuffer + dctx->litSize, 0, WILDCOPY_OVERLENGTH); ++ return lhSize + litSize; ++ } ++ /* direct reference into compressed stream */ ++ dctx->litPtr = istart + lhSize; ++ dctx->litSize = litSize; ++ return lhSize + litSize; ++ } ++ ++ case set_rle: { ++ U32 const lhlCode = ((istart[0]) >> 2) & 3; ++ size_t litSize, lhSize; ++ switch (lhlCode) { ++ case 0: ++ case 2: ++ default: /* note : default is impossible, since lhlCode into [0..3] */ ++ lhSize = 1; ++ litSize = istart[0] >> 3; ++ break; ++ case 1: ++ lhSize = 2; ++ litSize = ZSTD_readLE16(istart) >> 4; ++ break; ++ case 3: ++ lhSize = 3; ++ litSize = ZSTD_readLE24(istart) >> 4; ++ if (srcSize < 4) ++ return ERROR(corruption_detected); /* srcSize >= MIN_CBLOCK_SIZE == 3; here we need lhSize+1 = 4 */ ++ break; ++ } ++ if (litSize > ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(corruption_detected); ++ memset(dctx->litBuffer, istart[lhSize], litSize + WILDCOPY_OVERLENGTH); ++ dctx->litPtr = dctx->litBuffer; ++ dctx->litSize = litSize; ++ return lhSize + 1; ++ } ++ default: ++ return ERROR(corruption_detected); /* impossible */ ++ } ++ } ++} ++ ++typedef union { ++ FSE_decode_t realData; ++ U32 alignedBy4; ++} FSE_decode_t4; ++ ++static const FSE_decode_t4 LL_defaultDTable[(1 << LL_DEFAULTNORMLOG) + 1] = { ++ {{LL_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 4}}, /* 0 : base, symbol, bits */ ++ {{16, 0, 4}}, ++ {{32, 1, 5}}, ++ {{0, 3, 5}}, ++ {{0, 4, 5}}, ++ {{0, 6, 5}}, ++ {{0, 7, 5}}, ++ {{0, 9, 5}}, ++ {{0, 10, 5}}, ++ {{0, 12, 5}}, ++ {{0, 14, 6}}, ++ {{0, 16, 5}}, ++ {{0, 18, 5}}, ++ {{0, 19, 5}}, ++ {{0, 21, 5}}, ++ {{0, 22, 5}}, ++ {{0, 24, 5}}, ++ {{32, 25, 5}}, ++ {{0, 26, 5}}, ++ {{0, 27, 6}}, ++ {{0, 29, 6}}, ++ {{0, 31, 6}}, ++ {{32, 0, 4}}, ++ {{0, 1, 4}}, ++ {{0, 2, 5}}, ++ {{32, 4, 5}}, ++ {{0, 5, 5}}, ++ {{32, 7, 5}}, ++ {{0, 8, 5}}, ++ {{32, 10, 5}}, ++ {{0, 11, 5}}, ++ {{0, 13, 6}}, ++ {{32, 16, 5}}, ++ {{0, 17, 5}}, ++ {{32, 19, 5}}, ++ {{0, 20, 5}}, ++ {{32, 22, 5}}, ++ {{0, 23, 5}}, ++ {{0, 25, 4}}, ++ {{16, 25, 4}}, ++ {{32, 26, 5}}, ++ {{0, 28, 6}}, ++ {{0, 30, 6}}, ++ {{48, 0, 4}}, ++ {{16, 1, 4}}, ++ {{32, 2, 5}}, ++ {{32, 3, 5}}, ++ {{32, 5, 5}}, ++ {{32, 6, 5}}, ++ {{32, 8, 5}}, ++ {{32, 9, 5}}, ++ {{32, 11, 5}}, ++ {{32, 12, 5}}, ++ {{0, 15, 6}}, ++ {{32, 17, 5}}, ++ {{32, 18, 5}}, ++ {{32, 20, 5}}, ++ {{32, 21, 5}}, ++ {{32, 23, 5}}, ++ {{32, 24, 5}}, ++ {{0, 35, 6}}, ++ {{0, 34, 6}}, ++ {{0, 33, 6}}, ++ {{0, 32, 6}}, ++}; /* LL_defaultDTable */ ++ ++static const FSE_decode_t4 ML_defaultDTable[(1 << ML_DEFAULTNORMLOG) + 1] = { ++ {{ML_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 6}}, /* 0 : base, symbol, bits */ ++ {{0, 1, 4}}, ++ {{32, 2, 5}}, ++ {{0, 3, 5}}, ++ {{0, 5, 5}}, ++ {{0, 6, 5}}, ++ {{0, 8, 5}}, ++ {{0, 10, 6}}, ++ {{0, 13, 6}}, ++ {{0, 16, 6}}, ++ {{0, 19, 6}}, ++ {{0, 22, 6}}, ++ {{0, 25, 6}}, ++ {{0, 28, 6}}, ++ {{0, 31, 6}}, ++ {{0, 33, 6}}, ++ {{0, 35, 6}}, ++ {{0, 37, 6}}, ++ {{0, 39, 6}}, ++ {{0, 41, 6}}, ++ {{0, 43, 6}}, ++ {{0, 45, 6}}, ++ {{16, 1, 4}}, ++ {{0, 2, 4}}, ++ {{32, 3, 5}}, ++ {{0, 4, 5}}, ++ {{32, 6, 5}}, ++ {{0, 7, 5}}, ++ {{0, 9, 6}}, ++ {{0, 12, 6}}, ++ {{0, 15, 6}}, ++ {{0, 18, 6}}, ++ {{0, 21, 6}}, ++ {{0, 24, 6}}, ++ {{0, 27, 6}}, ++ {{0, 30, 6}}, ++ {{0, 32, 6}}, ++ {{0, 34, 6}}, ++ {{0, 36, 6}}, ++ {{0, 38, 6}}, ++ {{0, 40, 6}}, ++ {{0, 42, 6}}, ++ {{0, 44, 6}}, ++ {{32, 1, 4}}, ++ {{48, 1, 4}}, ++ {{16, 2, 4}}, ++ {{32, 4, 5}}, ++ {{32, 5, 5}}, ++ {{32, 7, 5}}, ++ {{32, 8, 5}}, ++ {{0, 11, 6}}, ++ {{0, 14, 6}}, ++ {{0, 17, 6}}, ++ {{0, 20, 6}}, ++ {{0, 23, 6}}, ++ {{0, 26, 6}}, ++ {{0, 29, 6}}, ++ {{0, 52, 6}}, ++ {{0, 51, 6}}, ++ {{0, 50, 6}}, ++ {{0, 49, 6}}, ++ {{0, 48, 6}}, ++ {{0, 47, 6}}, ++ {{0, 46, 6}}, ++}; /* ML_defaultDTable */ ++ ++static const FSE_decode_t4 OF_defaultDTable[(1 << OF_DEFAULTNORMLOG) + 1] = { ++ {{OF_DEFAULTNORMLOG, 1, 1}}, /* header : tableLog, fastMode, fastMode */ ++ {{0, 0, 5}}, /* 0 : base, symbol, bits */ ++ {{0, 6, 4}}, ++ {{0, 9, 5}}, ++ {{0, 15, 5}}, ++ {{0, 21, 5}}, ++ {{0, 3, 5}}, ++ {{0, 7, 4}}, ++ {{0, 12, 5}}, ++ {{0, 18, 5}}, ++ {{0, 23, 5}}, ++ {{0, 5, 5}}, ++ {{0, 8, 4}}, ++ {{0, 14, 5}}, ++ {{0, 20, 5}}, ++ {{0, 2, 5}}, ++ {{16, 7, 4}}, ++ {{0, 11, 5}}, ++ {{0, 17, 5}}, ++ {{0, 22, 5}}, ++ {{0, 4, 5}}, ++ {{16, 8, 4}}, ++ {{0, 13, 5}}, ++ {{0, 19, 5}}, ++ {{0, 1, 5}}, ++ {{16, 6, 4}}, ++ {{0, 10, 5}}, ++ {{0, 16, 5}}, ++ {{0, 28, 5}}, ++ {{0, 27, 5}}, ++ {{0, 26, 5}}, ++ {{0, 25, 5}}, ++ {{0, 24, 5}}, ++}; /* OF_defaultDTable */ ++ ++/*! ZSTD_buildSeqTable() : ++ @return : nb bytes read from src, ++ or an error code if it fails, testable with ZSTD_isError() ++*/ ++static size_t INIT ZSTD_buildSeqTable(FSE_DTable *DTableSpace, const FSE_DTable **DTablePtr, symbolEncodingType_e type, U32 max, U32 maxLog, const void *src, ++ size_t srcSize, const FSE_decode_t4 *defaultTable, U32 flagRepeatTable, void *workspace, size_t workspaceSize) ++{ ++ const void *const tmpPtr = defaultTable; /* bypass strict aliasing */ ++ switch (type) { ++ case set_rle: ++ if (!srcSize) ++ return ERROR(srcSize_wrong); ++ if ((*(const BYTE *)src) > max) ++ return ERROR(corruption_detected); ++ FSE_buildDTable_rle(DTableSpace, *(const BYTE *)src); ++ *DTablePtr = DTableSpace; ++ return 1; ++ case set_basic: *DTablePtr = (const FSE_DTable *)tmpPtr; return 0; ++ case set_repeat: ++ if (!flagRepeatTable) ++ return ERROR(corruption_detected); ++ return 0; ++ default: /* impossible */ ++ case set_compressed: { ++ U32 tableLog; ++ S16 *norm = (S16 *)workspace; ++ size_t const spaceUsed32 = ALIGN(sizeof(S16) * (MaxSeq + 1), sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(GENERIC); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ { ++ size_t const headerSize = FSE_readNCount(norm, &max, &tableLog, src, srcSize); ++ if (FSE_isError(headerSize)) ++ return ERROR(corruption_detected); ++ if (tableLog > maxLog) ++ return ERROR(corruption_detected); ++ FSE_buildDTable_wksp(DTableSpace, norm, max, tableLog, workspace, workspaceSize); ++ *DTablePtr = DTableSpace; ++ return headerSize; ++ } ++ } ++ } ++} ++ ++size_t INIT ZSTD_decodeSeqHeaders(ZSTD_DCtx *dctx, int *nbSeqPtr, const void *src, size_t srcSize) ++{ ++ const BYTE *const istart = (const BYTE *const)src; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *ip = istart; ++ ++ /* check */ ++ if (srcSize < MIN_SEQUENCES_SIZE) ++ return ERROR(srcSize_wrong); ++ ++ /* SeqHead */ ++ { ++ int nbSeq = *ip++; ++ if (!nbSeq) { ++ *nbSeqPtr = 0; ++ return 1; ++ } ++ if (nbSeq > 0x7F) { ++ if (nbSeq == 0xFF) { ++ if (ip + 2 > iend) ++ return ERROR(srcSize_wrong); ++ nbSeq = ZSTD_readLE16(ip) + LONGNBSEQ, ip += 2; ++ } else { ++ if (ip >= iend) ++ return ERROR(srcSize_wrong); ++ nbSeq = ((nbSeq - 0x80) << 8) + *ip++; ++ } ++ } ++ *nbSeqPtr = nbSeq; ++ } ++ ++ /* FSE table descriptors */ ++ if (ip + 4 > iend) ++ return ERROR(srcSize_wrong); /* minimum possible size */ ++ { ++ symbolEncodingType_e const LLtype = (symbolEncodingType_e)(*ip >> 6); ++ symbolEncodingType_e const OFtype = (symbolEncodingType_e)((*ip >> 4) & 3); ++ symbolEncodingType_e const MLtype = (symbolEncodingType_e)((*ip >> 2) & 3); ++ ip++; ++ ++ /* Build DTables */ ++ { ++ size_t const llhSize = ZSTD_buildSeqTable(dctx->entropy.LLTable, &dctx->LLTptr, LLtype, MaxLL, LLFSELog, ip, iend - ip, ++ LL_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(llhSize)) ++ return ERROR(corruption_detected); ++ ip += llhSize; ++ } ++ { ++ size_t const ofhSize = ZSTD_buildSeqTable(dctx->entropy.OFTable, &dctx->OFTptr, OFtype, MaxOff, OffFSELog, ip, iend - ip, ++ OF_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(ofhSize)) ++ return ERROR(corruption_detected); ++ ip += ofhSize; ++ } ++ { ++ size_t const mlhSize = ZSTD_buildSeqTable(dctx->entropy.MLTable, &dctx->MLTptr, MLtype, MaxML, MLFSELog, ip, iend - ip, ++ ML_defaultDTable, dctx->fseEntropy, dctx->entropy.workspace, sizeof(dctx->entropy.workspace)); ++ if (ZSTD_isError(mlhSize)) ++ return ERROR(corruption_detected); ++ ip += mlhSize; ++ } ++ } ++ ++ return ip - istart; ++} ++ ++typedef struct { ++ size_t litLength; ++ size_t matchLength; ++ size_t offset; ++ const BYTE *match; ++} seq_t; ++ ++typedef struct { ++ BIT_DStream_t DStream; ++ FSE_DState_t stateLL; ++ FSE_DState_t stateOffb; ++ FSE_DState_t stateML; ++ size_t prevOffset[ZSTD_REP_NUM]; ++ const BYTE *base; ++ size_t pos; ++ uPtrDiff gotoDict; ++} seqState_t; ++ ++FORCE_NOINLINE ++size_t INIT ZSTD_execSequenceLast7(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = oLitEnd - sequence.offset; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd <= oend_w) ++ return ERROR(GENERIC); /* Precondition */ ++ ++ /* copy literals */ ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, *litPtr, oend_w - op); ++ *litPtr += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oLitEnd) ++ *op++ = *(*litPtr)++; ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ match = dictEnd - (base - match); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ } ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ return sequenceLength; ++} ++ ++static seq_t INIT ZSTD_decodeSequence(seqState_t *seqState) ++{ ++ seq_t seq; ++ ++ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); ++ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); ++ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ ++ ++ U32 const llBits = LL_bits[llCode]; ++ U32 const mlBits = ML_bits[mlCode]; ++ U32 const ofBits = ofCode; ++ U32 const totalBits = llBits + mlBits + ofBits; ++ ++ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, ++ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; ++ ++ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ++ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, ++ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; ++ ++ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, ++ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, ++ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; ++ ++ /* sequence */ ++ { ++ size_t offset; ++ if (!ofCode) ++ offset = 0; ++ else { ++ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); ++ } ++ ++ if (ofCode <= 1) { ++ offset += (llCode == 0); ++ if (offset) { ++ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; ++ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ ++ if (offset != 1) ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset = temp; ++ } else { ++ offset = seqState->prevOffset[0]; ++ } ++ } else { ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset; ++ } ++ seq.offset = offset; ++ } ++ ++ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() && (mlBits + llBits > 24)) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ /* ANS state update */ ++ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ ++ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ++ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ ++ ++ seq.match = NULL; ++ ++ return seq; ++} ++ ++FORCE_INLINE ++size_t ZSTD_execSequence(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = oLitEnd - sequence.offset; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd > oend_w) ++ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); ++ ++ /* copy Literals */ ++ ZSTD_copy8(op, *litPtr); ++ if (sequence.litLength > 8) ++ ZSTD_wildcopy(op + 8, (*litPtr) + 8, ++ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ ++ op = oLitEnd; ++ *litPtr = iLitEnd; /* update for next sequence */ ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ match = dictEnd + (match - base); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ if (op > oend_w || sequence.matchLength < MINMATCH) { ++ U32 i; ++ for (i = 0; i < sequence.matchLength; ++i) ++ op[i] = match[i]; ++ return sequenceLength; ++ } ++ } ++ } ++ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ ++ ++ /* match within prefix */ ++ if (sequence.offset < 8) { ++ /* close range match, overlap */ ++ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ ++ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ ++ int const sub2 = dec64table[sequence.offset]; ++ op[0] = match[0]; ++ op[1] = match[1]; ++ op[2] = match[2]; ++ op[3] = match[3]; ++ match += dec32table[sequence.offset]; ++ ZSTD_copy4(op + 4, match); ++ match -= sub2; ++ } else { ++ ZSTD_copy8(op, match); ++ } ++ op += 8; ++ match += 8; ++ ++ if (oMatchEnd > oend - (16 - MINMATCH)) { ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, match, oend_w - op); ++ match += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ } else { ++ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ ++ } ++ return sequenceLength; ++} ++ ++static size_t INIT ZSTD_decompressSequences(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) ++{ ++ const BYTE *ip = (const BYTE *)seqStart; ++ const BYTE *const iend = ip + seqSize; ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + maxDstSize; ++ BYTE *op = ostart; ++ const BYTE *litPtr = dctx->litPtr; ++ const BYTE *const litEnd = litPtr + dctx->litSize; ++ const BYTE *const base = (const BYTE *)(dctx->base); ++ const BYTE *const vBase = (const BYTE *)(dctx->vBase); ++ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); ++ int nbSeq; ++ ++ /* Build Decoding Tables */ ++ { ++ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); ++ if (ZSTD_isError(seqHSize)) ++ return seqHSize; ++ ip += seqHSize; ++ } ++ ++ /* Regen sequences */ ++ if (nbSeq) { ++ seqState_t seqState; ++ dctx->fseEntropy = 1; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ seqState.prevOffset[i] = dctx->entropy.rep[i]; ++ } ++ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); ++ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ++ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ++ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); ++ ++ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && nbSeq;) { ++ nbSeq--; ++ { ++ seq_t const sequence = ZSTD_decodeSequence(&seqState); ++ size_t const oneSeqSize = ZSTD_execSequence(op, oend, sequence, &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ op += oneSeqSize; ++ } ++ } ++ ++ /* check if reached exact end */ ++ if (nbSeq) ++ return ERROR(corruption_detected); ++ /* save reps for next block */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); ++ } ++ } ++ ++ /* last literal segment */ ++ { ++ size_t const lastLLSize = litEnd - litPtr; ++ if (lastLLSize > (size_t)(oend - op)) ++ return ERROR(dstSize_tooSmall); ++ memcpy(op, litPtr, lastLLSize); ++ op += lastLLSize; ++ } ++ ++ return op - ostart; ++} ++ ++FORCE_INLINE seq_t INIT ZSTD_decodeSequenceLong_generic(seqState_t *seqState, int const longOffsets) ++{ ++ seq_t seq; ++ ++ U32 const llCode = FSE_peekSymbol(&seqState->stateLL); ++ U32 const mlCode = FSE_peekSymbol(&seqState->stateML); ++ U32 const ofCode = FSE_peekSymbol(&seqState->stateOffb); /* <= maxOff, by table construction */ ++ ++ U32 const llBits = LL_bits[llCode]; ++ U32 const mlBits = ML_bits[mlCode]; ++ U32 const ofBits = ofCode; ++ U32 const totalBits = llBits + mlBits + ofBits; ++ ++ static const U32 LL_base[MaxLL + 1] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, ++ 20, 22, 24, 28, 32, 40, 48, 64, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000, 0x10000}; ++ ++ static const U32 ML_base[MaxML + 1] = {3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ++ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 37, 39, 41, ++ 43, 47, 51, 59, 67, 83, 99, 0x83, 0x103, 0x203, 0x403, 0x803, 0x1003, 0x2003, 0x4003, 0x8003, 0x10003}; ++ ++ static const U32 OF_base[MaxOff + 1] = {0, 1, 1, 5, 0xD, 0x1D, 0x3D, 0x7D, 0xFD, 0x1FD, ++ 0x3FD, 0x7FD, 0xFFD, 0x1FFD, 0x3FFD, 0x7FFD, 0xFFFD, 0x1FFFD, 0x3FFFD, 0x7FFFD, ++ 0xFFFFD, 0x1FFFFD, 0x3FFFFD, 0x7FFFFD, 0xFFFFFD, 0x1FFFFFD, 0x3FFFFFD, 0x7FFFFFD, 0xFFFFFFD}; ++ ++ /* sequence */ ++ { ++ size_t offset; ++ if (!ofCode) ++ offset = 0; ++ else { ++ if (longOffsets) { ++ int const extraBits = ofBits - MIN(ofBits, STREAM_ACCUMULATOR_MIN); ++ offset = OF_base[ofCode] + (BIT_readBitsFast(&seqState->DStream, ofBits - extraBits) << extraBits); ++ if (ZSTD_32bits() || extraBits) ++ BIT_reloadDStream(&seqState->DStream); ++ if (extraBits) ++ offset += BIT_readBitsFast(&seqState->DStream, extraBits); ++ } else { ++ offset = OF_base[ofCode] + BIT_readBitsFast(&seqState->DStream, ofBits); /* <= (ZSTD_WINDOWLOG_MAX-1) bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); ++ } ++ } ++ ++ if (ofCode <= 1) { ++ offset += (llCode == 0); ++ if (offset) { ++ size_t temp = (offset == 3) ? seqState->prevOffset[0] - 1 : seqState->prevOffset[offset]; ++ temp += !temp; /* 0 is not valid; input is corrupted; force offset to 1 */ ++ if (offset != 1) ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset = temp; ++ } else { ++ offset = seqState->prevOffset[0]; ++ } ++ } else { ++ seqState->prevOffset[2] = seqState->prevOffset[1]; ++ seqState->prevOffset[1] = seqState->prevOffset[0]; ++ seqState->prevOffset[0] = offset; ++ } ++ seq.offset = offset; ++ } ++ ++ seq.matchLength = ML_base[mlCode] + ((mlCode > 31) ? BIT_readBitsFast(&seqState->DStream, mlBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() && (mlBits + llBits > 24)) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ seq.litLength = LL_base[llCode] + ((llCode > 15) ? BIT_readBitsFast(&seqState->DStream, llBits) : 0); /* <= 16 bits */ ++ if (ZSTD_32bits() || (totalBits > 64 - 7 - (LLFSELog + MLFSELog + OffFSELog))) ++ BIT_reloadDStream(&seqState->DStream); ++ ++ { ++ size_t const pos = seqState->pos + seq.litLength; ++ seq.match = seqState->base + pos - seq.offset; /* single memory segment */ ++ if (seq.offset > pos) ++ seq.match += seqState->gotoDict; /* separate memory segment */ ++ seqState->pos = pos + seq.matchLength; ++ } ++ ++ /* ANS state update */ ++ FSE_updateState(&seqState->stateLL, &seqState->DStream); /* <= 9 bits */ ++ FSE_updateState(&seqState->stateML, &seqState->DStream); /* <= 9 bits */ ++ if (ZSTD_32bits()) ++ BIT_reloadDStream(&seqState->DStream); /* <= 18 bits */ ++ FSE_updateState(&seqState->stateOffb, &seqState->DStream); /* <= 8 bits */ ++ ++ return seq; ++} ++ ++static seq_t INIT ZSTD_decodeSequenceLong(seqState_t *seqState, unsigned const windowSize) ++{ ++ if (ZSTD_highbit32(windowSize) > STREAM_ACCUMULATOR_MIN) { ++ return ZSTD_decodeSequenceLong_generic(seqState, 1); ++ } else { ++ return ZSTD_decodeSequenceLong_generic(seqState, 0); ++ } ++} ++ ++FORCE_INLINE ++size_t ZSTD_execSequenceLong(BYTE *op, BYTE *const oend, seq_t sequence, const BYTE **litPtr, const BYTE *const litLimit, const BYTE *const base, ++ const BYTE *const vBase, const BYTE *const dictEnd) ++{ ++ BYTE *const oLitEnd = op + sequence.litLength; ++ size_t const sequenceLength = sequence.litLength + sequence.matchLength; ++ BYTE *const oMatchEnd = op + sequenceLength; /* risk : address space overflow (32-bits) */ ++ BYTE *const oend_w = oend - WILDCOPY_OVERLENGTH; ++ const BYTE *const iLitEnd = *litPtr + sequence.litLength; ++ const BYTE *match = sequence.match; ++ ++ /* check */ ++ if (oMatchEnd > oend) ++ return ERROR(dstSize_tooSmall); /* last match must start at a minimum distance of WILDCOPY_OVERLENGTH from oend */ ++ if (iLitEnd > litLimit) ++ return ERROR(corruption_detected); /* over-read beyond lit buffer */ ++ if (oLitEnd > oend_w) ++ return ZSTD_execSequenceLast7(op, oend, sequence, litPtr, litLimit, base, vBase, dictEnd); ++ ++ /* copy Literals */ ++ ZSTD_copy8(op, *litPtr); ++ if (sequence.litLength > 8) ++ ZSTD_wildcopy(op + 8, (*litPtr) + 8, ++ sequence.litLength - 8); /* note : since oLitEnd <= oend-WILDCOPY_OVERLENGTH, no risk of overwrite beyond oend */ ++ op = oLitEnd; ++ *litPtr = iLitEnd; /* update for next sequence */ ++ ++ /* copy Match */ ++ if (sequence.offset > (size_t)(oLitEnd - base)) { ++ /* offset beyond prefix */ ++ if (sequence.offset > (size_t)(oLitEnd - vBase)) ++ return ERROR(corruption_detected); ++ if (match + sequence.matchLength <= dictEnd) { ++ memmove(oLitEnd, match, sequence.matchLength); ++ return sequenceLength; ++ } ++ /* span extDict & currPrefixSegment */ ++ { ++ size_t const length1 = dictEnd - match; ++ memmove(oLitEnd, match, length1); ++ op = oLitEnd + length1; ++ sequence.matchLength -= length1; ++ match = base; ++ if (op > oend_w || sequence.matchLength < MINMATCH) { ++ U32 i; ++ for (i = 0; i < sequence.matchLength; ++i) ++ op[i] = match[i]; ++ return sequenceLength; ++ } ++ } ++ } ++ /* Requirement: op <= oend_w && sequence.matchLength >= MINMATCH */ ++ ++ /* match within prefix */ ++ if (sequence.offset < 8) { ++ /* close range match, overlap */ ++ static const U32 dec32table[] = {0, 1, 2, 1, 4, 4, 4, 4}; /* added */ ++ static const int dec64table[] = {8, 8, 8, 7, 8, 9, 10, 11}; /* subtracted */ ++ int const sub2 = dec64table[sequence.offset]; ++ op[0] = match[0]; ++ op[1] = match[1]; ++ op[2] = match[2]; ++ op[3] = match[3]; ++ match += dec32table[sequence.offset]; ++ ZSTD_copy4(op + 4, match); ++ match -= sub2; ++ } else { ++ ZSTD_copy8(op, match); ++ } ++ op += 8; ++ match += 8; ++ ++ if (oMatchEnd > oend - (16 - MINMATCH)) { ++ if (op < oend_w) { ++ ZSTD_wildcopy(op, match, oend_w - op); ++ match += oend_w - op; ++ op = oend_w; ++ } ++ while (op < oMatchEnd) ++ *op++ = *match++; ++ } else { ++ ZSTD_wildcopy(op, match, (ptrdiff_t)sequence.matchLength - 8); /* works even if matchLength < 8 */ ++ } ++ return sequenceLength; ++} ++ ++static size_t INIT ZSTD_decompressSequencesLong(ZSTD_DCtx *dctx, void *dst, size_t maxDstSize, const void *seqStart, size_t seqSize) ++{ ++ const BYTE *ip = (const BYTE *)seqStart; ++ const BYTE *const iend = ip + seqSize; ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + maxDstSize; ++ BYTE *op = ostart; ++ const BYTE *litPtr = dctx->litPtr; ++ const BYTE *const litEnd = litPtr + dctx->litSize; ++ const BYTE *const base = (const BYTE *)(dctx->base); ++ const BYTE *const vBase = (const BYTE *)(dctx->vBase); ++ const BYTE *const dictEnd = (const BYTE *)(dctx->dictEnd); ++ unsigned const windowSize = dctx->fParams.windowSize; ++ int nbSeq; ++ ++ /* Build Decoding Tables */ ++ { ++ size_t const seqHSize = ZSTD_decodeSeqHeaders(dctx, &nbSeq, ip, seqSize); ++ if (ZSTD_isError(seqHSize)) ++ return seqHSize; ++ ip += seqHSize; ++ } ++ ++ /* Regen sequences */ ++ if (nbSeq) { ++#define STORED_SEQS 4 ++#define STOSEQ_MASK (STORED_SEQS - 1) ++#define ADVANCED_SEQS 4 ++ seq_t *sequences = (seq_t *)dctx->entropy.workspace; ++ int const seqAdvance = MIN(nbSeq, ADVANCED_SEQS); ++ seqState_t seqState; ++ int seqNb; ++ ZSTD_STATIC_ASSERT(sizeof(dctx->entropy.workspace) >= sizeof(seq_t) * STORED_SEQS); ++ dctx->fseEntropy = 1; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ seqState.prevOffset[i] = dctx->entropy.rep[i]; ++ } ++ seqState.base = base; ++ seqState.pos = (size_t)(op - base); ++ seqState.gotoDict = (uPtrDiff)dictEnd - (uPtrDiff)base; /* cast to avoid undefined behaviour */ ++ CHECK_E(BIT_initDStream(&seqState.DStream, ip, iend - ip), corruption_detected); ++ FSE_initDState(&seqState.stateLL, &seqState.DStream, dctx->LLTptr); ++ FSE_initDState(&seqState.stateOffb, &seqState.DStream, dctx->OFTptr); ++ FSE_initDState(&seqState.stateML, &seqState.DStream, dctx->MLTptr); ++ ++ /* prepare in advance */ ++ for (seqNb = 0; (BIT_reloadDStream(&seqState.DStream) <= BIT_DStream_completed) && seqNb < seqAdvance; seqNb++) { ++ sequences[seqNb] = ZSTD_decodeSequenceLong(&seqState, windowSize); ++ } ++ if (seqNb < seqAdvance) ++ return ERROR(corruption_detected); ++ ++ /* decode and decompress */ ++ for (; (BIT_reloadDStream(&(seqState.DStream)) <= BIT_DStream_completed) && seqNb < nbSeq; seqNb++) { ++ seq_t const sequence = ZSTD_decodeSequenceLong(&seqState, windowSize); ++ size_t const oneSeqSize = ++ ZSTD_execSequenceLong(op, oend, sequences[(seqNb - ADVANCED_SEQS) & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ ZSTD_PREFETCH(sequence.match); ++ sequences[seqNb & STOSEQ_MASK] = sequence; ++ op += oneSeqSize; ++ } ++ if (seqNb < nbSeq) ++ return ERROR(corruption_detected); ++ ++ /* finish queue */ ++ seqNb -= seqAdvance; ++ for (; seqNb < nbSeq; seqNb++) { ++ size_t const oneSeqSize = ZSTD_execSequenceLong(op, oend, sequences[seqNb & STOSEQ_MASK], &litPtr, litEnd, base, vBase, dictEnd); ++ if (ZSTD_isError(oneSeqSize)) ++ return oneSeqSize; ++ op += oneSeqSize; ++ } ++ ++ /* save reps for next block */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ dctx->entropy.rep[i] = (U32)(seqState.prevOffset[i]); ++ } ++ } ++ ++ /* last literal segment */ ++ { ++ size_t const lastLLSize = litEnd - litPtr; ++ if (lastLLSize > (size_t)(oend - op)) ++ return ERROR(dstSize_tooSmall); ++ memcpy(op, litPtr, lastLLSize); ++ op += lastLLSize; ++ } ++ ++ return op - ostart; ++} ++ ++static size_t INIT ZSTD_decompressBlock_internal(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ /* blockType == blockCompressed */ ++ const BYTE *ip = (const BYTE *)src; ++ ++ if (srcSize >= ZSTD_BLOCKSIZE_ABSOLUTEMAX) ++ return ERROR(srcSize_wrong); ++ ++ /* Decode literals section */ ++ { ++ size_t const litCSize = ZSTD_decodeLiteralsBlock(dctx, src, srcSize); ++ if (ZSTD_isError(litCSize)) ++ return litCSize; ++ ip += litCSize; ++ srcSize -= litCSize; ++ } ++ if (sizeof(size_t) > 4) /* do not enable prefetching on 32-bits x86, as it's performance detrimental */ ++ /* likely because of register pressure */ ++ /* if that's the correct cause, then 32-bits ARM should be affected differently */ ++ /* it would be good to test this on ARM real hardware, to see if prefetch version improves speed */ ++ if (dctx->fParams.windowSize > (1 << 23)) ++ return ZSTD_decompressSequencesLong(dctx, dst, dstCapacity, ip, srcSize); ++ return ZSTD_decompressSequences(dctx, dst, dstCapacity, ip, srcSize); ++} ++ ++static void INIT ZSTD_checkContinuity(ZSTD_DCtx *dctx, const void *dst) ++{ ++ if (dst != dctx->previousDstEnd) { /* not contiguous */ ++ dctx->dictEnd = dctx->previousDstEnd; ++ dctx->vBase = (const char *)dst - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); ++ dctx->base = dst; ++ dctx->previousDstEnd = dst; ++ } ++} ++ ++size_t INIT ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ size_t dSize; ++ ZSTD_checkContinuity(dctx, dst); ++ dSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); ++ dctx->previousDstEnd = (char *)dst + dSize; ++ return dSize; ++} ++ ++/** ZSTD_insertBlock() : ++ insert `src` block into `dctx` history. Useful to track uncompressed blocks. */ ++size_t INIT ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, size_t blockSize) ++{ ++ ZSTD_checkContinuity(dctx, blockStart); ++ dctx->previousDstEnd = (const char *)blockStart + blockSize; ++ return blockSize; ++} ++ ++size_t INIT ZSTD_generateNxBytes(void *dst, size_t dstCapacity, BYTE byte, size_t length) ++{ ++ if (length > dstCapacity) ++ return ERROR(dstSize_tooSmall); ++ memset(dst, byte, length); ++ return length; ++} ++ ++/** ZSTD_findFrameCompressedSize() : ++ * compatible with legacy mode ++ * `src` must point to the start of a ZSTD frame, ZSTD legacy frame, or skippable frame ++ * `srcSize` must be at least as large as the frame contained ++ * @return : the compressed size of the frame starting at `src` */ ++size_t INIT ZSTD_findFrameCompressedSize(const void *src, size_t srcSize) ++{ ++ if (srcSize >= ZSTD_skippableHeaderSize && (ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ return ZSTD_skippableHeaderSize + ZSTD_readLE32((const BYTE *)src + 4); ++ } else { ++ const BYTE *ip = (const BYTE *)src; ++ const BYTE *const ipstart = ip; ++ size_t remainingSize = srcSize; ++ ZSTD_frameParams fParams; ++ ++ size_t const headerSize = ZSTD_frameHeaderSize(ip, remainingSize); ++ if (ZSTD_isError(headerSize)) ++ return headerSize; ++ ++ /* Frame Header */ ++ { ++ size_t const ret = ZSTD_getFrameParams(&fParams, ip, remainingSize); ++ if (ZSTD_isError(ret)) ++ return ret; ++ if (ret > 0) ++ return ERROR(srcSize_wrong); ++ } ++ ++ ip += headerSize; ++ remainingSize -= headerSize; ++ ++ /* Loop on each block */ ++ while (1) { ++ blockProperties_t blockProperties; ++ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ ++ if (ZSTD_blockHeaderSize + cBlockSize > remainingSize) ++ return ERROR(srcSize_wrong); ++ ++ ip += ZSTD_blockHeaderSize + cBlockSize; ++ remainingSize -= ZSTD_blockHeaderSize + cBlockSize; ++ ++ if (blockProperties.lastBlock) ++ break; ++ } ++ ++ if (fParams.checksumFlag) { /* Frame content checksum */ ++ if (remainingSize < 4) ++ return ERROR(srcSize_wrong); ++ ip += 4; ++ remainingSize -= 4; ++ } ++ ++ return ip - ipstart; ++ } ++} ++ ++/*! ZSTD_decompressFrame() : ++* @dctx must be properly initialized */ ++static size_t INIT ZSTD_decompressFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void **srcPtr, size_t *srcSizePtr) ++{ ++ const BYTE *ip = (const BYTE *)(*srcPtr); ++ BYTE *const ostart = (BYTE * const)dst; ++ BYTE *const oend = ostart + dstCapacity; ++ BYTE *op = ostart; ++ size_t remainingSize = *srcSizePtr; ++ ++ /* check */ ++ if (remainingSize < ZSTD_frameHeaderSize_min + ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ ++ /* Frame Header */ ++ { ++ size_t const frameHeaderSize = ZSTD_frameHeaderSize(ip, ZSTD_frameHeaderSize_prefix); ++ if (ZSTD_isError(frameHeaderSize)) ++ return frameHeaderSize; ++ if (remainingSize < frameHeaderSize + ZSTD_blockHeaderSize) ++ return ERROR(srcSize_wrong); ++ CHECK_F(ZSTD_decodeFrameHeader(dctx, ip, frameHeaderSize)); ++ ip += frameHeaderSize; ++ remainingSize -= frameHeaderSize; ++ } ++ ++ /* Loop on each block */ ++ while (1) { ++ size_t decodedSize; ++ blockProperties_t blockProperties; ++ size_t const cBlockSize = ZSTD_getcBlockSize(ip, remainingSize, &blockProperties); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ ++ ip += ZSTD_blockHeaderSize; ++ remainingSize -= ZSTD_blockHeaderSize; ++ if (cBlockSize > remainingSize) ++ return ERROR(srcSize_wrong); ++ ++ switch (blockProperties.blockType) { ++ case bt_compressed: decodedSize = ZSTD_decompressBlock_internal(dctx, op, oend - op, ip, cBlockSize); break; ++ case bt_raw: decodedSize = ZSTD_copyRawBlock(op, oend - op, ip, cBlockSize); break; ++ case bt_rle: decodedSize = ZSTD_generateNxBytes(op, oend - op, *ip, blockProperties.origSize); break; ++ case bt_reserved: ++ default: return ERROR(corruption_detected); ++ } ++ ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ if (dctx->fParams.checksumFlag) ++ xxh64_update(&dctx->xxhState, op, decodedSize); ++ op += decodedSize; ++ ip += cBlockSize; ++ remainingSize -= cBlockSize; ++ if (blockProperties.lastBlock) ++ break; ++ } ++ ++ if (dctx->fParams.checksumFlag) { /* Frame content checksum verification */ ++ U32 const checkCalc = (U32)xxh64_digest(&dctx->xxhState); ++ U32 checkRead; ++ if (remainingSize < 4) ++ return ERROR(checksum_wrong); ++ checkRead = ZSTD_readLE32(ip); ++ if (checkRead != checkCalc) ++ return ERROR(checksum_wrong); ++ ip += 4; ++ remainingSize -= 4; ++ } ++ ++ /* Allow caller to get size read */ ++ *srcPtr = ip; ++ *srcSizePtr = remainingSize; ++ return op - ostart; ++} ++ ++static const void INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict); ++static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict); ++ ++static size_t INIT ZSTD_decompressMultiFrame(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize, ++ const ZSTD_DDict *ddict) ++{ ++ void *const dststart = dst; ++ ++ if (ddict) { ++ if (dict) { ++ /* programmer error, these two cases should be mutually exclusive */ ++ return ERROR(GENERIC); ++ } ++ ++ dict = ZSTD_DDictDictContent(ddict); ++ dictSize = ZSTD_DDictDictSize(ddict); ++ } ++ ++ while (srcSize >= ZSTD_frameHeaderSize_prefix) { ++ U32 magicNumber; ++ ++ magicNumber = ZSTD_readLE32(src); ++ if (magicNumber != ZSTD_MAGICNUMBER) { ++ if ((magicNumber & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { ++ size_t skippableSize; ++ if (srcSize < ZSTD_skippableHeaderSize) ++ return ERROR(srcSize_wrong); ++ skippableSize = ZSTD_readLE32((const BYTE *)src + 4) + ZSTD_skippableHeaderSize; ++ if (srcSize < skippableSize) { ++ return ERROR(srcSize_wrong); ++ } ++ ++ src = (const BYTE *)src + skippableSize; ++ srcSize -= skippableSize; ++ continue; ++ } else { ++ return ERROR(prefix_unknown); ++ } ++ } ++ ++ if (ddict) { ++ /* we were called from ZSTD_decompress_usingDDict */ ++ ZSTD_refDDict(dctx, ddict); ++ } else { ++ /* this will initialize correctly with no dict if dict == NULL, so ++ * use this in all cases but ddict */ ++ CHECK_F(ZSTD_decompressBegin_usingDict(dctx, dict, dictSize)); ++ } ++ ZSTD_checkContinuity(dctx, dst); ++ ++ { ++ const size_t res = ZSTD_decompressFrame(dctx, dst, dstCapacity, &src, &srcSize); ++ if (ZSTD_isError(res)) ++ return res; ++ /* don't need to bounds check this, ZSTD_decompressFrame will have ++ * already */ ++ dst = (BYTE *)dst + res; ++ dstCapacity -= res; ++ } ++ } ++ ++ if (srcSize) ++ return ERROR(srcSize_wrong); /* input not entirely consumed */ ++ ++ return (BYTE *)dst - (BYTE *)dststart; ++} ++ ++size_t INIT ZSTD_decompress_usingDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const void *dict, size_t dictSize) ++{ ++ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, dict, dictSize, NULL); ++} ++ ++size_t INIT ZSTD_decompressDCtx(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ return ZSTD_decompress_usingDict(dctx, dst, dstCapacity, src, srcSize, NULL, 0); ++} ++ ++/*-************************************** ++* Advanced Streaming Decompression API ++* Bufferless and synchronous ++****************************************/ ++size_t INIT ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx) { return dctx->expected; } ++ ++ZSTD_nextInputType_e INIT ZSTD_nextInputType(ZSTD_DCtx *dctx) ++{ ++ switch (dctx->stage) { ++ default: /* should not happen */ ++ case ZSTDds_getFrameHeaderSize: ++ case ZSTDds_decodeFrameHeader: return ZSTDnit_frameHeader; ++ case ZSTDds_decodeBlockHeader: return ZSTDnit_blockHeader; ++ case ZSTDds_decompressBlock: return ZSTDnit_block; ++ case ZSTDds_decompressLastBlock: return ZSTDnit_lastBlock; ++ case ZSTDds_checkChecksum: return ZSTDnit_checksum; ++ case ZSTDds_decodeSkippableHeader: ++ case ZSTDds_skipFrame: return ZSTDnit_skippableFrame; ++ } ++} ++ ++int INIT ZSTD_isSkipFrame(ZSTD_DCtx *dctx) { return dctx->stage == ZSTDds_skipFrame; } /* for zbuff */ ++ ++/** ZSTD_decompressContinue() : ++* @return : nb of bytes generated into `dst` (necessarily <= `dstCapacity) ++* or an error code, which can be tested using ZSTD_isError() */ ++size_t INIT ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ /* Sanity check */ ++ if (srcSize != dctx->expected) ++ return ERROR(srcSize_wrong); ++ if (dstCapacity) ++ ZSTD_checkContinuity(dctx, dst); ++ ++ switch (dctx->stage) { ++ case ZSTDds_getFrameHeaderSize: ++ if (srcSize != ZSTD_frameHeaderSize_prefix) ++ return ERROR(srcSize_wrong); /* impossible */ ++ if ((ZSTD_readLE32(src) & 0xFFFFFFF0U) == ZSTD_MAGIC_SKIPPABLE_START) { /* skippable frame */ ++ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); ++ dctx->expected = ZSTD_skippableHeaderSize - ZSTD_frameHeaderSize_prefix; /* magic number + skippable frame length */ ++ dctx->stage = ZSTDds_decodeSkippableHeader; ++ return 0; ++ } ++ dctx->headerSize = ZSTD_frameHeaderSize(src, ZSTD_frameHeaderSize_prefix); ++ if (ZSTD_isError(dctx->headerSize)) ++ return dctx->headerSize; ++ memcpy(dctx->headerBuffer, src, ZSTD_frameHeaderSize_prefix); ++ if (dctx->headerSize > ZSTD_frameHeaderSize_prefix) { ++ dctx->expected = dctx->headerSize - ZSTD_frameHeaderSize_prefix; ++ dctx->stage = ZSTDds_decodeFrameHeader; ++ return 0; ++ } ++ dctx->expected = 0; /* not necessary to copy more */ ++ /* fall through */ ++ ++ case ZSTDds_decodeFrameHeader: ++ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); ++ CHECK_F(ZSTD_decodeFrameHeader(dctx, dctx->headerBuffer, dctx->headerSize)); ++ dctx->expected = ZSTD_blockHeaderSize; ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ return 0; ++ ++ case ZSTDds_decodeBlockHeader: { ++ blockProperties_t bp; ++ size_t const cBlockSize = ZSTD_getcBlockSize(src, ZSTD_blockHeaderSize, &bp); ++ if (ZSTD_isError(cBlockSize)) ++ return cBlockSize; ++ dctx->expected = cBlockSize; ++ dctx->bType = bp.blockType; ++ dctx->rleSize = bp.origSize; ++ if (cBlockSize) { ++ dctx->stage = bp.lastBlock ? ZSTDds_decompressLastBlock : ZSTDds_decompressBlock; ++ return 0; ++ } ++ /* empty block */ ++ if (bp.lastBlock) { ++ if (dctx->fParams.checksumFlag) { ++ dctx->expected = 4; ++ dctx->stage = ZSTDds_checkChecksum; ++ } else { ++ dctx->expected = 0; /* end of frame */ ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ } ++ } else { ++ dctx->expected = 3; /* go directly to next header */ ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ } ++ return 0; ++ } ++ case ZSTDds_decompressLastBlock: ++ case ZSTDds_decompressBlock: { ++ size_t rSize; ++ switch (dctx->bType) { ++ case bt_compressed: rSize = ZSTD_decompressBlock_internal(dctx, dst, dstCapacity, src, srcSize); break; ++ case bt_raw: rSize = ZSTD_copyRawBlock(dst, dstCapacity, src, srcSize); break; ++ case bt_rle: rSize = ZSTD_setRleBlock(dst, dstCapacity, src, srcSize, dctx->rleSize); break; ++ case bt_reserved: /* should never happen */ ++ default: return ERROR(corruption_detected); ++ } ++ if (ZSTD_isError(rSize)) ++ return rSize; ++ if (dctx->fParams.checksumFlag) ++ xxh64_update(&dctx->xxhState, dst, rSize); ++ ++ if (dctx->stage == ZSTDds_decompressLastBlock) { /* end of frame */ ++ if (dctx->fParams.checksumFlag) { /* another round for frame checksum */ ++ dctx->expected = 4; ++ dctx->stage = ZSTDds_checkChecksum; ++ } else { ++ dctx->expected = 0; /* ends here */ ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ } ++ } else { ++ dctx->stage = ZSTDds_decodeBlockHeader; ++ dctx->expected = ZSTD_blockHeaderSize; ++ dctx->previousDstEnd = (char *)dst + rSize; ++ } ++ return rSize; ++ } ++ case ZSTDds_checkChecksum: { ++ U32 const h32 = (U32)xxh64_digest(&dctx->xxhState); ++ U32 const check32 = ZSTD_readLE32(src); /* srcSize == 4, guaranteed by dctx->expected */ ++ if (check32 != h32) ++ return ERROR(checksum_wrong); ++ dctx->expected = 0; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ return 0; ++ } ++ case ZSTDds_decodeSkippableHeader: { ++ memcpy(dctx->headerBuffer + ZSTD_frameHeaderSize_prefix, src, dctx->expected); ++ dctx->expected = ZSTD_readLE32(dctx->headerBuffer + 4); ++ dctx->stage = ZSTDds_skipFrame; ++ return 0; ++ } ++ case ZSTDds_skipFrame: { ++ dctx->expected = 0; ++ dctx->stage = ZSTDds_getFrameHeaderSize; ++ return 0; ++ } ++ default: ++ return ERROR(GENERIC); /* impossible */ ++ } ++} ++ ++static size_t INIT ZSTD_refDictContent(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ dctx->dictEnd = dctx->previousDstEnd; ++ dctx->vBase = (const char *)dict - ((const char *)(dctx->previousDstEnd) - (const char *)(dctx->base)); ++ dctx->base = dict; ++ dctx->previousDstEnd = (const char *)dict + dictSize; ++ return 0; ++} ++ ++/* ZSTD_loadEntropy() : ++ * dict : must point at beginning of a valid zstd dictionary ++ * @return : size of entropy tables read */ ++static size_t INIT ZSTD_loadEntropy(ZSTD_entropyTables_t *entropy, const void *const dict, size_t const dictSize) ++{ ++ const BYTE *dictPtr = (const BYTE *)dict; ++ const BYTE *const dictEnd = dictPtr + dictSize; ++ ++ if (dictSize <= 8) ++ return ERROR(dictionary_corrupted); ++ dictPtr += 8; /* skip header = magic + dictID */ ++ ++ { ++ size_t const hSize = HUF_readDTableX4_wksp(entropy->hufTable, dictPtr, dictEnd - dictPtr, entropy->workspace, sizeof(entropy->workspace)); ++ if (HUF_isError(hSize)) ++ return ERROR(dictionary_corrupted); ++ dictPtr += hSize; ++ } ++ ++ { ++ short offcodeNCount[MaxOff + 1]; ++ U32 offcodeMaxValue = MaxOff, offcodeLog; ++ size_t const offcodeHeaderSize = FSE_readNCount(offcodeNCount, &offcodeMaxValue, &offcodeLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(offcodeHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (offcodeLog > OffFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->OFTable, offcodeNCount, offcodeMaxValue, offcodeLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += offcodeHeaderSize; ++ } ++ ++ { ++ short matchlengthNCount[MaxML + 1]; ++ unsigned matchlengthMaxValue = MaxML, matchlengthLog; ++ size_t const matchlengthHeaderSize = FSE_readNCount(matchlengthNCount, &matchlengthMaxValue, &matchlengthLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(matchlengthHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (matchlengthLog > MLFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->MLTable, matchlengthNCount, matchlengthMaxValue, matchlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += matchlengthHeaderSize; ++ } ++ ++ { ++ short litlengthNCount[MaxLL + 1]; ++ unsigned litlengthMaxValue = MaxLL, litlengthLog; ++ size_t const litlengthHeaderSize = FSE_readNCount(litlengthNCount, &litlengthMaxValue, &litlengthLog, dictPtr, dictEnd - dictPtr); ++ if (FSE_isError(litlengthHeaderSize)) ++ return ERROR(dictionary_corrupted); ++ if (litlengthLog > LLFSELog) ++ return ERROR(dictionary_corrupted); ++ CHECK_E(FSE_buildDTable_wksp(entropy->LLTable, litlengthNCount, litlengthMaxValue, litlengthLog, entropy->workspace, sizeof(entropy->workspace)), dictionary_corrupted); ++ dictPtr += litlengthHeaderSize; ++ } ++ ++ if (dictPtr + 12 > dictEnd) ++ return ERROR(dictionary_corrupted); ++ { ++ int i; ++ size_t const dictContentSize = (size_t)(dictEnd - (dictPtr + 12)); ++ for (i = 0; i < 3; i++) { ++ U32 const rep = ZSTD_readLE32(dictPtr); ++ dictPtr += 4; ++ if (rep == 0 || rep >= dictContentSize) ++ return ERROR(dictionary_corrupted); ++ entropy->rep[i] = rep; ++ } ++ } ++ ++ return dictPtr - (const BYTE *)dict; ++} ++ ++static size_t INIT ZSTD_decompress_insertDictionary(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ if (dictSize < 8) ++ return ZSTD_refDictContent(dctx, dict, dictSize); ++ { ++ U32 const magic = ZSTD_readLE32(dict); ++ if (magic != ZSTD_DICT_MAGIC) { ++ return ZSTD_refDictContent(dctx, dict, dictSize); /* pure content mode */ ++ } ++ } ++ dctx->dictID = ZSTD_readLE32((const char *)dict + 4); ++ ++ /* load entropy tables */ ++ { ++ size_t const eSize = ZSTD_loadEntropy(&dctx->entropy, dict, dictSize); ++ if (ZSTD_isError(eSize)) ++ return ERROR(dictionary_corrupted); ++ dict = (const char *)dict + eSize; ++ dictSize -= eSize; ++ } ++ dctx->litEntropy = dctx->fseEntropy = 1; ++ ++ /* reference dictionary content */ ++ return ZSTD_refDictContent(dctx, dict, dictSize); ++} ++ ++size_t INIT ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, size_t dictSize) ++{ ++ CHECK_F(ZSTD_decompressBegin(dctx)); ++ if (dict && dictSize) ++ CHECK_E(ZSTD_decompress_insertDictionary(dctx, dict, dictSize), dictionary_corrupted); ++ return 0; ++} ++ ++/* ====== ZSTD_DDict ====== */ ++ ++struct ZSTD_DDict_s { ++ void *dictBuffer; ++ const void *dictContent; ++ size_t dictSize; ++ ZSTD_entropyTables_t entropy; ++ U32 dictID; ++ U32 entropyPresent; ++ ZSTD_customMem cMem; ++}; /* typedef'd to ZSTD_DDict within "zstd.h" */ ++ ++size_t INIT ZSTD_DDictWorkspaceBound(void) { return ZSTD_ALIGN(sizeof(ZSTD_stack)) + ZSTD_ALIGN(sizeof(ZSTD_DDict)); } ++ ++static const void INIT *ZSTD_DDictDictContent(const ZSTD_DDict *ddict) { return ddict->dictContent; } ++ ++static size_t INIT ZSTD_DDictDictSize(const ZSTD_DDict *ddict) { return ddict->dictSize; } ++ ++static void INIT ZSTD_refDDict(ZSTD_DCtx *dstDCtx, const ZSTD_DDict *ddict) ++{ ++ ZSTD_decompressBegin(dstDCtx); /* init */ ++ if (ddict) { /* support refDDict on NULL */ ++ dstDCtx->dictID = ddict->dictID; ++ dstDCtx->base = ddict->dictContent; ++ dstDCtx->vBase = ddict->dictContent; ++ dstDCtx->dictEnd = (const BYTE *)ddict->dictContent + ddict->dictSize; ++ dstDCtx->previousDstEnd = dstDCtx->dictEnd; ++ if (ddict->entropyPresent) { ++ dstDCtx->litEntropy = 1; ++ dstDCtx->fseEntropy = 1; ++ dstDCtx->LLTptr = ddict->entropy.LLTable; ++ dstDCtx->MLTptr = ddict->entropy.MLTable; ++ dstDCtx->OFTptr = ddict->entropy.OFTable; ++ dstDCtx->HUFptr = ddict->entropy.hufTable; ++ dstDCtx->entropy.rep[0] = ddict->entropy.rep[0]; ++ dstDCtx->entropy.rep[1] = ddict->entropy.rep[1]; ++ dstDCtx->entropy.rep[2] = ddict->entropy.rep[2]; ++ } else { ++ dstDCtx->litEntropy = 0; ++ dstDCtx->fseEntropy = 0; ++ } ++ } ++} ++ ++static size_t INIT ZSTD_loadEntropy_inDDict(ZSTD_DDict *ddict) ++{ ++ ddict->dictID = 0; ++ ddict->entropyPresent = 0; ++ if (ddict->dictSize < 8) ++ return 0; ++ { ++ U32 const magic = ZSTD_readLE32(ddict->dictContent); ++ if (magic != ZSTD_DICT_MAGIC) ++ return 0; /* pure content mode */ ++ } ++ ddict->dictID = ZSTD_readLE32((const char *)ddict->dictContent + 4); ++ ++ /* load entropy tables */ ++ CHECK_E(ZSTD_loadEntropy(&ddict->entropy, ddict->dictContent, ddict->dictSize), dictionary_corrupted); ++ ddict->entropyPresent = 1; ++ return 0; ++} ++ ++static ZSTD_DDict INIT *ZSTD_createDDict_advanced(const void *dict, size_t dictSize, unsigned byReference, ZSTD_customMem customMem) ++{ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ { ++ ZSTD_DDict *const ddict = (ZSTD_DDict *)ZSTD_malloc(sizeof(ZSTD_DDict), customMem); ++ if (!ddict) ++ return NULL; ++ ddict->cMem = customMem; ++ ++ if ((byReference) || (!dict) || (!dictSize)) { ++ ddict->dictBuffer = NULL; ++ ddict->dictContent = dict; ++ } else { ++ void *const internalBuffer = ZSTD_malloc(dictSize, customMem); ++ if (!internalBuffer) { ++ ZSTD_freeDDict(ddict); ++ return NULL; ++ } ++ memcpy(internalBuffer, dict, dictSize); ++ ddict->dictBuffer = internalBuffer; ++ ddict->dictContent = internalBuffer; ++ } ++ ddict->dictSize = dictSize; ++ ddict->entropy.hufTable[0] = (HUF_DTable)((HufLog)*0x1000001); /* cover both little and big endian */ ++ /* parse dictionary content */ ++ { ++ size_t const errorCode = ZSTD_loadEntropy_inDDict(ddict); ++ if (ZSTD_isError(errorCode)) { ++ ZSTD_freeDDict(ddict); ++ return NULL; ++ } ++ } ++ ++ return ddict; ++ } ++} ++ ++/*! ZSTD_initDDict() : ++* Create a digested dictionary, to start decompression without startup delay. ++* `dict` content is copied inside DDict. ++* Consequently, `dict` can be released after `ZSTD_DDict` creation */ ++ZSTD_DDict INIT *ZSTD_initDDict(const void *dict, size_t dictSize, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ return ZSTD_createDDict_advanced(dict, dictSize, 1, stackMem); ++} ++ ++size_t INIT ZSTD_freeDDict(ZSTD_DDict *ddict) ++{ ++ if (ddict == NULL) ++ return 0; /* support free on NULL */ ++ { ++ ZSTD_customMem const cMem = ddict->cMem; ++ ZSTD_free(ddict->dictBuffer, cMem); ++ ZSTD_free(ddict, cMem); ++ return 0; ++ } ++} ++ ++/*! ZSTD_getDictID_fromDict() : ++ * Provides the dictID stored within dictionary. ++ * if @return == 0, the dictionary is not conformant with Zstandard specification. ++ * It can still be loaded, but as a content-only dictionary. */ ++unsigned INIT ZSTD_getDictID_fromDict(const void *dict, size_t dictSize) ++{ ++ if (dictSize < 8) ++ return 0; ++ if (ZSTD_readLE32(dict) != ZSTD_DICT_MAGIC) ++ return 0; ++ return ZSTD_readLE32((const char *)dict + 4); ++} ++ ++/*! ZSTD_getDictID_fromDDict() : ++ * Provides the dictID of the dictionary loaded into `ddict`. ++ * If @return == 0, the dictionary is not conformant to Zstandard specification, or empty. ++ * Non-conformant dictionaries can still be loaded, but as content-only dictionaries. */ ++unsigned INIT ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict) ++{ ++ if (ddict == NULL) ++ return 0; ++ return ZSTD_getDictID_fromDict(ddict->dictContent, ddict->dictSize); ++} ++ ++/*! ZSTD_getDictID_fromFrame() : ++ * Provides the dictID required to decompressed the frame stored within `src`. ++ * If @return == 0, the dictID could not be decoded. ++ * This could for one of the following reasons : ++ * - The frame does not require a dictionary to be decoded (most common case). ++ * - The frame was built with dictID intentionally removed. Whatever dictionary is necessary is a hidden information. ++ * Note : this use case also happens when using a non-conformant dictionary. ++ * - `srcSize` is too small, and as a result, the frame header could not be decoded (only possible if `srcSize < ZSTD_FRAMEHEADERSIZE_MAX`). ++ * - This is not a Zstandard frame. ++ * When identifying the exact failure cause, it's possible to used ZSTD_getFrameParams(), which will provide a more precise error code. */ ++unsigned INIT ZSTD_getDictID_fromFrame(const void *src, size_t srcSize) ++{ ++ ZSTD_frameParams zfp = {0, 0, 0, 0}; ++ size_t const hError = ZSTD_getFrameParams(&zfp, src, srcSize); ++ if (ZSTD_isError(hError)) ++ return 0; ++ return zfp.dictID; ++} ++ ++/*! ZSTD_decompress_usingDDict() : ++* Decompression using a pre-digested Dictionary ++* Use dictionary without significant overhead. */ ++size_t INIT ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, const void *src, size_t srcSize, const ZSTD_DDict *ddict) ++{ ++ /* pass content and size in case legacy frames are encountered */ ++ return ZSTD_decompressMultiFrame(dctx, dst, dstCapacity, src, srcSize, NULL, 0, ddict); ++} ++ ++/*===================================== ++* Streaming decompression ++*====================================*/ ++ ++typedef enum { zdss_init, zdss_loadHeader, zdss_read, zdss_load, zdss_flush } ZSTD_dStreamStage; ++ ++/* *** Resource management *** */ ++struct ZSTD_DStream_s { ++ ZSTD_DCtx *dctx; ++ ZSTD_DDict *ddictLocal; ++ const ZSTD_DDict *ddict; ++ ZSTD_frameParams fParams; ++ ZSTD_dStreamStage stage; ++ char *inBuff; ++ size_t inBuffSize; ++ size_t inPos; ++ size_t maxWindowSize; ++ char *outBuff; ++ size_t outBuffSize; ++ size_t outStart; ++ size_t outEnd; ++ size_t blockSize; ++ BYTE headerBuffer[ZSTD_FRAMEHEADERSIZE_MAX]; /* tmp buffer to store frame header */ ++ size_t lhSize; ++ ZSTD_customMem customMem; ++ void *legacyContext; ++ U32 previousLegacyVersion; ++ U32 legacyVersion; ++ U32 hostageByte; ++}; /* typedef'd to ZSTD_DStream within "zstd.h" */ ++ ++size_t INIT ZSTD_DStreamWorkspaceBound(size_t maxWindowSize) ++{ ++ size_t const blockSize = MIN(maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const inBuffSize = blockSize; ++ size_t const outBuffSize = maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ return ZSTD_DCtxWorkspaceBound() + ZSTD_ALIGN(sizeof(ZSTD_DStream)) + ZSTD_ALIGN(inBuffSize) + ZSTD_ALIGN(outBuffSize); ++} ++ ++static ZSTD_DStream INIT *ZSTD_createDStream_advanced(ZSTD_customMem customMem) ++{ ++ ZSTD_DStream *zds; ++ ++ if (!customMem.customAlloc || !customMem.customFree) ++ return NULL; ++ ++ zds = (ZSTD_DStream *)ZSTD_malloc(sizeof(ZSTD_DStream), customMem); ++ if (zds == NULL) ++ return NULL; ++ memset(zds, 0, sizeof(ZSTD_DStream)); ++ memcpy(&zds->customMem, &customMem, sizeof(ZSTD_customMem)); ++ zds->dctx = ZSTD_createDCtx_advanced(customMem); ++ if (zds->dctx == NULL) { ++ ZSTD_freeDStream(zds); ++ return NULL; ++ } ++ zds->stage = zdss_init; ++ zds->maxWindowSize = ZSTD_MAXWINDOWSIZE_DEFAULT; ++ return zds; ++} ++ ++ZSTD_DStream INIT *ZSTD_initDStream(size_t maxWindowSize, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem const stackMem = ZSTD_initStack(workspace, workspaceSize); ++ ZSTD_DStream *zds = ZSTD_createDStream_advanced(stackMem); ++ if (!zds) { ++ return NULL; ++ } ++ ++ zds->maxWindowSize = maxWindowSize; ++ zds->stage = zdss_loadHeader; ++ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ++ ZSTD_freeDDict(zds->ddictLocal); ++ zds->ddictLocal = NULL; ++ zds->ddict = zds->ddictLocal; ++ zds->legacyVersion = 0; ++ zds->hostageByte = 0; ++ ++ { ++ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ ++ zds->inBuff = (char *)ZSTD_malloc(blockSize, zds->customMem); ++ zds->inBuffSize = blockSize; ++ zds->outBuff = (char *)ZSTD_malloc(neededOutSize, zds->customMem); ++ zds->outBuffSize = neededOutSize; ++ if (zds->inBuff == NULL || zds->outBuff == NULL) { ++ ZSTD_freeDStream(zds); ++ return NULL; ++ } ++ } ++ return zds; ++} ++ ++ZSTD_DStream INIT *ZSTD_initDStream_usingDDict(size_t maxWindowSize, const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize) ++{ ++ ZSTD_DStream *zds = ZSTD_initDStream(maxWindowSize, workspace, workspaceSize); ++ if (zds) { ++ zds->ddict = ddict; ++ } ++ return zds; ++} ++ ++size_t INIT ZSTD_freeDStream(ZSTD_DStream *zds) ++{ ++ if (zds == NULL) ++ return 0; /* support free on null */ ++ { ++ ZSTD_customMem const cMem = zds->customMem; ++ ZSTD_freeDCtx(zds->dctx); ++ zds->dctx = NULL; ++ ZSTD_freeDDict(zds->ddictLocal); ++ zds->ddictLocal = NULL; ++ ZSTD_free(zds->inBuff, cMem); ++ zds->inBuff = NULL; ++ ZSTD_free(zds->outBuff, cMem); ++ zds->outBuff = NULL; ++ ZSTD_free(zds, cMem); ++ return 0; ++ } ++} ++ ++/* *** Initialization *** */ ++ ++size_t INIT ZSTD_DStreamInSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX + ZSTD_blockHeaderSize; } ++size_t INIT ZSTD_DStreamOutSize(void) { return ZSTD_BLOCKSIZE_ABSOLUTEMAX; } ++ ++size_t INIT ZSTD_resetDStream(ZSTD_DStream *zds) ++{ ++ zds->stage = zdss_loadHeader; ++ zds->lhSize = zds->inPos = zds->outStart = zds->outEnd = 0; ++ zds->legacyVersion = 0; ++ zds->hostageByte = 0; ++ return ZSTD_frameHeaderSize_prefix; ++} ++ ++/* ***** Decompression ***** */ ++ ++ZSTD_STATIC size_t INIT ZSTD_limitCopy(void *dst, size_t dstCapacity, const void *src, size_t srcSize) ++{ ++ size_t const length = MIN(dstCapacity, srcSize); ++ memcpy(dst, src, length); ++ return length; ++} ++ ++size_t INIT ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ZSTD_inBuffer *input) ++{ ++ const char *const istart = (const char *)(input->src) + input->pos; ++ const char *const iend = (const char *)(input->src) + input->size; ++ const char *ip = istart; ++ char *const ostart = (char *)(output->dst) + output->pos; ++ char *const oend = (char *)(output->dst) + output->size; ++ char *op = ostart; ++ U32 someMoreWork = 1; ++ ++ while (someMoreWork) { ++ switch (zds->stage) { ++ case zdss_init: ++ ZSTD_resetDStream(zds); /* transparent reset on starting decoding a new frame */ ++ /* fall through */ ++ ++ case zdss_loadHeader: { ++ size_t const hSize = ZSTD_getFrameParams(&zds->fParams, zds->headerBuffer, zds->lhSize); ++ if (ZSTD_isError(hSize)) ++ return hSize; ++ if (hSize != 0) { /* need more input */ ++ size_t const toLoad = hSize - zds->lhSize; /* if hSize!=0, hSize > zds->lhSize */ ++ if (toLoad > (size_t)(iend - ip)) { /* not enough input to load full header */ ++ memcpy(zds->headerBuffer + zds->lhSize, ip, iend - ip); ++ zds->lhSize += iend - ip; ++ input->pos = input->size; ++ return (MAX(ZSTD_frameHeaderSize_min, hSize) - zds->lhSize) + ++ ZSTD_blockHeaderSize; /* remaining header bytes + next block header */ ++ } ++ memcpy(zds->headerBuffer + zds->lhSize, ip, toLoad); ++ zds->lhSize = hSize; ++ ip += toLoad; ++ break; ++ } ++ ++ /* check for single-pass mode opportunity */ ++ if (zds->fParams.frameContentSize && zds->fParams.windowSize /* skippable frame if == 0 */ ++ && (U64)(size_t)(oend - op) >= zds->fParams.frameContentSize) { ++ size_t const cSize = ZSTD_findFrameCompressedSize(istart, iend - istart); ++ if (cSize <= (size_t)(iend - istart)) { ++ size_t const decompressedSize = ZSTD_decompress_usingDDict(zds->dctx, op, oend - op, istart, cSize, zds->ddict); ++ if (ZSTD_isError(decompressedSize)) ++ return decompressedSize; ++ ip = istart + cSize; ++ op += decompressedSize; ++ zds->dctx->expected = 0; ++ zds->stage = zdss_init; ++ someMoreWork = 0; ++ break; ++ } ++ } ++ ++ /* Consume header */ ++ ZSTD_refDDict(zds->dctx, zds->ddict); ++ { ++ size_t const h1Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); /* == ZSTD_frameHeaderSize_prefix */ ++ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer, h1Size)); ++ { ++ size_t const h2Size = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ CHECK_F(ZSTD_decompressContinue(zds->dctx, NULL, 0, zds->headerBuffer + h1Size, h2Size)); ++ } ++ } ++ ++ zds->fParams.windowSize = MAX(zds->fParams.windowSize, 1U << ZSTD_WINDOWLOG_ABSOLUTEMIN); ++ if (zds->fParams.windowSize > zds->maxWindowSize) ++ return ERROR(frameParameter_windowTooLarge); ++ ++ /* Buffers are preallocated, but double check */ ++ { ++ size_t const blockSize = MIN(zds->maxWindowSize, ZSTD_BLOCKSIZE_ABSOLUTEMAX); ++ size_t const neededOutSize = zds->maxWindowSize + blockSize + WILDCOPY_OVERLENGTH * 2; ++ if (zds->inBuffSize < blockSize) { ++ return ERROR(GENERIC); ++ } ++ if (zds->outBuffSize < neededOutSize) { ++ return ERROR(GENERIC); ++ } ++ zds->blockSize = blockSize; ++ } ++ zds->stage = zdss_read; ++ } ++ /* fall through */ ++ ++ case zdss_read: { ++ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ if (neededInSize == 0) { /* end of frame */ ++ zds->stage = zdss_init; ++ someMoreWork = 0; ++ break; ++ } ++ if ((size_t)(iend - ip) >= neededInSize) { /* decode directly from src */ ++ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); ++ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, ++ (isSkipFrame ? 0 : zds->outBuffSize - zds->outStart), ip, neededInSize); ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ ip += neededInSize; ++ if (!decodedSize && !isSkipFrame) ++ break; /* this was just a header */ ++ zds->outEnd = zds->outStart + decodedSize; ++ zds->stage = zdss_flush; ++ break; ++ } ++ if (ip == iend) { ++ someMoreWork = 0; ++ break; ++ } /* no more input */ ++ zds->stage = zdss_load; ++ /* pass-through */ ++ } ++ /* fall through */ ++ ++ case zdss_load: { ++ size_t const neededInSize = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ size_t const toLoad = neededInSize - zds->inPos; /* should always be <= remaining space within inBuff */ ++ size_t loadedSize; ++ if (toLoad > zds->inBuffSize - zds->inPos) ++ return ERROR(corruption_detected); /* should never happen */ ++ loadedSize = ZSTD_limitCopy(zds->inBuff + zds->inPos, toLoad, ip, iend - ip); ++ ip += loadedSize; ++ zds->inPos += loadedSize; ++ if (loadedSize < toLoad) { ++ someMoreWork = 0; ++ break; ++ } /* not enough input, wait for more */ ++ ++ /* decode loaded input */ ++ { ++ const int isSkipFrame = ZSTD_isSkipFrame(zds->dctx); ++ size_t const decodedSize = ZSTD_decompressContinue(zds->dctx, zds->outBuff + zds->outStart, zds->outBuffSize - zds->outStart, ++ zds->inBuff, neededInSize); ++ if (ZSTD_isError(decodedSize)) ++ return decodedSize; ++ zds->inPos = 0; /* input is consumed */ ++ if (!decodedSize && !isSkipFrame) { ++ zds->stage = zdss_read; ++ break; ++ } /* this was just a header */ ++ zds->outEnd = zds->outStart + decodedSize; ++ zds->stage = zdss_flush; ++ /* pass-through */ ++ } ++ } ++ /* fall through */ ++ ++ case zdss_flush: { ++ size_t const toFlushSize = zds->outEnd - zds->outStart; ++ size_t const flushedSize = ZSTD_limitCopy(op, oend - op, zds->outBuff + zds->outStart, toFlushSize); ++ op += flushedSize; ++ zds->outStart += flushedSize; ++ if (flushedSize == toFlushSize) { /* flush completed */ ++ zds->stage = zdss_read; ++ if (zds->outStart + zds->blockSize > zds->outBuffSize) ++ zds->outStart = zds->outEnd = 0; ++ break; ++ } ++ /* cannot complete flush */ ++ someMoreWork = 0; ++ break; ++ } ++ default: ++ return ERROR(GENERIC); /* impossible */ ++ } ++ } ++ ++ /* result */ ++ input->pos += (size_t)(ip - istart); ++ output->pos += (size_t)(op - ostart); ++ { ++ size_t nextSrcSizeHint = ZSTD_nextSrcSizeToDecompress(zds->dctx); ++ if (!nextSrcSizeHint) { /* frame fully decoded */ ++ if (zds->outEnd == zds->outStart) { /* output fully flushed */ ++ if (zds->hostageByte) { ++ if (input->pos >= input->size) { ++ zds->stage = zdss_read; ++ return 1; ++ } /* can't release hostage (not present) */ ++ input->pos++; /* release hostage */ ++ } ++ return 0; ++ } ++ if (!zds->hostageByte) { /* output not fully flushed; keep last byte as hostage; will be released when all output is flushed */ ++ input->pos--; /* note : pos > 0, otherwise, impossible to finish reading last block */ ++ zds->hostageByte = 1; ++ } ++ return 1; ++ } ++ nextSrcSizeHint += ZSTD_blockHeaderSize * (ZSTD_nextInputType(zds->dctx) == ZSTDnit_block); /* preload header of next block */ ++ if (zds->inPos > nextSrcSizeHint) ++ return ERROR(GENERIC); /* should never happen */ ++ nextSrcSizeHint -= zds->inPos; /* already loaded*/ ++ return nextSrcSizeHint; ++ } ++} +diff --git a/xen/common/zstd/entropy_common.c b/xen/common/zstd/entropy_common.c +new file mode 100644 +index 0000000000..bcdb57982b +--- /dev/null ++++ b/xen/common/zstd/entropy_common.c +@@ -0,0 +1,243 @@ ++/* ++ * Common functions of New Generation Entropy library ++ * Copyright (C) 2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" /* ERR_*, ERROR */ ++#include "fse.h" ++#include "huf.h" ++#include "mem.h" ++ ++/*=== Version ===*/ ++unsigned INIT FSE_versionNumber(void) { return FSE_VERSION_NUMBER; } ++ ++/*=== Error Management ===*/ ++unsigned INIT FSE_isError(size_t code) { return ERR_isError(code); } ++ ++unsigned INIT HUF_isError(size_t code) { return ERR_isError(code); } ++ ++/*-************************************************************** ++* FSE NCount encoding-decoding ++****************************************************************/ ++size_t INIT FSE_readNCount(short *normalizedCounter, unsigned *maxSVPtr, unsigned *tableLogPtr, const void *headerBuffer, size_t hbSize) ++{ ++ const BYTE *const istart = (const BYTE *)headerBuffer; ++ const BYTE *const iend = istart + hbSize; ++ const BYTE *ip = istart; ++ int nbBits; ++ int remaining; ++ int threshold; ++ U32 bitStream; ++ int bitCount; ++ unsigned charnum = 0; ++ int previous0 = 0; ++ ++ if (hbSize < 4) ++ return ERROR(srcSize_wrong); ++ bitStream = ZSTD_readLE32(ip); ++ nbBits = (bitStream & 0xF) + FSE_MIN_TABLELOG; /* extract tableLog */ ++ if (nbBits > FSE_TABLELOG_ABSOLUTE_MAX) ++ return ERROR(tableLog_tooLarge); ++ bitStream >>= 4; ++ bitCount = 4; ++ *tableLogPtr = nbBits; ++ remaining = (1 << nbBits) + 1; ++ threshold = 1 << nbBits; ++ nbBits++; ++ ++ while ((remaining > 1) & (charnum <= *maxSVPtr)) { ++ if (previous0) { ++ unsigned n0 = charnum; ++ while ((bitStream & 0xFFFF) == 0xFFFF) { ++ n0 += 24; ++ if (ip < iend - 5) { ++ ip += 2; ++ bitStream = ZSTD_readLE32(ip) >> bitCount; ++ } else { ++ bitStream >>= 16; ++ bitCount += 16; ++ } ++ } ++ while ((bitStream & 3) == 3) { ++ n0 += 3; ++ bitStream >>= 2; ++ bitCount += 2; ++ } ++ n0 += bitStream & 3; ++ bitCount += 2; ++ if (n0 > *maxSVPtr) ++ return ERROR(maxSymbolValue_tooSmall); ++ while (charnum < n0) ++ normalizedCounter[charnum++] = 0; ++ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { ++ ip += bitCount >> 3; ++ bitCount &= 7; ++ bitStream = ZSTD_readLE32(ip) >> bitCount; ++ } else { ++ bitStream >>= 2; ++ } ++ } ++ { ++ int const max = (2 * threshold - 1) - remaining; ++ int count; ++ ++ if ((bitStream & (threshold - 1)) < (U32)max) { ++ count = bitStream & (threshold - 1); ++ bitCount += nbBits - 1; ++ } else { ++ count = bitStream & (2 * threshold - 1); ++ if (count >= threshold) ++ count -= max; ++ bitCount += nbBits; ++ } ++ ++ count--; /* extra accuracy */ ++ remaining -= count < 0 ? -count : count; /* -1 means +1 */ ++ normalizedCounter[charnum++] = (short)count; ++ previous0 = !count; ++ while (remaining < threshold) { ++ nbBits--; ++ threshold >>= 1; ++ } ++ ++ if ((ip <= iend - 7) || (ip + (bitCount >> 3) <= iend - 4)) { ++ ip += bitCount >> 3; ++ bitCount &= 7; ++ } else { ++ bitCount -= (int)(8 * (iend - 4 - ip)); ++ ip = iend - 4; ++ } ++ bitStream = ZSTD_readLE32(ip) >> (bitCount & 31); ++ } ++ } /* while ((remaining>1) & (charnum<=*maxSVPtr)) */ ++ if (remaining != 1) ++ return ERROR(corruption_detected); ++ if (bitCount > 32) ++ return ERROR(corruption_detected); ++ *maxSVPtr = charnum - 1; ++ ++ ip += (bitCount + 7) >> 3; ++ return ip - istart; ++} ++ ++/*! HUF_readStats() : ++ Read compact Huffman tree, saved by HUF_writeCTable(). ++ `huffWeight` is destination buffer. ++ `rankStats` is assumed to be a table of at least HUF_TABLELOG_MAX U32. ++ @return : size read from `src` , or an error Code . ++ Note : Needed by HUF_readCTable() and HUF_readDTableX?() . ++*/ ++size_t INIT HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 weightTotal; ++ const BYTE *ip = (const BYTE *)src; ++ size_t iSize; ++ size_t oSize; ++ ++ if (!srcSize) ++ return ERROR(srcSize_wrong); ++ iSize = ip[0]; ++ /* memset(huffWeight, 0, hwSize); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ if (iSize >= 128) { /* special header */ ++ oSize = iSize - 127; ++ iSize = ((oSize + 1) / 2); ++ if (iSize + 1 > srcSize) ++ return ERROR(srcSize_wrong); ++ if (oSize >= hwSize) ++ return ERROR(corruption_detected); ++ ip += 1; ++ { ++ U32 n; ++ for (n = 0; n < oSize; n += 2) { ++ huffWeight[n] = ip[n / 2] >> 4; ++ huffWeight[n + 1] = ip[n / 2] & 15; ++ } ++ } ++ } else { /* header compressed with FSE (normal case) */ ++ if (iSize + 1 > srcSize) ++ return ERROR(srcSize_wrong); ++ oSize = FSE_decompress_wksp(huffWeight, hwSize - 1, ip + 1, iSize, 6, workspace, workspaceSize); /* max (hwSize-1) values decoded, as last one is implied */ ++ if (FSE_isError(oSize)) ++ return oSize; ++ } ++ ++ /* collect weight stats */ ++ memset(rankStats, 0, (HUF_TABLELOG_MAX + 1) * sizeof(U32)); ++ weightTotal = 0; ++ { ++ U32 n; ++ for (n = 0; n < oSize; n++) { ++ if (huffWeight[n] >= HUF_TABLELOG_MAX) ++ return ERROR(corruption_detected); ++ rankStats[huffWeight[n]]++; ++ weightTotal += (1 << huffWeight[n]) >> 1; ++ } ++ } ++ if (weightTotal == 0) ++ return ERROR(corruption_detected); ++ ++ /* get last non-null symbol weight (implied, total must be 2^n) */ ++ { ++ U32 const tableLog = BIT_highbit32(weightTotal) + 1; ++ if (tableLog > HUF_TABLELOG_MAX) ++ return ERROR(corruption_detected); ++ *tableLogPtr = tableLog; ++ /* determine last weight */ ++ { ++ U32 const total = 1 << tableLog; ++ U32 const rest = total - weightTotal; ++ U32 const verif = 1 << BIT_highbit32(rest); ++ U32 const lastWeight = BIT_highbit32(rest) + 1; ++ if (verif != rest) ++ return ERROR(corruption_detected); /* last value must be a clean power of 2 */ ++ huffWeight[oSize] = (BYTE)lastWeight; ++ rankStats[lastWeight]++; ++ } ++ } ++ ++ /* check tree construction validity */ ++ if ((rankStats[1] < 2) || (rankStats[1] & 1)) ++ return ERROR(corruption_detected); /* by construction : at least 2 elts of rank 1, must be even */ ++ ++ /* results */ ++ *nbSymbolsPtr = (U32)(oSize + 1); ++ return iSize + 1; ++} +diff --git a/xen/common/zstd/error_private.h b/xen/common/zstd/error_private.h +new file mode 100644 +index 0000000000..ecbfe51dfb +--- /dev/null ++++ b/xen/common/zstd/error_private.h +@@ -0,0 +1,53 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* Note : this module is expected to remain private, do not expose it */ ++ ++#ifndef ERROR_H_MODULE ++#define ERROR_H_MODULE ++ ++/* **************************************** ++* Dependencies ++******************************************/ ++#include /* size_t */ ++#include /* enum list */ ++ ++/* **************************************** ++* Compiler-specific ++******************************************/ ++#define ERR_STATIC static __attribute__((unused)) ++ ++/*-**************************************** ++* Customization (error_public.h) ++******************************************/ ++typedef ZSTD_ErrorCode ERR_enum; ++#define PREFIX(name) ZSTD_error_##name ++ ++/*-**************************************** ++* Error codes handling ++******************************************/ ++#define ERROR(name) ((size_t)-PREFIX(name)) ++ ++ERR_STATIC unsigned ERR_isError(size_t code) { return (code > ERROR(maxCode)); } ++ ++ERR_STATIC ERR_enum ERR_getErrorCode(size_t code) ++{ ++ if (!ERR_isError(code)) ++ return (ERR_enum)0; ++ return (ERR_enum)(0 - code); ++} ++ ++#endif /* ERROR_H_MODULE */ +diff --git a/xen/common/zstd/fse.h b/xen/common/zstd/fse.h +new file mode 100644 +index 0000000000..b86717c34d +--- /dev/null ++++ b/xen/common/zstd/fse.h +@@ -0,0 +1,575 @@ ++/* ++ * FSE : Finite State Entropy codec ++ * Public Prototypes declaration ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef FSE_H ++#define FSE_H ++ ++/*-***************************************** ++* Dependencies ++******************************************/ ++#include /* size_t, ptrdiff_t */ ++ ++/*-***************************************** ++* FSE_PUBLIC_API : control library symbols visibility ++******************************************/ ++#define FSE_PUBLIC_API ++ ++/*------ Version ------*/ ++#define FSE_VERSION_MAJOR 0 ++#define FSE_VERSION_MINOR 9 ++#define FSE_VERSION_RELEASE 0 ++ ++#define FSE_LIB_VERSION FSE_VERSION_MAJOR.FSE_VERSION_MINOR.FSE_VERSION_RELEASE ++#define FSE_QUOTE(str) #str ++#define FSE_EXPAND_AND_QUOTE(str) FSE_QUOTE(str) ++#define FSE_VERSION_STRING FSE_EXPAND_AND_QUOTE(FSE_LIB_VERSION) ++ ++#define FSE_VERSION_NUMBER (FSE_VERSION_MAJOR * 100 * 100 + FSE_VERSION_MINOR * 100 + FSE_VERSION_RELEASE) ++FSE_PUBLIC_API unsigned FSE_versionNumber(void); /**< library version number; to be used when checking dll version */ ++ ++/*-***************************************** ++* Tool functions ++******************************************/ ++FSE_PUBLIC_API size_t FSE_compressBound(size_t size); /* maximum compressed size */ ++ ++/* Error Management */ ++FSE_PUBLIC_API unsigned FSE_isError(size_t code); /* tells if a return value is an error code */ ++ ++/*-***************************************** ++* FSE detailed API ++******************************************/ ++/*! ++FSE_compress() does the following: ++1. count symbol occurrence from source[] into table count[] ++2. normalize counters so that sum(count[]) == Power_of_2 (2^tableLog) ++3. save normalized counters to memory buffer using writeNCount() ++4. build encoding table 'CTable' from normalized counters ++5. encode the data stream using encoding table 'CTable' ++ ++FSE_decompress() does the following: ++1. read normalized counters with readNCount() ++2. build decoding table 'DTable' from normalized counters ++3. decode the data stream using decoding table 'DTable' ++ ++The following API allows targeting specific sub-functions for advanced tasks. ++For example, it's possible to compress several blocks using the same 'CTable', ++or to save and provide normalized distribution using external method. ++*/ ++ ++/* *** COMPRESSION *** */ ++/*! FSE_optimalTableLog(): ++ dynamically downsize 'tableLog' when conditions are met. ++ It saves CPU time, by using smaller tables, while preserving or even improving compression ratio. ++ @return : recommended tableLog (necessarily <= 'maxTableLog') */ ++FSE_PUBLIC_API unsigned FSE_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); ++ ++/*! FSE_normalizeCount(): ++ normalize counts so that sum(count[]) == Power_of_2 (2^tableLog) ++ 'normalizedCounter' is a table of short, of minimum size (maxSymbolValue+1). ++ @return : tableLog, ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_normalizeCount(short *normalizedCounter, unsigned tableLog, const unsigned *count, size_t srcSize, unsigned maxSymbolValue); ++ ++/*! FSE_NCountWriteBound(): ++ Provides the maximum possible size of an FSE normalized table, given 'maxSymbolValue' and 'tableLog'. ++ Typically useful for allocation purpose. */ ++FSE_PUBLIC_API size_t FSE_NCountWriteBound(unsigned maxSymbolValue, unsigned tableLog); ++ ++/*! FSE_writeNCount(): ++ Compactly save 'normalizedCounter' into 'buffer'. ++ @return : size of the compressed table, ++ or an errorCode, which can be tested using FSE_isError(). */ ++FSE_PUBLIC_API size_t FSE_writeNCount(void *buffer, size_t bufferSize, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog); ++ ++/*! Constructor and Destructor of FSE_CTable. ++ Note that FSE_CTable size depends on 'tableLog' and 'maxSymbolValue' */ ++typedef unsigned FSE_CTable; /* don't allocate that. It's only meant to be more restrictive than void* */ ++ ++/*! FSE_compress_usingCTable(): ++ Compress `src` using `ct` into `dst` which must be already allocated. ++ @return : size of compressed data (<= `dstCapacity`), ++ or 0 if compressed data could not fit into `dst`, ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_compress_usingCTable(void *dst, size_t dstCapacity, const void *src, size_t srcSize, const FSE_CTable *ct); ++ ++/*! ++Tutorial : ++---------- ++The first step is to count all symbols. FSE_count() does this job very fast. ++Result will be saved into 'count', a table of unsigned int, which must be already allocated, and have 'maxSymbolValuePtr[0]+1' cells. ++'src' is a table of bytes of size 'srcSize'. All values within 'src' MUST be <= maxSymbolValuePtr[0] ++maxSymbolValuePtr[0] will be updated, with its real value (necessarily <= original value) ++FSE_count() will return the number of occurrence of the most frequent symbol. ++This can be used to know if there is a single symbol within 'src', and to quickly evaluate its compressibility. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). ++ ++The next step is to normalize the frequencies. ++FSE_normalizeCount() will ensure that sum of frequencies is == 2 ^'tableLog'. ++It also guarantees a minimum of 1 to any Symbol with frequency >= 1. ++You can use 'tableLog'==0 to mean "use default tableLog value". ++If you are unsure of which tableLog value to use, you can ask FSE_optimalTableLog(), ++which will provide the optimal valid tableLog given sourceSize, maxSymbolValue, and a user-defined maximum (0 means "default"). ++ ++The result of FSE_normalizeCount() will be saved into a table, ++called 'normalizedCounter', which is a table of signed short. ++'normalizedCounter' must be already allocated, and have at least 'maxSymbolValue+1' cells. ++The return value is tableLog if everything proceeded as expected. ++It is 0 if there is a single symbol within distribution. ++If there is an error (ex: invalid tableLog value), the function will return an ErrorCode (which can be tested using FSE_isError()). ++ ++'normalizedCounter' can be saved in a compact manner to a memory area using FSE_writeNCount(). ++'buffer' must be already allocated. ++For guaranteed success, buffer size must be at least FSE_headerBound(). ++The result of the function is the number of bytes written into 'buffer'. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError(); ex : buffer size too small). ++ ++'normalizedCounter' can then be used to create the compression table 'CTable'. ++The space required by 'CTable' must be already allocated, using FSE_createCTable(). ++You can then use FSE_buildCTable() to fill 'CTable'. ++If there is an error, both functions will return an ErrorCode (which can be tested using FSE_isError()). ++ ++'CTable' can then be used to compress 'src', with FSE_compress_usingCTable(). ++Similar to FSE_count(), the convention is that 'src' is assumed to be a table of char of size 'srcSize' ++The function returns the size of compressed data (without header), necessarily <= `dstCapacity`. ++If it returns '0', compressed data could not fit into 'dst'. ++If there is an error, the function will return an ErrorCode (which can be tested using FSE_isError()). ++*/ ++ ++/* *** DECOMPRESSION *** */ ++ ++/*! FSE_readNCount(): ++ Read compactly saved 'normalizedCounter' from 'rBuffer'. ++ @return : size read from 'rBuffer', ++ or an errorCode, which can be tested using FSE_isError(). ++ maxSymbolValuePtr[0] and tableLogPtr[0] will also be updated with their respective values */ ++FSE_PUBLIC_API size_t FSE_readNCount(short *normalizedCounter, unsigned *maxSymbolValuePtr, unsigned *tableLogPtr, const void *rBuffer, size_t rBuffSize); ++ ++/*! Constructor and Destructor of FSE_DTable. ++ Note that its size depends on 'tableLog' */ ++typedef unsigned FSE_DTable; /* don't allocate that. It's just a way to be more restrictive than void* */ ++ ++/*! FSE_buildDTable(): ++ Builds 'dt', which must be already allocated, using FSE_createDTable(). ++ return : 0, or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize); ++ ++/*! FSE_decompress_usingDTable(): ++ Decompress compressed source `cSrc` of size `cSrcSize` using `dt` ++ into `dst` which must be already allocated. ++ @return : size of regenerated data (necessarily <= `dstCapacity`), ++ or an errorCode, which can be tested using FSE_isError() */ ++FSE_PUBLIC_API size_t FSE_decompress_usingDTable(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt); ++ ++/*! ++Tutorial : ++---------- ++(Note : these functions only decompress FSE-compressed blocks. ++ If block is uncompressed, use memcpy() instead ++ If block is a single repeated byte, use memset() instead ) ++ ++The first step is to obtain the normalized frequencies of symbols. ++This can be performed by FSE_readNCount() if it was saved using FSE_writeNCount(). ++'normalizedCounter' must be already allocated, and have at least 'maxSymbolValuePtr[0]+1' cells of signed short. ++In practice, that means it's necessary to know 'maxSymbolValue' beforehand, ++or size the table to handle worst case situations (typically 256). ++FSE_readNCount() will provide 'tableLog' and 'maxSymbolValue'. ++The result of FSE_readNCount() is the number of bytes read from 'rBuffer'. ++Note that 'rBufferSize' must be at least 4 bytes, even if useful information is less than that. ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). ++ ++The next step is to build the decompression tables 'FSE_DTable' from 'normalizedCounter'. ++This is performed by the function FSE_buildDTable(). ++The space required by 'FSE_DTable' must be already allocated using FSE_createDTable(). ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). ++ ++`FSE_DTable` can then be used to decompress `cSrc`, with FSE_decompress_usingDTable(). ++`cSrcSize` must be strictly correct, otherwise decompression will fail. ++FSE_decompress_usingDTable() result will tell how many bytes were regenerated (<=`dstCapacity`). ++If there is an error, the function will return an error code, which can be tested using FSE_isError(). (ex: dst buffer too small) ++*/ ++ ++/* *** Dependency *** */ ++#include "bitstream.h" ++ ++/* ***************************************** ++* Static allocation ++*******************************************/ ++/* FSE buffer bounds */ ++#define FSE_NCOUNTBOUND 512 ++#define FSE_BLOCKBOUND(size) (size + (size >> 7)) ++#define FSE_COMPRESSBOUND(size) (FSE_NCOUNTBOUND + FSE_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ ++ ++/* It is possible to statically allocate FSE CTable/DTable as a table of FSE_CTable/FSE_DTable using below macros */ ++#define FSE_CTABLE_SIZE_U32(maxTableLog, maxSymbolValue) (1 + (1 << (maxTableLog - 1)) + ((maxSymbolValue + 1) * 2)) ++#define FSE_DTABLE_SIZE_U32(maxTableLog) (1 + (1 << maxTableLog)) ++ ++/* ***************************************** ++* FSE advanced API ++*******************************************/ ++/* FSE_count_wksp() : ++ * Same as FSE_count(), but using an externally provided scratch buffer. ++ * `workSpace` size must be table of >= `1024` unsigned ++ */ ++size_t FSE_count_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *source, size_t sourceSize, unsigned *workSpace); ++ ++/* FSE_countFast_wksp() : ++ * Same as FSE_countFast(), but using an externally provided scratch buffer. ++ * `workSpace` must be a table of minimum `1024` unsigned ++ */ ++size_t FSE_countFast_wksp(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize, unsigned *workSpace); ++ ++/*! FSE_count_simple ++ * Same as FSE_countFast(), but does not use any additional memory (not even on stack). ++ * This function is unsafe, and will segfault if any value within `src` is `> *maxSymbolValuePtr` (presuming it's also the size of `count`). ++*/ ++size_t FSE_count_simple(unsigned *count, unsigned *maxSymbolValuePtr, const void *src, size_t srcSize); ++ ++unsigned FSE_optimalTableLog_internal(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue, unsigned minus); ++/**< same as FSE_optimalTableLog(), which used `minus==2` */ ++ ++size_t FSE_buildCTable_raw(FSE_CTable *ct, unsigned nbBits); ++/**< build a fake FSE_CTable, designed for a flat distribution, where each symbol uses nbBits */ ++ ++size_t FSE_buildCTable_rle(FSE_CTable *ct, unsigned char symbolValue); ++/**< build a fake FSE_CTable, designed to compress always the same symbolValue */ ++ ++/* FSE_buildCTable_wksp() : ++ * Same as FSE_buildCTable(), but using an externally allocated scratch buffer (`workSpace`). ++ * `wkspSize` must be >= `(1<= BIT_DStream_completed ++ ++When it's done, verify decompression is fully completed, by checking both DStream and the relevant states. ++Checking if DStream has reached its end is performed by : ++ BIT_endOfDStream(&DStream); ++Check also the states. There might be some symbols left there, if some high probability ones (>50%) are possible. ++ FSE_endOfDState(&DState); ++*/ ++ ++/* ***************************************** ++* FSE unsafe API ++*******************************************/ ++static unsigned char FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD); ++/* faster, but works only if nbBits is always >= 1 (otherwise, result will be corrupted) */ ++ ++/* ***************************************** ++* Implementation of inlined functions ++*******************************************/ ++typedef struct { ++ int deltaFindState; ++ U32 deltaNbBits; ++} FSE_symbolCompressionTransform; /* total 8 bytes */ ++ ++ZSTD_STATIC void FSE_initCState(FSE_CState_t *statePtr, const FSE_CTable *ct) ++{ ++ const void *ptr = ct; ++ const U16 *u16ptr = (const U16 *)ptr; ++ const U32 tableLog = ZSTD_read16(ptr); ++ statePtr->value = (ptrdiff_t)1 << tableLog; ++ statePtr->stateTable = u16ptr + 2; ++ statePtr->symbolTT = ((const U32 *)ct + 1 + (tableLog ? (1 << (tableLog - 1)) : 1)); ++ statePtr->stateLog = tableLog; ++} ++ ++/*! FSE_initCState2() : ++* Same as FSE_initCState(), but the first symbol to include (which will be the last to be read) ++* uses the smallest state value possible, saving the cost of this symbol */ ++ZSTD_STATIC void FSE_initCState2(FSE_CState_t *statePtr, const FSE_CTable *ct, U32 symbol) ++{ ++ FSE_initCState(statePtr, ct); ++ { ++ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; ++ const U16 *stateTable = (const U16 *)(statePtr->stateTable); ++ U32 nbBitsOut = (U32)((symbolTT.deltaNbBits + (1 << 15)) >> 16); ++ statePtr->value = (nbBitsOut << 16) - symbolTT.deltaNbBits; ++ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; ++ } ++} ++ ++ZSTD_STATIC void FSE_encodeSymbol(BIT_CStream_t *bitC, FSE_CState_t *statePtr, U32 symbol) ++{ ++ const FSE_symbolCompressionTransform symbolTT = ((const FSE_symbolCompressionTransform *)(statePtr->symbolTT))[symbol]; ++ const U16 *const stateTable = (const U16 *)(statePtr->stateTable); ++ U32 nbBitsOut = (U32)((statePtr->value + symbolTT.deltaNbBits) >> 16); ++ BIT_addBits(bitC, statePtr->value, nbBitsOut); ++ statePtr->value = stateTable[(statePtr->value >> nbBitsOut) + symbolTT.deltaFindState]; ++} ++ ++ZSTD_STATIC void FSE_flushCState(BIT_CStream_t *bitC, const FSE_CState_t *statePtr) ++{ ++ BIT_addBits(bitC, statePtr->value, statePtr->stateLog); ++ BIT_flushBits(bitC); ++} ++ ++/* ====== Decompression ====== */ ++ ++typedef struct { ++ U16 tableLog; ++ U16 fastMode; ++} FSE_DTableHeader; /* sizeof U32 */ ++ ++typedef struct { ++ unsigned short newState; ++ unsigned char symbol; ++ unsigned char nbBits; ++} FSE_decode_t; /* size == U32 */ ++ ++ZSTD_STATIC void FSE_initDState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD, const FSE_DTable *dt) ++{ ++ const void *ptr = dt; ++ const FSE_DTableHeader *const DTableH = (const FSE_DTableHeader *)ptr; ++ DStatePtr->state = BIT_readBits(bitD, DTableH->tableLog); ++ BIT_reloadDStream(bitD); ++ DStatePtr->table = dt + 1; ++} ++ ++ZSTD_STATIC BYTE FSE_peekSymbol(const FSE_DState_t *DStatePtr) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ return DInfo.symbol; ++} ++ ++ZSTD_STATIC void FSE_updateState(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ size_t const lowBits = BIT_readBits(bitD, nbBits); ++ DStatePtr->state = DInfo.newState + lowBits; ++} ++ ++ZSTD_STATIC BYTE FSE_decodeSymbol(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ BYTE const symbol = DInfo.symbol; ++ size_t const lowBits = BIT_readBits(bitD, nbBits); ++ ++ DStatePtr->state = DInfo.newState + lowBits; ++ return symbol; ++} ++ ++/*! FSE_decodeSymbolFast() : ++ unsafe, only works if no symbol has a probability > 50% */ ++ZSTD_STATIC BYTE FSE_decodeSymbolFast(FSE_DState_t *DStatePtr, BIT_DStream_t *bitD) ++{ ++ FSE_decode_t const DInfo = ((const FSE_decode_t *)(DStatePtr->table))[DStatePtr->state]; ++ U32 const nbBits = DInfo.nbBits; ++ BYTE const symbol = DInfo.symbol; ++ size_t const lowBits = BIT_readBitsFast(bitD, nbBits); ++ ++ DStatePtr->state = DInfo.newState + lowBits; ++ return symbol; ++} ++ ++ZSTD_STATIC unsigned FSE_endOfDState(const FSE_DState_t *DStatePtr) { return DStatePtr->state == 0; } ++ ++/* ************************************************************** ++* Tuning parameters ++****************************************************************/ ++/*!MEMORY_USAGE : ++* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) ++* Increasing memory usage improves compression ratio ++* Reduced memory usage can improve speed, due to cache effect ++* Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ ++#ifndef FSE_MAX_MEMORY_USAGE ++#define FSE_MAX_MEMORY_USAGE 14 ++#endif ++#ifndef FSE_DEFAULT_MEMORY_USAGE ++#define FSE_DEFAULT_MEMORY_USAGE 13 ++#endif ++ ++/*!FSE_MAX_SYMBOL_VALUE : ++* Maximum symbol value authorized. ++* Required for proper stack allocation */ ++#ifndef FSE_MAX_SYMBOL_VALUE ++#define FSE_MAX_SYMBOL_VALUE 255 ++#endif ++ ++/* ************************************************************** ++* template functions type & suffix ++****************************************************************/ ++#define FSE_FUNCTION_TYPE BYTE ++#define FSE_FUNCTION_EXTENSION ++#define FSE_DECODE_TYPE FSE_decode_t ++ ++/* *************************************************************** ++* Constants ++*****************************************************************/ ++#define FSE_MAX_TABLELOG (FSE_MAX_MEMORY_USAGE - 2) ++#define FSE_MAX_TABLESIZE (1U << FSE_MAX_TABLELOG) ++#define FSE_MAXTABLESIZE_MASK (FSE_MAX_TABLESIZE - 1) ++#define FSE_DEFAULT_TABLELOG (FSE_DEFAULT_MEMORY_USAGE - 2) ++#define FSE_MIN_TABLELOG 5 ++ ++#define FSE_TABLELOG_ABSOLUTE_MAX 15 ++#if FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX ++#error "FSE_MAX_TABLELOG > FSE_TABLELOG_ABSOLUTE_MAX is not supported" ++#endif ++ ++#define FSE_TABLESTEP(tableSize) ((tableSize >> 1) + (tableSize >> 3) + 3) ++ ++#endif /* FSE_H */ +diff --git a/xen/common/zstd/fse_decompress.c b/xen/common/zstd/fse_decompress.c +new file mode 100644 +index 0000000000..041a5a1f0a +--- /dev/null ++++ b/xen/common/zstd/fse_decompress.c +@@ -0,0 +1,323 @@ ++/* ++ * FSE : Finite State Entropy decoder ++ * Copyright (C) 2013-2015, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************************************** ++* Compiler specifics ++****************************************************************/ ++#define FORCE_INLINE static always_inline ++ ++/* ************************************************************** ++* Includes ++****************************************************************/ ++#include "bitstream.h" ++#include "fse.h" ++#include "zstd_internal.h" ++#include /* memcpy, memset */ ++ ++/* ************************************************************** ++* Error Management ++****************************************************************/ ++#define FSE_isError ERR_isError ++#define FSE_STATIC_ASSERT(c) \ ++ { \ ++ enum { FSE_static_assert = 1 / (int)(!!(c)) }; \ ++ } /* use only *after* variable declarations */ ++ ++/* ************************************************************** ++* Templates ++****************************************************************/ ++/* ++ designed to be included ++ for type-specific functions (template emulation in C) ++ Objective is to write these functions only once, for improved maintenance ++*/ ++ ++/* safety checks */ ++#ifndef FSE_FUNCTION_EXTENSION ++#error "FSE_FUNCTION_EXTENSION must be defined" ++#endif ++#ifndef FSE_FUNCTION_TYPE ++#error "FSE_FUNCTION_TYPE must be defined" ++#endif ++ ++/* Function names */ ++#define FSE_CAT(X, Y) X##Y ++#define FSE_FUNCTION_NAME(X, Y) FSE_CAT(X, Y) ++#define FSE_TYPE_NAME(X, Y) FSE_CAT(X, Y) ++ ++/* Function templates */ ++ ++size_t INIT FSE_buildDTable_wksp(FSE_DTable *dt, const short *normalizedCounter, unsigned maxSymbolValue, unsigned tableLog, void *workspace, size_t workspaceSize) ++{ ++ void *const tdPtr = dt + 1; /* because *dt is unsigned, 32-bits aligned on 32-bits */ ++ FSE_DECODE_TYPE *const tableDecode = (FSE_DECODE_TYPE *)(tdPtr); ++ U16 *symbolNext = (U16 *)workspace; ++ ++ U32 const maxSV1 = maxSymbolValue + 1; ++ U32 const tableSize = 1 << tableLog; ++ U32 highThreshold = tableSize - 1; ++ ++ /* Sanity Checks */ ++ if (workspaceSize < sizeof(U16) * (FSE_MAX_SYMBOL_VALUE + 1)) ++ return ERROR(tableLog_tooLarge); ++ if (maxSymbolValue > FSE_MAX_SYMBOL_VALUE) ++ return ERROR(maxSymbolValue_tooLarge); ++ if (tableLog > FSE_MAX_TABLELOG) ++ return ERROR(tableLog_tooLarge); ++ ++ /* Init, lay down lowprob symbols */ ++ { ++ FSE_DTableHeader DTableH; ++ DTableH.tableLog = (U16)tableLog; ++ DTableH.fastMode = 1; ++ { ++ S16 const largeLimit = (S16)(1 << (tableLog - 1)); ++ U32 s; ++ for (s = 0; s < maxSV1; s++) { ++ if (normalizedCounter[s] == -1) { ++ tableDecode[highThreshold--].symbol = (FSE_FUNCTION_TYPE)s; ++ symbolNext[s] = 1; ++ } else { ++ if (normalizedCounter[s] >= largeLimit) ++ DTableH.fastMode = 0; ++ symbolNext[s] = normalizedCounter[s]; ++ } ++ } ++ } ++ memcpy(dt, &DTableH, sizeof(DTableH)); ++ } ++ ++ /* Spread symbols */ ++ { ++ U32 const tableMask = tableSize - 1; ++ U32 const step = FSE_TABLESTEP(tableSize); ++ U32 s, position = 0; ++ for (s = 0; s < maxSV1; s++) { ++ int i; ++ for (i = 0; i < normalizedCounter[s]; i++) { ++ tableDecode[position].symbol = (FSE_FUNCTION_TYPE)s; ++ position = (position + step) & tableMask; ++ while (position > highThreshold) ++ position = (position + step) & tableMask; /* lowprob area */ ++ } ++ } ++ if (position != 0) ++ return ERROR(GENERIC); /* position must reach all cells once, otherwise normalizedCounter is incorrect */ ++ } ++ ++ /* Build Decoding table */ ++ { ++ U32 u; ++ for (u = 0; u < tableSize; u++) { ++ FSE_FUNCTION_TYPE const symbol = (FSE_FUNCTION_TYPE)(tableDecode[u].symbol); ++ U16 nextState = symbolNext[symbol]++; ++ tableDecode[u].nbBits = (BYTE)(tableLog - BIT_highbit32((U32)nextState)); ++ tableDecode[u].newState = (U16)((nextState << tableDecode[u].nbBits) - tableSize); ++ } ++ } ++ ++ return 0; ++} ++ ++/*-******************************************************* ++* Decompression (Byte symbols) ++*********************************************************/ ++size_t INIT FSE_buildDTable_rle(FSE_DTable *dt, BYTE symbolValue) ++{ ++ void *ptr = dt; ++ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; ++ void *dPtr = dt + 1; ++ FSE_decode_t *const cell = (FSE_decode_t *)dPtr; ++ ++ DTableH->tableLog = 0; ++ DTableH->fastMode = 0; ++ ++ cell->newState = 0; ++ cell->symbol = symbolValue; ++ cell->nbBits = 0; ++ ++ return 0; ++} ++ ++size_t INIT FSE_buildDTable_raw(FSE_DTable *dt, unsigned nbBits) ++{ ++ void *ptr = dt; ++ FSE_DTableHeader *const DTableH = (FSE_DTableHeader *)ptr; ++ void *dPtr = dt + 1; ++ FSE_decode_t *const dinfo = (FSE_decode_t *)dPtr; ++ const unsigned tableSize = 1 << nbBits; ++ const unsigned tableMask = tableSize - 1; ++ const unsigned maxSV1 = tableMask + 1; ++ unsigned s; ++ ++ /* Sanity checks */ ++ if (nbBits < 1) ++ return ERROR(GENERIC); /* min size */ ++ ++ /* Build Decoding Table */ ++ DTableH->tableLog = (U16)nbBits; ++ DTableH->fastMode = 1; ++ for (s = 0; s < maxSV1; s++) { ++ dinfo[s].newState = 0; ++ dinfo[s].symbol = (BYTE)s; ++ dinfo[s].nbBits = (BYTE)nbBits; ++ } ++ ++ return 0; ++} ++ ++FORCE_INLINE size_t FSE_decompress_usingDTable_generic(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt, ++ const unsigned fast) ++{ ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *op = ostart; ++ BYTE *const omax = op + maxDstSize; ++ BYTE *const olimit = omax - 3; ++ ++ BIT_DStream_t bitD; ++ FSE_DState_t state1; ++ FSE_DState_t state2; ++ ++ /* Init */ ++ CHECK_F(BIT_initDStream(&bitD, cSrc, cSrcSize)); ++ ++ FSE_initDState(&state1, &bitD, dt); ++ FSE_initDState(&state2, &bitD, dt); ++ ++#define FSE_GETSYMBOL(statePtr) fast ? FSE_decodeSymbolFast(statePtr, &bitD) : FSE_decodeSymbol(statePtr, &bitD) ++ ++ /* 4 symbols per loop */ ++ for (; (BIT_reloadDStream(&bitD) == BIT_DStream_unfinished) & (op < olimit); op += 4) { ++ op[0] = FSE_GETSYMBOL(&state1); ++ ++ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ BIT_reloadDStream(&bitD); ++ ++ op[1] = FSE_GETSYMBOL(&state2); ++ ++ if (FSE_MAX_TABLELOG * 4 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ { ++ if (BIT_reloadDStream(&bitD) > BIT_DStream_unfinished) { ++ op += 2; ++ break; ++ } ++ } ++ ++ op[2] = FSE_GETSYMBOL(&state1); ++ ++ if (FSE_MAX_TABLELOG * 2 + 7 > sizeof(bitD.bitContainer) * 8) /* This test must be static */ ++ BIT_reloadDStream(&bitD); ++ ++ op[3] = FSE_GETSYMBOL(&state2); ++ } ++ ++ /* tail */ ++ /* note : BIT_reloadDStream(&bitD) >= FSE_DStream_partiallyFilled; Ends at exactly BIT_DStream_completed */ ++ while (1) { ++ if (op > (omax - 2)) ++ return ERROR(dstSize_tooSmall); ++ *op++ = FSE_GETSYMBOL(&state1); ++ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { ++ *op++ = FSE_GETSYMBOL(&state2); ++ break; ++ } ++ ++ if (op > (omax - 2)) ++ return ERROR(dstSize_tooSmall); ++ *op++ = FSE_GETSYMBOL(&state2); ++ if (BIT_reloadDStream(&bitD) == BIT_DStream_overflow) { ++ *op++ = FSE_GETSYMBOL(&state1); ++ break; ++ } ++ } ++ ++ return op - ostart; ++} ++ ++size_t INIT FSE_decompress_usingDTable(void *dst, size_t originalSize, const void *cSrc, size_t cSrcSize, const FSE_DTable *dt) ++{ ++ const void *ptr = dt; ++ const FSE_DTableHeader *DTableH = (const FSE_DTableHeader *)ptr; ++ const U32 fastMode = DTableH->fastMode; ++ ++ /* select fast mode (static) */ ++ if (fastMode) ++ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 1); ++ return FSE_decompress_usingDTable_generic(dst, originalSize, cSrc, cSrcSize, dt, 0); ++} ++ ++size_t INIT FSE_decompress_wksp(void *dst, size_t dstCapacity, const void *cSrc, size_t cSrcSize, unsigned maxLog, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *const istart = (const BYTE *)cSrc; ++ const BYTE *ip = istart; ++ unsigned tableLog; ++ unsigned maxSymbolValue = FSE_MAX_SYMBOL_VALUE; ++ size_t NCountLength; ++ ++ FSE_DTable *dt; ++ short *counting; ++ size_t spaceUsed32 = 0; ++ ++ FSE_STATIC_ASSERT(sizeof(FSE_DTable) == sizeof(U32)); ++ ++ dt = (FSE_DTable *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += FSE_DTABLE_SIZE_U32(maxLog); ++ counting = (short *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(sizeof(short) * (FSE_MAX_SYMBOL_VALUE + 1), sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ /* normal FSE decoding mode */ ++ NCountLength = FSE_readNCount(counting, &maxSymbolValue, &tableLog, istart, cSrcSize); ++ if (FSE_isError(NCountLength)) ++ return NCountLength; ++ // if (NCountLength >= cSrcSize) return ERROR(srcSize_wrong); /* too small input size; supposed to be already checked in NCountLength, only remaining ++ // case : NCountLength==cSrcSize */ ++ if (tableLog > maxLog) ++ return ERROR(tableLog_tooLarge); ++ ip += NCountLength; ++ cSrcSize -= NCountLength; ++ ++ CHECK_F(FSE_buildDTable_wksp(dt, counting, maxSymbolValue, tableLog, workspace, workspaceSize)); ++ ++ return FSE_decompress_usingDTable(dst, dstCapacity, ip, cSrcSize, dt); /* always return, even if it is an error code */ ++} +diff --git a/xen/common/zstd/huf.h b/xen/common/zstd/huf.h +new file mode 100644 +index 0000000000..a9d522c7bb +--- /dev/null ++++ b/xen/common/zstd/huf.h +@@ -0,0 +1,212 @@ ++/* ++ * Huffman coder, part of New Generation Entropy library ++ * header file ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++#ifndef HUF_H_298734234 ++#define HUF_H_298734234 ++ ++/* *** Dependencies *** */ ++#include /* size_t */ ++ ++/* *** Tool functions *** */ ++#define HUF_BLOCKSIZE_MAX (128 * 1024) /**< maximum input size for a single block compressed with HUF_compress */ ++size_t HUF_compressBound(size_t size); /**< maximum compressed size (worst case) */ ++ ++/* Error Management */ ++unsigned HUF_isError(size_t code); /**< tells if a return value is an error code */ ++ ++/* *** Advanced function *** */ ++ ++/** HUF_compress4X_wksp() : ++* Same as HUF_compress2(), but uses externally allocated `workSpace`, which must be a table of >= 1024 unsigned */ ++size_t HUF_compress4X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++/* *** Dependencies *** */ ++#include "mem.h" /* U32 */ ++ ++/* *** Constants *** */ ++#define HUF_TABLELOG_MAX 12 /* max configured tableLog (for static allocation); can be modified up to HUF_ABSOLUTEMAX_TABLELOG */ ++#define HUF_TABLELOG_DEFAULT 11 /* tableLog by default, when not specified */ ++#define HUF_SYMBOLVALUE_MAX 255 ++ ++#define HUF_TABLELOG_ABSOLUTEMAX 15 /* absolute limit of HUF_MAX_TABLELOG. Beyond that value, code does not work */ ++#if (HUF_TABLELOG_MAX > HUF_TABLELOG_ABSOLUTEMAX) ++#error "HUF_TABLELOG_MAX is too large !" ++#endif ++ ++/* **************************************** ++* Static allocation ++******************************************/ ++/* HUF buffer bounds */ ++#define HUF_CTABLEBOUND 129 ++#define HUF_BLOCKBOUND(size) (size + (size >> 8) + 8) /* only true if incompressible pre-filtered with fast heuristic */ ++#define HUF_COMPRESSBOUND(size) (HUF_CTABLEBOUND + HUF_BLOCKBOUND(size)) /* Macro version, useful for static allocation */ ++ ++/* static allocation of HUF's Compression Table */ ++#define HUF_CREATE_STATIC_CTABLE(name, maxSymbolValue) \ ++ U32 name##hb[maxSymbolValue + 1]; \ ++ void *name##hv = &(name##hb); \ ++ HUF_CElt *name = (HUF_CElt *)(name##hv) /* no final ; */ ++ ++/* static allocation of HUF's DTable */ ++typedef U32 HUF_DTable; ++#define HUF_DTABLE_SIZE(maxTableLog) (1 + (1 << (maxTableLog))) ++#define HUF_CREATE_STATIC_DTABLEX2(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE((maxTableLog)-1)] = {((U32)((maxTableLog)-1) * 0x01000001)} ++#define HUF_CREATE_STATIC_DTABLEX4(DTable, maxTableLog) HUF_DTable DTable[HUF_DTABLE_SIZE(maxTableLog)] = {((U32)(maxTableLog)*0x01000001)} ++ ++/* The workspace must have alignment at least 4 and be at least this large */ ++#define HUF_COMPRESS_WORKSPACE_SIZE (6 << 10) ++#define HUF_COMPRESS_WORKSPACE_SIZE_U32 (HUF_COMPRESS_WORKSPACE_SIZE / sizeof(U32)) ++ ++/* The workspace must have alignment at least 4 and be at least this large */ ++#define HUF_DECOMPRESS_WORKSPACE_SIZE (3 << 10) ++#define HUF_DECOMPRESS_WORKSPACE_SIZE_U32 (HUF_DECOMPRESS_WORKSPACE_SIZE / sizeof(U32)) ++ ++/* **************************************** ++* Advanced decompression functions ++******************************************/ ++size_t HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); /**< decodes RLE and uncompressed */ ++size_t HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< considers RLE and uncompressed as errors */ ++size_t HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< single-symbol decoder */ ++size_t HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< double-symbols decoder */ ++ ++/* **************************************** ++* HUF detailed API ++******************************************/ ++/*! ++HUF_compress() does the following: ++1. count symbol occurrence from source[] into table count[] using FSE_count() ++2. (optional) refine tableLog using HUF_optimalTableLog() ++3. build Huffman table from count using HUF_buildCTable() ++4. save Huffman table to memory buffer using HUF_writeCTable_wksp() ++5. encode the data stream using HUF_compress4X_usingCTable() ++ ++The following API allows targeting specific sub-functions for advanced tasks. ++For example, it's possible to compress several blocks using the same 'CTable', ++or to save and regenerate 'CTable' using external methods. ++*/ ++/* FSE_count() : find it within "fse.h" */ ++unsigned HUF_optimalTableLog(unsigned maxTableLog, size_t srcSize, unsigned maxSymbolValue); ++typedef struct HUF_CElt_s HUF_CElt; /* incomplete type */ ++size_t HUF_writeCTable_wksp(void *dst, size_t maxDstSize, const HUF_CElt *CTable, unsigned maxSymbolValue, unsigned huffLog, void *workspace, size_t workspaceSize); ++size_t HUF_compress4X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); ++ ++typedef enum { ++ HUF_repeat_none, /**< Cannot use the previous table */ ++ HUF_repeat_check, /**< Can use the previous table but it must be checked. Note : The previous table must have been constructed by HUF_compress{1, ++ 4}X_repeat */ ++ HUF_repeat_valid /**< Can use the previous table and it is asumed to be valid */ ++} HUF_repeat; ++/** HUF_compress4X_repeat() : ++* Same as HUF_compress4X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. ++* If it uses hufTable it does not modify hufTable or repeat. ++* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. ++* If preferRepeat then the old table will always be used if valid. */ ++size_t HUF_compress4X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, ++ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++/** HUF_buildCTable_wksp() : ++ * Same as HUF_buildCTable(), but using externally allocated scratch buffer. ++ * `workSpace` must be aligned on 4-bytes boundaries, and be at least as large as a table of 1024 unsigned. ++ */ ++size_t HUF_buildCTable_wksp(HUF_CElt *tree, const U32 *count, U32 maxSymbolValue, U32 maxNbBits, void *workSpace, size_t wkspSize); ++ ++/*! HUF_readStats() : ++ Read compact Huffman tree, saved by HUF_writeCTable(). ++ `huffWeight` is destination buffer. ++ @return : size read from `src` , or an error Code . ++ Note : Needed by HUF_readCTable() and HUF_readDTableXn() . */ ++size_t HUF_readStats_wksp(BYTE *huffWeight, size_t hwSize, U32 *rankStats, U32 *nbSymbolsPtr, U32 *tableLogPtr, const void *src, size_t srcSize, ++ void *workspace, size_t workspaceSize); ++ ++/** HUF_readCTable() : ++* Loading a CTable saved with HUF_writeCTable() */ ++size_t HUF_readCTable_wksp(HUF_CElt *CTable, unsigned maxSymbolValue, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++ ++/* ++HUF_decompress() does the following: ++1. select the decompression algorithm (X2, X4) based on pre-computed heuristics ++2. build Huffman table from save, using HUF_readDTableXn() ++3. decode 1 or 4 segments in parallel using HUF_decompressSXn_usingDTable ++*/ ++ ++/** HUF_selectDecoder() : ++* Tells which decoder is likely to decode faster, ++* based on a set of pre-determined metrics. ++* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . ++* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ ++U32 HUF_selectDecoder(size_t dstSize, size_t cSrcSize); ++ ++size_t HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++size_t HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize); ++ ++size_t HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress4X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress4X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++ ++/* single stream variants */ ++ ++size_t HUF_compress1X_wksp(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++size_t HUF_compress1X_usingCTable(void *dst, size_t dstSize, const void *src, size_t srcSize, const HUF_CElt *CTable); ++/** HUF_compress1X_repeat() : ++* Same as HUF_compress1X_wksp(), but considers using hufTable if *repeat != HUF_repeat_none. ++* If it uses hufTable it does not modify hufTable or repeat. ++* If it doesn't, it sets *repeat = HUF_repeat_none, and it sets hufTable to the table used. ++* If preferRepeat then the old table will always be used if valid. */ ++size_t HUF_compress1X_repeat(void *dst, size_t dstSize, const void *src, size_t srcSize, unsigned maxSymbolValue, unsigned tableLog, void *workSpace, ++ size_t wkspSize, HUF_CElt *hufTable, HUF_repeat *repeat, ++ int preferRepeat); /**< `workSpace` must be a table of at least HUF_COMPRESS_WORKSPACE_SIZE_U32 unsigned */ ++ ++size_t HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize); ++size_t HUF_decompress1X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< single-symbol decoder */ ++size_t HUF_decompress1X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, ++ size_t workspaceSize); /**< double-symbols decoder */ ++ ++size_t HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, ++ const HUF_DTable *DTable); /**< automatic selection of sing or double symbol decoder, based on DTable */ ++size_t HUF_decompress1X2_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++size_t HUF_decompress1X4_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable); ++ ++#endif /* HUF_H_298734234 */ +diff --git a/xen/common/zstd/huf_decompress.c b/xen/common/zstd/huf_decompress.c +new file mode 100644 +index 0000000000..f79603a12f +--- /dev/null ++++ b/xen/common/zstd/huf_decompress.c +@@ -0,0 +1,958 @@ ++/* ++ * Huffman decoder, part of New Generation Entropy library ++ * Copyright (C) 2013-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at : ++ * - Source repository : https://github.com/Cyan4973/FiniteStateEntropy ++ */ ++ ++/* ************************************************************** ++* Compiler specifics ++****************************************************************/ ++#define FORCE_INLINE static always_inline ++ ++/* ************************************************************** ++* Dependencies ++****************************************************************/ ++#include "bitstream.h" /* BIT_* */ ++#include "fse.h" /* header compression */ ++#include "huf.h" ++#include /* memcpy, memset */ ++ ++/* ************************************************************** ++* Error Management ++****************************************************************/ ++#define HUF_STATIC_ASSERT(c) \ ++ { \ ++ enum { HUF_static_assert = 1 / (int)(!!(c)) }; \ ++ } /* use only *after* variable declarations */ ++ ++/*-***************************/ ++/* generic DTableDesc */ ++/*-***************************/ ++ ++typedef struct { ++ BYTE maxTableLog; ++ BYTE tableType; ++ BYTE tableLog; ++ BYTE reserved; ++} DTableDesc; ++ ++static DTableDesc INIT HUF_getDTableDesc(const HUF_DTable *table) ++{ ++ DTableDesc dtd; ++ memcpy(&dtd, table, sizeof(dtd)); ++ return dtd; ++} ++ ++/*-***************************/ ++/* single-symbol decoding */ ++/*-***************************/ ++ ++typedef struct { ++ BYTE byte; ++ BYTE nbBits; ++} HUF_DEltX2; /* single-symbol decoding */ ++ ++size_t INIT HUF_readDTableX2_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 tableLog = 0; ++ U32 nbSymbols = 0; ++ size_t iSize; ++ void *const dtPtr = DTable + 1; ++ HUF_DEltX2 *const dt = (HUF_DEltX2 *)dtPtr; ++ ++ U32 *rankVal; ++ BYTE *huffWeight; ++ size_t spaceUsed32 = 0; ++ ++ rankVal = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_ABSOLUTEMAX + 1; ++ huffWeight = (BYTE *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ HUF_STATIC_ASSERT(sizeof(DTableDesc) == sizeof(HUF_DTable)); ++ /* memset(huffWeight, 0, sizeof(huffWeight)); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ iSize = HUF_readStats_wksp(huffWeight, HUF_SYMBOLVALUE_MAX + 1, rankVal, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); ++ if (HUF_isError(iSize)) ++ return iSize; ++ ++ /* Table header */ ++ { ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (tableLog > (U32)(dtd.maxTableLog + 1)) ++ return ERROR(tableLog_tooLarge); /* DTable too small, Huffman tree cannot fit in */ ++ dtd.tableType = 0; ++ dtd.tableLog = (BYTE)tableLog; ++ memcpy(DTable, &dtd, sizeof(dtd)); ++ } ++ ++ /* Calculate starting value for each rank */ ++ { ++ U32 n, nextRankStart = 0; ++ for (n = 1; n < tableLog + 1; n++) { ++ U32 const curr = nextRankStart; ++ nextRankStart += (rankVal[n] << (n - 1)); ++ rankVal[n] = curr; ++ } ++ } ++ ++ /* fill DTable */ ++ { ++ U32 n; ++ for (n = 0; n < nbSymbols; n++) { ++ U32 const w = huffWeight[n]; ++ U32 const length = (1 << w) >> 1; ++ U32 u; ++ HUF_DEltX2 D; ++ D.byte = (BYTE)n; ++ D.nbBits = (BYTE)(tableLog + 1 - w); ++ for (u = rankVal[w]; u < rankVal[w] + length; u++) ++ dt[u] = D; ++ rankVal[w] += length; ++ } ++ } ++ ++ return iSize; ++} ++ ++static BYTE INIT HUF_decodeSymbolX2(BIT_DStream_t *Dstream, const HUF_DEltX2 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(Dstream, dtLog); /* note : dtLog >= 1 */ ++ BYTE const c = dt[val].byte; ++ BIT_skipBits(Dstream, dt[val].nbBits); ++ return c; ++} ++ ++#define HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) *ptr++ = HUF_decodeSymbolX2(DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX2_1(ptr, DStreamPtr) \ ++ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ ++ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) ++ ++#define HUF_DECODE_SYMBOLX2_2(ptr, DStreamPtr) \ ++ if (ZSTD_64bits()) \ ++ HUF_DECODE_SYMBOLX2_0(ptr, DStreamPtr) ++ ++FORCE_INLINE size_t HUF_decodeStreamX2(BYTE *p, BIT_DStream_t *const bitDPtr, BYTE *const pEnd, const HUF_DEltX2 *const dt, const U32 dtLog) ++{ ++ BYTE *const pStart = p; ++ ++ /* up to 4 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p <= pEnd - 4)) { ++ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_1(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ } ++ ++ /* closer to the end */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) && (p < pEnd)) ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ ++ /* no more data to retrieve from bitstream, hence no need to reload */ ++ while (p < pEnd) ++ HUF_DECODE_SYMBOLX2_0(p, bitDPtr); ++ ++ return pEnd - pStart; ++} ++ ++static size_t INIT HUF_decompress1X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ BYTE *op = (BYTE *)dst; ++ BYTE *const oend = op + dstSize; ++ const void *dtPtr = DTable + 1; ++ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; ++ BIT_DStream_t bitD; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ HUF_decodeStreamX2(op, &bitD, oend, dt, dtLog); ++ ++ /* check */ ++ if (!BIT_endOfDStream(&bitD)) ++ return ERROR(corruption_detected); ++ ++ return dstSize; ++} ++ ++size_t INIT HUF_decompress1X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 0) ++ return ERROR(GENERIC); ++ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress1X2_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX2_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress1X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); ++} ++ ++static size_t INIT HUF_decompress4X2_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ /* Check */ ++ if (cSrcSize < 10) ++ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ ++ ++ { ++ const BYTE *const istart = (const BYTE *)cSrc; ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; ++ const HUF_DEltX2 *const dt = (const HUF_DEltX2 *)dtPtr; ++ ++ /* Init */ ++ BIT_DStream_t bitD1; ++ BIT_DStream_t bitD2; ++ BIT_DStream_t bitD3; ++ BIT_DStream_t bitD4; ++ size_t const length1 = ZSTD_readLE16(istart); ++ size_t const length2 = ZSTD_readLE16(istart + 2); ++ size_t const length3 = ZSTD_readLE16(istart + 4); ++ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); ++ const BYTE *const istart1 = istart + 6; /* jumpTable */ ++ const BYTE *const istart2 = istart1 + length1; ++ const BYTE *const istart3 = istart2 + length2; ++ const BYTE *const istart4 = istart3 + length3; ++ const size_t segmentSize = (dstSize + 3) / 4; ++ BYTE *const opStart2 = ostart + segmentSize; ++ BYTE *const opStart3 = opStart2 + segmentSize; ++ BYTE *const opStart4 = opStart3 + segmentSize; ++ BYTE *op1 = ostart; ++ BYTE *op2 = opStart2; ++ BYTE *op3 = opStart3; ++ BYTE *op4 = opStart4; ++ U32 endSignal; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ if (length4 > cSrcSize) ++ return ERROR(corruption_detected); /* overflow */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* 16-32 symbols per loop (4-8 symbols per stream) */ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ for (; (endSignal == BIT_DStream_unfinished) && (op4 < (oend - 7));) { ++ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_1(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_1(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_1(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_1(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX2_0(op1, &bitD1); ++ HUF_DECODE_SYMBOLX2_0(op2, &bitD2); ++ HUF_DECODE_SYMBOLX2_0(op3, &bitD3); ++ HUF_DECODE_SYMBOLX2_0(op4, &bitD4); ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ } ++ ++ /* check corruption */ ++ if (op1 > opStart2) ++ return ERROR(corruption_detected); ++ if (op2 > opStart3) ++ return ERROR(corruption_detected); ++ if (op3 > opStart4) ++ return ERROR(corruption_detected); ++ /* note : op4 supposed already verified within main loop */ ++ ++ /* finish bitStreams one by one */ ++ HUF_decodeStreamX2(op1, &bitD1, opStart2, dt, dtLog); ++ HUF_decodeStreamX2(op2, &bitD2, opStart3, dt, dtLog); ++ HUF_decodeStreamX2(op3, &bitD3, opStart4, dt, dtLog); ++ HUF_decodeStreamX2(op4, &bitD4, oend, dt, dtLog); ++ ++ /* check */ ++ endSignal = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); ++ if (!endSignal) ++ return ERROR(corruption_detected); ++ ++ /* decoded size */ ++ return dstSize; ++ } ++} ++ ++size_t INIT HUF_decompress4X2_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 0) ++ return ERROR(GENERIC); ++ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X2_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX2_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress4X2_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); ++} ++ ++/* *************************/ ++/* double-symbols decoding */ ++/* *************************/ ++typedef struct { ++ U16 sequence; ++ BYTE nbBits; ++ BYTE length; ++} HUF_DEltX4; /* double-symbols decoding */ ++ ++typedef struct { ++ BYTE symbol; ++ BYTE weight; ++} sortedSymbol_t; ++ ++/* HUF_fillDTableX4Level2() : ++ * `rankValOrigin` must be a table of at least (HUF_TABLELOG_MAX + 1) U32 */ ++static void INIT HUF_fillDTableX4Level2(HUF_DEltX4 *DTable, U32 sizeLog, const U32 consumed, const U32 *rankValOrigin, const int minWeight, ++ const sortedSymbol_t *sortedSymbols, const U32 sortedListSize, U32 nbBitsBaseline, U16 baseSeq) ++{ ++ HUF_DEltX4 DElt; ++ U32 rankVal[HUF_TABLELOG_MAX + 1]; ++ ++ /* get pre-calculated rankVal */ ++ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); ++ ++ /* fill skipped values */ ++ if (minWeight > 1) { ++ U32 i, skipSize = rankVal[minWeight]; ++ ZSTD_writeLE16(&(DElt.sequence), baseSeq); ++ DElt.nbBits = (BYTE)(consumed); ++ DElt.length = 1; ++ for (i = 0; i < skipSize; i++) ++ DTable[i] = DElt; ++ } ++ ++ /* fill DTable */ ++ { ++ U32 s; ++ for (s = 0; s < sortedListSize; s++) { /* note : sortedSymbols already skipped */ ++ const U32 symbol = sortedSymbols[s].symbol; ++ const U32 weight = sortedSymbols[s].weight; ++ const U32 nbBits = nbBitsBaseline - weight; ++ const U32 length = 1 << (sizeLog - nbBits); ++ const U32 start = rankVal[weight]; ++ U32 i = start; ++ const U32 end = start + length; ++ ++ ZSTD_writeLE16(&(DElt.sequence), (U16)(baseSeq + (symbol << 8))); ++ DElt.nbBits = (BYTE)(nbBits + consumed); ++ DElt.length = 2; ++ do { ++ DTable[i++] = DElt; ++ } while (i < end); /* since length >= 1 */ ++ ++ rankVal[weight] += length; ++ } ++ } ++} ++ ++typedef U32 rankVal_t[HUF_TABLELOG_MAX][HUF_TABLELOG_MAX + 1]; ++typedef U32 rankValCol_t[HUF_TABLELOG_MAX + 1]; ++ ++static void INIT HUF_fillDTableX4(HUF_DEltX4 *DTable, const U32 targetLog, const sortedSymbol_t *sortedList, const U32 sortedListSize, const U32 *rankStart, ++ rankVal_t rankValOrigin, const U32 maxWeight, const U32 nbBitsBaseline) ++{ ++ U32 rankVal[HUF_TABLELOG_MAX + 1]; ++ const int scaleLog = nbBitsBaseline - targetLog; /* note : targetLog >= srcLog, hence scaleLog <= 1 */ ++ const U32 minBits = nbBitsBaseline - maxWeight; ++ U32 s; ++ ++ memcpy(rankVal, rankValOrigin, sizeof(rankVal)); ++ ++ /* fill DTable */ ++ for (s = 0; s < sortedListSize; s++) { ++ const U16 symbol = sortedList[s].symbol; ++ const U32 weight = sortedList[s].weight; ++ const U32 nbBits = nbBitsBaseline - weight; ++ const U32 start = rankVal[weight]; ++ const U32 length = 1 << (targetLog - nbBits); ++ ++ if (targetLog - nbBits >= minBits) { /* enough room for a second symbol */ ++ U32 sortedRank; ++ int minWeight = nbBits + scaleLog; ++ if (minWeight < 1) ++ minWeight = 1; ++ sortedRank = rankStart[minWeight]; ++ HUF_fillDTableX4Level2(DTable + start, targetLog - nbBits, nbBits, rankValOrigin[nbBits], minWeight, sortedList + sortedRank, ++ sortedListSize - sortedRank, nbBitsBaseline, symbol); ++ } else { ++ HUF_DEltX4 DElt; ++ ZSTD_writeLE16(&(DElt.sequence), symbol); ++ DElt.nbBits = (BYTE)(nbBits); ++ DElt.length = 1; ++ { ++ U32 const end = start + length; ++ U32 u; ++ for (u = start; u < end; u++) ++ DTable[u] = DElt; ++ } ++ } ++ rankVal[weight] += length; ++ } ++} ++ ++size_t INIT HUF_readDTableX4_wksp(HUF_DTable *DTable, const void *src, size_t srcSize, void *workspace, size_t workspaceSize) ++{ ++ U32 tableLog, maxW, sizeOfSort, nbSymbols; ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ U32 const maxTableLog = dtd.maxTableLog; ++ size_t iSize; ++ void *dtPtr = DTable + 1; /* force compiler to avoid strict-aliasing */ ++ HUF_DEltX4 *const dt = (HUF_DEltX4 *)dtPtr; ++ U32 *rankStart; ++ ++ rankValCol_t *rankVal; ++ U32 *rankStats; ++ U32 *rankStart0; ++ sortedSymbol_t *sortedSymbol; ++ BYTE *weightList; ++ size_t spaceUsed32 = 0; ++ ++ HUF_STATIC_ASSERT((sizeof(rankValCol_t) & 3) == 0); ++ ++ rankVal = (rankValCol_t *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += (sizeof(rankValCol_t) * HUF_TABLELOG_MAX) >> 2; ++ rankStats = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_MAX + 1; ++ rankStart0 = (U32 *)workspace + spaceUsed32; ++ spaceUsed32 += HUF_TABLELOG_MAX + 2; ++ sortedSymbol = (sortedSymbol_t *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(sizeof(sortedSymbol_t) * (HUF_SYMBOLVALUE_MAX + 1), sizeof(U32)) >> 2; ++ weightList = (BYTE *)((U32 *)workspace + spaceUsed32); ++ spaceUsed32 += ALIGN(HUF_SYMBOLVALUE_MAX + 1, sizeof(U32)) >> 2; ++ ++ if ((spaceUsed32 << 2) > workspaceSize) ++ return ERROR(tableLog_tooLarge); ++ workspace = (U32 *)workspace + spaceUsed32; ++ workspaceSize -= (spaceUsed32 << 2); ++ ++ rankStart = rankStart0 + 1; ++ memset(rankStats, 0, sizeof(U32) * (2 * HUF_TABLELOG_MAX + 2 + 1)); ++ ++ HUF_STATIC_ASSERT(sizeof(HUF_DEltX4) == sizeof(HUF_DTable)); /* if compiler fails here, assertion is wrong */ ++ if (maxTableLog > HUF_TABLELOG_MAX) ++ return ERROR(tableLog_tooLarge); ++ /* memset(weightList, 0, sizeof(weightList)); */ /* is not necessary, even though some analyzer complain ... */ ++ ++ iSize = HUF_readStats_wksp(weightList, HUF_SYMBOLVALUE_MAX + 1, rankStats, &nbSymbols, &tableLog, src, srcSize, workspace, workspaceSize); ++ if (HUF_isError(iSize)) ++ return iSize; ++ ++ /* check result */ ++ if (tableLog > maxTableLog) ++ return ERROR(tableLog_tooLarge); /* DTable can't fit code depth */ ++ ++ /* find maxWeight */ ++ for (maxW = tableLog; rankStats[maxW] == 0; maxW--) { ++ } /* necessarily finds a solution before 0 */ ++ ++ /* Get start index of each weight */ ++ { ++ U32 w, nextRankStart = 0; ++ for (w = 1; w < maxW + 1; w++) { ++ U32 curr = nextRankStart; ++ nextRankStart += rankStats[w]; ++ rankStart[w] = curr; ++ } ++ rankStart[0] = nextRankStart; /* put all 0w symbols at the end of sorted list*/ ++ sizeOfSort = nextRankStart; ++ } ++ ++ /* sort symbols by weight */ ++ { ++ U32 s; ++ for (s = 0; s < nbSymbols; s++) { ++ U32 const w = weightList[s]; ++ U32 const r = rankStart[w]++; ++ sortedSymbol[r].symbol = (BYTE)s; ++ sortedSymbol[r].weight = (BYTE)w; ++ } ++ rankStart[0] = 0; /* forget 0w symbols; this is beginning of weight(1) */ ++ } ++ ++ /* Build rankVal */ ++ { ++ U32 *const rankVal0 = rankVal[0]; ++ { ++ int const rescale = (maxTableLog - tableLog) - 1; /* tableLog <= maxTableLog */ ++ U32 nextRankVal = 0; ++ U32 w; ++ for (w = 1; w < maxW + 1; w++) { ++ U32 curr = nextRankVal; ++ nextRankVal += rankStats[w] << (w + rescale); ++ rankVal0[w] = curr; ++ } ++ } ++ { ++ U32 const minBits = tableLog + 1 - maxW; ++ U32 consumed; ++ for (consumed = minBits; consumed < maxTableLog - minBits + 1; consumed++) { ++ U32 *const rankValPtr = rankVal[consumed]; ++ U32 w; ++ for (w = 1; w < maxW + 1; w++) { ++ rankValPtr[w] = rankVal0[w] >> consumed; ++ } ++ } ++ } ++ } ++ ++ HUF_fillDTableX4(dt, maxTableLog, sortedSymbol, sizeOfSort, rankStart0, rankVal, maxW, tableLog + 1); ++ ++ dtd.tableLog = (BYTE)maxTableLog; ++ dtd.tableType = 1; ++ memcpy(DTable, &dtd, sizeof(dtd)); ++ return iSize; ++} ++ ++static U32 INIT HUF_decodeSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ ++ memcpy(op, dt + val, 2); ++ BIT_skipBits(DStream, dt[val].nbBits); ++ return dt[val].length; ++} ++ ++static U32 INIT HUF_decodeLastSymbolX4(void *op, BIT_DStream_t *DStream, const HUF_DEltX4 *dt, const U32 dtLog) ++{ ++ size_t const val = BIT_lookBitsFast(DStream, dtLog); /* note : dtLog >= 1 */ ++ memcpy(op, dt + val, 1); ++ if (dt[val].length == 1) ++ BIT_skipBits(DStream, dt[val].nbBits); ++ else { ++ if (DStream->bitsConsumed < (sizeof(DStream->bitContainer) * 8)) { ++ BIT_skipBits(DStream, dt[val].nbBits); ++ if (DStream->bitsConsumed > (sizeof(DStream->bitContainer) * 8)) ++ /* ugly hack; works only because it's the last symbol. Note : can't easily extract nbBits from just this symbol */ ++ DStream->bitsConsumed = (sizeof(DStream->bitContainer) * 8); ++ } ++ } ++ return 1; ++} ++ ++#define HUF_DECODE_SYMBOLX4_0(ptr, DStreamPtr) ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX4_1(ptr, DStreamPtr) \ ++ if (ZSTD_64bits() || (HUF_TABLELOG_MAX <= 12)) \ ++ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++#define HUF_DECODE_SYMBOLX4_2(ptr, DStreamPtr) \ ++ if (ZSTD_64bits()) \ ++ ptr += HUF_decodeSymbolX4(ptr, DStreamPtr, dt, dtLog) ++ ++FORCE_INLINE size_t HUF_decodeStreamX4(BYTE *p, BIT_DStream_t *bitDPtr, BYTE *const pEnd, const HUF_DEltX4 *const dt, const U32 dtLog) ++{ ++ BYTE *const pStart = p; ++ ++ /* up to 8 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p < pEnd - (sizeof(bitDPtr->bitContainer) - 1))) { ++ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_1(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_2(p, bitDPtr); ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); ++ } ++ ++ /* closer to end : up to 2 symbols at a time */ ++ while ((BIT_reloadDStream(bitDPtr) == BIT_DStream_unfinished) & (p <= pEnd - 2)) ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); ++ ++ while (p <= pEnd - 2) ++ HUF_DECODE_SYMBOLX4_0(p, bitDPtr); /* no need to reload : reached the end of DStream */ ++ ++ if (p < pEnd) ++ p += HUF_decodeLastSymbolX4(p, bitDPtr, dt, dtLog); ++ ++ return p - pStart; ++} ++ ++static size_t INIT HUF_decompress1X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ BIT_DStream_t bitD; ++ ++ /* Init */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD, cSrc, cSrcSize); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* decode */ ++ { ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; /* force compiler to not use strict-aliasing */ ++ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ HUF_decodeStreamX4(ostart, &bitD, oend, dt, dtd.tableLog); ++ } ++ ++ /* check */ ++ if (!BIT_endOfDStream(&bitD)) ++ return ERROR(corruption_detected); ++ ++ /* decoded size */ ++ return dstSize; ++} ++ ++size_t INIT HUF_decompress1X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 1) ++ return ERROR(GENERIC); ++ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress1X4_DCtx_wksp(HUF_DTable *DCtx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t const hSize = HUF_readDTableX4_wksp(DCtx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress1X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, DCtx); ++} ++ ++static size_t INIT HUF_decompress4X4_usingDTable_internal(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ if (cSrcSize < 10) ++ return ERROR(corruption_detected); /* strict minimum : jump table + 1 byte per stream */ ++ ++ { ++ const BYTE *const istart = (const BYTE *)cSrc; ++ BYTE *const ostart = (BYTE *)dst; ++ BYTE *const oend = ostart + dstSize; ++ const void *const dtPtr = DTable + 1; ++ const HUF_DEltX4 *const dt = (const HUF_DEltX4 *)dtPtr; ++ ++ /* Init */ ++ BIT_DStream_t bitD1; ++ BIT_DStream_t bitD2; ++ BIT_DStream_t bitD3; ++ BIT_DStream_t bitD4; ++ size_t const length1 = ZSTD_readLE16(istart); ++ size_t const length2 = ZSTD_readLE16(istart + 2); ++ size_t const length3 = ZSTD_readLE16(istart + 4); ++ size_t const length4 = cSrcSize - (length1 + length2 + length3 + 6); ++ const BYTE *const istart1 = istart + 6; /* jumpTable */ ++ const BYTE *const istart2 = istart1 + length1; ++ const BYTE *const istart3 = istart2 + length2; ++ const BYTE *const istart4 = istart3 + length3; ++ size_t const segmentSize = (dstSize + 3) / 4; ++ BYTE *const opStart2 = ostart + segmentSize; ++ BYTE *const opStart3 = opStart2 + segmentSize; ++ BYTE *const opStart4 = opStart3 + segmentSize; ++ BYTE *op1 = ostart; ++ BYTE *op2 = opStart2; ++ BYTE *op3 = opStart3; ++ BYTE *op4 = opStart4; ++ U32 endSignal; ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ U32 const dtLog = dtd.tableLog; ++ ++ if (length4 > cSrcSize) ++ return ERROR(corruption_detected); /* overflow */ ++ { ++ size_t const errorCode = BIT_initDStream(&bitD1, istart1, length1); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD2, istart2, length2); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD3, istart3, length3); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ { ++ size_t const errorCode = BIT_initDStream(&bitD4, istart4, length4); ++ if (HUF_isError(errorCode)) ++ return errorCode; ++ } ++ ++ /* 16-32 symbols per loop (4-8 symbols per stream) */ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ for (; (endSignal == BIT_DStream_unfinished) & (op4 < (oend - (sizeof(bitD4.bitContainer) - 1)));) { ++ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_1(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_1(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_1(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_1(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_2(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_2(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_2(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_2(op4, &bitD4); ++ HUF_DECODE_SYMBOLX4_0(op1, &bitD1); ++ HUF_DECODE_SYMBOLX4_0(op2, &bitD2); ++ HUF_DECODE_SYMBOLX4_0(op3, &bitD3); ++ HUF_DECODE_SYMBOLX4_0(op4, &bitD4); ++ ++ endSignal = BIT_reloadDStream(&bitD1) | BIT_reloadDStream(&bitD2) | BIT_reloadDStream(&bitD3) | BIT_reloadDStream(&bitD4); ++ } ++ ++ /* check corruption */ ++ if (op1 > opStart2) ++ return ERROR(corruption_detected); ++ if (op2 > opStart3) ++ return ERROR(corruption_detected); ++ if (op3 > opStart4) ++ return ERROR(corruption_detected); ++ /* note : op4 already verified within main loop */ ++ ++ /* finish bitStreams one by one */ ++ HUF_decodeStreamX4(op1, &bitD1, opStart2, dt, dtLog); ++ HUF_decodeStreamX4(op2, &bitD2, opStart3, dt, dtLog); ++ HUF_decodeStreamX4(op3, &bitD3, opStart4, dt, dtLog); ++ HUF_decodeStreamX4(op4, &bitD4, oend, dt, dtLog); ++ ++ /* check */ ++ { ++ U32 const endCheck = BIT_endOfDStream(&bitD1) & BIT_endOfDStream(&bitD2) & BIT_endOfDStream(&bitD3) & BIT_endOfDStream(&bitD4); ++ if (!endCheck) ++ return ERROR(corruption_detected); ++ } ++ ++ /* decoded size */ ++ return dstSize; ++ } ++} ++ ++size_t INIT HUF_decompress4X4_usingDTable(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc dtd = HUF_getDTableDesc(DTable); ++ if (dtd.tableType != 1) ++ return ERROR(GENERIC); ++ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X4_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ const BYTE *ip = (const BYTE *)cSrc; ++ ++ size_t hSize = HUF_readDTableX4_wksp(dctx, cSrc, cSrcSize, workspace, workspaceSize); ++ if (HUF_isError(hSize)) ++ return hSize; ++ if (hSize >= cSrcSize) ++ return ERROR(srcSize_wrong); ++ ip += hSize; ++ cSrcSize -= hSize; ++ ++ return HUF_decompress4X4_usingDTable_internal(dst, dstSize, ip, cSrcSize, dctx); ++} ++ ++/* ********************************/ ++/* Generic decompression selector */ ++/* ********************************/ ++ ++size_t INIT HUF_decompress1X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ return dtd.tableType ? HUF_decompress1X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) ++ : HUF_decompress1X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); ++} ++ ++size_t INIT HUF_decompress4X_usingDTable(void *dst, size_t maxDstSize, const void *cSrc, size_t cSrcSize, const HUF_DTable *DTable) ++{ ++ DTableDesc const dtd = HUF_getDTableDesc(DTable); ++ return dtd.tableType ? HUF_decompress4X4_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable) ++ : HUF_decompress4X2_usingDTable_internal(dst, maxDstSize, cSrc, cSrcSize, DTable); ++} ++ ++typedef struct { ++ U32 tableTime; ++ U32 decode256Time; ++} algo_time_t; ++static const algo_time_t algoTime[16 /* Quantization */][3 /* single, double, quad */] = { ++ /* single, double, quad */ ++ {{0, 0}, {1, 1}, {2, 2}}, /* Q==0 : impossible */ ++ {{0, 0}, {1, 1}, {2, 2}}, /* Q==1 : impossible */ ++ {{38, 130}, {1313, 74}, {2151, 38}}, /* Q == 2 : 12-18% */ ++ {{448, 128}, {1353, 74}, {2238, 41}}, /* Q == 3 : 18-25% */ ++ {{556, 128}, {1353, 74}, {2238, 47}}, /* Q == 4 : 25-32% */ ++ {{714, 128}, {1418, 74}, {2436, 53}}, /* Q == 5 : 32-38% */ ++ {{883, 128}, {1437, 74}, {2464, 61}}, /* Q == 6 : 38-44% */ ++ {{897, 128}, {1515, 75}, {2622, 68}}, /* Q == 7 : 44-50% */ ++ {{926, 128}, {1613, 75}, {2730, 75}}, /* Q == 8 : 50-56% */ ++ {{947, 128}, {1729, 77}, {3359, 77}}, /* Q == 9 : 56-62% */ ++ {{1107, 128}, {2083, 81}, {4006, 84}}, /* Q ==10 : 62-69% */ ++ {{1177, 128}, {2379, 87}, {4785, 88}}, /* Q ==11 : 69-75% */ ++ {{1242, 128}, {2415, 93}, {5155, 84}}, /* Q ==12 : 75-81% */ ++ {{1349, 128}, {2644, 106}, {5260, 106}}, /* Q ==13 : 81-87% */ ++ {{1455, 128}, {2422, 124}, {4174, 124}}, /* Q ==14 : 87-93% */ ++ {{722, 128}, {1891, 145}, {1936, 146}}, /* Q ==15 : 93-99% */ ++}; ++ ++/** HUF_selectDecoder() : ++* Tells which decoder is likely to decode faster, ++* based on a set of pre-determined metrics. ++* @return : 0==HUF_decompress4X2, 1==HUF_decompress4X4 . ++* Assumption : 0 < cSrcSize < dstSize <= 128 KB */ ++U32 INIT HUF_selectDecoder(size_t dstSize, size_t cSrcSize) ++{ ++ /* decoder timing evaluation */ ++ U32 const Q = (U32)(cSrcSize * 16 / dstSize); /* Q < 16 since dstSize > cSrcSize */ ++ U32 const D256 = (U32)(dstSize >> 8); ++ U32 const DTime0 = algoTime[Q][0].tableTime + (algoTime[Q][0].decode256Time * D256); ++ U32 DTime1 = algoTime[Q][1].tableTime + (algoTime[Q][1].decode256Time * D256); ++ DTime1 += DTime1 >> 3; /* advantage to algorithm using less memory, for cache eviction */ ++ ++ return DTime1 < DTime0; ++} ++ ++typedef size_t (*decompressionAlgo)(void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize); ++ ++size_t INIT HUF_decompress4X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if (cSrcSize > dstSize) ++ return ERROR(corruption_detected); /* invalid */ ++ if (cSrcSize == dstSize) { ++ memcpy(dst, cSrc, dstSize); ++ return dstSize; ++ } /* not compressed */ ++ if (cSrcSize == 1) { ++ memset(dst, *(const BYTE *)cSrc, dstSize); ++ return dstSize; ++ } /* RLE */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} ++ ++size_t INIT HUF_decompress4X_hufOnly_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if ((cSrcSize >= dstSize) || (cSrcSize <= 1)) ++ return ERROR(corruption_detected); /* invalid */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress4X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress4X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} ++ ++size_t INIT HUF_decompress1X_DCtx_wksp(HUF_DTable *dctx, void *dst, size_t dstSize, const void *cSrc, size_t cSrcSize, void *workspace, size_t workspaceSize) ++{ ++ /* validation checks */ ++ if (dstSize == 0) ++ return ERROR(dstSize_tooSmall); ++ if (cSrcSize > dstSize) ++ return ERROR(corruption_detected); /* invalid */ ++ if (cSrcSize == dstSize) { ++ memcpy(dst, cSrc, dstSize); ++ return dstSize; ++ } /* not compressed */ ++ if (cSrcSize == 1) { ++ memset(dst, *(const BYTE *)cSrc, dstSize); ++ return dstSize; ++ } /* RLE */ ++ ++ { ++ U32 const algoNb = HUF_selectDecoder(dstSize, cSrcSize); ++ return algoNb ? HUF_decompress1X4_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize) ++ : HUF_decompress1X2_DCtx_wksp(dctx, dst, dstSize, cSrc, cSrcSize, workspace, workspaceSize); ++ } ++} +diff --git a/xen/common/zstd/mem.h b/xen/common/zstd/mem.h +new file mode 100644 +index 0000000000..d2fa444687 +--- /dev/null ++++ b/xen/common/zstd/mem.h +@@ -0,0 +1,151 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef MEM_H_MODULE ++#define MEM_H_MODULE ++ ++/*-**************************************** ++* Dependencies ++******************************************/ ++#include /* memcpy */ ++#include /* size_t, ptrdiff_t */ ++#include "private.h" ++ ++/*-**************************************** ++* Compiler specifics ++******************************************/ ++#define ZSTD_STATIC static inline ++ ++/*-************************************************************** ++* Basic Types ++*****************************************************************/ ++typedef uint8_t BYTE; ++typedef uint16_t U16; ++typedef int16_t S16; ++typedef uint32_t U32; ++typedef int32_t S32; ++typedef uint64_t U64; ++typedef int64_t S64; ++typedef ptrdiff_t iPtrDiff; ++typedef uintptr_t uPtrDiff; ++ ++/*-************************************************************** ++* Memory I/O ++*****************************************************************/ ++ZSTD_STATIC unsigned ZSTD_32bits(void) { return sizeof(size_t) == 4; } ++ZSTD_STATIC unsigned ZSTD_64bits(void) { return sizeof(size_t) == 8; } ++ ++#if defined(__LITTLE_ENDIAN) ++#define ZSTD_LITTLE_ENDIAN 1 ++#else ++#define ZSTD_LITTLE_ENDIAN 0 ++#endif ++ ++ZSTD_STATIC unsigned ZSTD_isLittleEndian(void) { return ZSTD_LITTLE_ENDIAN; } ++ ++ZSTD_STATIC U16 ZSTD_read16(const void *memPtr) { return get_unaligned((const U16 *)memPtr); } ++ ++ZSTD_STATIC U32 ZSTD_read32(const void *memPtr) { return get_unaligned((const U32 *)memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_read64(const void *memPtr) { return get_unaligned((const U64 *)memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readST(const void *memPtr) { return get_unaligned((const size_t *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write16(void *memPtr, U16 value) { put_unaligned(value, (U16 *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write32(void *memPtr, U32 value) { put_unaligned(value, (U32 *)memPtr); } ++ ++ZSTD_STATIC void ZSTD_write64(void *memPtr, U64 value) { put_unaligned(value, (U64 *)memPtr); } ++ ++/*=== Little endian r/w ===*/ ++ ++ZSTD_STATIC U16 ZSTD_readLE16(const void *memPtr) { return get_unaligned_le16(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE16(void *memPtr, U16 val) { put_unaligned_le16(val, memPtr); } ++ ++ZSTD_STATIC U32 ZSTD_readLE24(const void *memPtr) { return ZSTD_readLE16(memPtr) + (((const BYTE *)memPtr)[2] << 16); } ++ ++ZSTD_STATIC void ZSTD_writeLE24(void *memPtr, U32 val) ++{ ++ ZSTD_writeLE16(memPtr, (U16)val); ++ ((BYTE *)memPtr)[2] = (BYTE)(val >> 16); ++} ++ ++ZSTD_STATIC U32 ZSTD_readLE32(const void *memPtr) { return get_unaligned_le32(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE32(void *memPtr, U32 val32) { put_unaligned_le32(val32, memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_readLE64(const void *memPtr) { return get_unaligned_le64(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeLE64(void *memPtr, U64 val64) { put_unaligned_le64(val64, memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readLEST(const void *memPtr) ++{ ++ if (ZSTD_32bits()) ++ return (size_t)ZSTD_readLE32(memPtr); ++ else ++ return (size_t)ZSTD_readLE64(memPtr); ++} ++ ++ZSTD_STATIC void ZSTD_writeLEST(void *memPtr, size_t val) ++{ ++ if (ZSTD_32bits()) ++ ZSTD_writeLE32(memPtr, (U32)val); ++ else ++ ZSTD_writeLE64(memPtr, (U64)val); ++} ++ ++/*=== Big endian r/w ===*/ ++ ++ZSTD_STATIC U32 ZSTD_readBE32(const void *memPtr) { return get_unaligned_be32(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeBE32(void *memPtr, U32 val32) { put_unaligned_be32(val32, memPtr); } ++ ++ZSTD_STATIC U64 ZSTD_readBE64(const void *memPtr) { return get_unaligned_be64(memPtr); } ++ ++ZSTD_STATIC void ZSTD_writeBE64(void *memPtr, U64 val64) { put_unaligned_be64(val64, memPtr); } ++ ++ZSTD_STATIC size_t ZSTD_readBEST(const void *memPtr) ++{ ++ if (ZSTD_32bits()) ++ return (size_t)ZSTD_readBE32(memPtr); ++ else ++ return (size_t)ZSTD_readBE64(memPtr); ++} ++ ++ZSTD_STATIC void ZSTD_writeBEST(void *memPtr, size_t val) ++{ ++ if (ZSTD_32bits()) ++ ZSTD_writeBE32(memPtr, (U32)val); ++ else ++ ZSTD_writeBE64(memPtr, (U64)val); ++} ++ ++/* function safe only for comparisons */ ++ZSTD_STATIC U32 ZSTD_readMINMATCH(const void *memPtr, U32 length) ++{ ++ switch (length) { ++ default: ++ case 4: return ZSTD_read32(memPtr); ++ case 3: ++ if (ZSTD_isLittleEndian()) ++ return ZSTD_read32(memPtr) << 8; ++ else ++ return ZSTD_read32(memPtr) >> 8; ++ } ++} ++ ++#endif /* MEM_H_MODULE */ +diff --git a/xen/common/zstd/private.h b/xen/common/zstd/private.h +new file mode 100644 +index 0000000000..fac4d3c095 +--- /dev/null ++++ b/xen/common/zstd/private.h +@@ -0,0 +1,105 @@ ++#ifndef ZSTD_PRIVATE_H ++#define ZSTD_PRIVATE_H ++ ++#include ++#include ++#include ++ ++typedef ssize_t __attribute__((__mode__(__pointer__))) ptrdiff_t; ++ ++/* from kernel include/linux/unaligned/access_ok.h */ ++ ++static always_inline u16 get_unaligned_le16(const void *p) ++{ ++ return le16_to_cpup((__le16 *)p); ++} ++ ++static always_inline u32 get_unaligned_le32(const void *p) ++{ ++ return le32_to_cpup((__le32 *)p); ++} ++ ++static always_inline u64 get_unaligned_le64(const void *p) ++{ ++ return le64_to_cpup((__le64 *)p); ++} ++ ++static always_inline u32 get_unaligned_be32(const void *p) ++{ ++ return be32_to_cpup((__be32 *)p); ++} ++ ++static always_inline u64 get_unaligned_be64(const void *p) ++{ ++ return be64_to_cpup((__be64 *)p); ++} ++ ++static always_inline void put_unaligned_le16(u16 val, void *p) ++{ ++ *((__le16 *)p) = cpu_to_le16(val); ++} ++ ++static always_inline void put_unaligned_le32(u32 val, void *p) ++{ ++ *((__le32 *)p) = cpu_to_le32(val); ++} ++ ++static always_inline void put_unaligned_le64(u64 val, void *p) ++{ ++ *((__le64 *)p) = cpu_to_le64(val); ++} ++ ++static always_inline void put_unaligned_be32(u32 val, void *p) ++{ ++ *((__be32 *)p) = cpu_to_be32(val); ++} ++ ++static always_inline void put_unaligned_be64(u64 val, void *p) ++{ ++ *((__be64 *)p) = cpu_to_be64(val); ++} ++ ++ ++/* from kernel include/asm-generic/unaligned.h with linux/unaligned/generic.h ++ assuming little endian */ ++ ++extern void __bad_unaligned_access_size(void); ++ ++#define get_unaligned(ptr) ((__force typeof(*(ptr)))({ \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \ ++ __builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \ ++ __bad_unaligned_access_size())))); \ ++ })) ++ ++#define put_unaligned(val, ptr) ({ \ ++ void *__gu_p = (ptr); \ ++ switch (sizeof(*(ptr))) { \ ++ case 1: \ ++ *(u8 *)__gu_p = (__force u8)(val); \ ++ break; \ ++ case 2: \ ++ put_unaligned_le16((__force u16)(val), __gu_p); \ ++ break; \ ++ case 4: \ ++ put_unaligned_le32((__force u32)(val), __gu_p); \ ++ break; \ ++ case 8: \ ++ put_unaligned_le64((__force u64)(val), __gu_p); \ ++ break; \ ++ default: \ ++ __bad_unaligned_access_size(); \ ++ break; \ ++ } \ ++ (void)0; }) ++ ++ ++/* from kernel linux/kernel.h and uapi/linux/kernel.h */ ++ ++#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) ++#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) ++#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) ++#define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) ++ ++#endif /* ZSTD_PRIVATE_H */ +diff --git a/xen/common/zstd/zstd_common.c b/xen/common/zstd/zstd_common.c +new file mode 100644 +index 0000000000..1b13903538 +--- /dev/null ++++ b/xen/common/zstd/zstd_common.c +@@ -0,0 +1,74 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/*-************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" ++#include "zstd_internal.h" /* declaration of ZSTD_isError, ZSTD_getErrorName, ZSTD_getErrorCode, ZSTD_getErrorString, ZSTD_versionNumber */ ++ ++/*=************************************************************** ++* Custom allocator ++****************************************************************/ ++ ++#define stack_push(stack, size) \ ++ ({ \ ++ void *const ptr = ZSTD_PTR_ALIGN((stack)->ptr); \ ++ (stack)->ptr = (char *)ptr + (size); \ ++ (stack)->ptr <= (stack)->end ? ptr : NULL; \ ++ }) ++ ++ZSTD_customMem INIT ZSTD_initStack(void *workspace, size_t workspaceSize) ++{ ++ ZSTD_customMem stackMem = {ZSTD_stackAlloc, ZSTD_stackFree, workspace}; ++ ZSTD_stack *stack = (ZSTD_stack *)workspace; ++ /* Verify preconditions */ ++ if (!workspace || workspaceSize < sizeof(ZSTD_stack) || workspace != ZSTD_PTR_ALIGN(workspace)) { ++ ZSTD_customMem error = {NULL, NULL, NULL}; ++ return error; ++ } ++ /* Initialize the stack */ ++ stack->ptr = workspace; ++ stack->end = (char *)workspace + workspaceSize; ++ stack_push(stack, sizeof(ZSTD_stack)); ++ return stackMem; ++} ++ ++void INIT *ZSTD_stackAllocAll(void *opaque, size_t *size) ++{ ++ ZSTD_stack *stack = (ZSTD_stack *)opaque; ++ *size = (BYTE const *)stack->end - (BYTE *)ZSTD_PTR_ALIGN(stack->ptr); ++ return stack_push(stack, *size); ++} ++ ++void INIT *ZSTD_stackAlloc(void *opaque, size_t size) ++{ ++ ZSTD_stack *stack = (ZSTD_stack *)opaque; ++ return stack_push(stack, size); ++} ++void INIT ZSTD_stackFree(void *opaque, void *address) ++{ ++ (void)opaque; ++ (void)address; ++} ++ ++void INIT *ZSTD_malloc(size_t size, ZSTD_customMem customMem) { return customMem.customAlloc(customMem.opaque, size); } ++ ++void INIT ZSTD_free(void *ptr, ZSTD_customMem customMem) ++{ ++ if (ptr != NULL) ++ customMem.customFree(customMem.opaque, ptr); ++} +diff --git a/xen/common/zstd/zstd_internal.h b/xen/common/zstd/zstd_internal.h +new file mode 100644 +index 0000000000..1b13840c44 +--- /dev/null ++++ b/xen/common/zstd/zstd_internal.h +@@ -0,0 +1,265 @@ ++/** ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef ZSTD_CCOMMON_H_MODULE ++#define ZSTD_CCOMMON_H_MODULE ++ ++/*-******************************************************* ++* Compiler specifics ++*********************************************************/ ++#define FORCE_INLINE static always_inline ++#define FORCE_NOINLINE static noinline ++ ++/*-************************************* ++* Dependencies ++***************************************/ ++#include "error_private.h" ++#include "mem.h" ++#include ++#include ++ ++/*-************************************* ++* shared macros ++***************************************/ ++#define CHECK_F(f) \ ++ { \ ++ size_t const errcod = f; \ ++ if (ERR_isError(errcod)) \ ++ return errcod; \ ++ } /* check and Forward error code */ ++#define CHECK_E(f, e) \ ++ { \ ++ size_t const errcod = f; \ ++ if (ERR_isError(errcod)) \ ++ return ERROR(e); \ ++ } /* check and send Error code */ ++#define ZSTD_STATIC_ASSERT(c) \ ++ { \ ++ enum { ZSTD_static_assert = 1 / (int)(!!(c)) }; \ ++ } ++ ++/*-************************************* ++* Common constants ++***************************************/ ++#define ZSTD_OPT_NUM (1 << 12) ++#define ZSTD_DICT_MAGIC 0xEC30A437 /* v0.7+ */ ++ ++#define ZSTD_REP_NUM 3 /* number of repcodes */ ++#define ZSTD_REP_CHECK (ZSTD_REP_NUM) /* number of repcodes to check by the optimal parser */ ++#define ZSTD_REP_MOVE (ZSTD_REP_NUM - 1) ++#define ZSTD_REP_MOVE_OPT (ZSTD_REP_NUM) ++static const U32 repStartValue[ZSTD_REP_NUM] = {1, 4, 8}; ++ ++#define BIT7 128 ++#define BIT6 64 ++#define BIT5 32 ++#define BIT4 16 ++#define BIT1 2 ++#define BIT0 1 ++ ++#define ZSTD_WINDOWLOG_ABSOLUTEMIN 10 ++static const size_t ZSTD_fcs_fieldSize[4] = {0, 2, 4, 8}; ++static const size_t ZSTD_did_fieldSize[4] = {0, 1, 2, 4}; ++ ++#define ZSTD_BLOCKHEADERSIZE 3 /* C standard doesn't allow `static const` variable to be init using another `static const` variable */ ++static const size_t ZSTD_blockHeaderSize = ZSTD_BLOCKHEADERSIZE; ++typedef enum { bt_raw, bt_rle, bt_compressed, bt_reserved } blockType_e; ++ ++#define MIN_SEQUENCES_SIZE 1 /* nbSeq==0 */ ++#define MIN_CBLOCK_SIZE (1 /*litCSize*/ + 1 /* RLE or RAW */ + MIN_SEQUENCES_SIZE /* nbSeq==0 */) /* for a non-null block */ ++ ++#define HufLog 12 ++typedef enum { set_basic, set_rle, set_compressed, set_repeat } symbolEncodingType_e; ++ ++#define LONGNBSEQ 0x7F00 ++ ++#define MINMATCH 3 ++#define EQUAL_READ32 4 ++ ++#define Litbits 8 ++#define MaxLit ((1 << Litbits) - 1) ++#define MaxML 52 ++#define MaxLL 35 ++#define MaxOff 28 ++#define MaxSeq MAX(MaxLL, MaxML) /* Assumption : MaxOff < MaxLL,MaxML */ ++#define MLFSELog 9 ++#define LLFSELog 9 ++#define OffFSELog 8 ++ ++static const U32 LL_bits[MaxLL + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; ++static const S16 LL_defaultNorm[MaxLL + 1] = {4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1, -1, -1, -1, -1}; ++#define LL_DEFAULTNORMLOG 6 /* for static allocation */ ++static const U32 LL_defaultNormLog = LL_DEFAULTNORMLOG; ++ ++static const U32 ML_bits[MaxML + 1] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ++ 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; ++static const S16 ML_defaultNorm[MaxML + 1] = {1, 4, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ++ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1}; ++#define ML_DEFAULTNORMLOG 6 /* for static allocation */ ++static const U32 ML_defaultNormLog = ML_DEFAULTNORMLOG; ++ ++static const S16 OF_defaultNorm[MaxOff + 1] = {1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1}; ++#define OF_DEFAULTNORMLOG 5 /* for static allocation */ ++static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG; ++ ++/*-******************************************* ++* Shared functions to include for inlining ++*********************************************/ ++ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { ++ /* ++ * zstd relies heavily on gcc being able to analyze and inline this ++ * memcpy() call, since it is called in a tight loop. Preboot mode ++ * is compiled in freestanding mode, which stops gcc from analyzing ++ * memcpy(). Use __builtin_memcpy() to tell gcc to analyze this as a ++ * regular memcpy(). ++ */ ++ __builtin_memcpy(dst, src, 8); ++} ++/*! ZSTD_wildcopy() : ++* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ ++#define WILDCOPY_OVERLENGTH 8 ++ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length) ++{ ++ const BYTE* ip = (const BYTE*)src; ++ BYTE* op = (BYTE*)dst; ++ BYTE* const oend = op + length; ++#if defined(GCC_VERSION) && GCC_VERSION >= 70000 && GCC_VERSION < 70200 ++ /* ++ * Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. ++ * Avoid the bad case where the loop only runs once by handling the ++ * special case separately. This doesn't trigger the bug because it ++ * doesn't involve pointer/integer overflow. ++ */ ++ if (length <= 8) ++ return ZSTD_copy8(dst, src); ++#endif ++ do { ++ ZSTD_copy8(op, ip); ++ op += 8; ++ ip += 8; ++ } while (op < oend); ++} ++ ++/*-******************************************* ++* Private interfaces ++*********************************************/ ++typedef struct ZSTD_stats_s ZSTD_stats_t; ++ ++typedef struct { ++ U32 off; ++ U32 len; ++} ZSTD_match_t; ++ ++typedef struct { ++ U32 price; ++ U32 off; ++ U32 mlen; ++ U32 litlen; ++ U32 rep[ZSTD_REP_NUM]; ++} ZSTD_optimal_t; ++ ++typedef struct seqDef_s { ++ U32 offset; ++ U16 litLength; ++ U16 matchLength; ++} seqDef; ++ ++typedef struct { ++ seqDef *sequencesStart; ++ seqDef *sequences; ++ BYTE *litStart; ++ BYTE *lit; ++ BYTE *llCode; ++ BYTE *mlCode; ++ BYTE *ofCode; ++ U32 longLengthID; /* 0 == no longLength; 1 == Lit.longLength; 2 == Match.longLength; */ ++ U32 longLengthPos; ++ /* opt */ ++ ZSTD_optimal_t *priceTable; ++ ZSTD_match_t *matchTable; ++ U32 *matchLengthFreq; ++ U32 *litLengthFreq; ++ U32 *litFreq; ++ U32 *offCodeFreq; ++ U32 matchLengthSum; ++ U32 matchSum; ++ U32 litLengthSum; ++ U32 litSum; ++ U32 offCodeSum; ++ U32 log2matchLengthSum; ++ U32 log2matchSum; ++ U32 log2litLengthSum; ++ U32 log2litSum; ++ U32 log2offCodeSum; ++ U32 factor; ++ U32 staticPrices; ++ U32 cachedPrice; ++ U32 cachedLitLength; ++ const BYTE *cachedLiterals; ++} seqStore_t; ++ ++const seqStore_t *ZSTD_getSeqStore(const ZSTD_CCtx *ctx); ++void ZSTD_seqToCodes(const seqStore_t *seqStorePtr); ++int ZSTD_isSkipFrame(ZSTD_DCtx *dctx); ++ ++/*= Custom memory allocation functions */ ++typedef void *(*ZSTD_allocFunction)(void *opaque, size_t size); ++typedef void (*ZSTD_freeFunction)(void *opaque, void *address); ++typedef struct { ++ ZSTD_allocFunction customAlloc; ++ ZSTD_freeFunction customFree; ++ void *opaque; ++} ZSTD_customMem; ++ ++void *ZSTD_malloc(size_t size, ZSTD_customMem customMem); ++void ZSTD_free(void *ptr, ZSTD_customMem customMem); ++ ++/*====== stack allocation ======*/ ++ ++typedef struct { ++ void *ptr; ++ const void *end; ++} ZSTD_stack; ++ ++#define ZSTD_ALIGN(x) ALIGN(x, sizeof(size_t)) ++#define ZSTD_PTR_ALIGN(p) PTR_ALIGN(p, sizeof(size_t)) ++ ++ZSTD_customMem ZSTD_initStack(void *workspace, size_t workspaceSize); ++ ++void *ZSTD_stackAllocAll(void *opaque, size_t *size); ++void *ZSTD_stackAlloc(void *opaque, size_t size); ++void ZSTD_stackFree(void *opaque, void *address); ++ ++/*====== common function ======*/ ++ ++ZSTD_STATIC U32 ZSTD_highbit32(U32 val) { return 31 - __builtin_clz(val); } ++ ++/* hidden functions */ ++ ++/* ZSTD_invalidateRepCodes() : ++ * ensures next compression will not use repcodes from previous block. ++ * Note : only works with regular variant; ++ * do not use with extDict variant ! */ ++void ZSTD_invalidateRepCodes(ZSTD_CCtx *cctx); ++ ++size_t ZSTD_freeCCtx(ZSTD_CCtx *cctx); ++size_t ZSTD_freeDCtx(ZSTD_DCtx *dctx); ++size_t ZSTD_freeCDict(ZSTD_CDict *cdict); ++size_t ZSTD_freeDDict(ZSTD_DDict *cdict); ++size_t ZSTD_freeCStream(ZSTD_CStream *zcs); ++size_t ZSTD_freeDStream(ZSTD_DStream *zds); ++ ++#endif /* ZSTD_CCOMMON_H_MODULE */ +diff --git a/xen/common/zstd/zstd_opt.h b/xen/common/zstd/zstd_opt.h +new file mode 100644 +index 0000000000..55e1b4cba8 +--- /dev/null ++++ b/xen/common/zstd/zstd_opt.h +@@ -0,0 +1,1014 @@ ++/** ++ * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++/* Note : this file is intended to be included within zstd_compress.c */ ++ ++#ifndef ZSTD_OPT_H_91842398743 ++#define ZSTD_OPT_H_91842398743 ++ ++#define ZSTD_LITFREQ_ADD 2 ++#define ZSTD_FREQ_DIV 4 ++#define ZSTD_MAX_PRICE (1 << 30) ++ ++/*-************************************* ++* Price functions for optimal parser ++***************************************/ ++FORCE_INLINE void ZSTD_setLog2Prices(seqStore_t *ssPtr) ++{ ++ ssPtr->log2matchLengthSum = ZSTD_highbit32(ssPtr->matchLengthSum + 1); ++ ssPtr->log2litLengthSum = ZSTD_highbit32(ssPtr->litLengthSum + 1); ++ ssPtr->log2litSum = ZSTD_highbit32(ssPtr->litSum + 1); ++ ssPtr->log2offCodeSum = ZSTD_highbit32(ssPtr->offCodeSum + 1); ++ ssPtr->factor = 1 + ((ssPtr->litSum >> 5) / ssPtr->litLengthSum) + ((ssPtr->litSum << 1) / (ssPtr->litSum + ssPtr->matchSum)); ++} ++ ++ZSTD_STATIC void ZSTD_rescaleFreqs(seqStore_t *ssPtr, const BYTE *src, size_t srcSize) ++{ ++ unsigned u; ++ ++ ssPtr->cachedLiterals = NULL; ++ ssPtr->cachedPrice = ssPtr->cachedLitLength = 0; ++ ssPtr->staticPrices = 0; ++ ++ if (ssPtr->litLengthSum == 0) { ++ if (srcSize <= 1024) ++ ssPtr->staticPrices = 1; ++ ++ for (u = 0; u <= MaxLit; u++) ++ ssPtr->litFreq[u] = 0; ++ for (u = 0; u < srcSize; u++) ++ ssPtr->litFreq[src[u]]++; ++ ++ ssPtr->litSum = 0; ++ ssPtr->litLengthSum = MaxLL + 1; ++ ssPtr->matchLengthSum = MaxML + 1; ++ ssPtr->offCodeSum = (MaxOff + 1); ++ ssPtr->matchSum = (ZSTD_LITFREQ_ADD << Litbits); ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) ++ ssPtr->litLengthFreq[u] = 1; ++ for (u = 0; u <= MaxML; u++) ++ ssPtr->matchLengthFreq[u] = 1; ++ for (u = 0; u <= MaxOff; u++) ++ ssPtr->offCodeFreq[u] = 1; ++ } else { ++ ssPtr->matchLengthSum = 0; ++ ssPtr->litLengthSum = 0; ++ ssPtr->offCodeSum = 0; ++ ssPtr->matchSum = 0; ++ ssPtr->litSum = 0; ++ ++ for (u = 0; u <= MaxLit; u++) { ++ ssPtr->litFreq[u] = 1 + (ssPtr->litFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litSum += ssPtr->litFreq[u]; ++ } ++ for (u = 0; u <= MaxLL; u++) { ++ ssPtr->litLengthFreq[u] = 1 + (ssPtr->litLengthFreq[u] >> (ZSTD_FREQ_DIV + 1)); ++ ssPtr->litLengthSum += ssPtr->litLengthFreq[u]; ++ } ++ for (u = 0; u <= MaxML; u++) { ++ ssPtr->matchLengthFreq[u] = 1 + (ssPtr->matchLengthFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->matchLengthSum += ssPtr->matchLengthFreq[u]; ++ ssPtr->matchSum += ssPtr->matchLengthFreq[u] * (u + 3); ++ } ++ ssPtr->matchSum *= ZSTD_LITFREQ_ADD; ++ for (u = 0; u <= MaxOff; u++) { ++ ssPtr->offCodeFreq[u] = 1 + (ssPtr->offCodeFreq[u] >> ZSTD_FREQ_DIV); ++ ssPtr->offCodeSum += ssPtr->offCodeFreq[u]; ++ } ++ } ++ ++ ZSTD_setLog2Prices(ssPtr); ++} ++ ++FORCE_INLINE U32 ZSTD_getLiteralPrice(seqStore_t *ssPtr, U32 litLength, const BYTE *literals) ++{ ++ U32 price, u; ++ ++ if (ssPtr->staticPrices) ++ return ZSTD_highbit32((U32)litLength + 1) + (litLength * 6); ++ ++ if (litLength == 0) ++ return ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[0] + 1); ++ ++ /* literals */ ++ if (ssPtr->cachedLiterals == literals) { ++ U32 const additional = litLength - ssPtr->cachedLitLength; ++ const BYTE *literals2 = ssPtr->cachedLiterals + ssPtr->cachedLitLength; ++ price = ssPtr->cachedPrice + additional * ssPtr->log2litSum; ++ for (u = 0; u < additional; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals2[u]] + 1); ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } else { ++ price = litLength * ssPtr->log2litSum; ++ for (u = 0; u < litLength; u++) ++ price -= ZSTD_highbit32(ssPtr->litFreq[literals[u]] + 1); ++ ++ if (litLength >= 12) { ++ ssPtr->cachedLiterals = literals; ++ ssPtr->cachedPrice = price; ++ ssPtr->cachedLitLength = litLength; ++ } ++ } ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ price += LL_bits[llCode] + ssPtr->log2litLengthSum - ZSTD_highbit32(ssPtr->litLengthFreq[llCode] + 1); ++ } ++ ++ return price; ++} ++ ++FORCE_INLINE U32 ZSTD_getPrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength, const int ultra) ++{ ++ /* offset */ ++ U32 price; ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ ++ if (seqStorePtr->staticPrices) ++ return ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + ZSTD_highbit32((U32)matchLength + 1) + 16 + offCode; ++ ++ price = offCode + seqStorePtr->log2offCodeSum - ZSTD_highbit32(seqStorePtr->offCodeFreq[offCode] + 1); ++ if (!ultra && offCode >= 20) ++ price += (offCode - 19) * 2; ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ price += ML_bits[mlCode] + seqStorePtr->log2matchLengthSum - ZSTD_highbit32(seqStorePtr->matchLengthFreq[mlCode] + 1); ++ } ++ ++ return price + ZSTD_getLiteralPrice(seqStorePtr, litLength, literals) + seqStorePtr->factor; ++} ++ ++ZSTD_STATIC void ZSTD_updatePrice(seqStore_t *seqStorePtr, U32 litLength, const BYTE *literals, U32 offset, U32 matchLength) ++{ ++ U32 u; ++ ++ /* literals */ ++ seqStorePtr->litSum += litLength * ZSTD_LITFREQ_ADD; ++ for (u = 0; u < litLength; u++) ++ seqStorePtr->litFreq[literals[u]] += ZSTD_LITFREQ_ADD; ++ ++ /* literal Length */ ++ { ++ const BYTE LL_deltaCode = 19; ++ const BYTE llCode = (litLength > 63) ? (BYTE)ZSTD_highbit32(litLength) + LL_deltaCode : LL_Code[litLength]; ++ seqStorePtr->litLengthFreq[llCode]++; ++ seqStorePtr->litLengthSum++; ++ } ++ ++ /* match offset */ ++ { ++ BYTE const offCode = (BYTE)ZSTD_highbit32(offset + 1); ++ seqStorePtr->offCodeSum++; ++ seqStorePtr->offCodeFreq[offCode]++; ++ } ++ ++ /* match Length */ ++ { ++ const BYTE ML_deltaCode = 36; ++ const BYTE mlCode = (matchLength > 127) ? (BYTE)ZSTD_highbit32(matchLength) + ML_deltaCode : ML_Code[matchLength]; ++ seqStorePtr->matchLengthFreq[mlCode]++; ++ seqStorePtr->matchLengthSum++; ++ } ++ ++ ZSTD_setLog2Prices(seqStorePtr); ++} ++ ++#define SET_PRICE(pos, mlen_, offset_, litlen_, price_) \ ++ { \ ++ while (last_pos < pos) { \ ++ opt[last_pos + 1].price = ZSTD_MAX_PRICE; \ ++ last_pos++; \ ++ } \ ++ opt[pos].mlen = mlen_; \ ++ opt[pos].off = offset_; \ ++ opt[pos].litlen = litlen_; \ ++ opt[pos].price = price_; \ ++ } ++ ++/* Update hashTable3 up to ip (excluded) ++ Assumption : always within prefix (i.e. not within extDict) */ ++FORCE_INLINE ++U32 ZSTD_insertAndFindFirstIndexHash3(ZSTD_CCtx *zc, const BYTE *ip) ++{ ++ U32 *const hashTable3 = zc->hashTable3; ++ U32 const hashLog3 = zc->hashLog3; ++ const BYTE *const base = zc->base; ++ U32 idx = zc->nextToUpdate3; ++ const U32 target = zc->nextToUpdate3 = (U32)(ip - base); ++ const size_t hash3 = ZSTD_hash3Ptr(ip, hashLog3); ++ ++ while (idx < target) { ++ hashTable3[ZSTD_hash3Ptr(base + idx, hashLog3)] = idx; ++ idx++; ++ } ++ ++ return hashTable3[hash3]; ++} ++ ++/*-************************************* ++* Binary Tree search ++***************************************/ ++static U32 ZSTD_insertBtAndGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, U32 nbCompares, const U32 mls, U32 extDict, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ const BYTE *const base = zc->base; ++ const U32 curr = (U32)(ip - base); ++ const U32 hashLog = zc->params.cParams.hashLog; ++ const size_t h = ZSTD_hashPtr(ip, hashLog, mls); ++ U32 *const hashTable = zc->hashTable; ++ U32 matchIndex = hashTable[h]; ++ U32 *const bt = zc->chainTable; ++ const U32 btLog = zc->params.cParams.chainLog - 1; ++ const U32 btMask = (1U << btLog) - 1; ++ size_t commonLengthSmaller = 0, commonLengthLarger = 0; ++ const BYTE *const dictBase = zc->dictBase; ++ const U32 dictLimit = zc->dictLimit; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const U32 btLow = btMask >= curr ? 0 : curr - btMask; ++ const U32 windowLow = zc->lowLimit; ++ U32 *smallerPtr = bt + 2 * (curr & btMask); ++ U32 *largerPtr = bt + 2 * (curr & btMask) + 1; ++ U32 matchEndIdx = curr + 8; ++ U32 dummy32; /* to be nullified at the end */ ++ U32 mnum = 0; ++ ++ const U32 minMatch = (mls == 3) ? 3 : 4; ++ size_t bestLength = minMatchLen - 1; ++ ++ if (minMatch == 3) { /* HC3 match finder */ ++ U32 const matchIndex3 = ZSTD_insertAndFindFirstIndexHash3(zc, ip); ++ if (matchIndex3 > windowLow && (curr - matchIndex3 < (1 << 18))) { ++ const BYTE *match; ++ size_t currMl = 0; ++ if ((!extDict) || matchIndex3 >= dictLimit) { ++ match = base + matchIndex3; ++ if (match[bestLength] == ip[bestLength]) ++ currMl = ZSTD_count(ip, match, iLimit); ++ } else { ++ match = dictBase + matchIndex3; ++ if (ZSTD_readMINMATCH(match, MINMATCH) == ++ ZSTD_readMINMATCH(ip, MINMATCH)) /* assumption : matchIndex3 <= dictLimit-4 (by table construction) */ ++ currMl = ZSTD_count_2segments(ip + MINMATCH, match + MINMATCH, iLimit, dictEnd, prefixStart) + MINMATCH; ++ } ++ ++ /* save best solution */ ++ if (currMl > bestLength) { ++ bestLength = currMl; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex3; ++ matches[mnum].len = (U32)currMl; ++ mnum++; ++ if (currMl > ZSTD_OPT_NUM) ++ goto update; ++ if (ip + currMl == iLimit) ++ goto update; /* best possible, and avoid read overflow*/ ++ } ++ } ++ } ++ ++ hashTable[h] = curr; /* Update Hash Table */ ++ ++ while (nbCompares-- && (matchIndex > windowLow)) { ++ U32 *nextPtr = bt + 2 * (matchIndex & btMask); ++ size_t matchLength = MIN(commonLengthSmaller, commonLengthLarger); /* guaranteed minimum nb of common bytes */ ++ const BYTE *match; ++ ++ if ((!extDict) || (matchIndex + matchLength >= dictLimit)) { ++ match = base + matchIndex; ++ if (match[matchLength] == ip[matchLength]) { ++ matchLength += ZSTD_count(ip + matchLength + 1, match + matchLength + 1, iLimit) + 1; ++ } ++ } else { ++ match = dictBase + matchIndex; ++ matchLength += ZSTD_count_2segments(ip + matchLength, match + matchLength, iLimit, dictEnd, prefixStart); ++ if (matchIndex + matchLength >= dictLimit) ++ match = base + matchIndex; /* to prepare for next usage of match[matchLength] */ ++ } ++ ++ if (matchLength > bestLength) { ++ if (matchLength > matchEndIdx - matchIndex) ++ matchEndIdx = matchIndex + (U32)matchLength; ++ bestLength = matchLength; ++ matches[mnum].off = ZSTD_REP_MOVE_OPT + curr - matchIndex; ++ matches[mnum].len = (U32)matchLength; ++ mnum++; ++ if (matchLength > ZSTD_OPT_NUM) ++ break; ++ if (ip + matchLength == iLimit) /* equal : no way to know if inf or sup */ ++ break; /* drop, to guarantee consistency (miss a little bit of compression) */ ++ } ++ ++ if (match[matchLength] < ip[matchLength]) { ++ /* match is smaller than curr */ ++ *smallerPtr = matchIndex; /* update smaller idx */ ++ commonLengthSmaller = matchLength; /* all smaller will now have at least this guaranteed common length */ ++ if (matchIndex <= btLow) { ++ smallerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ smallerPtr = nextPtr + 1; /* new "smaller" => larger of match */ ++ matchIndex = nextPtr[1]; /* new matchIndex larger than previous (closer to curr) */ ++ } else { ++ /* match is larger than curr */ ++ *largerPtr = matchIndex; ++ commonLengthLarger = matchLength; ++ if (matchIndex <= btLow) { ++ largerPtr = &dummy32; ++ break; ++ } /* beyond tree size, stop the search */ ++ largerPtr = nextPtr; ++ matchIndex = nextPtr[0]; ++ } ++ } ++ ++ *smallerPtr = *largerPtr = 0; ++ ++update: ++ zc->nextToUpdate = (matchEndIdx > curr + 8) ? matchEndIdx - 8 : curr + 1; ++ return mnum; ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ZSTD_match_t *matches, ++ const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 0, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/** Tree updater, providing best match */ ++static U32 ZSTD_BtGetAllMatches_extDict(ZSTD_CCtx *zc, const BYTE *const ip, const BYTE *const iLimit, const U32 maxNbAttempts, const U32 mls, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ if (ip < zc->base + zc->nextToUpdate) ++ return 0; /* skipped area */ ++ ZSTD_updateTree_extDict(zc, ip, iLimit, maxNbAttempts, mls); ++ return ZSTD_insertBtAndGetAllMatches(zc, ip, iLimit, maxNbAttempts, mls, 1, matches, minMatchLen); ++} ++ ++static U32 ZSTD_BtGetAllMatches_selectMLS_extDict(ZSTD_CCtx *zc, /* Index table will be updated */ ++ const BYTE *ip, const BYTE *const iHighLimit, const U32 maxNbAttempts, const U32 matchLengthSearch, ++ ZSTD_match_t *matches, const U32 minMatchLen) ++{ ++ switch (matchLengthSearch) { ++ case 3: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 3, matches, minMatchLen); ++ default: ++ case 4: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 4, matches, minMatchLen); ++ case 5: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 5, matches, minMatchLen); ++ case 7: ++ case 6: return ZSTD_BtGetAllMatches_extDict(zc, ip, iHighLimit, maxNbAttempts, 6, matches, minMatchLen); ++ } ++} ++ ++/*-******************************* ++* Optimal parser ++*********************************/ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const BYTE *const prefixStart = base + ctx->dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ U32 offset, rep[ZSTD_REP_NUM]; ++ ++ /* init */ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(ip - prefixStart)) && ++ (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(ip - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(ip + minMatch, ip + minMatch - repCur, iend) + minMatch; ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ best_off = i - (ip == anchor); ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, ip, iend, maxSearches, mls, matches, minMatch); ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = 0 */ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); /* note : macro modifies last_pos */ ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* initialize opt[0] */ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ opt[0].litlen = litlen; ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (opt[cur].mlen != 1); i < last_i; i++) { /* check rep */ ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ if ((repCur > 0) && (repCur < (S32)(inr - prefixStart)) && ++ (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(inr - repCur, minMatch))) { ++ mlen = (U32)ZSTD_count(inr + minMatch, inr + minMatch - repCur, iend) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS(ctx, inr, iend, maxSearches, mls, matches, best_mlen); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t const lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++FORCE_INLINE ++void ZSTD_compressBlock_opt_extDict_generic(ZSTD_CCtx *ctx, const void *src, size_t srcSize, const int ultra) ++{ ++ seqStore_t *seqStorePtr = &(ctx->seqStore); ++ const BYTE *const istart = (const BYTE *)src; ++ const BYTE *ip = istart; ++ const BYTE *anchor = istart; ++ const BYTE *const iend = istart + srcSize; ++ const BYTE *const ilimit = iend - 8; ++ const BYTE *const base = ctx->base; ++ const U32 lowestIndex = ctx->lowLimit; ++ const U32 dictLimit = ctx->dictLimit; ++ const BYTE *const prefixStart = base + dictLimit; ++ const BYTE *const dictBase = ctx->dictBase; ++ const BYTE *const dictEnd = dictBase + dictLimit; ++ ++ const U32 maxSearches = 1U << ctx->params.cParams.searchLog; ++ const U32 sufficient_len = ctx->params.cParams.targetLength; ++ const U32 mls = ctx->params.cParams.searchLength; ++ const U32 minMatch = (ctx->params.cParams.searchLength == 3) ? 3 : 4; ++ ++ ZSTD_optimal_t *opt = seqStorePtr->priceTable; ++ ZSTD_match_t *matches = seqStorePtr->matchTable; ++ const BYTE *inr; ++ ++ /* init */ ++ U32 offset, rep[ZSTD_REP_NUM]; ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ rep[i] = ctx->rep[i]; ++ } ++ ++ ctx->nextToUpdate3 = ctx->nextToUpdate; ++ ZSTD_rescaleFreqs(seqStorePtr, (const BYTE *)src, srcSize); ++ ip += (ip == prefixStart); ++ ++ /* Match Loop */ ++ while (ip < ilimit) { ++ U32 cur, match_num, last_pos, litlen, price; ++ U32 u, mlen, best_mlen, best_off, litLength; ++ U32 curr = (U32)(ip - base); ++ memset(opt, 0, sizeof(ZSTD_optimal_t)); ++ last_pos = 0; ++ opt[0].litlen = (U32)(ip - anchor); ++ ++ /* check repCode */ ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (ip == anchor); ++ for (i = (ip == anchor); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : rep[i]; ++ const U32 repIndex = (U32)(curr - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)curr) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(ip, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected we should take it */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(ip + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (ip == anchor); ++ litlen = opt[0].litlen; ++ do { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, i, litlen, price); /* note : macro modifies last_pos */ ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, ip, iend, maxSearches, mls, matches, minMatch); /* first search (depth 0) */ ++ ++ if (!last_pos && !match_num) { ++ ip++; ++ continue; ++ } ++ ++ { ++ U32 i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ opt[0].rep[i] = rep[i]; ++ } ++ opt[0].mlen = 1; ++ ++ if (match_num && (matches[match_num - 1].len > sufficient_len || matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ cur = 0; ++ last_pos = 1; ++ goto _storeSequence; ++ } ++ ++ best_mlen = (last_pos) ? last_pos : minMatch; ++ ++ /* set prices using matches at position = 0 */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ litlen = opt[0].litlen; ++ while (mlen <= best_mlen) { ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ if (mlen > last_pos || price < opt[mlen].price) ++ SET_PRICE(mlen, mlen, matches[u].off, litlen, price); ++ mlen++; ++ } ++ } ++ ++ if (last_pos < minMatch) { ++ ip++; ++ continue; ++ } ++ ++ /* check further positions */ ++ for (cur = 1; cur <= last_pos; cur++) { ++ inr = ip + cur; ++ ++ if (opt[cur - 1].mlen == 1) { ++ litlen = opt[cur - 1].litlen + 1; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - litlen); ++ } else ++ price = ZSTD_getLiteralPrice(seqStorePtr, litlen, anchor); ++ } else { ++ litlen = 1; ++ price = opt[cur - 1].price + ZSTD_getLiteralPrice(seqStorePtr, litlen, inr - 1); ++ } ++ ++ if (cur > last_pos || price <= opt[cur].price) ++ SET_PRICE(cur, 1, 0, litlen, price); ++ ++ if (cur == last_pos) ++ break; ++ ++ if (inr > ilimit) /* last match must start at a minimum distance of 8 from oend */ ++ continue; ++ ++ mlen = opt[cur].mlen; ++ if (opt[cur].off > ZSTD_REP_MOVE_OPT) { ++ opt[cur].rep[2] = opt[cur - mlen].rep[1]; ++ opt[cur].rep[1] = opt[cur - mlen].rep[0]; ++ opt[cur].rep[0] = opt[cur].off - ZSTD_REP_MOVE_OPT; ++ } else { ++ opt[cur].rep[2] = (opt[cur].off > 1) ? opt[cur - mlen].rep[1] : opt[cur - mlen].rep[2]; ++ opt[cur].rep[1] = (opt[cur].off > 0) ? opt[cur - mlen].rep[0] : opt[cur - mlen].rep[1]; ++ opt[cur].rep[0] = ++ ((opt[cur].off == ZSTD_REP_MOVE_OPT) && (mlen != 1)) ? (opt[cur - mlen].rep[0] - 1) : (opt[cur - mlen].rep[opt[cur].off]); ++ } ++ ++ best_mlen = minMatch; ++ { ++ U32 i, last_i = ZSTD_REP_CHECK + (mlen != 1); ++ for (i = (mlen != 1); i < last_i; i++) { ++ const S32 repCur = (i == ZSTD_REP_MOVE_OPT) ? (opt[cur].rep[0] - 1) : opt[cur].rep[i]; ++ const U32 repIndex = (U32)(curr + cur - repCur); ++ const BYTE *const repBase = repIndex < dictLimit ? dictBase : base; ++ const BYTE *const repMatch = repBase + repIndex; ++ if ((repCur > 0 && repCur <= (S32)(curr + cur)) && ++ (((U32)((dictLimit - 1) - repIndex) >= 3) & (repIndex > lowestIndex)) /* intentional overflow */ ++ && (ZSTD_readMINMATCH(inr, minMatch) == ZSTD_readMINMATCH(repMatch, minMatch))) { ++ /* repcode detected */ ++ const BYTE *const repEnd = repIndex < dictLimit ? dictEnd : iend; ++ mlen = (U32)ZSTD_count_2segments(inr + minMatch, repMatch + minMatch, iend, repEnd, prefixStart) + minMatch; ++ ++ if (mlen > sufficient_len || cur + mlen >= ZSTD_OPT_NUM) { ++ best_mlen = mlen; ++ best_off = i; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ best_off = i - (opt[cur].mlen != 1); ++ if (mlen > best_mlen) ++ best_mlen = mlen; ++ ++ do { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) { ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, inr - litlen, ++ best_off, mlen - MINMATCH, ultra); ++ } else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, best_off, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, best_off, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || price <= opt[cur + mlen].price) ++ SET_PRICE(cur + mlen, mlen, i, litlen, price); ++ mlen--; ++ } while (mlen >= minMatch); ++ } ++ } ++ } ++ ++ match_num = ZSTD_BtGetAllMatches_selectMLS_extDict(ctx, inr, iend, maxSearches, mls, matches, minMatch); ++ ++ if (match_num > 0 && (matches[match_num - 1].len > sufficient_len || cur + matches[match_num - 1].len >= ZSTD_OPT_NUM)) { ++ best_mlen = matches[match_num - 1].len; ++ best_off = matches[match_num - 1].off; ++ last_pos = cur + 1; ++ goto _storeSequence; ++ } ++ ++ /* set prices using matches at position = cur */ ++ for (u = 0; u < match_num; u++) { ++ mlen = (u > 0) ? matches[u - 1].len + 1 : best_mlen; ++ best_mlen = matches[u].len; ++ ++ while (mlen <= best_mlen) { ++ if (opt[cur].mlen == 1) { ++ litlen = opt[cur].litlen; ++ if (cur > litlen) ++ price = opt[cur - litlen].price + ZSTD_getPrice(seqStorePtr, litlen, ip + cur - litlen, ++ matches[u].off - 1, mlen - MINMATCH, ultra); ++ else ++ price = ZSTD_getPrice(seqStorePtr, litlen, anchor, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } else { ++ litlen = 0; ++ price = opt[cur].price + ZSTD_getPrice(seqStorePtr, 0, NULL, matches[u].off - 1, mlen - MINMATCH, ultra); ++ } ++ ++ if (cur + mlen > last_pos || (price < opt[cur + mlen].price)) ++ SET_PRICE(cur + mlen, mlen, matches[u].off, litlen, price); ++ ++ mlen++; ++ } ++ } ++ } /* for (cur = 1; cur <= last_pos; cur++) */ ++ ++ best_mlen = opt[last_pos].mlen; ++ best_off = opt[last_pos].off; ++ cur = last_pos - best_mlen; ++ ++ /* store sequence */ ++_storeSequence: /* cur, last_pos, best_mlen, best_off have to be set */ ++ opt[0].mlen = 1; ++ ++ while (1) { ++ mlen = opt[cur].mlen; ++ offset = opt[cur].off; ++ opt[cur].mlen = best_mlen; ++ opt[cur].off = best_off; ++ best_mlen = mlen; ++ best_off = offset; ++ if (mlen > cur) ++ break; ++ cur -= mlen; ++ } ++ ++ for (u = 0; u <= last_pos;) { ++ u += opt[u].mlen; ++ } ++ ++ for (cur = 0; cur < last_pos;) { ++ mlen = opt[cur].mlen; ++ if (mlen == 1) { ++ ip++; ++ cur++; ++ continue; ++ } ++ offset = opt[cur].off; ++ cur += mlen; ++ litLength = (U32)(ip - anchor); ++ ++ if (offset > ZSTD_REP_MOVE_OPT) { ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = offset - ZSTD_REP_MOVE_OPT; ++ offset--; ++ } else { ++ if (offset != 0) { ++ best_off = (offset == ZSTD_REP_MOVE_OPT) ? (rep[0] - 1) : (rep[offset]); ++ if (offset != 1) ++ rep[2] = rep[1]; ++ rep[1] = rep[0]; ++ rep[0] = best_off; ++ } ++ ++ if (litLength == 0) ++ offset--; ++ } ++ ++ ZSTD_updatePrice(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ ZSTD_storeSeq(seqStorePtr, litLength, anchor, offset, mlen - MINMATCH); ++ anchor = ip = ip + mlen; ++ } ++ } /* for (cur=0; cur < last_pos; ) */ ++ ++ /* Save reps for next block */ ++ { ++ int i; ++ for (i = 0; i < ZSTD_REP_NUM; i++) ++ ctx->repToConfirm[i] = rep[i]; ++ } ++ ++ /* Last Literals */ ++ { ++ size_t lastLLSize = iend - anchor; ++ memcpy(seqStorePtr->lit, anchor, lastLLSize); ++ seqStorePtr->lit += lastLLSize; ++ } ++} ++ ++#endif /* ZSTD_OPT_H_91842398743 */ +diff --git a/xen/include/xen/decompress.h b/xen/include/xen/decompress.h +index b2955faa4b..f5bc17f2b6 100644 +--- a/xen/include/xen/decompress.h ++++ b/xen/include/xen/decompress.h +@@ -31,7 +31,7 @@ typedef int decompress_fn(unsigned char *inbuf, unsigned int len, + * dependent). + */ + +-decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4; ++decompress_fn bunzip2, unxz, unlzma, unlzo, unlz4, unzstd; + + int decompress(void *inbuf, unsigned int len, void *outbuf); + +diff --git a/xen/include/xen/xxhash.h b/xen/include/xen/xxhash.h +new file mode 100644 +index 0000000000..13ddc616d1 +--- /dev/null ++++ b/xen/include/xen/xxhash.h +@@ -0,0 +1,259 @@ ++/* ++ * xxHash - Extremely Fast Hash algorithm ++ * Copyright (C) 2012-2016, Yann Collet. ++ * ++ * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are ++ * met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above ++ * copyright notice, this list of conditions and the following disclaimer ++ * in the documentation and/or other materials provided with the ++ * distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ++ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ++ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ++ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ * ++ * You can contact the author at: ++ * - xxHash homepage: https://cyan4973.github.io/xxHash/ ++ * - xxHash source repository: https://github.com/Cyan4973/xxHash ++ */ ++ ++/* ++ * Notice extracted from xxHash homepage: ++ * ++ * xxHash is an extremely fast Hash algorithm, running at RAM speed limits. ++ * It also successfully passes all tests from the SMHasher suite. ++ * ++ * Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 ++ * Duo @3GHz) ++ * ++ * Name Speed Q.Score Author ++ * xxHash 5.4 GB/s 10 ++ * CrapWow 3.2 GB/s 2 Andrew ++ * MumurHash 3a 2.7 GB/s 10 Austin Appleby ++ * SpookyHash 2.0 GB/s 10 Bob Jenkins ++ * SBox 1.4 GB/s 9 Bret Mulvey ++ * Lookup3 1.2 GB/s 9 Bob Jenkins ++ * SuperFastHash 1.2 GB/s 1 Paul Hsieh ++ * CityHash64 1.05 GB/s 10 Pike & Alakuijala ++ * FNV 0.55 GB/s 5 Fowler, Noll, Vo ++ * CRC32 0.43 GB/s 9 ++ * MD5-32 0.33 GB/s 10 Ronald L. Rivest ++ * SHA1-32 0.28 GB/s 10 ++ * ++ * Q.Score is a measure of quality of the hash function. ++ * It depends on successfully passing SMHasher test set. ++ * 10 is a perfect score. ++ * ++ * A 64-bits version, named xxh64 offers much better speed, ++ * but for 64-bits applications only. ++ * Name Speed on 64 bits Speed on 32 bits ++ * xxh64 13.8 GB/s 1.9 GB/s ++ * xxh32 6.8 GB/s 6.0 GB/s ++ */ ++ ++#ifndef XXHASH_H ++#define XXHASH_H ++ ++#include ++ ++/*-**************************** ++ * Simple Hash Functions ++ *****************************/ ++ ++/** ++ * xxh32() - calculate the 32-bit hash of the input with a given seed. ++ * ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s ++ * ++ * Return: The 32-bit hash of the data. ++ */ ++uint32_t xxh32(const void *input, size_t length, uint32_t seed); ++ ++/** ++ * xxh64() - calculate the 64-bit hash of the input with a given seed. ++ * ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * This function runs 2x faster on 64-bit systems, but slower on 32-bit systems. ++ * ++ * Return: The 64-bit hash of the data. ++ */ ++uint64_t xxh64(const void *input, size_t length, uint64_t seed); ++ ++/** ++ * xxhash() - calculate wordsize hash of the input with a given seed ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * @seed: The seed can be used to alter the result predictably. ++ * ++ * If the hash does not need to be comparable between machines with ++ * different word sizes, this function will call whichever of xxh32() ++ * or xxh64() is faster. ++ * ++ * Return: wordsize hash of the data. ++ */ ++ ++static inline unsigned long xxhash(const void *input, size_t length, ++ uint64_t seed) ++{ ++#if BITS_PER_LONG == 64 ++ return xxh64(input, length, seed); ++#else ++ return xxh32(input, length, seed); ++#endif ++} ++ ++/*-**************************** ++ * Streaming Hash Functions ++ *****************************/ ++ ++/* ++ * These definitions are only meant to allow allocation of XXH state ++ * statically, on stack, or in a struct for example. ++ * Do not use members directly. ++ */ ++ ++/** ++ * struct xxh32_state - private xxh32 state, do not use members directly ++ */ ++struct xxh32_state { ++ uint32_t total_len_32; ++ uint32_t large_len; ++ uint32_t v1; ++ uint32_t v2; ++ uint32_t v3; ++ uint32_t v4; ++ uint32_t mem32[4]; ++ uint32_t memsize; ++}; ++ ++/** ++ * struct xxh32_state - private xxh64 state, do not use members directly ++ */ ++struct xxh64_state { ++ uint64_t total_len; ++ uint64_t v1; ++ uint64_t v2; ++ uint64_t v3; ++ uint64_t v4; ++ uint64_t mem64[4]; ++ uint32_t memsize; ++}; ++ ++/** ++ * xxh32_reset() - reset the xxh32 state to start a new hashing operation ++ * ++ * @state: The xxh32 state to reset. ++ * @seed: Initialize the hash state with this seed. ++ * ++ * Call this function on any xxh32_state to prepare for a new hashing operation. ++ */ ++void xxh32_reset(struct xxh32_state *state, uint32_t seed); ++ ++/** ++ * xxh32_update() - hash the data given and update the xxh32 state ++ * ++ * @state: The xxh32 state to update. ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * ++ * After calling xxh32_reset() call xxh32_update() as many times as necessary. ++ * ++ * Return: Zero on success, otherwise an error code. ++ */ ++int xxh32_update(struct xxh32_state *state, const void *input, size_t length); ++ ++/** ++ * xxh32_digest() - produce the current xxh32 hash ++ * ++ * @state: Produce the current xxh32 hash of this state. ++ * ++ * A hash value can be produced at any time. It is still possible to continue ++ * inserting input into the hash state after a call to xxh32_digest(), and ++ * generate new hashes later on, by calling xxh32_digest() again. ++ * ++ * Return: The xxh32 hash stored in the state. ++ */ ++uint32_t xxh32_digest(const struct xxh32_state *state); ++ ++/** ++ * xxh64_reset() - reset the xxh64 state to start a new hashing operation ++ * ++ * @state: The xxh64 state to reset. ++ * @seed: Initialize the hash state with this seed. ++ */ ++void xxh64_reset(struct xxh64_state *state, uint64_t seed); ++ ++/** ++ * xxh64_update() - hash the data given and update the xxh64 state ++ * @state: The xxh64 state to update. ++ * @input: The data to hash. ++ * @length: The length of the data to hash. ++ * ++ * After calling xxh64_reset() call xxh64_update() as many times as necessary. ++ * ++ * Return: Zero on success, otherwise an error code. ++ */ ++int xxh64_update(struct xxh64_state *state, const void *input, size_t length); ++ ++/** ++ * xxh64_digest() - produce the current xxh64 hash ++ * ++ * @state: Produce the current xxh64 hash of this state. ++ * ++ * A hash value can be produced at any time. It is still possible to continue ++ * inserting input into the hash state after a call to xxh64_digest(), and ++ * generate new hashes later on, by calling xxh64_digest() again. ++ * ++ * Return: The xxh64 hash stored in the state. ++ */ ++uint64_t xxh64_digest(const struct xxh64_state *state); ++ ++/*-************************** ++ * Utils ++ ***************************/ ++ ++/** ++ * xxh32_copy_state() - copy the source state into the destination state ++ * ++ * @src: The source xxh32 state. ++ * @dst: The destination xxh32 state. ++ */ ++void xxh32_copy_state(struct xxh32_state *dst, const struct xxh32_state *src); ++ ++/** ++ * xxh64_copy_state() - copy the source state into the destination state ++ * ++ * @src: The source xxh64 state. ++ * @dst: The destination xxh64 state. ++ */ ++void xxh64_copy_state(struct xxh64_state *dst, const struct xxh64_state *src); ++ ++#endif /* XXHASH_H */ +diff --git a/xen/include/xen/zstd.h b/xen/include/xen/zstd.h +new file mode 100644 +index 0000000000..eb33582a18 +--- /dev/null ++++ b/xen/include/xen/zstd.h +@@ -0,0 +1,1157 @@ ++/* ++ * Copyright (c) 2016-present, Yann Collet, Facebook, Inc. ++ * All rights reserved. ++ * ++ * This source code is licensed under the BSD-style license found in the ++ * LICENSE file in the root directory of https://github.com/facebook/zstd. ++ * An additional grant of patent rights can be found in the PATENTS file in the ++ * same directory. ++ * ++ * This program is free software; you can redistribute it and/or modify it under ++ * the terms of the GNU General Public License version 2 as published by the ++ * Free Software Foundation. This program is dual-licensed; you may select ++ * either version 2 of the GNU General Public License ("GPL") or BSD license ++ * ("BSD"). ++ */ ++ ++#ifndef ZSTD_H ++#define ZSTD_H ++ ++/* ====== Dependency ======*/ ++#include /* size_t */ ++ ++ ++/*-***************************************************************************** ++ * Introduction ++ * ++ * zstd, short for Zstandard, is a fast lossless compression algorithm, ++ * targeting real-time compression scenarios at zlib-level and better ++ * compression ratios. The zstd compression library provides in-memory ++ * compression and decompression functions. The library supports compression ++ * levels from 1 up to ZSTD_maxCLevel() which is 22. Levels >= 20, labeled ++ * ultra, should be used with caution, as they require more memory. ++ * Compression can be done in: ++ * - a single step, reusing a context (described as Explicit memory management) ++ * - unbounded multiple steps (described as Streaming compression) ++ * The compression ratio achievable on small data can be highly improved using ++ * compression with a dictionary in: ++ * - a single step (described as Simple dictionary API) ++ * - a single step, reusing a dictionary (described as Fast dictionary API) ++ ******************************************************************************/ ++ ++/*====== Helper functions ======*/ ++ ++/** ++ * enum ZSTD_ErrorCode - zstd error codes ++ * ++ * Functions that return size_t can be checked for errors using ZSTD_isError() ++ * and the ZSTD_ErrorCode can be extracted using ZSTD_getErrorCode(). ++ */ ++typedef enum { ++ ZSTD_error_no_error, ++ ZSTD_error_GENERIC, ++ ZSTD_error_prefix_unknown, ++ ZSTD_error_version_unsupported, ++ ZSTD_error_parameter_unknown, ++ ZSTD_error_frameParameter_unsupported, ++ ZSTD_error_frameParameter_unsupportedBy32bits, ++ ZSTD_error_frameParameter_windowTooLarge, ++ ZSTD_error_compressionParameter_unsupported, ++ ZSTD_error_init_missing, ++ ZSTD_error_memory_allocation, ++ ZSTD_error_stage_wrong, ++ ZSTD_error_dstSize_tooSmall, ++ ZSTD_error_srcSize_wrong, ++ ZSTD_error_corruption_detected, ++ ZSTD_error_checksum_wrong, ++ ZSTD_error_tableLog_tooLarge, ++ ZSTD_error_maxSymbolValue_tooLarge, ++ ZSTD_error_maxSymbolValue_tooSmall, ++ ZSTD_error_dictionary_corrupted, ++ ZSTD_error_dictionary_wrong, ++ ZSTD_error_dictionaryCreation_failed, ++ ZSTD_error_maxCode ++} ZSTD_ErrorCode; ++ ++/** ++ * ZSTD_maxCLevel() - maximum compression level available ++ * ++ * Return: Maximum compression level available. ++ */ ++int ZSTD_maxCLevel(void); ++/** ++ * ZSTD_compressBound() - maximum compressed size in worst case scenario ++ * @srcSize: The size of the data to compress. ++ * ++ * Return: The maximum compressed size in the worst case scenario. ++ */ ++size_t ZSTD_compressBound(size_t srcSize); ++/** ++ * ZSTD_isError() - tells if a size_t function result is an error code ++ * @code: The function result to check for error. ++ * ++ * Return: Non-zero iff the code is an error. ++ */ ++static __attribute__((unused)) unsigned int ZSTD_isError(size_t code) ++{ ++ return code > (size_t)-ZSTD_error_maxCode; ++} ++/** ++ * ZSTD_getErrorCode() - translates an error function result to a ZSTD_ErrorCode ++ * @functionResult: The result of a function for which ZSTD_isError() is true. ++ * ++ * Return: The ZSTD_ErrorCode corresponding to the functionResult or 0 ++ * if the functionResult isn't an error. ++ */ ++static __attribute__((unused)) ZSTD_ErrorCode ZSTD_getErrorCode( ++ size_t functionResult) ++{ ++ if (!ZSTD_isError(functionResult)) ++ return (ZSTD_ErrorCode)0; ++ return (ZSTD_ErrorCode)(0 - functionResult); ++} ++ ++/** ++ * enum ZSTD_strategy - zstd compression search strategy ++ * ++ * From faster to stronger. ++ */ ++typedef enum { ++ ZSTD_fast, ++ ZSTD_dfast, ++ ZSTD_greedy, ++ ZSTD_lazy, ++ ZSTD_lazy2, ++ ZSTD_btlazy2, ++ ZSTD_btopt, ++ ZSTD_btopt2 ++} ZSTD_strategy; ++ ++/** ++ * struct ZSTD_compressionParameters - zstd compression parameters ++ * @windowLog: Log of the largest match distance. Larger means more ++ * compression, and more memory needed during decompression. ++ * @chainLog: Fully searched segment. Larger means more compression, slower, ++ * and more memory (useless for fast). ++ * @hashLog: Dispatch table. Larger means more compression, ++ * slower, and more memory. ++ * @searchLog: Number of searches. Larger means more compression and slower. ++ * @searchLength: Match length searched. Larger means faster decompression, ++ * sometimes less compression. ++ * @targetLength: Acceptable match size for optimal parser (only). Larger means ++ * more compression, and slower. ++ * @strategy: The zstd compression strategy. ++ */ ++typedef struct { ++ unsigned int windowLog; ++ unsigned int chainLog; ++ unsigned int hashLog; ++ unsigned int searchLog; ++ unsigned int searchLength; ++ unsigned int targetLength; ++ ZSTD_strategy strategy; ++} ZSTD_compressionParameters; ++ ++/** ++ * struct ZSTD_frameParameters - zstd frame parameters ++ * @contentSizeFlag: Controls whether content size will be present in the frame ++ * header (when known). ++ * @checksumFlag: Controls whether a 32-bit checksum is generated at the end ++ * of the frame for error detection. ++ * @noDictIDFlag: Controls whether dictID will be saved into the frame header ++ * when using dictionary compression. ++ * ++ * The default value is all fields set to 0. ++ */ ++typedef struct { ++ unsigned int contentSizeFlag; ++ unsigned int checksumFlag; ++ unsigned int noDictIDFlag; ++} ZSTD_frameParameters; ++ ++/** ++ * struct ZSTD_parameters - zstd parameters ++ * @cParams: The compression parameters. ++ * @fParams: The frame parameters. ++ */ ++typedef struct { ++ ZSTD_compressionParameters cParams; ++ ZSTD_frameParameters fParams; ++} ZSTD_parameters; ++ ++/** ++ * ZSTD_getCParams() - returns ZSTD_compressionParameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * Return: The selected ZSTD_compressionParameters. ++ */ ++ZSTD_compressionParameters ZSTD_getCParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/** ++ * ZSTD_getParams() - returns ZSTD_parameters for selected level ++ * @compressionLevel: The compression level from 1 to ZSTD_maxCLevel(). ++ * @estimatedSrcSize: The estimated source size to compress or 0 if unknown. ++ * @dictSize: The dictionary size or 0 if a dictionary isn't being used. ++ * ++ * The same as ZSTD_getCParams() except also selects the default frame ++ * parameters (all zero). ++ * ++ * Return: The selected ZSTD_parameters. ++ */ ++ZSTD_parameters ZSTD_getParams(int compressionLevel, ++ unsigned long long estimatedSrcSize, size_t dictSize); ++ ++/*-************************************* ++ * Explicit memory management ++ **************************************/ ++ ++/** ++ * ZSTD_CCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_CCtx ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * If multiple compression parameters might be used, the caller must call ++ * ZSTD_CCtxWorkspaceBound() for each set of parameters and use the maximum ++ * size. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCCtx(). ++ */ ++size_t ZSTD_CCtxWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CCtx - the zstd compression context ++ * ++ * When compressing many times it is recommended to allocate a context just once ++ * and reuse it for each successive compression operation. ++ */ ++typedef struct ZSTD_CCtx_s ZSTD_CCtx; ++/** ++ * ZSTD_initCCtx() - initialize a zstd compression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_CCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A compression context emplaced into workspace. ++ */ ++ZSTD_CCtx *ZSTD_initCCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compressCCtx() - compress src into dst ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressCCtx(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, ZSTD_parameters params); ++ ++/** ++ * ZSTD_DCtxWorkspaceBound() - amount of memory needed to initialize a ZSTD_DCtx ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDCtx(). ++ */ ++size_t ZSTD_DCtxWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DCtx - the zstd decompression context ++ * ++ * When decompressing many times it is recommended to allocate a context just ++ * once and reuse it for each successive decompression operation. ++ */ ++typedef struct ZSTD_DCtx_s ZSTD_DCtx; ++/** ++ * ZSTD_initDCtx() - initialize a zstd decompression context ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Use ZSTD_DCtxWorkspaceBound() to ++ * determine how large the workspace must be. ++ * ++ * Return: A decompression context emplaced into workspace. ++ */ ++ZSTD_DCtx *ZSTD_initDCtx(void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompressDCtx() - decompress zstd compressed src into dst ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompressDCtx(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++/*-************************ ++ * Simple dictionary API ++ **************************/ ++ ++/** ++ * ZSTD_compress_usingDict() - compress src into dst using a dictionary ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(params.cParams). ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @dict: The dictionary to use for compression. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a predefined dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingDict(ZSTD_CCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize, ++ ZSTD_parameters params); ++ ++/** ++ * ZSTD_decompress_usingDict() - decompress src into dst using a dictionary ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @dict: The dictionary to use for decompression. The same dictionary ++ * must've been used to compress the data. ++ * @dictSize: The size of the dictionary. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDict(ZSTD_DCtx *ctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const void *dict, size_t dictSize); ++ ++/*-************************** ++ * Fast dictionary API ++ ***************************/ ++ ++/** ++ * ZSTD_CDictWorkspaceBound() - memory needed to initialize a ZSTD_CDict ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCDict(). ++ */ ++size_t ZSTD_CDictWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CDict - a digested dictionary to be used for compression ++ */ ++typedef struct ZSTD_CDict_s ZSTD_CDict; ++ ++/** ++ * ZSTD_initCDict() - initialize a digested dictionary for compression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_CDict so it must outlive the returned ZSTD_CDict. ++ * @dictSize: The size of the dictionary. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * @workspace: The workspace. It must outlive the returned ZSTD_CDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_CDictWorkspaceBound(params.cParams). ++ * ++ * When compressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_CDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_CDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_CDict *ZSTD_initCDict(const void *dictBuffer, size_t dictSize, ++ ZSTD_parameters params, void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_compress_usingCDict() - compress src into dst using a ZSTD_CDict ++ * @ctx: The context. Must have been initialized with a workspace at ++ * least as large as ZSTD_CCtxWorkspaceBound(cParams) where ++ * cParams are the compression parameters used to initialize the ++ * cdict. ++ * @dst: The buffer to compress src into. ++ * @dstCapacity: The size of the destination buffer. May be any size, but ++ * ZSTD_compressBound(srcSize) is guaranteed to be large enough. ++ * @src: The data to compress. ++ * @srcSize: The size of the data to compress. ++ * @cdict: The digested dictionary to use for compression. ++ * @params: The parameters to use for compression. See ZSTD_getParams(). ++ * ++ * Compression using a digested dictionary. The same dictionary must be used ++ * during decompression. ++ * ++ * Return: The compressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compress_usingCDict(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize, const ZSTD_CDict *cdict); ++ ++ ++/** ++ * ZSTD_DDictWorkspaceBound() - memory needed to initialize a ZSTD_DDict ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDDict(). ++ */ ++size_t ZSTD_DDictWorkspaceBound(void); ++ ++/** ++ * struct ZSTD_DDict - a digested dictionary to be used for decompression ++ */ ++typedef struct ZSTD_DDict_s ZSTD_DDict; ++ ++/** ++ * ZSTD_initDDict() - initialize a digested dictionary for decompression ++ * @dictBuffer: The dictionary to digest. The buffer is referenced by the ++ * ZSTD_DDict so it must outlive the returned ZSTD_DDict. ++ * @dictSize: The size of the dictionary. ++ * @workspace: The workspace. It must outlive the returned ZSTD_DDict. ++ * @workspaceSize: The workspace size. Must be at least ++ * ZSTD_DDictWorkspaceBound(). ++ * ++ * When decompressing multiple messages / blocks with the same dictionary it is ++ * recommended to load it just once. The ZSTD_DDict merely references the ++ * dictBuffer, so it must outlive the returned ZSTD_DDict. ++ * ++ * Return: The digested dictionary emplaced into workspace. ++ */ ++ZSTD_DDict *ZSTD_initDDict(const void *dictBuffer, size_t dictSize, ++ void *workspace, size_t workspaceSize); ++ ++/** ++ * ZSTD_decompress_usingDDict() - decompress src into dst using a ZSTD_DDict ++ * @ctx: The decompression context. ++ * @dst: The buffer to decompress src into. ++ * @dstCapacity: The size of the destination buffer. Must be at least as large ++ * as the decompressed size. If the caller cannot upper bound the ++ * decompressed size, then it's better to use the streaming API. ++ * @src: The zstd compressed data to decompress. Multiple concatenated ++ * frames and skippable frames are allowed. ++ * @srcSize: The exact size of the data to decompress. ++ * @ddict: The digested dictionary to use for decompression. The same ++ * dictionary must've been used to compress the data. ++ * ++ * Return: The decompressed size or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_decompress_usingDDict(ZSTD_DCtx *dctx, void *dst, ++ size_t dstCapacity, const void *src, size_t srcSize, ++ const ZSTD_DDict *ddict); ++ ++ ++/*-************************** ++ * Streaming ++ ***************************/ ++ ++/** ++ * struct ZSTD_inBuffer - input buffer for streaming ++ * @src: Start of the input buffer. ++ * @size: Size of the input buffer. ++ * @pos: Position where reading stopped. Will be updated. ++ * Necessarily 0 <= pos <= size. ++ */ ++typedef struct ZSTD_inBuffer_s { ++ const void *src; ++ size_t size; ++ size_t pos; ++} ZSTD_inBuffer; ++ ++/** ++ * struct ZSTD_outBuffer - output buffer for streaming ++ * @dst: Start of the output buffer. ++ * @size: Size of the output buffer. ++ * @pos: Position where writing stopped. Will be updated. ++ * Necessarily 0 <= pos <= size. ++ */ ++typedef struct ZSTD_outBuffer_s { ++ void *dst; ++ size_t size; ++ size_t pos; ++} ZSTD_outBuffer; ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming compression - HowTo ++ * ++ * A ZSTD_CStream object is required to track streaming operation. ++ * Use ZSTD_initCStream() to initialize a ZSTD_CStream object. ++ * ZSTD_CStream objects can be reused multiple times on consecutive compression ++ * operations. It is recommended to re-use ZSTD_CStream in situations where many ++ * streaming operations will be achieved consecutively. Use one separate ++ * ZSTD_CStream per thread for parallel execution. ++ * ++ * Use ZSTD_compressStream() repetitively to consume input stream. ++ * The function will automatically update both `pos` fields. ++ * Note that it may not consume the entire input, in which case `pos < size`, ++ * and it's up to the caller to present again remaining data. ++ * It returns a hint for the preferred number of bytes to use as an input for ++ * the next function call. ++ * ++ * At any moment, it's possible to flush whatever data remains within internal ++ * buffer, using ZSTD_flushStream(). `output->pos` will be updated. There might ++ * still be some content left within the internal buffer if `output->size` is ++ * too small. It returns the number of bytes left in the internal buffer and ++ * must be called until it returns 0. ++ * ++ * ZSTD_endStream() instructs to finish a frame. It will perform a flush and ++ * write frame epilogue. The epilogue is required for decoders to consider a ++ * frame completed. Similar to ZSTD_flushStream(), it may not be able to flush ++ * the full content if `output->size` is too small. In which case, call again ++ * ZSTD_endStream() to complete the flush. It returns the number of bytes left ++ * in the internal buffer and must be called until it returns 0. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_CStreamWorkspaceBound() - memory needed to initialize a ZSTD_CStream ++ * @cParams: The compression parameters to be used for compression. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initCStream() and ZSTD_initCStream_usingCDict(). ++ */ ++size_t ZSTD_CStreamWorkspaceBound(ZSTD_compressionParameters cParams); ++ ++/** ++ * struct ZSTD_CStream - the zstd streaming compression context ++ */ ++typedef struct ZSTD_CStream_s ZSTD_CStream; ++ ++/*===== ZSTD_CStream management functions =====*/ ++/** ++ * ZSTD_initCStream() - initialize a zstd streaming compression context ++ * @params: The zstd compression parameters. ++ * @pledgedSrcSize: If params.fParams.contentSizeFlag == 1 then the caller must ++ * pass the source size (zero means empty source). Otherwise, ++ * the caller may optionally pass the source size, or zero if ++ * unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_CStreamWorkspaceBound(params.cParams) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream(ZSTD_parameters params, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/** ++ * ZSTD_initCStream_usingCDict() - initialize a streaming compression context ++ * @cdict: The digested dictionary to use for compression. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. Call ZSTD_CStreamWorkspaceBound() ++ * with the cParams used to initialize the cdict to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming compression context. ++ */ ++ZSTD_CStream *ZSTD_initCStream_usingCDict(const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize, void *workspace, ++ size_t workspaceSize); ++ ++/*===== Streaming compression functions =====*/ ++/** ++ * ZSTD_resetCStream() - reset the context using parameters from creation ++ * @zcs: The zstd streaming compression context to reset. ++ * @pledgedSrcSize: Optionally the source size, or zero if unknown. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. If `pledgedSrcSize` is non-zero the frame ++ * content size is always written into the frame header. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetCStream(ZSTD_CStream *zcs, unsigned long long pledgedSrcSize); ++/** ++ * ZSTD_compressStream() - streaming compress some of input into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * @input: Source buffer. `input->pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input->pos < input->size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ++ * Return: A hint for the number of bytes to use as the input for the next ++ * function call or an error, which can be checked using ++ * ZSTD_isError(). ++ */ ++size_t ZSTD_compressStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++/** ++ * ZSTD_flushStream() - flush internal buffers into output ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_flushStream() must be called until it returns 0, meaning all the data ++ * has been flushed. Since ZSTD_flushStream() causes a block to be ended, ++ * calling it too often will degrade the compression ratio. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_flushStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++/** ++ * ZSTD_endStream() - flush internal buffers into output and end the frame ++ * @zcs: The zstd streaming compression context. ++ * @output: Destination buffer. `output->pos` is updated to indicate how much ++ * compressed data was written. ++ * ++ * ZSTD_endStream() must be called until it returns 0, meaning all the data has ++ * been flushed and the frame epilogue has been written. ++ * ++ * Return: The number of bytes still present within internal buffers or an ++ * error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_endStream(ZSTD_CStream *zcs, ZSTD_outBuffer *output); ++ ++/** ++ * ZSTD_CStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_CStreamInSize(void); ++/** ++ * ZSTD_CStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete compressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_CStreamOutSize(void); ++ ++ ++ ++/*-***************************************************************************** ++ * Streaming decompression - HowTo ++ * ++ * A ZSTD_DStream object is required to track streaming operations. ++ * Use ZSTD_initDStream() to initialize a ZSTD_DStream object. ++ * ZSTD_DStream objects can be re-used multiple times. ++ * ++ * Use ZSTD_decompressStream() repetitively to consume your input. ++ * The function will update both `pos` fields. ++ * If `input->pos < input->size`, some input has not been consumed. ++ * It's up to the caller to present again remaining data. ++ * If `output->pos < output->size`, decoder has flushed everything it could. ++ * Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise it returns a suggested next input size that will never load more ++ * than the current frame. ++ ******************************************************************************/ ++ ++/** ++ * ZSTD_DStreamWorkspaceBound() - memory needed to initialize a ZSTD_DStream ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * ++ * Return: A lower bound on the size of the workspace that is passed to ++ * ZSTD_initDStream() and ZSTD_initDStream_usingDDict(). ++ */ ++size_t ZSTD_DStreamWorkspaceBound(size_t maxWindowSize); ++ ++/** ++ * struct ZSTD_DStream - the zstd streaming decompression context ++ */ ++typedef struct ZSTD_DStream_s ZSTD_DStream; ++/*===== ZSTD_DStream management functions =====*/ ++/** ++ * ZSTD_initDStream() - initialize a zstd streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream(size_t maxWindowSize, void *workspace, ++ size_t workspaceSize); ++/** ++ * ZSTD_initDStream_usingDDict() - initialize streaming decompression context ++ * @maxWindowSize: The maximum window size allowed for compressed frames. ++ * @ddict: The digested dictionary to use for decompression. ++ * @workspace: The workspace to emplace the context into. It must outlive ++ * the returned context. ++ * @workspaceSize: The size of workspace. ++ * Use ZSTD_DStreamWorkspaceBound(maxWindowSize) to determine ++ * how large the workspace must be. ++ * ++ * Return: The zstd streaming decompression context. ++ */ ++ZSTD_DStream *ZSTD_initDStream_usingDDict(size_t maxWindowSize, ++ const ZSTD_DDict *ddict, void *workspace, size_t workspaceSize); ++ ++/*===== Streaming decompression functions =====*/ ++/** ++ * ZSTD_resetDStream() - reset the context using parameters from creation ++ * @zds: The zstd streaming decompression context to reset. ++ * ++ * Resets the context using the parameters from creation. Skips dictionary ++ * loading, since it can be reused. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_resetDStream(ZSTD_DStream *zds); ++/** ++ * ZSTD_decompressStream() - streaming decompress some of input into output ++ * @zds: The zstd streaming decompression context. ++ * @output: Destination buffer. `output.pos` is updated to indicate how much ++ * decompressed data was written. ++ * @input: Source buffer. `input.pos` is updated to indicate how much data was ++ * read. Note that it may not consume the entire input, in which case ++ * `input.pos < input.size`, and it's up to the caller to present ++ * remaining data again. ++ * ++ * The `input` and `output` buffers may be any size. Guaranteed to make some ++ * forward progress if `input` and `output` are not empty. ++ * ZSTD_decompressStream() will not consume the last byte of the frame until ++ * the entire frame is flushed. ++ * ++ * Return: Returns 0 iff a frame is completely decoded and fully flushed. ++ * Otherwise returns a hint for the number of bytes to use as the input ++ * for the next function call or an error, which can be checked using ++ * ZSTD_isError(). The size hint will never load more than the frame. ++ */ ++size_t ZSTD_decompressStream(ZSTD_DStream *zds, ZSTD_outBuffer *output, ++ ZSTD_inBuffer *input); ++ ++/** ++ * ZSTD_DStreamInSize() - recommended size for the input buffer ++ * ++ * Return: The recommended size for the input buffer. ++ */ ++size_t ZSTD_DStreamInSize(void); ++/** ++ * ZSTD_DStreamOutSize() - recommended size for the output buffer ++ * ++ * When the output buffer is at least this large, it is guaranteed to be large ++ * enough to flush at least one complete decompressed block. ++ * ++ * Return: The recommended size for the output buffer. ++ */ ++size_t ZSTD_DStreamOutSize(void); ++ ++ ++/* --- Constants ---*/ ++#define ZSTD_MAGICNUMBER 0xFD2FB528 /* >= v0.8.0 */ ++#define ZSTD_MAGIC_SKIPPABLE_START 0x184D2A50U ++ ++#define ZSTD_CONTENTSIZE_UNKNOWN (0ULL - 1) ++#define ZSTD_CONTENTSIZE_ERROR (0ULL - 2) ++ ++#define ZSTD_WINDOWLOG_MAX_32 27 ++#define ZSTD_WINDOWLOG_MAX_64 27 ++#define ZSTD_WINDOWLOG_MAX \ ++ ((unsigned int)(sizeof(size_t) == 4 \ ++ ? ZSTD_WINDOWLOG_MAX_32 \ ++ : ZSTD_WINDOWLOG_MAX_64)) ++#define ZSTD_WINDOWLOG_MIN 10 ++#define ZSTD_HASHLOG_MAX ZSTD_WINDOWLOG_MAX ++#define ZSTD_HASHLOG_MIN 6 ++#define ZSTD_CHAINLOG_MAX (ZSTD_WINDOWLOG_MAX+1) ++#define ZSTD_CHAINLOG_MIN ZSTD_HASHLOG_MIN ++#define ZSTD_HASHLOG3_MAX 17 ++#define ZSTD_SEARCHLOG_MAX (ZSTD_WINDOWLOG_MAX-1) ++#define ZSTD_SEARCHLOG_MIN 1 ++/* only for ZSTD_fast, other strategies are limited to 6 */ ++#define ZSTD_SEARCHLENGTH_MAX 7 ++/* only for ZSTD_btopt, other strategies are limited to 4 */ ++#define ZSTD_SEARCHLENGTH_MIN 3 ++#define ZSTD_TARGETLENGTH_MIN 4 ++#define ZSTD_TARGETLENGTH_MAX 999 ++ ++/* for static allocation */ ++#define ZSTD_FRAMEHEADERSIZE_MAX 18 ++#define ZSTD_FRAMEHEADERSIZE_MIN 6 ++static const size_t ZSTD_frameHeaderSize_prefix = 5; ++static const size_t ZSTD_frameHeaderSize_min = ZSTD_FRAMEHEADERSIZE_MIN; ++static const size_t ZSTD_frameHeaderSize_max = ZSTD_FRAMEHEADERSIZE_MAX; ++/* magic number + skippable frame length */ ++static const size_t ZSTD_skippableHeaderSize = 8; ++ ++ ++/*-************************************* ++ * Compressed size functions ++ **************************************/ ++ ++/** ++ * ZSTD_findFrameCompressedSize() - returns the size of a compressed frame ++ * @src: Source buffer. It should point to the start of a zstd encoded frame ++ * or a skippable frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * size of the frame. ++ * ++ * Return: The compressed size of the frame pointed to by `src` or an error, ++ * which can be check with ZSTD_isError(). ++ * Suitable to pass to ZSTD_decompress() or similar functions. ++ */ ++size_t ZSTD_findFrameCompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Decompressed size functions ++ **************************************/ ++/** ++ * ZSTD_getFrameContentSize() - returns the content size in a zstd frame header ++ * @src: It should point to the start of a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The frame content size stored in the frame header if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the content size isn't stored in the ++ * frame header. `ZSTD_CONTENTSIZE_ERROR` on invalid input. ++ */ ++unsigned long long ZSTD_getFrameContentSize(const void *src, size_t srcSize); ++ ++/** ++ * ZSTD_findDecompressedSize() - returns decompressed size of a series of frames ++ * @src: It should point to the start of a series of zstd encoded and/or ++ * skippable frames. ++ * @srcSize: The exact size of the series of frames. ++ * ++ * If any zstd encoded frame in the series doesn't have the frame content size ++ * set, `ZSTD_CONTENTSIZE_UNKNOWN` is returned. But frame content size is always ++ * set when using ZSTD_compress(). The decompressed size can be very large. ++ * If the source is untrusted, the decompressed size could be wrong or ++ * intentionally modified. Always ensure the result fits within the ++ * application's authorized limits. ZSTD_findDecompressedSize() handles multiple ++ * frames, and so it must traverse the input to read each frame header. This is ++ * efficient as most of the data is skipped, however it does mean that all frame ++ * data must be present and valid. ++ * ++ * Return: Decompressed size of all the data contained in the frames if known. ++ * `ZSTD_CONTENTSIZE_UNKNOWN` if the decompressed size is unknown. ++ * `ZSTD_CONTENTSIZE_ERROR` if an error occurred. ++ */ ++unsigned long long ZSTD_findDecompressedSize(const void *src, size_t srcSize); ++ ++/*-************************************* ++ * Advanced compression functions ++ **************************************/ ++/** ++ * ZSTD_checkCParams() - ensure parameter values remain within authorized range ++ * @cParams: The zstd compression parameters. ++ * ++ * Return: Zero or an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_checkCParams(ZSTD_compressionParameters cParams); ++ ++/** ++ * ZSTD_adjustCParams() - optimize parameters for a given srcSize and dictSize ++ * @srcSize: Optionally the estimated source size, or zero if unknown. ++ * @dictSize: Optionally the estimated dictionary size, or zero if unknown. ++ * ++ * Return: The optimized parameters. ++ */ ++ZSTD_compressionParameters ZSTD_adjustCParams( ++ ZSTD_compressionParameters cParams, unsigned long long srcSize, ++ size_t dictSize); ++ ++/*--- Advanced decompression functions ---*/ ++ ++/** ++ * ZSTD_isFrame() - returns true iff the buffer starts with a valid frame ++ * @buffer: The source buffer to check. ++ * @size: The size of the source buffer, must be at least 4 bytes. ++ * ++ * Return: True iff the buffer starts with a zstd or skippable frame identifier. ++ */ ++unsigned int ZSTD_isFrame(const void *buffer, size_t size); ++ ++/** ++ * ZSTD_getDictID_fromDict() - returns the dictionary id stored in a dictionary ++ * @dict: The dictionary buffer. ++ * @dictSize: The size of the dictionary buffer. ++ * ++ * Return: The dictionary id stored within the dictionary or 0 if the ++ * dictionary is not a zstd dictionary. If it returns 0 the ++ * dictionary can still be loaded as a content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDict(const void *dict, size_t dictSize); ++ ++/** ++ * ZSTD_getDictID_fromDDict() - returns the dictionary id stored in a ZSTD_DDict ++ * @ddict: The ddict to find the id of. ++ * ++ * Return: The dictionary id stored within `ddict` or 0 if the dictionary is not ++ * a zstd dictionary. If it returns 0 `ddict` will be loaded as a ++ * content-only dictionary. ++ */ ++unsigned int ZSTD_getDictID_fromDDict(const ZSTD_DDict *ddict); ++ ++/** ++ * ZSTD_getDictID_fromFrame() - returns the dictionary id stored in a zstd frame ++ * @src: Source buffer. It must be a zstd encoded frame. ++ * @srcSize: The size of the source buffer. It must be at least as large as the ++ * frame header. `ZSTD_frameHeaderSize_max` is always large enough. ++ * ++ * Return: The dictionary id required to decompress the frame stored within ++ * `src` or 0 if the dictionary id could not be decoded. It can return ++ * 0 if the frame does not require a dictionary, the dictionary id ++ * wasn't stored in the frame, `src` is not a zstd frame, or `srcSize` ++ * is too small. ++ */ ++unsigned int ZSTD_getDictID_fromFrame(const void *src, size_t srcSize); ++ ++/** ++ * struct ZSTD_frameParams - zstd frame parameters stored in the frame header ++ * @frameContentSize: The frame content size, or 0 if not present. ++ * @windowSize: The window size, or 0 if the frame is a skippable frame. ++ * @dictID: The dictionary id, or 0 if not present. ++ * @checksumFlag: Whether a checksum was used. ++ */ ++typedef struct { ++ unsigned long long frameContentSize; ++ unsigned int windowSize; ++ unsigned int dictID; ++ unsigned int checksumFlag; ++} ZSTD_frameParams; ++ ++/** ++ * ZSTD_getFrameParams() - extracts parameters from a zstd or skippable frame ++ * @fparamsPtr: On success the frame parameters are written here. ++ * @src: The source buffer. It must point to a zstd or skippable frame. ++ * @srcSize: The size of the source buffer. `ZSTD_frameHeaderSize_max` is ++ * always large enough to succeed. ++ * ++ * Return: 0 on success. If more data is required it returns how many bytes ++ * must be provided to make forward progress. Otherwise it returns ++ * an error, which can be checked using ZSTD_isError(). ++ */ ++size_t ZSTD_getFrameParams(ZSTD_frameParams *fparamsPtr, const void *src, ++ size_t srcSize); ++ ++/*-***************************************************************************** ++ * Buffer-less and synchronous inner streaming functions ++ * ++ * This is an advanced API, giving full control over buffer management, for ++ * users which need direct control over memory. ++ * But it's also a complex one, with many restrictions (documented below). ++ * Prefer using normal streaming API for an easier experience ++ ******************************************************************************/ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming compression (synchronous mode) ++ * ++ * A ZSTD_CCtx object is required to track streaming operations. ++ * Use ZSTD_initCCtx() to initialize a context. ++ * ZSTD_CCtx object can be re-used multiple times within successive compression ++ * operations. ++ * ++ * Start by initializing a context. ++ * Use ZSTD_compressBegin(), or ZSTD_compressBegin_usingDict() for dictionary ++ * compression, ++ * or ZSTD_compressBegin_advanced(), for finer parameter control. ++ * It's also possible to duplicate a reference context which has already been ++ * initialized, using ZSTD_copyCCtx() ++ * ++ * Then, consume your input using ZSTD_compressContinue(). ++ * There are some important considerations to keep in mind when using this ++ * advanced function : ++ * - ZSTD_compressContinue() has no internal buffer. It uses externally provided ++ * buffer only. ++ * - Interface is synchronous : input is consumed entirely and produce 1+ ++ * (or more) compressed blocks. ++ * - Caller must ensure there is enough space in `dst` to store compressed data ++ * under worst case scenario. Worst case evaluation is provided by ++ * ZSTD_compressBound(). ++ * ZSTD_compressContinue() doesn't guarantee recover after a failed ++ * compression. ++ * - ZSTD_compressContinue() presumes prior input ***is still accessible and ++ * unmodified*** (up to maximum distance size, see WindowLog). ++ * It remembers all previous contiguous blocks, plus one separated memory ++ * segment (which can itself consists of multiple contiguous blocks) ++ * - ZSTD_compressContinue() detects that prior input has been overwritten when ++ * `src` buffer overlaps. In which case, it will "discard" the relevant memory ++ * section from its history. ++ * ++ * Finish a frame with ZSTD_compressEnd(), which will write the last block(s) ++ * and optional checksum. It's possible to use srcSize==0, in which case, it ++ * will write a final empty block to end the frame. Without last block mark, ++ * frames will be considered unfinished (corrupted) by decoders. ++ * ++ * `ZSTD_CCtx` object can be re-used (ZSTD_compressBegin()) to compress some new ++ * frame. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming compression functions =====*/ ++size_t ZSTD_compressBegin(ZSTD_CCtx *cctx, int compressionLevel); ++size_t ZSTD_compressBegin_usingDict(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, int compressionLevel); ++size_t ZSTD_compressBegin_advanced(ZSTD_CCtx *cctx, const void *dict, ++ size_t dictSize, ZSTD_parameters params, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_copyCCtx(ZSTD_CCtx *cctx, const ZSTD_CCtx *preparedCCtx, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressBegin_usingCDict(ZSTD_CCtx *cctx, const ZSTD_CDict *cdict, ++ unsigned long long pledgedSrcSize); ++size_t ZSTD_compressContinue(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_compressEnd(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++ ++ ++ ++/*-***************************************************************************** ++ * Buffer-less streaming decompression (synchronous mode) ++ * ++ * A ZSTD_DCtx object is required to track streaming operations. ++ * Use ZSTD_initDCtx() to initialize a context. ++ * A ZSTD_DCtx object can be re-used multiple times. ++ * ++ * First typical operation is to retrieve frame parameters, using ++ * ZSTD_getFrameParams(). It fills a ZSTD_frameParams structure which provide ++ * important information to correctly decode the frame, such as the minimum ++ * rolling buffer size to allocate to decompress data (`windowSize`), and the ++ * dictionary ID used. ++ * Note: content size is optional, it may not be present. 0 means unknown. ++ * Note that these values could be wrong, either because of data malformation, ++ * or because an attacker is spoofing deliberate false information. As a ++ * consequence, check that values remain within valid application range, ++ * especially `windowSize`, before allocation. Each application can set its own ++ * limit, depending on local restrictions. For extended interoperability, it is ++ * recommended to support at least 8 MB. ++ * Frame parameters are extracted from the beginning of the compressed frame. ++ * Data fragment must be large enough to ensure successful decoding, typically ++ * `ZSTD_frameHeaderSize_max` bytes. ++ * Result: 0: successful decoding, the `ZSTD_frameParams` structure is filled. ++ * >0: `srcSize` is too small, provide at least this many bytes. ++ * errorCode, which can be tested using ZSTD_isError(). ++ * ++ * Start decompression, with ZSTD_decompressBegin() or ++ * ZSTD_decompressBegin_usingDict(). Alternatively, you can copy a prepared ++ * context, using ZSTD_copyDCtx(). ++ * ++ * Then use ZSTD_nextSrcSizeToDecompress() and ZSTD_decompressContinue() ++ * alternatively. ++ * ZSTD_nextSrcSizeToDecompress() tells how many bytes to provide as 'srcSize' ++ * to ZSTD_decompressContinue(). ++ * ZSTD_decompressContinue() requires this _exact_ amount of bytes, or it will ++ * fail. ++ * ++ * The result of ZSTD_decompressContinue() is the number of bytes regenerated ++ * within 'dst' (necessarily <= dstCapacity). It can be zero, which is not an ++ * error; it just means ZSTD_decompressContinue() has decoded some metadata ++ * item. It can also be an error code, which can be tested with ZSTD_isError(). ++ * ++ * ZSTD_decompressContinue() needs previous data blocks during decompression, up ++ * to `windowSize`. They should preferably be located contiguously, prior to ++ * current block. Alternatively, a round buffer of sufficient size is also ++ * possible. Sufficient size is determined by frame parameters. ++ * ZSTD_decompressContinue() is very sensitive to contiguity, if 2 blocks don't ++ * follow each other, make sure that either the compressor breaks contiguity at ++ * the same place, or that previous contiguous segment is large enough to ++ * properly handle maximum back-reference. ++ * ++ * A frame is fully decoded when ZSTD_nextSrcSizeToDecompress() returns zero. ++ * Context can then be reset to start a new decompression. ++ * ++ * Note: it's possible to know if next input to present is a header or a block, ++ * using ZSTD_nextInputType(). This information is not required to properly ++ * decode a frame. ++ * ++ * == Special case: skippable frames == ++ * ++ * Skippable frames allow integration of user-defined data into a flow of ++ * concatenated frames. Skippable frames will be ignored (skipped) by a ++ * decompressor. The format of skippable frames is as follows: ++ * a) Skippable frame ID - 4 Bytes, Little endian format, any value from ++ * 0x184D2A50 to 0x184D2A5F ++ * b) Frame Size - 4 Bytes, Little endian format, unsigned 32-bits ++ * c) Frame Content - any content (User Data) of length equal to Frame Size ++ * For skippable frames ZSTD_decompressContinue() always returns 0. ++ * For skippable frames ZSTD_getFrameParams() returns fparamsPtr->windowLog==0 ++ * what means that a frame is skippable. ++ * Note: If fparamsPtr->frameContentSize==0, it is ambiguous: the frame might ++ * actually be a zstd encoded frame with no content. For purposes of ++ * decompression, it is valid in both cases to skip the frame using ++ * ZSTD_findFrameCompressedSize() to find its size in bytes. ++ * It also returns frame size as fparamsPtr->frameContentSize. ++ ******************************************************************************/ ++ ++/*===== Buffer-less streaming decompression functions =====*/ ++size_t ZSTD_decompressBegin(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressBegin_usingDict(ZSTD_DCtx *dctx, const void *dict, ++ size_t dictSize); ++void ZSTD_copyDCtx(ZSTD_DCtx *dctx, const ZSTD_DCtx *preparedDCtx); ++size_t ZSTD_nextSrcSizeToDecompress(ZSTD_DCtx *dctx); ++size_t ZSTD_decompressContinue(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++typedef enum { ++ ZSTDnit_frameHeader, ++ ZSTDnit_blockHeader, ++ ZSTDnit_block, ++ ZSTDnit_lastBlock, ++ ZSTDnit_checksum, ++ ZSTDnit_skippableFrame ++} ZSTD_nextInputType_e; ++ZSTD_nextInputType_e ZSTD_nextInputType(ZSTD_DCtx *dctx); ++ ++/*-***************************************************************************** ++ * Block functions ++ * ++ * Block functions produce and decode raw zstd blocks, without frame metadata. ++ * Frame metadata cost is typically ~18 bytes, which can be non-negligible for ++ * very small blocks (< 100 bytes). User will have to take in charge required ++ * information to regenerate data, such as compressed and content sizes. ++ * ++ * A few rules to respect: ++ * - Compressing and decompressing require a context structure ++ * + Use ZSTD_initCCtx() and ZSTD_initDCtx() ++ * - It is necessary to init context before starting ++ * + compression : ZSTD_compressBegin() ++ * + decompression : ZSTD_decompressBegin() ++ * + variants _usingDict() are also allowed ++ * + copyCCtx() and copyDCtx() work too ++ * - Block size is limited, it must be <= ZSTD_getBlockSizeMax() ++ * + If you need to compress more, cut data into multiple blocks ++ * + Consider using the regular ZSTD_compress() instead, as frame metadata ++ * costs become negligible when source size is large. ++ * - When a block is considered not compressible enough, ZSTD_compressBlock() ++ * result will be zero. In which case, nothing is produced into `dst`. ++ * + User must test for such outcome and deal directly with uncompressed data ++ * + ZSTD_decompressBlock() doesn't accept uncompressed data as input!!! ++ * + In case of multiple successive blocks, decoder must be informed of ++ * uncompressed block existence to follow proper history. Use ++ * ZSTD_insertBlock() in such a case. ++ ******************************************************************************/ ++ ++/* Define for static allocation */ ++#define ZSTD_BLOCKSIZE_ABSOLUTEMAX (128 * 1024) ++/*===== Raw zstd block functions =====*/ ++size_t ZSTD_getBlockSizeMax(ZSTD_CCtx *cctx); ++size_t ZSTD_compressBlock(ZSTD_CCtx *cctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_decompressBlock(ZSTD_DCtx *dctx, void *dst, size_t dstCapacity, ++ const void *src, size_t srcSize); ++size_t ZSTD_insertBlock(ZSTD_DCtx *dctx, const void *blockStart, ++ size_t blockSize); ++ ++#endif /* ZSTD_H */ From 86dbccba26bb4d9add18591b7a04a1c57007d9ce Mon Sep 17 00:00:00 2001 From: Michael Young Date: Tue, 24 Nov 2020 20:10:47 +0000 Subject: [PATCH 10/15] stack corruption from XSA-346 change [XSA-355] --- xen.spec | 7 ++++++- xsa355.patch | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 xsa355.patch diff --git a/xen.spec b/xen.spec index 214d461..c7c15c4 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 3%{?dist} +Release: 4%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -120,6 +120,7 @@ Patch62: xsa351-arm.patch Patch63: xsa351-x86-4.13-1.patch Patch64: xsa351-x86-4.13-2.patch Patch65: zstd-dom0.patch +Patch66: xsa355.patch %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -330,6 +331,7 @@ manage Xen virtual machines. %patch63 -p1 %patch64 -p1 %patch65 -p1 +%patch66 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -923,6 +925,9 @@ fi %endif %changelog +* Tue Nov 24 2020 Michael Young - 4.13.2-4 +- stack corruption from XSA-346 change [XSA-355] + * Mon Nov 23 2020 Michael Young - 4.13.2-3 - support zstd compressed kernels (dom0 only) based on linux kernel code diff --git a/xsa355.patch b/xsa355.patch new file mode 100644 index 0000000..491dd05 --- /dev/null +++ b/xsa355.patch @@ -0,0 +1,23 @@ +From: Jan Beulich +Subject: memory: fix off-by-one in XSA-346 change + +The comparison against ARRAY_SIZE() needs to be >= in order to avoid +overrunning the pages[] array. + +This is XSA-355. + +Fixes: 5777a3742d88 ("IOMMU: hold page ref until after deferred TLB flush") +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/memory.c ++++ b/xen/common/memory.c +@@ -854,7 +854,7 @@ int xenmem_add_to_physmap(struct domain + ++extra.ppage; + + /* Check for continuation if it's not the last iteration. */ +- if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || ++ if ( (++done >= ARRAY_SIZE(pages) && extra.ppage) || + (xatp->size > done && hypercall_preempt_check()) ) + { + rc = start + done; From 24e155b60df23a2251cd39f1ede3b142d51822c8 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 16 Dec 2020 21:12:53 +0000 Subject: [PATCH 11/15] multiple security updates xenstore watch notifications lacking permission checks [XSA-115, CVE-2020-29480] (#1908091) Xenstore: new domains inheriting existing node permissions [XSA-322, CVE-2020-29481] (#1908095) Xenstore: wrong path length check [XSA-323, CVE-2020-29482] (#1908096) Xenstore: guests can crash xenstored via watchs [XSA-324, CVE-2020-29484] (#1908088) Xenstore: guests can disturb domain cleanup [XSA-325, CVE-2020-29483] (#1908087) oxenstored memory leak in reset_watches [XSA-330, CVE-2020-29485] (#1908000) undue recursion in x86 HVM context switch code [XSA-348, CVE-2020-29566] (#1908085) oxenstored: node ownership can be changed by unprivileged clients [XSA-352, CVE-2020-29486] (#1908003) oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] (#1908002) FIFO event channels control block related ordering [XSA-358, CVE-2020-29570] (#1907931) FIFO event channels control structure ordering [XSA-359, CVE-2020-29571] (#1908089) --- ...729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch | 240 +++++++ ...52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch | 592 ++++++++++++++++++ ...b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch | 97 +++ xen.spec | 90 ++- ...llow-removing-child-of-a-node-exceed.patch | 157 +++++ ...e-ignore-transaction-id-for-un-watch.patch | 86 +++ ...ix-node-accounting-after-failed-node.patch | 104 +++ ...simplify-and-rename-check_event_node.patch | 55 ++ ...heck-privilege-for-XS_IS_DOMAIN_INTR.patch | 115 ++++ ...6-tools-xenstore-rework-node-removal.patch | 217 +++++++ ...ire-watches-only-when-removing-a-spe.patch | 118 ++++ ...store-introduce-node_perms-structure.patch | 289 +++++++++ ...llow-special-watches-for-privileged-.patch | 237 +++++++ ...void-watch-events-for-nodes-without-.patch | 375 +++++++++++ ...tored-ignore-transaction-id-for-un-w.patch | 43 ++ ...tored-check-privilege-for-XS_IS_DOMA.patch | 30 + ...s-ocaml-xenstored-unify-watch-firing.patch | 29 + ...tored-introduce-permissions-for-spec.patch | 117 ++++ ...tored-avoid-watch-events-for-nodes-w.patch | 406 ++++++++++++ ...tored-add-xenstored.conf-flag-to-tur.patch | 84 +++ xsa322-4.14-c.patch | 532 ++++++++++++++++ xsa322-o.patch | 110 ++++ xsa323.patch | 140 +++++ xsa324.patch | 48 ++ xsa325-4.14.patch | 192 ++++++ xsa330.patch | 66 ++ xsa348-4.13-1.patch | 106 ++++ xsa348-4.13-2.patch | 85 +++ xsa348-4.13-3.patch | 163 +++++ xsa352.patch | 42 ++ xsa353.patch | 89 +++ xsa358-4.14.patch | 54 ++ xsa359.patch | 40 ++ 33 files changed, 5147 insertions(+), 1 deletion(-) create mode 100644 xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch create mode 100644 xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch create mode 100644 xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch create mode 100644 xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch create mode 100644 xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch create mode 100644 xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch create mode 100644 xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch create mode 100644 xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch create mode 100644 xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch create mode 100644 xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch create mode 100644 xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch create mode 100644 xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch create mode 100644 xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch create mode 100644 xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch create mode 100644 xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch create mode 100644 xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch create mode 100644 xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch create mode 100644 xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch create mode 100644 xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch create mode 100644 xsa322-4.14-c.patch create mode 100644 xsa322-o.patch create mode 100644 xsa323.patch create mode 100644 xsa324.patch create mode 100644 xsa325-4.14.patch create mode 100644 xsa330.patch create mode 100644 xsa348-4.13-1.patch create mode 100644 xsa348-4.13-2.patch create mode 100644 xsa348-4.13-3.patch create mode 100644 xsa352.patch create mode 100644 xsa353.patch create mode 100644 xsa358-4.14.patch create mode 100644 xsa359.patch diff --git a/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch b/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch new file mode 100644 index 0000000..b3a5ad4 --- /dev/null +++ b/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch @@ -0,0 +1,240 @@ +From 74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:40:24 +0100 +Subject: [PATCH] xen/events: rework fifo queue locking + +Two cpus entering evtchn_fifo_set_pending() for the same event channel +can race in case the first one gets interrupted after setting +EVTCHN_FIFO_PENDING and when the other one manages to set +EVTCHN_FIFO_LINKED before the first one is testing that bit. This can +lead to evtchn_check_pollers() being called before the event is put +properly into the queue, resulting eventually in the guest not seeing +the event pending and thus blocking forever afterwards. + +Note that commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel +lock") made the race just more obvious, while the fifo event channel +implementation had this race forever since the introduction and use of +per-channel locks, when an unmask operation was running in parallel with +an event channel send operation. + +Using a spinlock for the per event channel lock had turned out +problematic due to some paths needing to take the lock are called with +interrupts off, so the lock would need to disable interrupts, which in +turn broke some use cases related to vm events. + +For avoiding this race the queue locking in evtchn_fifo_set_pending() +needs to be reworked to cover the test of EVTCHN_FIFO_PENDING, +EVTCHN_FIFO_MASKED and EVTCHN_FIFO_LINKED, too. Additionally when an +event channel needs to change queues both queues need to be locked +initially, in order to avoid having a window with no lock held at all. + +Reported-by: Jan Beulich +Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +Fixes: de6acb78bf0e137c ("evtchn: use a per-event channel lock for sending events") +Signed-off-by: Juergen Gross +Reviewed-by: Jan Beulich +master commit: 71ac522909e9302350a88bc378be99affa87067c +master date: 2020-11-30 14:05:39 +0100 +--- + xen/common/event_fifo.c | 128 ++++++++++++++++++++++------------------ + 1 file changed, 70 insertions(+), 58 deletions(-) + +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 2037b24196..2f5e868b7a 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -66,38 +66,6 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) + d->domain_id, evtchn->port); + } + +-static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, +- struct evtchn *evtchn, +- unsigned long *flags) +-{ +- struct vcpu *v; +- struct evtchn_fifo_queue *q, *old_q; +- unsigned int try; +- union evtchn_fifo_lastq lastq; +- +- for ( try = 0; try < 3; try++ ) +- { +- lastq.raw = read_atomic(&evtchn->fifo_lastq); +- v = d->vcpu[lastq.last_vcpu_id]; +- old_q = &v->evtchn_fifo->queue[lastq.last_priority]; +- +- spin_lock_irqsave(&old_q->lock, *flags); +- +- v = d->vcpu[lastq.last_vcpu_id]; +- q = &v->evtchn_fifo->queue[lastq.last_priority]; +- +- if ( old_q == q ) +- return old_q; +- +- spin_unlock_irqrestore(&old_q->lock, *flags); +- } +- +- gprintk(XENLOG_WARNING, +- "dom%d port %d lost event (too many queue changes)\n", +- d->domain_id, evtchn->port); +- return NULL; +-} +- + static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) + { + event_word_t new, old; +@@ -169,6 +137,9 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + event_word_t *word; + unsigned long flags; + bool_t was_pending; ++ struct evtchn_fifo_queue *q, *old_q; ++ unsigned int try; ++ bool linked = true; + + port = evtchn->port; + word = evtchn_fifo_word_from_port(d, port); +@@ -183,17 +154,67 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + return; + } + ++ /* ++ * Lock all queues related to the event channel (in case of a queue change ++ * this might be two). ++ * It is mandatory to do that before setting and testing the PENDING bit ++ * and to hold the current queue lock until the event has been put into the ++ * list of pending events in order to avoid waking up a guest without the ++ * event being visibly pending in the guest. ++ */ ++ for ( try = 0; try < 3; try++ ) ++ { ++ union evtchn_fifo_lastq lastq; ++ const struct vcpu *old_v; ++ ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ old_v = d->vcpu[lastq.last_vcpu_id]; ++ ++ q = &v->evtchn_fifo->queue[evtchn->priority]; ++ old_q = &old_v->evtchn_fifo->queue[lastq.last_priority]; ++ ++ if ( q == old_q ) ++ spin_lock_irqsave(&q->lock, flags); ++ else if ( q < old_q ) ++ { ++ spin_lock_irqsave(&q->lock, flags); ++ spin_lock(&old_q->lock); ++ } ++ else ++ { ++ spin_lock_irqsave(&old_q->lock, flags); ++ spin_lock(&q->lock); ++ } ++ ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ old_v = d->vcpu[lastq.last_vcpu_id]; ++ if ( q == &v->evtchn_fifo->queue[evtchn->priority] && ++ old_q == &old_v->evtchn_fifo->queue[lastq.last_priority] ) ++ break; ++ ++ if ( q != old_q ) ++ spin_unlock(&old_q->lock); ++ spin_unlock_irqrestore(&q->lock, flags); ++ } ++ + was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); + ++ /* If we didn't get the lock bail out. */ ++ if ( try == 3 ) ++ { ++ gprintk(XENLOG_WARNING, ++ "%pd port %u lost event (too many queue changes)\n", ++ d, evtchn->port); ++ goto done; ++ } ++ + /* + * Link the event if it unmasked and not already linked. + */ + if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && + !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) + { +- struct evtchn_fifo_queue *q, *old_q; + event_word_t *tail_word; +- bool_t linked = 0; + + /* + * Control block not mapped. The guest must not unmask an +@@ -204,25 +225,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + { + printk(XENLOG_G_WARNING + "%pv has no FIFO event channel control block\n", v); +- goto done; ++ goto unlock; + } + +- /* +- * No locking around getting the queue. This may race with +- * changing the priority but we are allowed to signal the +- * event once on the old priority. +- */ +- q = &v->evtchn_fifo->queue[evtchn->priority]; +- +- old_q = lock_old_queue(d, evtchn, &flags); +- if ( !old_q ) +- goto done; +- + if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) +- { +- spin_unlock_irqrestore(&old_q->lock, flags); +- goto done; +- } ++ goto unlock; + + /* + * If this event was a tail, the old queue is now empty and +@@ -241,8 +248,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + lastq.last_priority = q->priority; + write_atomic(&evtchn->fifo_lastq, lastq.raw); + +- spin_unlock_irqrestore(&old_q->lock, flags); +- spin_lock_irqsave(&q->lock, flags); ++ spin_unlock(&old_q->lock); ++ old_q = q; + } + + /* +@@ -255,6 +262,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + * If the queue is empty (i.e., we haven't linked to the new + * event), head must be updated. + */ ++ linked = false; + if ( q->tail ) + { + tail_word = evtchn_fifo_word_from_port(d, q->tail); +@@ -263,15 +271,19 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + if ( !linked ) + write_atomic(q->head, port); + q->tail = port; ++ } + +- spin_unlock_irqrestore(&q->lock, flags); ++ unlock: ++ if ( q != old_q ) ++ spin_unlock(&old_q->lock); ++ spin_unlock_irqrestore(&q->lock, flags); + +- if ( !linked +- && !guest_test_and_set_bit(d, q->priority, +- &v->evtchn_fifo->control_block->ready) ) +- vcpu_mark_events_pending(v); +- } + done: ++ if ( !linked && ++ !guest_test_and_set_bit(d, q->priority, ++ &v->evtchn_fifo->control_block->ready) ) ++ vcpu_mark_events_pending(v); ++ + if ( !was_pending ) + evtchn_check_pollers(d, port); + } +-- +2.20.1 + diff --git a/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch b/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch new file mode 100644 index 0000000..86bcfbd --- /dev/null +++ b/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch @@ -0,0 +1,592 @@ +From 7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:36:36 +0100 +Subject: [PATCH] xen/evtchn: rework per event channel lock + +Currently the lock for a single event channel needs to be taken with +interrupts off, which causes deadlocks in some cases. + +Rework the per event channel lock to be non-blocking for the case of +sending an event and removing the need for disabling interrupts for +taking the lock. + +The lock is needed for avoiding races between event channel state +changes (creation, closing, binding) against normal operations (set +pending, [un]masking, priority changes). + +Use a rwlock, but with some restrictions: + +- Changing the state of an event channel (creation, closing, binding) + needs to use write_lock(), with ASSERT()ing that the lock is taken as + writer only when the state of the event channel is either before or + after the locked region appropriate (either free or unbound). + +- Sending an event needs to use read_trylock() mostly, in case of not + obtaining the lock the operation is omitted. This is needed as + sending an event can happen with interrupts off (at least in some + cases). + +- Dumping the event channel state for debug purposes is using + read_trylock(), too, in order to avoid blocking in case the lock is + taken as writer for a long time. + +- All other cases can use read_lock(). + +Fixes: e045199c7c9c54 ("evtchn: address races with evtchn_reset()") +Signed-off-by: Juergen Gross +Reviewed-by: Jan Beulich +Acked-by: Julien Grall + +xen/events: fix build + +Commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +introduced a build failure for NDEBUG builds. + +Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") +Signed-off-by: Juergen Gross +Signed-off-by: Jan Beulich +master commit: 5f2df45ead7c1195142f68b7923047a1e9479d54 +master date: 2020-11-10 14:36:15 +0100 +master commit: 53bacb86f496fdb11560d9e3b361bca7de60d268 +master date: 2020-11-11 08:56:21 +0100 +--- + xen/arch/x86/irq.c | 6 +- + xen/arch/x86/pv/shim.c | 9 +-- + xen/common/event_channel.c | 141 ++++++++++++++++++++++--------------- + xen/include/xen/event.h | 27 +++++-- + xen/include/xen/sched.h | 5 +- + 5 files changed, 116 insertions(+), 72 deletions(-) + +diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c +index bb95583742..f05defbd7d 100644 +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -2481,14 +2481,12 @@ static void dump_irqs(unsigned char key) + pirq = domain_irq_to_pirq(d, irq); + info = pirq_info(d, pirq); + evtchn = evtchn_from_port(d, info->evtchn); +- local_irq_disable(); +- if ( spin_trylock(&evtchn->lock) ) ++ if ( evtchn_read_trylock(evtchn) ) + { + pending = evtchn_is_pending(d, evtchn); + masked = evtchn_is_masked(d, evtchn); +- spin_unlock(&evtchn->lock); ++ evtchn_read_unlock(evtchn); + } +- local_irq_enable(); + printk("d%d:%3d(%c%c%c)%c", + d->domain_id, pirq, "-P?"[pending], + "-M?"[masked], info->masked ? 'M' : '-', +diff --git a/xen/arch/x86/pv/shim.c b/xen/arch/x86/pv/shim.c +index bbecbfa16f..36a7e30605 100644 +--- a/xen/arch/x86/pv/shim.c ++++ b/xen/arch/x86/pv/shim.c +@@ -660,11 +660,12 @@ void pv_shim_inject_evtchn(unsigned int port) + if ( port_is_valid(guest, port) ) + { + struct evtchn *chn = evtchn_from_port(guest, port); +- unsigned long flags; + +- spin_lock_irqsave(&chn->lock, flags); +- evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); +- spin_unlock_irqrestore(&chn->lock, flags); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + } + } + +diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c +index 12f666cb79..181e5abaa6 100644 +--- a/xen/common/event_channel.c ++++ b/xen/common/event_channel.c +@@ -50,6 +50,40 @@ + + #define consumer_is_xen(e) (!!(e)->xen_consumer) + ++/* ++ * Lock an event channel exclusively. This is allowed only when the channel is ++ * free or unbound either when taking or when releasing the lock, as any ++ * concurrent operation on the event channel using evtchn_read_trylock() will ++ * just assume the event channel is free or unbound at the moment when the ++ * evtchn_read_trylock() returns false. ++ */ ++static inline void evtchn_write_lock(struct evtchn *evtchn) ++{ ++ write_lock(&evtchn->lock); ++ ++#ifndef NDEBUG ++ evtchn->old_state = evtchn->state; ++#endif ++} ++ ++static inline unsigned int old_state(const struct evtchn *evtchn) ++{ ++#ifndef NDEBUG ++ return evtchn->old_state; ++#else ++ return ECS_RESERVED; /* Just to allow things to build. */ ++#endif ++} ++ ++static inline void evtchn_write_unlock(struct evtchn *evtchn) ++{ ++ /* Enforce lock discipline. */ ++ ASSERT(old_state(evtchn) == ECS_FREE || old_state(evtchn) == ECS_UNBOUND || ++ evtchn->state == ECS_FREE || evtchn->state == ECS_UNBOUND); ++ ++ write_unlock(&evtchn->lock); ++} ++ + /* + * The function alloc_unbound_xen_event_channel() allows an arbitrary + * notifier function to be specified. However, very few unique functions +@@ -131,7 +165,7 @@ static struct evtchn *alloc_evtchn_bucket(struct domain *d, unsigned int port) + return NULL; + } + chn[i].port = port + i; +- spin_lock_init(&chn[i].lock); ++ rwlock_init(&chn[i].lock); + } + return chn; + } +@@ -249,7 +283,6 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + int port; + domid_t dom = alloc->dom; + long rc; +- unsigned long flags; + + d = rcu_lock_domain_by_any_id(dom); + if ( d == NULL ) +@@ -265,14 +298,14 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + if ( rc ) + goto out; + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_UNBOUND; + if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) + chn->u.unbound.remote_domid = current->domain->domain_id; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + alloc->port = port; + +@@ -285,32 +318,26 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) + } + + +-static unsigned long double_evtchn_lock(struct evtchn *lchn, +- struct evtchn *rchn) ++static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) + { +- unsigned long flags; +- + if ( lchn <= rchn ) + { +- spin_lock_irqsave(&lchn->lock, flags); ++ evtchn_write_lock(lchn); + if ( lchn != rchn ) +- spin_lock(&rchn->lock); ++ evtchn_write_lock(rchn); + } + else + { +- spin_lock_irqsave(&rchn->lock, flags); +- spin_lock(&lchn->lock); ++ evtchn_write_lock(rchn); ++ evtchn_write_lock(lchn); + } +- +- return flags; + } + +-static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, +- unsigned long flags) ++static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) + { + if ( lchn != rchn ) +- spin_unlock(&lchn->lock); +- spin_unlock_irqrestore(&rchn->lock, flags); ++ evtchn_write_unlock(lchn); ++ evtchn_write_unlock(rchn); + } + + static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) +@@ -320,7 +347,6 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + int lport, rport = bind->remote_port; + domid_t rdom = bind->remote_dom; + long rc; +- unsigned long flags; + + if ( rdom == DOMID_SELF ) + rdom = current->domain->domain_id; +@@ -356,7 +382,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + if ( rc ) + goto out; + +- flags = double_evtchn_lock(lchn, rchn); ++ double_evtchn_lock(lchn, rchn); + + lchn->u.interdomain.remote_dom = rd; + lchn->u.interdomain.remote_port = rport; +@@ -373,7 +399,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) + */ + evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); + +- double_evtchn_unlock(lchn, rchn, flags); ++ double_evtchn_unlock(lchn, rchn); + + bind->local_port = lport; + +@@ -396,7 +422,6 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) + struct domain *d = current->domain; + int virq = bind->virq, vcpu = bind->vcpu; + int rc = 0; +- unsigned long flags; + + if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) + return -EINVAL; +@@ -434,14 +459,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) + + chn = evtchn_from_port(d, port); + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_VIRQ; + chn->notify_vcpu_id = vcpu; + chn->u.virq = virq; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + v->virq_to_evtchn[virq] = bind->port = port; + +@@ -458,7 +483,6 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) + struct domain *d = current->domain; + int port, vcpu = bind->vcpu; + long rc = 0; +- unsigned long flags; + + if ( domain_vcpu(d, vcpu) == NULL ) + return -ENOENT; +@@ -470,13 +494,13 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) + + chn = evtchn_from_port(d, port); + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_IPI; + chn->notify_vcpu_id = vcpu; + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + bind->port = port; + +@@ -520,7 +544,6 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) + struct pirq *info; + int port = 0, pirq = bind->pirq; + long rc; +- unsigned long flags; + + if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) + return -EINVAL; +@@ -553,14 +576,14 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) + goto out; + } + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_PIRQ; + chn->u.pirq.irq = pirq; + link_pirq_port(port, chn, v); + evtchn_port_init(d, chn); + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + bind->port = port; + +@@ -581,7 +604,6 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + struct evtchn *chn1, *chn2; + int port2; + long rc = 0; +- unsigned long flags; + + again: + spin_lock(&d1->event_lock); +@@ -681,14 +703,14 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + BUG_ON(chn2->state != ECS_INTERDOMAIN); + BUG_ON(chn2->u.interdomain.remote_dom != d1); + +- flags = double_evtchn_lock(chn1, chn2); ++ double_evtchn_lock(chn1, chn2); + + evtchn_free(d1, chn1); + + chn2->state = ECS_UNBOUND; + chn2->u.unbound.remote_domid = d1->domain_id; + +- double_evtchn_unlock(chn1, chn2, flags); ++ double_evtchn_unlock(chn1, chn2); + + goto out; + +@@ -696,9 +718,9 @@ int evtchn_close(struct domain *d1, int port1, bool guest) + BUG(); + } + +- spin_lock_irqsave(&chn1->lock, flags); ++ evtchn_write_lock(chn1); + evtchn_free(d1, chn1); +- spin_unlock_irqrestore(&chn1->lock, flags); ++ evtchn_write_unlock(chn1); + + out: + if ( d2 != NULL ) +@@ -718,7 +740,6 @@ int evtchn_send(struct domain *ld, unsigned int lport) + struct evtchn *lchn, *rchn; + struct domain *rd; + int rport, ret = 0; +- unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + return -EINVAL; +@@ -731,7 +752,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) + + lchn = evtchn_from_port(ld, lport); + +- spin_lock_irqsave(&lchn->lock, flags); ++ evtchn_read_lock(lchn); + + /* Guest cannot send via a Xen-attached event channel. */ + if ( unlikely(consumer_is_xen(lchn)) ) +@@ -766,7 +787,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) + } + + out: +- spin_unlock_irqrestore(&lchn->lock, flags); ++ evtchn_read_unlock(lchn); + + return ret; + } +@@ -793,9 +814,11 @@ void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) + + d = v->domain; + chn = evtchn_from_port(d, port); +- spin_lock(&chn->lock); +- evtchn_port_set_pending(d, v->vcpu_id, chn); +- spin_unlock(&chn->lock); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, v->vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -824,9 +847,11 @@ void send_guest_global_virq(struct domain *d, uint32_t virq) + goto out; + + chn = evtchn_from_port(d, port); +- spin_lock(&chn->lock); +- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); +- spin_unlock(&chn->lock); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + + out: + spin_unlock_irqrestore(&v->virq_lock, flags); +@@ -836,7 +861,6 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) + { + int port; + struct evtchn *chn; +- unsigned long flags; + + /* + * PV guests: It should not be possible to race with __evtchn_close(). The +@@ -851,9 +875,11 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) + } + + chn = evtchn_from_port(d, port); +- spin_lock_irqsave(&chn->lock, flags); +- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); +- spin_unlock_irqrestore(&chn->lock, flags); ++ if ( evtchn_read_trylock(chn) ) ++ { ++ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); ++ evtchn_read_unlock(chn); ++ } + } + + static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; +@@ -1050,15 +1076,17 @@ int evtchn_unmask(unsigned int port) + { + struct domain *d = current->domain; + struct evtchn *evtchn; +- unsigned long flags; + + if ( unlikely(!port_is_valid(d, port)) ) + return -EINVAL; + + evtchn = evtchn_from_port(d, port); +- spin_lock_irqsave(&evtchn->lock, flags); ++ ++ evtchn_read_lock(evtchn); ++ + evtchn_port_unmask(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ evtchn_read_unlock(evtchn); + + return 0; + } +@@ -1304,7 +1332,6 @@ int alloc_unbound_xen_event_channel( + { + struct evtchn *chn; + int port, rc; +- unsigned long flags; + + spin_lock(&ld->event_lock); + +@@ -1317,14 +1344,14 @@ int alloc_unbound_xen_event_channel( + if ( rc ) + goto out; + +- spin_lock_irqsave(&chn->lock, flags); ++ evtchn_write_lock(chn); + + chn->state = ECS_UNBOUND; + chn->xen_consumer = get_xen_consumer(notification_fn); + chn->notify_vcpu_id = lvcpu; + chn->u.unbound.remote_domid = remote_domid; + +- spin_unlock_irqrestore(&chn->lock, flags); ++ evtchn_write_unlock(chn); + + write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); + +@@ -1356,7 +1383,6 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + { + struct evtchn *lchn, *rchn; + struct domain *rd; +- unsigned long flags; + + if ( !port_is_valid(ld, lport) ) + { +@@ -1371,7 +1397,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + + lchn = evtchn_from_port(ld, lport); + +- spin_lock_irqsave(&lchn->lock, flags); ++ if ( !evtchn_read_trylock(lchn) ) ++ return; + + if ( likely(lchn->state == ECS_INTERDOMAIN) ) + { +@@ -1381,7 +1408,7 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) + evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); + } + +- spin_unlock_irqrestore(&lchn->lock, flags); ++ evtchn_read_unlock(lchn); + } + + void evtchn_check_pollers(struct domain *d, unsigned int port) +diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h +index fa93a3684a..6588333f42 100644 +--- a/xen/include/xen/event.h ++++ b/xen/include/xen/event.h +@@ -111,6 +111,21 @@ static inline unsigned int max_evtchns(const struct domain *d) + : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); + } + ++static inline void evtchn_read_lock(struct evtchn *evtchn) ++{ ++ read_lock(&evtchn->lock); ++} ++ ++static inline bool evtchn_read_trylock(struct evtchn *evtchn) ++{ ++ return read_trylock(&evtchn->lock); ++} ++ ++static inline void evtchn_read_unlock(struct evtchn *evtchn) ++{ ++ read_unlock(&evtchn->lock); ++} ++ + static inline bool_t port_is_valid(struct domain *d, unsigned int p) + { + if ( p >= read_atomic(&d->valid_evtchns) ) +@@ -244,11 +259,10 @@ static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) + { + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; +- unsigned long flags; + +- spin_lock_irqsave(&evtchn->lock, flags); ++ evtchn_read_lock(evtchn); + rc = evtchn_is_pending(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ evtchn_read_unlock(evtchn); + + return rc; + } +@@ -263,11 +277,12 @@ static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) + { + struct evtchn *evtchn = evtchn_from_port(d, port); + bool rc; +- unsigned long flags; + +- spin_lock_irqsave(&evtchn->lock, flags); ++ evtchn_read_lock(evtchn); ++ + rc = evtchn_is_masked(d, evtchn); +- spin_unlock_irqrestore(&evtchn->lock, flags); ++ ++ evtchn_read_unlock(evtchn); + + return rc; + } +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index 8bb5bd7b38..89a9df5a63 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -83,7 +83,7 @@ extern domid_t hardware_domid; + + struct evtchn + { +- spinlock_t lock; ++ rwlock_t lock; + #define ECS_FREE 0 /* Channel is available for use. */ + #define ECS_RESERVED 1 /* Channel is reserved. */ + #define ECS_UNBOUND 2 /* Channel is waiting to bind to a remote domain. */ +@@ -112,6 +112,9 @@ struct evtchn + u16 virq; /* state == ECS_VIRQ */ + } u; + u8 priority; ++#ifndef NDEBUG ++ u8 old_state; /* State when taking lock in write mode. */ ++#endif + u8 last_priority; + u16 last_vcpu_id; + #ifdef CONFIG_XSM +-- +2.20.1 + diff --git a/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch b/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch new file mode 100644 index 0000000..0d84566 --- /dev/null +++ b/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch @@ -0,0 +1,97 @@ +From d064b6581bbddf9ef4a8817a2f077dd2a0afa1da Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Tue, 1 Dec 2020 15:39:02 +0100 +Subject: [PATCH] xen/events: access last_priority and last_vcpu_id together + +The queue for a fifo event is depending on the vcpu_id and the +priority of the event. When sending an event it might happen the +event needs to change queues and the old queue needs to be kept for +keeping the links between queue elements intact. For this purpose +the event channel contains last_priority and last_vcpu_id values +elements for being able to identify the old queue. + +In order to avoid races always access last_priority and last_vcpu_id +with a single atomic operation avoiding any inconsistencies. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +master commit: 1277cb9dc5e966f1faf665bcded02b7533e38078 +master date: 2020-11-24 11:23:42 +0100 +--- + xen/common/event_fifo.c | 25 +++++++++++++++++++------ + xen/include/xen/sched.h | 3 +-- + 2 files changed, 20 insertions(+), 8 deletions(-) + +diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c +index 27ab3a1c3f..2037b24196 100644 +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -21,6 +21,14 @@ + + #include + ++union evtchn_fifo_lastq { ++ uint32_t raw; ++ struct { ++ uint8_t last_priority; ++ uint16_t last_vcpu_id; ++ }; ++}; ++ + static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, + unsigned int port) + { +@@ -65,16 +73,18 @@ static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, + struct vcpu *v; + struct evtchn_fifo_queue *q, *old_q; + unsigned int try; ++ union evtchn_fifo_lastq lastq; + + for ( try = 0; try < 3; try++ ) + { +- v = d->vcpu[evtchn->last_vcpu_id]; +- old_q = &v->evtchn_fifo->queue[evtchn->last_priority]; ++ lastq.raw = read_atomic(&evtchn->fifo_lastq); ++ v = d->vcpu[lastq.last_vcpu_id]; ++ old_q = &v->evtchn_fifo->queue[lastq.last_priority]; + + spin_lock_irqsave(&old_q->lock, *flags); + +- v = d->vcpu[evtchn->last_vcpu_id]; +- q = &v->evtchn_fifo->queue[evtchn->last_priority]; ++ v = d->vcpu[lastq.last_vcpu_id]; ++ q = &v->evtchn_fifo->queue[lastq.last_priority]; + + if ( old_q == q ) + return old_q; +@@ -225,8 +235,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) + /* Moved to a different queue? */ + if ( old_q != q ) + { +- evtchn->last_vcpu_id = v->vcpu_id; +- evtchn->last_priority = q->priority; ++ union evtchn_fifo_lastq lastq = { }; ++ ++ lastq.last_vcpu_id = v->vcpu_id; ++ lastq.last_priority = q->priority; ++ write_atomic(&evtchn->fifo_lastq, lastq.raw); + + spin_unlock_irqrestore(&old_q->lock, flags); + spin_lock_irqsave(&q->lock, flags); +diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h +index 89a9df5a63..8bf1b90261 100644 +--- a/xen/include/xen/sched.h ++++ b/xen/include/xen/sched.h +@@ -115,8 +115,7 @@ struct evtchn + #ifndef NDEBUG + u8 old_state; /* State when taking lock in write mode. */ + #endif +- u8 last_priority; +- u16 last_vcpu_id; ++ u32 fifo_lastq; /* Data for fifo events identifying last queue. */ + #ifdef CONFIG_XSM + union { + #ifdef XSM_NEED_GENERIC_EVTCHN_SSID +-- +2.20.1 + diff --git a/xen.spec b/xen.spec index c7c15c4..b516974 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 4%{?dist} +Release: 5%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -121,6 +121,39 @@ Patch63: xsa351-x86-4.13-1.patch Patch64: xsa351-x86-4.13-2.patch Patch65: zstd-dom0.patch Patch66: xsa355.patch +Patch67: xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch +Patch68: xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch +Patch69: xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch +Patch70: xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch +Patch71: xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch +Patch72: xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch +Patch73: xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch +Patch74: xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch +Patch75: xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch +Patch76: xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch +Patch77: xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch +Patch78: xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch +Patch79: xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch +Patch80: xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch +Patch81: xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch +Patch82: xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch +Patch83: xsa322-4.14-c.patch +Patch84: xsa322-o.patch +Patch85: xsa323.patch +Patch86: xsa324.patch +Patch87: xsa325-4.14.patch +Patch88: xsa330.patch +Patch89: xsa348-4.13-1.patch +Patch90: xsa348-4.13-2.patch +Patch91: xsa348-4.13-3.patch +Patch92: xsa352.patch +Patch93: xsa353.patch +Patch94: xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch +Patch95: xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch +Patch96: xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch +Patch97: xsa358-4.14.patch +Patch98: xsa359.patch + %if %build_qemutrad BuildRequires: libidn-devel zlib-devel SDL-devel curl-devel @@ -332,6 +365,38 @@ manage Xen virtual machines. %patch64 -p1 %patch65 -p1 %patch66 -p1 +%patch67 -p1 +%patch68 -p1 +%patch69 -p1 +%patch70 -p1 +%patch71 -p1 +%patch72 -p1 +%patch73 -p1 +%patch74 -p1 +%patch75 -p1 +%patch76 -p1 +%patch77 -p1 +%patch78 -p1 +%patch79 -p1 +%patch80 -p1 +%patch81 -p1 +%patch82 -p1 +%patch83 -p1 +%patch84 -p1 +%patch85 -p1 +%patch86 -p1 +%patch87 -p1 +%patch88 -p1 +%patch89 -p1 +%patch90 -p1 +%patch91 -p1 +%patch92 -p1 +%patch93 -p1 +%patch94 -p1 +%patch95 -p1 +%patch96 -p1 +%patch97 -p1 +%patch98 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -925,6 +990,29 @@ fi %endif %changelog +* Wed Dec 16 2020 Michael Young - 4.13.2-5 +- xenstore watch notifications lacking permission checks [XSA-115, + CVE-2020-29480] (#1908091) +- Xenstore: new domains inheriting existing node permissions [XSA-322, + CVE-2020-29481] (#1908095) +- Xenstore: wrong path length check [XSA-323, CVE-2020-29482] (#1908096) +- Xenstore: guests can crash xenstored via watchs [XSA-324, CVE-2020-29484] + (#1908088) +- Xenstore: guests can disturb domain cleanup [XSA-325, CVE-2020-29483] + (#1908087) +- oxenstored memory leak in reset_watches [XSA-330, CVE-2020-29485] + (#1908000) +- undue recursion in x86 HVM context switch code [XSA-348, CVE-2020-29566] + (#1908085) +- oxenstored: node ownership can be changed by unprivileged clients + [XSA-352, CVE-2020-29486] (#1908003) +- oxenstored: permissions not checked on root node [XSA-353, CVE-2020-29479] + (#1908002) +- FIFO event channels control block related ordering [XSA-358, + CVE-2020-29570] (#1907931) +- FIFO event channels control structure ordering [XSA-359, CVE-2020-29571] + (#1908089) + * Tue Nov 24 2020 Michael Young - 4.13.2-4 - stack corruption from XSA-346 change [XSA-355] diff --git a/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch b/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch new file mode 100644 index 0000000..ee2c1a7 --- /dev/null +++ b/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch @@ -0,0 +1,157 @@ +From e92f3dfeaae21a335e666c9247954424e34e5c56 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:37 +0200 +Subject: [PATCH 01/10] tools/xenstore: allow removing child of a node + exceeding quota + +An unprivileged user of Xenstore is not allowed to write nodes with a +size exceeding a global quota, while privileged users like dom0 are +allowed to write such nodes. The size of a node is the needed space +to store all node specific data, this includes the names of all +children of the node. + +When deleting a node its parent has to be modified by removing the +name of the to be deleted child from it. + +This results in the strange situation that an unprivileged owner of a +node might not succeed in deleting that node in case its parent is +exceeding the quota of that unprivileged user (it might have been +written by dom0), as the user is not allowed to write the updated +parent node. + +Fix that by not checking the quota when writing a node for the +purpose of removing a child's name only. + +The same applies to transaction handling: a node being read during a +transaction is written to the transaction specific area and it should +not be tested for exceeding the quota, as it might not be owned by +the reader and presumably the original write would have failed if the +node is owned by the reader. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 20 +++++++++++--------- + tools/xenstore/xenstored_core.h | 3 ++- + tools/xenstore/xenstored_transaction.c | 2 +- + 3 files changed, 14 insertions(+), 11 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 97ceabf9642d..b43e1018babd 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -417,7 +417,8 @@ static struct node *read_node(struct connection *conn, const void *ctx, + return node; + } + +-int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) ++int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, ++ bool no_quota_check) + { + TDB_DATA data; + void *p; +@@ -427,7 +428,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) + + node->num_perms*sizeof(node->perms[0]) + + node->datalen + node->childlen; + +- if (domain_is_unprivileged(conn) && ++ if (!no_quota_check && domain_is_unprivileged(conn) && + data.dsize >= quota_max_entry_size) { + errno = ENOSPC; + return errno; +@@ -455,14 +456,15 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) + return 0; + } + +-static int write_node(struct connection *conn, struct node *node) ++static int write_node(struct connection *conn, struct node *node, ++ bool no_quota_check) + { + TDB_DATA key; + + if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) + return errno; + +- return write_node_raw(conn, &key, node); ++ return write_node_raw(conn, &key, node, no_quota_check); + } + + static enum xs_perm_type perm_for_conn(struct connection *conn, +@@ -999,7 +1001,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, + /* We write out the nodes down, setting destructor in case + * something goes wrong. */ + for (i = node; i; i = i->parent) { +- if (write_node(conn, i)) { ++ if (write_node(conn, i, false)) { + domain_entry_dec(conn, i); + return NULL; + } +@@ -1039,7 +1041,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) + } else { + node->data = in->buffer + offset; + node->datalen = datalen; +- if (write_node(conn, node)) ++ if (write_node(conn, node, false)) + return errno; + } + +@@ -1115,7 +1117,7 @@ static int remove_child_entry(struct connection *conn, struct node *node, + size_t childlen = strlen(node->children + offset); + memdel(node->children, offset, childlen + 1, node->childlen); + node->childlen -= childlen + 1; +- return write_node(conn, node); ++ return write_node(conn, node, true); + } + + +@@ -1254,7 +1256,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + node->num_perms = num; + domain_entry_inc(conn, node); + +- if (write_node(conn, node)) ++ if (write_node(conn, node, false)) + return errno; + + fire_watches(conn, in, name, false); +@@ -1514,7 +1516,7 @@ static void manual_node(const char *name, const char *child) + if (child) + node->childlen = strlen(child) + 1; + +- if (write_node(NULL, node)) ++ if (write_node(NULL, node, false)) + barf_perror("Could not create initial node %s", name); + talloc_free(node); + } +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 56a279cfbb47..3cb1c235a101 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -149,7 +149,8 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); + char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); + + /* Write a node to the tdb data base. */ +-int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); ++int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, ++ bool no_quota_check); + + /* Get this node, checking we have permissions. */ + struct node *get_node(struct connection *conn, +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index 2824f7b359b8..e87897573469 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -276,7 +276,7 @@ int access_node(struct connection *conn, struct node *node, + i->check_gen = true; + if (node->generation != NO_GENERATION) { + set_tdb_key(trans_name, &local_key); +- ret = write_node_raw(conn, &local_key, node); ++ ret = write_node_raw(conn, &local_key, node, true); + if (ret) + goto err; + i->ta_node = true; +-- +2.17.1 + diff --git a/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch b/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch new file mode 100644 index 0000000..d143818 --- /dev/null +++ b/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch @@ -0,0 +1,86 @@ +From e8076f73de65c4816f69d6ebf75839c706145fcd Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:38 +0200 +Subject: [PATCH 02/10] tools/xenstore: ignore transaction id for [un]watch + +Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH +commands as it is documented in docs/misc/xenstore.txt, it is tested +for validity today. + +Really ignore the transaction id for XS_WATCH and XS_UNWATCH. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 26 ++++++++++++++++---------- + 1 file changed, 16 insertions(+), 10 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index b43e1018babd..bb2f9fd4e76e 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1268,13 +1268,17 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + static struct { + const char *str; + int (*func)(struct connection *conn, struct buffered_data *in); ++ unsigned int flags; ++#define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ + } const wire_funcs[XS_TYPE_COUNT] = { + [XS_CONTROL] = { "CONTROL", do_control }, + [XS_DIRECTORY] = { "DIRECTORY", send_directory }, + [XS_READ] = { "READ", do_read }, + [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, +- [XS_WATCH] = { "WATCH", do_watch }, +- [XS_UNWATCH] = { "UNWATCH", do_unwatch }, ++ [XS_WATCH] = ++ { "WATCH", do_watch, XS_FLAG_NOTID }, ++ [XS_UNWATCH] = ++ { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, + [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, + [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, + [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, +@@ -1296,7 +1300,7 @@ static struct { + + static const char *sockmsg_string(enum xsd_sockmsg_type type) + { +- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].str) ++ if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) + return wire_funcs[type].str; + + return "**UNKNOWN**"; +@@ -1311,7 +1315,14 @@ static void process_message(struct connection *conn, struct buffered_data *in) + enum xsd_sockmsg_type type = in->hdr.msg.type; + int ret; + +- trans = transaction_lookup(conn, in->hdr.msg.tx_id); ++ if ((unsigned int)type >= XS_TYPE_COUNT || !wire_funcs[type].func) { ++ eprintf("Client unknown operation %i", type); ++ send_error(conn, ENOSYS); ++ return; ++ } ++ ++ trans = (wire_funcs[type].flags & XS_FLAG_NOTID) ++ ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); + if (IS_ERR(trans)) { + send_error(conn, -PTR_ERR(trans)); + return; +@@ -1320,12 +1331,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) + assert(conn->transaction == NULL); + conn->transaction = trans; + +- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].func) +- ret = wire_funcs[type].func(conn, in); +- else { +- eprintf("Client unknown operation %i", type); +- ret = ENOSYS; +- } ++ ret = wire_funcs[type].func(conn, in); + if (ret) + send_error(conn, ret); + +-- +2.17.1 + diff --git a/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch b/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch new file mode 100644 index 0000000..2c2304a --- /dev/null +++ b/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch @@ -0,0 +1,104 @@ +From b8c6dbb67ebb449126023446a7d209eedf966537 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:39 +0200 +Subject: [PATCH 03/10] tools/xenstore: fix node accounting after failed node + creation + +When a node creation fails the number of nodes of the domain should be +the same as before the failed node creation. In case of failure when +trying to create a node requiring to create one or more intermediate +nodes as well (e.g. when /a/b/c/d is to be created, but /a/b isn't +existing yet) it might happen that the number of nodes of the creating +domain is not reset to the value it had before. + +So move the quota accounting out of construct_node() and into the node +write loop in create_node() in order to be able to undo the accounting +in case of an error in the intermediate node destructor. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Paul Durrant +Acked-by: Julien Grall +--- + tools/xenstore/xenstored_core.c | 37 ++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 12 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index bb2f9fd4e76e..db9b9ca7957d 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -925,11 +925,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + if (!parent) + return NULL; + +- if (domain_entry(conn) >= quota_nb_entry_per_domain) { +- errno = ENOSPC; +- return NULL; +- } +- + /* Add child to parent. */ + base = basename(name); + baselen = strlen(base) + 1; +@@ -962,7 +957,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + node->children = node->data = NULL; + node->childlen = node->datalen = 0; + node->parent = parent; +- domain_entry_inc(conn, node); + return node; + + nomem: +@@ -982,6 +976,9 @@ static int destroy_node(void *_node) + key.dsize = strlen(node->name); + + tdb_delete(tdb_ctx, key); ++ ++ domain_entry_dec(talloc_parent(node), node); ++ + return 0; + } + +@@ -998,18 +995,34 @@ static struct node *create_node(struct connection *conn, const void *ctx, + node->data = data; + node->datalen = datalen; + +- /* We write out the nodes down, setting destructor in case +- * something goes wrong. */ ++ /* ++ * We write out the nodes bottom up. ++ * All new created nodes will have i->parent set, while the final ++ * node will be already existing and won't have i->parent set. ++ * New nodes are subject to quota handling. ++ * Initially set a destructor for all new nodes removing them from ++ * TDB again and undoing quota accounting for the case of an error ++ * during the write loop. ++ */ + for (i = node; i; i = i->parent) { +- if (write_node(conn, i, false)) { +- domain_entry_dec(conn, i); ++ /* i->parent is set for each new node, so check quota. */ ++ if (i->parent && ++ domain_entry(conn) >= quota_nb_entry_per_domain) { ++ errno = ENOSPC; + return NULL; + } +- talloc_set_destructor(i, destroy_node); ++ if (write_node(conn, i, false)) ++ return NULL; ++ ++ /* Account for new node, set destructor for error case. */ ++ if (i->parent) { ++ domain_entry_inc(conn, i); ++ talloc_set_destructor(i, destroy_node); ++ } + } + + /* OK, now remove destructors so they stay around */ +- for (i = node; i; i = i->parent) ++ for (i = node; i->parent; i = i->parent) + talloc_set_destructor(i, NULL); + return node; + } +-- +2.17.1 + diff --git a/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch b/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch new file mode 100644 index 0000000..2424384 --- /dev/null +++ b/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch @@ -0,0 +1,55 @@ +From 318aa75bd0c05423e717ad0b64adb204282025db Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:40 +0200 +Subject: [PATCH 04/10] tools/xenstore: simplify and rename check_event_node() + +There is no path which allows to call check_event_node() without a +event name. So don't let the result depend on the name being NULL and +add an assert() covering that case. + +Rename the function to check_special_event() to better match the +semantics. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_watch.c | 12 +++++------- + 1 file changed, 5 insertions(+), 7 deletions(-) + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 7dedca60dfd6..f2f1bed47cc6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -47,13 +47,11 @@ struct watch + char *node; + }; + +-static bool check_event_node(const char *node) ++static bool check_special_event(const char *name) + { +- if (!node || !strstarts(node, "@")) { +- errno = EINVAL; +- return false; +- } +- return true; ++ assert(name); ++ ++ return strstarts(name, "@"); + } + + /* Is child a subnode of parent, or equal? */ +@@ -87,7 +85,7 @@ static void add_event(struct connection *conn, + unsigned int len; + char *data; + +- if (!check_event_node(name)) { ++ if (!check_special_event(name)) { + /* Can this conn load node, or see that it doesn't exist? */ + struct node *node = get_node(conn, ctx, name, XS_PERM_READ); + /* +-- +2.17.1 + diff --git a/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch b/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch new file mode 100644 index 0000000..a7695e2 --- /dev/null +++ b/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch @@ -0,0 +1,115 @@ +From c625fae44aedc246776b52eb1173cf847a3d4d80 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:41 +0200 +Subject: [PATCH 05/10] tools/xenstore: check privilege for + XS_IS_DOMAIN_INTRODUCED + +The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for +privileged domains only (the only user in the tree is the xenpaging +daemon). + +Instead of having the privilege test for each command introduce a +per-command flag for that purpose. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 24 ++++++++++++++++++------ + tools/xenstore/xenstored_domain.c | 7 ++----- + 2 files changed, 20 insertions(+), 11 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index db9b9ca7957d..6afd58431111 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1283,8 +1283,10 @@ static struct { + int (*func)(struct connection *conn, struct buffered_data *in); + unsigned int flags; + #define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ ++#define XS_FLAG_PRIV (1U << 1) /* Privileged domain only. */ + } const wire_funcs[XS_TYPE_COUNT] = { +- [XS_CONTROL] = { "CONTROL", do_control }, ++ [XS_CONTROL] = ++ { "CONTROL", do_control, XS_FLAG_PRIV }, + [XS_DIRECTORY] = { "DIRECTORY", send_directory }, + [XS_READ] = { "READ", do_read }, + [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, +@@ -1294,8 +1296,10 @@ static struct { + { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, + [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, + [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, +- [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, +- [XS_RELEASE] = { "RELEASE", do_release }, ++ [XS_INTRODUCE] = ++ { "INTRODUCE", do_introduce, XS_FLAG_PRIV }, ++ [XS_RELEASE] = ++ { "RELEASE", do_release, XS_FLAG_PRIV }, + [XS_GET_DOMAIN_PATH] = { "GET_DOMAIN_PATH", do_get_domain_path }, + [XS_WRITE] = { "WRITE", do_write }, + [XS_MKDIR] = { "MKDIR", do_mkdir }, +@@ -1304,9 +1308,11 @@ static struct { + [XS_WATCH_EVENT] = { "WATCH_EVENT", NULL }, + [XS_ERROR] = { "ERROR", NULL }, + [XS_IS_DOMAIN_INTRODUCED] = +- { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced }, +- [XS_RESUME] = { "RESUME", do_resume }, +- [XS_SET_TARGET] = { "SET_TARGET", do_set_target }, ++ { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced, XS_FLAG_PRIV }, ++ [XS_RESUME] = ++ { "RESUME", do_resume, XS_FLAG_PRIV }, ++ [XS_SET_TARGET] = ++ { "SET_TARGET", do_set_target, XS_FLAG_PRIV }, + [XS_RESET_WATCHES] = { "RESET_WATCHES", do_reset_watches }, + [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, + }; +@@ -1334,6 +1340,12 @@ static void process_message(struct connection *conn, struct buffered_data *in) + return; + } + ++ if ((wire_funcs[type].flags & XS_FLAG_PRIV) && ++ domain_is_unprivileged(conn)) { ++ send_error(conn, EACCES); ++ return; ++ } ++ + trans = (wire_funcs[type].flags & XS_FLAG_NOTID) + ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); + if (IS_ERR(trans)) { +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 1eae703ef680..0e2926e2a3d0 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -377,7 +377,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) + return EINVAL; + +- if (domain_is_unprivileged(conn) || !conn->can_write) ++ if (!conn->can_write) + return EACCES; + + domid = atoi(vec[0]); +@@ -445,7 +445,7 @@ int do_set_target(struct connection *conn, struct buffered_data *in) + if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) + return EINVAL; + +- if (domain_is_unprivileged(conn) || !conn->can_write) ++ if (!conn->can_write) + return EACCES; + + domid = atoi(vec[0]); +@@ -480,9 +480,6 @@ static struct domain *onearg_domain(struct connection *conn, + if (!domid) + return ERR_PTR(-EINVAL); + +- if (domain_is_unprivileged(conn)) +- return ERR_PTR(-EACCES); +- + return find_connected_domain(domid); + } + +-- +2.17.1 + diff --git a/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch b/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch new file mode 100644 index 0000000..fc6c2b6 --- /dev/null +++ b/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch @@ -0,0 +1,217 @@ +From 461c880600175c06e23a63e62d9f1ccab755d708 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:42 +0200 +Subject: [PATCH 06/10] tools/xenstore: rework node removal + +Today a Xenstore node is being removed by deleting it from the parent +first and then deleting itself and all its children. This results in +stale entries remaining in the data base in case e.g. a memory +allocation is failing during processing. This would result in the +rather strange behavior to be able to read a node (as its still in the +data base) while not being visible in the tree view of Xenstore. + +Fix that by deleting the nodes from the leaf side instead of starting +at the root. + +As fire_watches() is now called from _rm() the ctx parameter needs a +const attribute. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 99 ++++++++++++++++---------------- + tools/xenstore/xenstored_watch.c | 4 +- + tools/xenstore/xenstored_watch.h | 2 +- + 3 files changed, 54 insertions(+), 51 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 6afd58431111..1cb729a2cd5f 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1087,74 +1087,76 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) + return 0; + } + +-static void delete_node(struct connection *conn, struct node *node) +-{ +- unsigned int i; +- char *name; +- +- /* Delete self, then delete children. If we crash, then the worst +- that can happen is the children will continue to take up space, but +- will otherwise be unreachable. */ +- delete_node_single(conn, node); +- +- /* Delete children, too. */ +- for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { +- struct node *child; +- +- name = talloc_asprintf(node, "%s/%s", node->name, +- node->children + i); +- child = name ? read_node(conn, node, name) : NULL; +- if (child) { +- delete_node(conn, child); +- } +- else { +- trace("delete_node: Error deleting child '%s/%s'!\n", +- node->name, node->children + i); +- /* Skip it, we've already deleted the parent. */ +- } +- talloc_free(name); +- } +-} +- +- + /* Delete memory using memmove. */ + static void memdel(void *mem, unsigned off, unsigned len, unsigned total) + { + memmove(mem + off, mem + off + len, total - off - len); + } + +- +-static int remove_child_entry(struct connection *conn, struct node *node, +- size_t offset) ++static void remove_child_entry(struct connection *conn, struct node *node, ++ size_t offset) + { + size_t childlen = strlen(node->children + offset); ++ + memdel(node->children, offset, childlen + 1, node->childlen); + node->childlen -= childlen + 1; +- return write_node(conn, node, true); ++ if (write_node(conn, node, true)) ++ corrupt(conn, "Can't update parent node '%s'", node->name); + } + +- +-static int delete_child(struct connection *conn, +- struct node *node, const char *childname) ++static void delete_child(struct connection *conn, ++ struct node *node, const char *childname) + { + unsigned int i; + + for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { + if (streq(node->children+i, childname)) { +- return remove_child_entry(conn, node, i); ++ remove_child_entry(conn, node, i); ++ return; + } + } + corrupt(conn, "Can't find child '%s' in %s", childname, node->name); +- return ENOENT; + } + ++static int delete_node(struct connection *conn, struct node *parent, ++ struct node *node) ++{ ++ char *name; ++ ++ /* Delete children. */ ++ while (node->childlen) { ++ struct node *child; ++ ++ name = talloc_asprintf(node, "%s/%s", node->name, ++ node->children); ++ child = name ? read_node(conn, node, name) : NULL; ++ if (child) { ++ if (delete_node(conn, node, child)) ++ return errno; ++ } else { ++ trace("delete_node: Error deleting child '%s/%s'!\n", ++ node->name, node->children); ++ /* Quit deleting. */ ++ errno = ENOMEM; ++ return errno; ++ } ++ talloc_free(name); ++ } ++ ++ delete_node_single(conn, node); ++ delete_child(conn, parent, basename(node->name)); ++ talloc_free(node); ++ ++ return 0; ++} + + static int _rm(struct connection *conn, const void *ctx, struct node *node, + const char *name) + { +- /* Delete from parent first, then if we crash, the worst that can +- happen is the child will continue to take up space, but will +- otherwise be unreachable. */ ++ /* ++ * Deleting node by node, so the result is always consistent even in ++ * case of a failure. ++ */ + struct node *parent; + char *parentname = get_parent(ctx, name); + +@@ -1165,11 +1167,13 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + if (!parent) + return (errno == ENOMEM) ? ENOMEM : EINVAL; + +- if (delete_child(conn, parent, basename(name))) +- return EINVAL; +- +- delete_node(conn, node); +- return 0; ++ /* ++ * Fire the watches now, when we can still see the node permissions. ++ * This fine as we are single threaded and the next possible read will ++ * be handled only after the node has been really removed. ++ */ ++ fire_watches(conn, ctx, name, true); ++ return delete_node(conn, parent, node); + } + + +@@ -1207,7 +1211,6 @@ static int do_rm(struct connection *conn, struct buffered_data *in) + if (ret) + return ret; + +- fire_watches(conn, in, name, true); + send_ack(conn, XS_RM); + + return 0; +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f2f1bed47cc6..f0bbfe7a6dc6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -77,7 +77,7 @@ static bool is_child(const char *child, const char *parent) + * Temporary memory allocations are done with ctx. + */ + static void add_event(struct connection *conn, +- void *ctx, ++ const void *ctx, + struct watch *watch, + const char *name) + { +@@ -121,7 +121,7 @@ static void add_event(struct connection *conn, + * Check whether any watch events are to be sent. + * Temporary memory allocations are done with ctx. + */ +-void fire_watches(struct connection *conn, void *ctx, const char *name, ++void fire_watches(struct connection *conn, const void *ctx, const char *name, + bool recurse) + { + struct connection *i; +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index c72ea6a68542..54d4ea7e0d41 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -25,7 +25,7 @@ int do_watch(struct connection *conn, struct buffered_data *in); + int do_unwatch(struct connection *conn, struct buffered_data *in); + + /* Fire all watches: recurse means all the children are affected (ie. rm). */ +-void fire_watches(struct connection *conn, void *tmp, const char *name, ++void fire_watches(struct connection *conn, const void *tmp, const char *name, + bool recurse); + + void conn_delete_all_watches(struct connection *conn); +-- +2.17.1 + diff --git a/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch b/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch new file mode 100644 index 0000000..739a02f --- /dev/null +++ b/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch @@ -0,0 +1,118 @@ +From 6ca2e14b43aecc79effc1a0cd528a4aceef44d42 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:43 +0200 +Subject: [PATCH 07/10] tools/xenstore: fire watches only when removing a + specific node + +Instead of firing all watches for removing a subtree in one go, do so +only when the related node is being removed. + +The watches for the top-most node being removed include all watches +including that node, while watches for nodes below that are only fired +if they are matching exactly. This avoids firing any watch more than +once when removing a subtree. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 11 ++++++----- + tools/xenstore/xenstored_watch.c | 13 ++++++++----- + tools/xenstore/xenstored_watch.h | 4 ++-- + 3 files changed, 16 insertions(+), 12 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 1cb729a2cd5f..d7c025616ead 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1118,8 +1118,8 @@ static void delete_child(struct connection *conn, + corrupt(conn, "Can't find child '%s' in %s", childname, node->name); + } + +-static int delete_node(struct connection *conn, struct node *parent, +- struct node *node) ++static int delete_node(struct connection *conn, const void *ctx, ++ struct node *parent, struct node *node) + { + char *name; + +@@ -1131,7 +1131,7 @@ static int delete_node(struct connection *conn, struct node *parent, + node->children); + child = name ? read_node(conn, node, name) : NULL; + if (child) { +- if (delete_node(conn, node, child)) ++ if (delete_node(conn, ctx, node, child)) + return errno; + } else { + trace("delete_node: Error deleting child '%s/%s'!\n", +@@ -1143,6 +1143,7 @@ static int delete_node(struct connection *conn, struct node *parent, + talloc_free(name); + } + ++ fire_watches(conn, ctx, node->name, true); + delete_node_single(conn, node); + delete_child(conn, parent, basename(node->name)); + talloc_free(node); +@@ -1172,8 +1173,8 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + * This fine as we are single threaded and the next possible read will + * be handled only after the node has been really removed. + */ +- fire_watches(conn, ctx, name, true); +- return delete_node(conn, parent, node); ++ fire_watches(conn, ctx, name, false); ++ return delete_node(conn, ctx, parent, node); + } + + +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f0bbfe7a6dc6..3836675459fa 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -122,7 +122,7 @@ static void add_event(struct connection *conn, + * Temporary memory allocations are done with ctx. + */ + void fire_watches(struct connection *conn, const void *ctx, const char *name, +- bool recurse) ++ bool exact) + { + struct connection *i; + struct watch *watch; +@@ -134,10 +134,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { + list_for_each_entry(watch, &i->watches, list) { +- if (is_child(name, watch->node)) +- add_event(i, ctx, watch, name); +- else if (recurse && is_child(watch->node, name)) +- add_event(i, ctx, watch, watch->node); ++ if (exact) { ++ if (streq(name, watch->node)) ++ add_event(i, ctx, watch, name); ++ } else { ++ if (is_child(name, watch->node)) ++ add_event(i, ctx, watch, name); ++ } + } + } + } +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index 54d4ea7e0d41..1b3c80d3dda1 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -24,9 +24,9 @@ + int do_watch(struct connection *conn, struct buffered_data *in); + int do_unwatch(struct connection *conn, struct buffered_data *in); + +-/* Fire all watches: recurse means all the children are affected (ie. rm). */ ++/* Fire all watches: !exact means all the children are affected (ie. rm). */ + void fire_watches(struct connection *conn, const void *tmp, const char *name, +- bool recurse); ++ bool exact); + + void conn_delete_all_watches(struct connection *conn); + +-- +2.17.1 + diff --git a/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch b/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch new file mode 100644 index 0000000..17ba0b3 --- /dev/null +++ b/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch @@ -0,0 +1,289 @@ +From 2d4f410899bf59e112c107f371c3d164f8a592f8 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:44 +0200 +Subject: [PATCH 08/10] tools/xenstore: introduce node_perms structure + +There are several places in xenstored using a permission array and the +size of that array. Introduce a new struct node_perms containing both. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Acked-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 79 +++++++++++++++---------------- + tools/xenstore/xenstored_core.h | 8 +++- + tools/xenstore/xenstored_domain.c | 12 ++--- + 3 files changed, 50 insertions(+), 49 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index d7c025616ead..fe9943113b9f 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -401,14 +401,14 @@ static struct node *read_node(struct connection *conn, const void *ctx, + /* Datalen, childlen, number of permissions */ + hdr = (void *)data.dptr; + node->generation = hdr->generation; +- node->num_perms = hdr->num_perms; ++ node->perms.num = hdr->num_perms; + node->datalen = hdr->datalen; + node->childlen = hdr->childlen; + + /* Permissions are struct xs_permissions. */ +- node->perms = hdr->perms; ++ node->perms.p = hdr->perms; + /* Data is binary blob (usually ascii, no nul). */ +- node->data = node->perms + node->num_perms; ++ node->data = node->perms.p + node->perms.num; + /* Children is strings, nul separated. */ + node->children = node->data + node->datalen; + +@@ -425,7 +425,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + struct xs_tdb_record_hdr *hdr; + + data.dsize = sizeof(*hdr) +- + node->num_perms*sizeof(node->perms[0]) ++ + node->perms.num * sizeof(node->perms.p[0]) + + node->datalen + node->childlen; + + if (!no_quota_check && domain_is_unprivileged(conn) && +@@ -437,12 +437,13 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + data.dptr = talloc_size(node, data.dsize); + hdr = (void *)data.dptr; + hdr->generation = node->generation; +- hdr->num_perms = node->num_perms; ++ hdr->num_perms = node->perms.num; + hdr->datalen = node->datalen; + hdr->childlen = node->childlen; + +- memcpy(hdr->perms, node->perms, node->num_perms*sizeof(node->perms[0])); +- p = hdr->perms + node->num_perms; ++ memcpy(hdr->perms, node->perms.p, ++ node->perms.num * sizeof(*node->perms.p)); ++ p = hdr->perms + node->perms.num; + memcpy(p, node->data, node->datalen); + p += node->datalen; + memcpy(p, node->children, node->childlen); +@@ -468,8 +469,7 @@ static int write_node(struct connection *conn, struct node *node, + } + + static enum xs_perm_type perm_for_conn(struct connection *conn, +- struct xs_permissions *perms, +- unsigned int num) ++ const struct node_perms *perms) + { + unsigned int i; + enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; +@@ -478,16 +478,16 @@ static enum xs_perm_type perm_for_conn(struct connection *conn, + mask &= ~XS_PERM_WRITE; + + /* Owners and tools get it all... */ +- if (!domain_is_unprivileged(conn) || perms[0].id == conn->id +- || (conn->target && perms[0].id == conn->target->id)) ++ if (!domain_is_unprivileged(conn) || perms->p[0].id == conn->id ++ || (conn->target && perms->p[0].id == conn->target->id)) + return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; + +- for (i = 1; i < num; i++) +- if (perms[i].id == conn->id +- || (conn->target && perms[i].id == conn->target->id)) +- return perms[i].perms & mask; ++ for (i = 1; i < perms->num; i++) ++ if (perms->p[i].id == conn->id ++ || (conn->target && perms->p[i].id == conn->target->id)) ++ return perms->p[i].perms & mask; + +- return perms[0].perms & mask; ++ return perms->p[0].perms & mask; + } + + /* +@@ -534,7 +534,7 @@ static int ask_parents(struct connection *conn, const void *ctx, + return 0; + } + +- *perm = perm_for_conn(conn, node->perms, node->num_perms); ++ *perm = perm_for_conn(conn, &node->perms); + return 0; + } + +@@ -580,8 +580,7 @@ struct node *get_node(struct connection *conn, + node = read_node(conn, ctx, name); + /* If we don't have permission, we don't have node. */ + if (node) { +- if ((perm_for_conn(conn, node->perms, node->num_perms) & perm) +- != perm) { ++ if ((perm_for_conn(conn, &node->perms) & perm) != perm) { + errno = EACCES; + node = NULL; + } +@@ -757,16 +756,15 @@ const char *onearg(struct buffered_data *in) + return in->buffer; + } + +-static char *perms_to_strings(const void *ctx, +- struct xs_permissions *perms, unsigned int num, ++static char *perms_to_strings(const void *ctx, const struct node_perms *perms, + unsigned int *len) + { + unsigned int i; + char *strings = NULL; + char buffer[MAX_STRLEN(unsigned int) + 1]; + +- for (*len = 0, i = 0; i < num; i++) { +- if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) ++ for (*len = 0, i = 0; i < perms->num; i++) { ++ if (!xs_perm_to_string(&perms->p[i], buffer, sizeof(buffer))) + return NULL; + + strings = talloc_realloc(ctx, strings, char, +@@ -945,13 +943,13 @@ static struct node *construct_node(struct connection *conn, const void *ctx, + goto nomem; + + /* Inherit permissions, except unprivileged domains own what they create */ +- node->num_perms = parent->num_perms; +- node->perms = talloc_memdup(node, parent->perms, +- node->num_perms * sizeof(node->perms[0])); +- if (!node->perms) ++ node->perms.num = parent->perms.num; ++ node->perms.p = talloc_memdup(node, parent->perms.p, ++ node->perms.num * sizeof(*node->perms.p)); ++ if (!node->perms.p) + goto nomem; + if (domain_is_unprivileged(conn)) +- node->perms[0].id = conn->id; ++ node->perms.p[0].id = conn->id; + + /* No children, no data */ + node->children = node->data = NULL; +@@ -1228,7 +1226,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + if (!node) + return errno; + +- strings = perms_to_strings(node, node->perms, node->num_perms, &len); ++ strings = perms_to_strings(node, &node->perms, &len); + if (!strings) + return errno; + +@@ -1239,13 +1237,12 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + + static int do_set_perms(struct connection *conn, struct buffered_data *in) + { +- unsigned int num; +- struct xs_permissions *perms; ++ struct node_perms perms; + char *name, *permstr; + struct node *node; + +- num = xs_count_strings(in->buffer, in->used); +- if (num < 2) ++ perms.num = xs_count_strings(in->buffer, in->used); ++ if (perms.num < 2) + return EINVAL; + + /* First arg is node name. */ +@@ -1256,21 +1253,21 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + return errno; + + permstr = in->buffer + strlen(in->buffer) + 1; +- num--; ++ perms.num--; + +- perms = talloc_array(node, struct xs_permissions, num); +- if (!perms) ++ perms.p = talloc_array(node, struct xs_permissions, perms.num); ++ if (!perms.p) + return ENOMEM; +- if (!xs_strings_to_perms(perms, num, permstr)) ++ if (!xs_strings_to_perms(perms.p, perms.num, permstr)) + return errno; + + /* Unprivileged domains may not change the owner. */ +- if (domain_is_unprivileged(conn) && perms[0].id != node->perms[0].id) ++ if (domain_is_unprivileged(conn) && ++ perms.p[0].id != node->perms.p[0].id) + return EPERM; + + domain_entry_dec(conn, node); + node->perms = perms; +- node->num_perms = num; + domain_entry_inc(conn, node); + + if (write_node(conn, node, false)) +@@ -1545,8 +1542,8 @@ static void manual_node(const char *name, const char *child) + barf_perror("Could not allocate initial node %s", name); + + node->name = name; +- node->perms = &perms; +- node->num_perms = 1; ++ node->perms.p = &perms; ++ node->perms.num = 1; + node->children = (char *)child; + if (child) + node->childlen = strlen(child) + 1; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 3cb1c235a101..193d93142636 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -109,6 +109,11 @@ struct connection + }; + extern struct list_head connections; + ++struct node_perms { ++ unsigned int num; ++ struct xs_permissions *p; ++}; ++ + struct node { + const char *name; + +@@ -120,8 +125,7 @@ struct node { + #define NO_GENERATION ~((uint64_t)0) + + /* Permissions. */ +- unsigned int num_perms; +- struct xs_permissions *perms; ++ struct node_perms perms; + + /* Contents. */ + unsigned int datalen; +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 0e2926e2a3d0..dc51cdfa9aa7 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -657,12 +657,12 @@ void domain_entry_inc(struct connection *conn, struct node *node) + if (!conn) + return; + +- if (node->perms && node->perms[0].id != conn->id) { ++ if (node->perms.p && node->perms.p[0].id != conn->id) { + if (conn->transaction) { + transaction_entry_inc(conn->transaction, +- node->perms[0].id); ++ node->perms.p[0].id); + } else { +- d = find_domain_by_domid(node->perms[0].id); ++ d = find_domain_by_domid(node->perms.p[0].id); + if (d) + d->nbentry++; + } +@@ -683,12 +683,12 @@ void domain_entry_dec(struct connection *conn, struct node *node) + if (!conn) + return; + +- if (node->perms && node->perms[0].id != conn->id) { ++ if (node->perms.p && node->perms.p[0].id != conn->id) { + if (conn->transaction) { + transaction_entry_dec(conn->transaction, +- node->perms[0].id); ++ node->perms.p[0].id); + } else { +- d = find_domain_by_domid(node->perms[0].id); ++ d = find_domain_by_domid(node->perms.p[0].id); + if (d && d->nbentry) + d->nbentry--; + } +-- +2.17.1 + diff --git a/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch b/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch new file mode 100644 index 0000000..7804103 --- /dev/null +++ b/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch @@ -0,0 +1,237 @@ +From cddf74031b3c8a108e8fd7db0bf56e9c2809d3e2 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:45 +0200 +Subject: [PATCH 09/10] tools/xenstore: allow special watches for privileged + callers only + +The special watches "@introduceDomain" and "@releaseDomain" should be +allowed for privileged callers only, as they allow to gain information +about presence of other guests on the host. So send watch events for +those watches via privileged connections only. + +In order to allow for disaggregated setups where e.g. driver domains +need to make use of those special watches add support for calling +"set permissions" for those special nodes, too. + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + docs/misc/xenstore.txt | 5 +++ + tools/xenstore/xenstored_core.c | 27 ++++++++------ + tools/xenstore/xenstored_core.h | 2 ++ + tools/xenstore/xenstored_domain.c | 60 +++++++++++++++++++++++++++++++ + tools/xenstore/xenstored_domain.h | 5 +++ + tools/xenstore/xenstored_watch.c | 4 +++ + 6 files changed, 93 insertions(+), 10 deletions(-) + +diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt +index 6f8569d5760f..32969eb3fecd 100644 +--- a/docs/misc/xenstore.txt ++++ b/docs/misc/xenstore.txt +@@ -170,6 +170,9 @@ SET_PERMS ||+? + n no access + See http://wiki.xen.org/wiki/XenBus section + `Permissions' for details of the permissions system. ++ It is possible to set permissions for the special watch paths ++ "@introduceDomain" and "@releaseDomain" to enable receiving those ++ watches in unprivileged domains. + + ---------- Watches ---------- + +@@ -194,6 +197,8 @@ WATCH ||? + @releaseDomain occurs on any domain crash or + shutdown, and also on RELEASE + and domain destruction ++ events are sent to privileged callers or explicitly ++ via SET_PERMS enabled domains only. + + When a watch is first set up it is triggered once straight + away, with equal to . Watches may be triggered +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index fe9943113b9f..720bec269dd3 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -468,8 +468,8 @@ static int write_node(struct connection *conn, struct node *node, + return write_node_raw(conn, &key, node, no_quota_check); + } + +-static enum xs_perm_type perm_for_conn(struct connection *conn, +- const struct node_perms *perms) ++enum xs_perm_type perm_for_conn(struct connection *conn, ++ const struct node_perms *perms) + { + unsigned int i; + enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; +@@ -1245,22 +1245,29 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (perms.num < 2) + return EINVAL; + +- /* First arg is node name. */ +- /* We must own node to do this (tools can do this too). */ +- node = get_node_canonicalized(conn, in, in->buffer, &name, +- XS_PERM_WRITE | XS_PERM_OWNER); +- if (!node) +- return errno; +- + permstr = in->buffer + strlen(in->buffer) + 1; + perms.num--; + +- perms.p = talloc_array(node, struct xs_permissions, perms.num); ++ perms.p = talloc_array(in, struct xs_permissions, perms.num); + if (!perms.p) + return ENOMEM; + if (!xs_strings_to_perms(perms.p, perms.num, permstr)) + return errno; + ++ /* First arg is node name. */ ++ if (strstarts(in->buffer, "@")) { ++ if (set_perms_special(conn, in->buffer, &perms)) ++ return errno; ++ send_ack(conn, XS_SET_PERMS); ++ return 0; ++ } ++ ++ /* We must own node to do this (tools can do this too). */ ++ node = get_node_canonicalized(conn, in, in->buffer, &name, ++ XS_PERM_WRITE | XS_PERM_OWNER); ++ if (!node) ++ return errno; ++ + /* Unprivileged domains may not change the owner. */ + if (domain_is_unprivileged(conn) && + perms.p[0].id != node->perms.p[0].id) +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index 193d93142636..f3da6bbc943d 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -165,6 +165,8 @@ struct node *get_node(struct connection *conn, + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); + void check_store(void); + void corrupt(struct connection *conn, const char *fmt, ...); ++enum xs_perm_type perm_for_conn(struct connection *conn, ++ const struct node_perms *perms); + + /* Is this a valid node name? */ + bool is_valid_nodename(const char *node); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index dc51cdfa9aa7..7afabe0ae084 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -41,6 +41,9 @@ static evtchn_port_t virq_port; + + xenevtchn_handle *xce_handle = NULL; + ++static struct node_perms dom_release_perms; ++static struct node_perms dom_introduce_perms; ++ + struct domain + { + struct list_head list; +@@ -589,6 +592,59 @@ void restore_existing_connections(void) + { + } + ++static int set_dom_perms_default(struct node_perms *perms) ++{ ++ perms->num = 1; ++ perms->p = talloc_array(NULL, struct xs_permissions, perms->num); ++ if (!perms->p) ++ return -1; ++ perms->p->id = 0; ++ perms->p->perms = XS_PERM_NONE; ++ ++ return 0; ++} ++ ++static struct node_perms *get_perms_special(const char *name) ++{ ++ if (!strcmp(name, "@releaseDomain")) ++ return &dom_release_perms; ++ if (!strcmp(name, "@introduceDomain")) ++ return &dom_introduce_perms; ++ return NULL; ++} ++ ++int set_perms_special(struct connection *conn, const char *name, ++ struct node_perms *perms) ++{ ++ struct node_perms *p; ++ ++ p = get_perms_special(name); ++ if (!p) ++ return EINVAL; ++ ++ if ((perm_for_conn(conn, p) & (XS_PERM_WRITE | XS_PERM_OWNER)) != ++ (XS_PERM_WRITE | XS_PERM_OWNER)) ++ return EACCES; ++ ++ p->num = perms->num; ++ talloc_free(p->p); ++ p->p = perms->p; ++ talloc_steal(NULL, perms->p); ++ ++ return 0; ++} ++ ++bool check_perms_special(const char *name, struct connection *conn) ++{ ++ struct node_perms *p; ++ ++ p = get_perms_special(name); ++ if (!p) ++ return false; ++ ++ return perm_for_conn(conn, p) & XS_PERM_READ; ++} ++ + static int dom0_init(void) + { + evtchn_port_t port; +@@ -610,6 +666,10 @@ static int dom0_init(void) + + xenevtchn_notify(xce_handle, dom0->port); + ++ if (set_dom_perms_default(&dom_release_perms) || ++ set_dom_perms_default(&dom_introduce_perms)) ++ return -1; ++ + return 0; + } + +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 56ae01597475..259183962a9c 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -65,6 +65,11 @@ void domain_watch_inc(struct connection *conn); + void domain_watch_dec(struct connection *conn); + int domain_watch(struct connection *conn); + ++/* Special node permission handling. */ ++int set_perms_special(struct connection *conn, const char *name, ++ struct node_perms *perms); ++bool check_perms_special(const char *name, struct connection *conn); ++ + /* Write rate limiting */ + + #define WRL_FACTOR 1000 /* for fixed-point arithmetic */ +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 3836675459fa..f4e289362eb6 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -133,6 +133,10 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { ++ /* introduce/release domain watches */ ++ if (check_special_event(name) && !check_perms_special(name, i)) ++ continue; ++ + list_for_each_entry(watch, &i->watches, list) { + if (exact) { + if (streq(name, watch->node)) +-- +2.17.1 + diff --git a/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch b/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch new file mode 100644 index 0000000..8ad1aca --- /dev/null +++ b/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch @@ -0,0 +1,375 @@ +From e57b7687b43b033fe45e755e285efbe67bc71921 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Thu, 11 Jun 2020 16:12:46 +0200 +Subject: [PATCH 10/10] tools/xenstore: avoid watch events for nodes without + access + +Today watch events are sent regardless of the access rights of the +node the event is sent for. This enables any guest to e.g. setup a +watch for "/" in order to have a detailed record of all Xenstore +modifications. + +Modify that by sending only watch events for nodes that the watcher +has a chance to see otherwise (either via direct reads or by querying +the children of a node). This includes cases where the visibility of +a node for a watcher is changing (permissions being removed). + +This is part of XSA-115. + +Signed-off-by: Juergen Gross +[julieng: Handle rebase conflict] +Reviewed-by: Julien Grall +Reviewed-by: Paul Durrant +--- + tools/xenstore/xenstored_core.c | 28 +++++----- + tools/xenstore/xenstored_core.h | 15 ++++-- + tools/xenstore/xenstored_domain.c | 6 +-- + tools/xenstore/xenstored_transaction.c | 21 +++++++- + tools/xenstore/xenstored_watch.c | 75 +++++++++++++++++++------- + tools/xenstore/xenstored_watch.h | 2 +- + 6 files changed, 104 insertions(+), 43 deletions(-) + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 720bec269dd3..1c2845454560 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -358,8 +358,8 @@ static void initialize_fds(int sock, int *p_sock_pollfd_idx, + * If it fails, returns NULL and sets errno. + * Temporary memory allocations will be done with ctx. + */ +-static struct node *read_node(struct connection *conn, const void *ctx, +- const char *name) ++struct node *read_node(struct connection *conn, const void *ctx, ++ const char *name) + { + TDB_DATA key, data; + struct xs_tdb_record_hdr *hdr; +@@ -494,7 +494,7 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + * Get name of node parent. + * Temporary memory allocations are done with ctx. + */ +-static char *get_parent(const void *ctx, const char *node) ++char *get_parent(const void *ctx, const char *node) + { + char *parent; + char *slash = strrchr(node + 1, '/'); +@@ -566,10 +566,10 @@ static int errno_from_parents(struct connection *conn, const void *ctx, + * If it fails, returns NULL and sets errno. + * Temporary memory allocations are done with ctx. + */ +-struct node *get_node(struct connection *conn, +- const void *ctx, +- const char *name, +- enum xs_perm_type perm) ++static struct node *get_node(struct connection *conn, ++ const void *ctx, ++ const char *name, ++ enum xs_perm_type perm) + { + struct node *node; + +@@ -1056,7 +1056,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) + return errno; + } + +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, NULL); + send_ack(conn, XS_WRITE); + + return 0; +@@ -1078,7 +1078,7 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) + node = create_node(conn, in, name, NULL, 0); + if (!node) + return errno; +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, NULL); + } + send_ack(conn, XS_MKDIR); + +@@ -1141,7 +1141,7 @@ static int delete_node(struct connection *conn, const void *ctx, + talloc_free(name); + } + +- fire_watches(conn, ctx, node->name, true); ++ fire_watches(conn, ctx, node->name, node, true, NULL); + delete_node_single(conn, node); + delete_child(conn, parent, basename(node->name)); + talloc_free(node); +@@ -1165,13 +1165,14 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, + parent = read_node(conn, ctx, parentname); + if (!parent) + return (errno == ENOMEM) ? ENOMEM : EINVAL; ++ node->parent = parent; + + /* + * Fire the watches now, when we can still see the node permissions. + * This fine as we are single threaded and the next possible read will + * be handled only after the node has been really removed. + */ +- fire_watches(conn, ctx, name, false); ++ fire_watches(conn, ctx, name, node, false, NULL); + return delete_node(conn, ctx, parent, node); + } + +@@ -1237,7 +1238,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) + + static int do_set_perms(struct connection *conn, struct buffered_data *in) + { +- struct node_perms perms; ++ struct node_perms perms, old_perms; + char *name, *permstr; + struct node *node; + +@@ -1273,6 +1274,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + perms.p[0].id != node->perms.p[0].id) + return EPERM; + ++ old_perms = node->perms; + domain_entry_dec(conn, node); + node->perms = perms; + domain_entry_inc(conn, node); +@@ -1280,7 +1282,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (write_node(conn, node, false)) + return errno; + +- fire_watches(conn, in, name, false); ++ fire_watches(conn, in, name, node, false, &old_perms); + send_ack(conn, XS_SET_PERMS); + + return 0; +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index f3da6bbc943d..e050b27cbdde 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -152,15 +152,17 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); + /* Canonicalize this path if possible. */ + char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); + ++/* Get access permissions. */ ++enum xs_perm_type perm_for_conn(struct connection *conn, ++ const struct node_perms *perms); ++ + /* Write a node to the tdb data base. */ + int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + bool no_quota_check); + +-/* Get this node, checking we have permissions. */ +-struct node *get_node(struct connection *conn, +- const void *ctx, +- const char *name, +- enum xs_perm_type perm); ++/* Get a node from the tdb data base. */ ++struct node *read_node(struct connection *conn, const void *ctx, ++ const char *name); + + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); + void check_store(void); +@@ -171,6 +173,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + /* Is this a valid node name? */ + bool is_valid_nodename(const char *node); + ++/* Get name of parent node. */ ++char *get_parent(const void *ctx, const char *node); ++ + /* Tracing infrastructure. */ + void trace_create(const void *data, const char *type); + void trace_destroy(const void *data, const char *type); +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 7afabe0ae084..711a11b18ad6 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -206,7 +206,7 @@ static int destroy_domain(void *_domain) + unmap_interface(domain->interface); + } + +- fire_watches(NULL, domain, "@releaseDomain", false); ++ fire_watches(NULL, domain, "@releaseDomain", NULL, false, NULL); + + wrl_domain_destroy(domain); + +@@ -244,7 +244,7 @@ static void domain_cleanup(void) + } + + if (notify) +- fire_watches(NULL, NULL, "@releaseDomain", false); ++ fire_watches(NULL, NULL, "@releaseDomain", NULL, false, NULL); + } + + /* We scan all domains rather than use the information given here. */ +@@ -410,7 +410,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + /* Now domain belongs to its connection. */ + talloc_steal(domain->conn, domain); + +- fire_watches(NULL, in, "@introduceDomain", false); ++ fire_watches(NULL, in, "@introduceDomain", NULL, false, NULL); + } else if ((domain->mfn == mfn) && (domain->conn != conn)) { + /* Use XS_INTRODUCE for recreating the xenbus event-channel. */ + if (domain->port) +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index e87897573469..a7d8c5d475ec 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -114,6 +114,9 @@ struct accessed_node + /* Generation count (or NO_GENERATION) for conflict checking. */ + uint64_t generation; + ++ /* Original node permissions. */ ++ struct node_perms perms; ++ + /* Generation count checking required? */ + bool check_gen; + +@@ -260,6 +263,15 @@ int access_node(struct connection *conn, struct node *node, + i->node = talloc_strdup(i, node->name); + if (!i->node) + goto nomem; ++ if (node->generation != NO_GENERATION && node->perms.num) { ++ i->perms.p = talloc_array(i, struct xs_permissions, ++ node->perms.num); ++ if (!i->perms.p) ++ goto nomem; ++ i->perms.num = node->perms.num; ++ memcpy(i->perms.p, node->perms.p, ++ i->perms.num * sizeof(*i->perms.p)); ++ } + + introduce = true; + i->ta_node = false; +@@ -368,9 +380,14 @@ static int finalize_transaction(struct connection *conn, + talloc_free(data.dptr); + if (ret) + goto err; +- } else if (tdb_delete(tdb_ctx, key)) ++ fire_watches(conn, trans, i->node, NULL, false, ++ i->perms.p ? &i->perms : NULL); ++ } else { ++ fire_watches(conn, trans, i->node, NULL, false, ++ i->perms.p ? &i->perms : NULL); ++ if (tdb_delete(tdb_ctx, key)) + goto err; +- fire_watches(conn, trans, i->node, false); ++ } + } + + if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index f4e289362eb6..71c108ea99f1 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -85,22 +85,6 @@ static void add_event(struct connection *conn, + unsigned int len; + char *data; + +- if (!check_special_event(name)) { +- /* Can this conn load node, or see that it doesn't exist? */ +- struct node *node = get_node(conn, ctx, name, XS_PERM_READ); +- /* +- * XXX We allow EACCES here because otherwise a non-dom0 +- * backend driver cannot watch for disappearance of a frontend +- * xenstore directory. When the directory disappears, we +- * revert to permissions of the parent directory for that path, +- * which will typically disallow access for the backend. +- * But this breaks device-channel teardown! +- * Really we should fix this better... +- */ +- if (!node && errno != ENOENT && errno != EACCES) +- return; +- } +- + if (watch->relative_path) { + name += strlen(watch->relative_path); + if (*name == '/') /* Could be "" */ +@@ -117,12 +101,60 @@ static void add_event(struct connection *conn, + talloc_free(data); + } + ++/* ++ * Check permissions of a specific watch to fire: ++ * Either the node itself or its parent have to be readable by the connection ++ * the watch has been setup for. In case a watch event is created due to ++ * changed permissions we need to take the old permissions into account, too. ++ */ ++static bool watch_permitted(struct connection *conn, const void *ctx, ++ const char *name, struct node *node, ++ struct node_perms *perms) ++{ ++ enum xs_perm_type perm; ++ struct node *parent; ++ char *parent_name; ++ ++ if (perms) { ++ perm = perm_for_conn(conn, perms); ++ if (perm & XS_PERM_READ) ++ return true; ++ } ++ ++ if (!node) { ++ node = read_node(conn, ctx, name); ++ if (!node) ++ return false; ++ } ++ ++ perm = perm_for_conn(conn, &node->perms); ++ if (perm & XS_PERM_READ) ++ return true; ++ ++ parent = node->parent; ++ if (!parent) { ++ parent_name = get_parent(ctx, node->name); ++ if (!parent_name) ++ return false; ++ parent = read_node(conn, ctx, parent_name); ++ if (!parent) ++ return false; ++ } ++ ++ perm = perm_for_conn(conn, &parent->perms); ++ ++ return perm & XS_PERM_READ; ++} ++ + /* + * Check whether any watch events are to be sent. + * Temporary memory allocations are done with ctx. ++ * We need to take the (potential) old permissions of the node into account ++ * as a watcher losing permissions to access a node should receive the ++ * watch event, too. + */ + void fire_watches(struct connection *conn, const void *ctx, const char *name, +- bool exact) ++ struct node *node, bool exact, struct node_perms *perms) + { + struct connection *i; + struct watch *watch; +@@ -134,8 +166,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, + /* Create an event for each watch. */ + list_for_each_entry(i, &connections, list) { + /* introduce/release domain watches */ +- if (check_special_event(name) && !check_perms_special(name, i)) +- continue; ++ if (check_special_event(name)) { ++ if (!check_perms_special(name, i)) ++ continue; ++ } else { ++ if (!watch_permitted(i, ctx, name, node, perms)) ++ continue; ++ } + + list_for_each_entry(watch, &i->watches, list) { + if (exact) { +diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h +index 1b3c80d3dda1..03094374f379 100644 +--- a/tools/xenstore/xenstored_watch.h ++++ b/tools/xenstore/xenstored_watch.h +@@ -26,7 +26,7 @@ int do_unwatch(struct connection *conn, struct buffered_data *in); + + /* Fire all watches: !exact means all the children are affected (ie. rm). */ + void fire_watches(struct connection *conn, const void *tmp, const char *name, +- bool exact); ++ struct node *node, bool exact, struct node_perms *perms); + + void conn_delete_all_watches(struct connection *conn); + +-- +2.17.1 + diff --git a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch new file mode 100644 index 0000000..0072c68 --- /dev/null +++ b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch @@ -0,0 +1,43 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: ignore transaction id for [un]watch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH +commands as it is documented in docs/misc/xenstore.txt, it is tested +for validity today. + +Really ignore the transaction id for XS_WATCH and XS_UNWATCH. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index ff5c9484fc..2fa6798e3b 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -498,12 +498,19 @@ let retain_op_in_history ty = + | Xenbus.Xb.Op.Reset_watches + | Xenbus.Xb.Op.Invalid -> false + ++let maybe_ignore_transaction = function ++ | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch -> fun tid -> ++ if tid <> Transaction.none then ++ debug "Ignoring transaction ID %d for watch/unwatch" tid; ++ Transaction.none ++ | _ -> fun x -> x ++ + (** + * Nothrow guarantee. + *) + let process_packet ~store ~cons ~doms ~con ~req = + let ty = req.Packet.ty in +- let tid = req.Packet.tid in ++ let tid = maybe_ignore_transaction ty req.Packet.tid in + let rid = req.Packet.rid in + try + let fct = function_of_type ty in diff --git a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch new file mode 100644 index 0000000..26033c7 --- /dev/null +++ b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch @@ -0,0 +1,30 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: check privilege for XS_IS_DOMAIN_INTRODUCED +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for privileged +domains only (the only user in the tree is the xenpaging daemon). + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 2fa6798e3b..fd79ef564f 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -166,7 +166,9 @@ let do_setperms con t _domains _cons data = + let do_error _con _t _domains _cons _data = + raise Define.Unknown_operation + +-let do_isintroduced _con _t domains _cons data = ++let do_isintroduced con _t domains _cons data = ++ if not (Connection.is_dom0 con) ++ then raise Define.Permission_denied; + let domid = + match (split None '\000' data) with + | domid :: _ -> int_of_string domid diff --git a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch new file mode 100644 index 0000000..fea94a9 --- /dev/null +++ b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch @@ -0,0 +1,29 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: unify watch firing +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This will make it easier insert additional checks in a follow-up patch. +All watches are now fired from a single function. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 24750ada43..e5df62d9e7 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -210,8 +210,7 @@ let fire_watch watch path = + end else + path + in +- let data = Utils.join_by_null [ new_path; watch.token; "" ] in +- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data ++ fire_single_watch { watch with path = new_path } + + (* Search for a valid unused transaction id. *) + let rec valid_transaction_id con proposed_id = diff --git a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch new file mode 100644 index 0000000..76f98e9 --- /dev/null +++ b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch @@ -0,0 +1,117 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: introduce permissions for special watches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The special watches "@introduceDomain" and "@releaseDomain" should be +allowed for privileged callers only, as they allow to gain information +about presence of other guests on the host. So send watch events for +those watches via privileged connections only. + +Start to address this by treating the special watches as regular nodes +in the tree, which gives them normal semantics for permissions. A later +change will restrict the handling, so that they can't be listed, etc. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index fd79ef564f..e528d1ecb2 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -420,7 +420,7 @@ let do_introduce con _t domains cons data = + else try + let ndom = Domains.create domains domid mfn port in + Connections.add_domain cons ndom; +- Connections.fire_spec_watches cons "@introduceDomain"; ++ Connections.fire_spec_watches cons Store.Path.introduce_domain; + ndom + with _ -> raise Invalid_Cmd_Args + in +@@ -439,7 +439,7 @@ let do_release con _t domains cons data = + Domains.del domains domid; + Connections.del_domain cons domid; + if fire_spec_watches +- then Connections.fire_spec_watches cons "@releaseDomain" ++ then Connections.fire_spec_watches cons Store.Path.release_domain + else raise Invalid_Cmd_Args + + let do_resume con _t domains _cons data = +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 92b6289b5e..52b88b3ee1 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -214,6 +214,11 @@ let rec lookup node path fct = + + let apply rnode path fct = + lookup rnode path fct ++ ++let introduce_domain = "@introduceDomain" ++let release_domain = "@releaseDomain" ++let specials = List.map of_string [ introduce_domain; release_domain ] ++ + end + + (* The Store.t type *) +diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml +index b252db799b..e8c9fe4e94 100644 +--- a/tools/ocaml/xenstored/utils.ml ++++ b/tools/ocaml/xenstored/utils.ml +@@ -88,19 +88,17 @@ let read_file_single_integer filename = + Unix.close fd; + int_of_string (Bytes.sub_string buf 0 sz) + +-let path_complete path connection_path = +- if String.get path 0 <> '/' then +- connection_path ^ path +- else +- path +- ++(* @path may be guest data and needs its length validating. @connection_path ++ * is generated locally in xenstored and always of the form "/local/domain/$N/" *) + let path_validate path connection_path = +- if String.length path = 0 || String.length path > 1024 then +- raise Define.Invalid_path +- else +- let cpath = path_complete path connection_path in +- if String.get cpath 0 <> '/' then +- raise Define.Invalid_path +- else +- cpath ++ let len = String.length path in ++ ++ if len = 0 || len > 1024 then raise Define.Invalid_path; ++ ++ let abs_path = ++ match String.get path 0 with ++ | '/' | '@' -> path ++ | _ -> connection_path ^ path ++ in + ++ abs_path +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 7e7824761b..8d0c50bfa4 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -286,6 +286,8 @@ let _ = + let quit = ref false in + + Logging.init_xenstored_log(); ++ List.iter (fun path -> ++ Store.write store Perms.Connection.full_rights path "") Store.Path.specials; + + let filename = Paths.xen_run_stored ^ "/db" in + if cf.restart && Sys.file_exists filename then ( +@@ -335,7 +337,7 @@ let _ = + let (notify, deaddom) = Domains.cleanup domains in + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then +- Connections.fire_spec_watches cons "@releaseDomain" ++ Connections.fire_spec_watches cons Store.Path.release_domain + ) + else + let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch new file mode 100644 index 0000000..866d415 --- /dev/null +++ b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch @@ -0,0 +1,406 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: avoid watch events for nodes without access +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Today watch events are sent regardless of the access rights of the +node the event is sent for. This enables any guest to e.g. setup a +watch for "/" in order to have a detailed record of all Xenstore +modifications. + +Modify that by sending only watch events for nodes that the watcher +has a chance to see otherwise (either via direct reads or by querying +the children of a node). This includes cases where the visibility of +a node for a watcher is changing (permissions being removed). + +Permissions for nodes are looked up either in the old (pre +transaction/command) or current trees (post transaction). If +permissions are changed multiple times in a transaction only the final +version is checked, because considering a transaction atomic the +individual permission changes would not be noticable to an outside +observer. + +Two trees are only needed for set_perms: here we can either notice the +node disappearing (if we loose permission), appearing +(if we gain permission), or changing (if we preserve permission). + +RM needs to only look at the old tree: in the new tree the node would be +gone, or could have different permissions if it was recreated (the +recreation would get its own watch fired). + +Inside a tree we lookup the watch path's parent, and then the watch path +child itself. This gets us 4 sets of permissions in worst case, and if +either of these allows a watch, then we permit it to fire. The +permission lookups are done without logging the failures, otherwise we'd +get confusing errors about permission denied for some paths, but a watch +still firing. The actual result is logged in xenstored-access log: + + 'w event ...' as usual if watch was fired + 'w notfired...' if the watch was not fired, together with path and + permission set to help in troubleshooting + +Adding a watch bypasses permission checks and always fires the watch +once immediately. This is consistent with the specification, and no +information is gained (the watch is fired both if the path exists or +doesn't, and both if you have or don't have access, i.e. it reflects the +path a domain gave it back to that domain). + +There are some semantic changes here: + + * Write+rm in a single transaction of the same path is unobservable + now via watches: both before and after a transaction the path + doesn't exist, thus both tree lookups come up with the empty + permission set, and noone, not even Dom0 can see this. This is + consistent with transaction atomicity though. + * Similar to above if we temporarily grant and then revoke permission + on a path any watches fired inbetween are ignored as well + * There is a new log event (w notfired) which shows the permission set + of the path, and the path. + * Watches on paths that a domain doesn't have access to are now not + seen, which is the purpose of the security fix. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index e5df62d9e7..644a448f2e 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -196,11 +196,36 @@ let list_watches con = + con.watches [] in + List.concat ll + +-let fire_single_watch watch = ++let dbg fmt = Logging.debug "connection" fmt ++let info fmt = Logging.info "connection" fmt ++ ++let lookup_watch_perm path = function ++| None -> [] ++| Some root -> ++ try Store.Path.apply root path @@ fun parent name -> ++ Store.Node.get_perms parent :: ++ try [Store.Node.get_perms (Store.Node.find parent name)] ++ with Not_found -> [] ++ with Define.Invalid_path | Not_found -> [] ++ ++let lookup_watch_perms oldroot root path = ++ lookup_watch_perm path oldroot @ lookup_watch_perm path (Some root) ++ ++let fire_single_watch_unchecked watch = + let data = Utils.join_by_null [watch.path; watch.token; ""] in + send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data + +-let fire_watch watch path = ++let fire_single_watch (oldroot, root) watch = ++ let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in ++ let perms = lookup_watch_perms oldroot root abspath in ++ if List.exists (Perms.has watch.con.perm READ) perms then ++ fire_single_watch_unchecked watch ++ else ++ let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in ++ let con = get_domstr watch.con in ++ Logging.watch_not_fired ~con perms (Store.Path.to_string abspath) ++ ++let fire_watch roots watch path = + let new_path = + if watch.is_relative && path.[0] = '/' + then begin +@@ -210,7 +235,7 @@ let fire_watch watch path = + end else + path + in +- fire_single_watch { watch with path = new_path } ++ fire_single_watch roots { watch with path = new_path } + + (* Search for a valid unused transaction id. *) + let rec valid_transaction_id con proposed_id = +diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml +index f2c4318c88..9f9f7ee2f0 100644 +--- a/tools/ocaml/xenstored/connections.ml ++++ b/tools/ocaml/xenstored/connections.ml +@@ -135,25 +135,26 @@ let del_watch cons con path token = + watch + + (* path is absolute *) +-let fire_watches cons path recurse = ++let fire_watches ?oldroot root cons path recurse = + let key = key_of_path path in + let path = Store.Path.to_string path in ++ let roots = oldroot, root in + let fire_watch _ = function + | None -> () +- | Some watches -> List.iter (fun w -> Connection.fire_watch w path) watches ++ | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches + in + let fire_rec _x = function + | None -> () + | Some watches -> +- List.iter (fun w -> Connection.fire_single_watch w) watches ++ List.iter (Connection.fire_single_watch roots) watches + in + Trie.iter_path fire_watch cons.watches key; + if recurse then + Trie.iter fire_rec (Trie.sub cons.watches key) + +-let fire_spec_watches cons specpath = ++let fire_spec_watches root cons specpath = + iter cons (fun con -> +- List.iter (fun w -> Connection.fire_single_watch w) (Connection.get_watches con specpath)) ++ List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) + + let set_target cons domain target_domain = + let con = find_domain cons domain in +diff --git a/tools/ocaml/xenstored/logging.ml b/tools/ocaml/xenstored/logging.ml +index c5cba79e92..1ede131329 100644 +--- a/tools/ocaml/xenstored/logging.ml ++++ b/tools/ocaml/xenstored/logging.ml +@@ -161,6 +161,8 @@ let xenstored_log_nb_lines = ref 13215 + let xenstored_log_nb_chars = ref (-1) + let xenstored_logger = ref (None: logger option) + ++let debug_enabled () = !xenstored_log_level = Debug ++ + let set_xenstored_log_destination s = + xenstored_log_destination := log_destination_of_string s + +@@ -204,6 +206,7 @@ type access_type = + | Commit + | Newconn + | Endconn ++ | Watch_not_fired + | XbOp of Xenbus.Xb.Op.operation + + let string_of_tid ~con tid = +@@ -217,6 +220,7 @@ let string_of_access_type = function + | Commit -> "commit " + | Newconn -> "newconn " + | Endconn -> "endconn " ++ | Watch_not_fired -> "w notfired" + + | XbOp op -> match op with + | Xenbus.Xb.Op.Debug -> "debug " +@@ -331,3 +335,7 @@ let xb_answer ~tid ~con ~ty data = + | _ -> false, Debug + in + if print then access_logging ~tid ~con ~data (XbOp ty) ~level ++ ++let watch_not_fired ~con perms path = ++ let data = Printf.sprintf "EPERM perms=[%s] path=%s" perms path in ++ access_logging ~tid:0 ~con ~data Watch_not_fired ~level:Info +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index 3ea193ea14..23b80aba3d 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -79,9 +79,9 @@ let of_string s = + let string_of_perm perm = + Printf.sprintf "%c%u" (char_of_permty (snd perm)) (fst perm) + +-let to_string permvec = ++let to_string ?(sep="\000") permvec = + let l = ((permvec.owner, permvec.other) :: permvec.acl) in +- String.concat "\000" (List.map string_of_perm l) ++ String.concat sep (List.map string_of_perm l) + + end + +@@ -132,8 +132,8 @@ let check_owner (connection:Connection.t) (node:Node.t) = + then Connection.is_owner connection (Node.get_owner node) + else true + +-(* check if the current connection has the requested perm on the current node *) +-let check (connection:Connection.t) request (node:Node.t) = ++(* check if the current connection lacks the requested perm on the current node *) ++let lacks (connection:Connection.t) request (node:Node.t) = + let check_acl domainid = + let perm = + if List.mem_assoc domainid (Node.get_acl node) +@@ -154,11 +154,19 @@ let check (connection:Connection.t) request (node:Node.t) = + info "Permission denied: Domain %d has write only access" domainid; + false + in +- if !activate ++ !activate + && not (Connection.is_dom0 connection) + && not (check_owner connection node) + && not (List.exists check_acl (Connection.get_owners connection)) ++ ++(* check if the current connection has the requested perm on the current node. ++* Raises an exception if it doesn't. *) ++let check connection request node = ++ if lacks connection request node + then raise Define.Permission_denied + ++(* check if the current connection has the requested perm on the current node *) ++let has connection request node = not (lacks connection request node) ++ + let equiv perm1 perm2 = + (Node.to_string perm1) = (Node.to_string perm2) +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index e528d1ecb2..f99b9e935c 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -56,15 +56,17 @@ let split_one_path data con = + | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) + | _ -> raise Invalid_Cmd_Args + +-let process_watch ops cons = ++let process_watch t cons = ++ let oldroot = t.Transaction.oldroot in ++ let newroot = Store.get_root t.store in ++ let ops = Transaction.get_paths t |> List.rev in + let do_op_watch op cons = +- let recurse = match (fst op) with +- | Xenbus.Xb.Op.Write -> false +- | Xenbus.Xb.Op.Mkdir -> false +- | Xenbus.Xb.Op.Rm -> true +- | Xenbus.Xb.Op.Setperms -> false ++ let recurse, oldroot, root = match (fst op) with ++ | Xenbus.Xb.Op.Write|Xenbus.Xb.Op.Mkdir -> false, None, newroot ++ | Xenbus.Xb.Op.Rm -> true, None, oldroot ++ | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot + | _ -> raise (Failure "huh ?") in +- Connections.fire_watches cons (snd op) recurse in ++ Connections.fire_watches ?oldroot root cons (snd op) recurse in + List.iter (fun op -> do_op_watch op cons) ops + + let create_implicit_path t perm path = +@@ -205,7 +207,7 @@ let reply_ack fct con t doms cons data = + fct con t doms cons data; + Packet.Ack (fun () -> + if Transaction.get_id t = Transaction.none then +- process_watch (Transaction.get_paths t) cons ++ process_watch t cons + ) + + let reply_data fct con t doms cons data = +@@ -353,14 +355,17 @@ let transaction_replay c t doms cons = + ignore @@ Connection.end_transaction c tid None + ) + +-let do_watch con _t _domains cons data = ++let do_watch con t _domains cons data = + let (node, token) = + match (split None '\000' data) with + | [node; token; ""] -> node, token + | _ -> raise Invalid_Cmd_Args + in + let watch = Connections.add_watch cons con node token in +- Packet.Ack (fun () -> Connection.fire_single_watch watch) ++ Packet.Ack (fun () -> ++ (* xenstore.txt says this watch is fired immediately, ++ implying even if path doesn't exist or is unreadable *) ++ Connection.fire_single_watch_unchecked watch) + + let do_unwatch con _t _domains cons data = + let (node, token) = +@@ -391,7 +396,7 @@ let do_transaction_end con t domains cons data = + if not success then + raise Transaction_again; + if commit then begin +- process_watch (List.rev (Transaction.get_paths t)) cons; ++ process_watch t cons; + match t.Transaction.ty with + | Transaction.No -> + () (* no need to record anything *) +@@ -399,7 +404,7 @@ let do_transaction_end con t domains cons data = + record_commit ~con ~tid:id ~before:oldstore ~after:cstore + end + +-let do_introduce con _t domains cons data = ++let do_introduce con t domains cons data = + if not (Connection.is_dom0 con) + then raise Define.Permission_denied; + let (domid, mfn, port) = +@@ -420,14 +425,14 @@ let do_introduce con _t domains cons data = + else try + let ndom = Domains.create domains domid mfn port in + Connections.add_domain cons ndom; +- Connections.fire_spec_watches cons Store.Path.introduce_domain; ++ Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.introduce_domain; + ndom + with _ -> raise Invalid_Cmd_Args + in + if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn then + raise Domain_not_match + +-let do_release con _t domains cons data = ++let do_release con t domains cons data = + if not (Connection.is_dom0 con) + then raise Define.Permission_denied; + let domid = +@@ -439,7 +444,7 @@ let do_release con _t domains cons data = + Domains.del domains domid; + Connections.del_domain cons domid; + if fire_spec_watches +- then Connections.fire_spec_watches cons Store.Path.release_domain ++ then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain + else raise Invalid_Cmd_Args + + let do_resume con _t domains _cons data = +@@ -507,6 +512,8 @@ let maybe_ignore_transaction = function + Transaction.none + | _ -> fun x -> x + ++ ++let () = Printexc.record_backtrace true + (** + * Nothrow guarantee. + *) +@@ -548,7 +555,8 @@ let process_packet ~store ~cons ~doms ~con ~req = + (* Put the response on the wire *) + send_response ty con t rid response + with exn -> +- error "process packet: %s" (Printexc.to_string exn); ++ let bt = Printexc.get_backtrace () in ++ error "process packet: %s. %s" (Printexc.to_string exn) bt; + Connection.send_error con tid rid "EIO" + + let do_input store cons doms con = +diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml +index 963734a653..25bc8c3b4a 100644 +--- a/tools/ocaml/xenstored/transaction.ml ++++ b/tools/ocaml/xenstored/transaction.ml +@@ -82,6 +82,7 @@ type t = { + start_count: int64; + store: Store.t; (* This is the store that we change in write operations. *) + quota: Quota.t; ++ oldroot: Store.Node.t; + mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; + mutable operations: (Packet.request * Packet.response) list; + mutable read_lowpath: Store.Path.t option; +@@ -123,6 +124,7 @@ let make ?(internal=false) id store = + start_count = !counter; + store = if id = none then store else Store.copy store; + quota = Quota.copy store.Store.quota; ++ oldroot = Store.get_root store; + paths = []; + operations = []; + read_lowpath = None; +@@ -137,6 +139,8 @@ let make ?(internal=false) id store = + let get_store t = t.store + let get_paths t = t.paths + ++let get_root t = Store.get_root t.store ++ + let is_read_only t = t.paths = [] + let add_wop t ty path = t.paths <- (ty, path) :: t.paths + let add_operation ~perm t request response = +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 8d0c50bfa4..f7b88065bb 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -337,7 +337,9 @@ let _ = + let (notify, deaddom) = Domains.cleanup domains in + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then +- Connections.fire_spec_watches cons Store.Path.release_domain ++ Connections.fire_spec_watches ++ (Store.get_root store) ++ cons Store.Path.release_domain + ) + else + let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch new file mode 100644 index 0000000..d1fa8b2 --- /dev/null +++ b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch @@ -0,0 +1,84 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: add xenstored.conf flag to turn off watch + permission checks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are flags to turn off quotas and the permission system, so add one +that turns off the newly introduced watch permission checks as well. + +This is part of XSA-115. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml +index 644a448f2e..fa0d3c4d92 100644 +--- a/tools/ocaml/xenstored/connection.ml ++++ b/tools/ocaml/xenstored/connection.ml +@@ -218,7 +218,7 @@ let fire_single_watch_unchecked watch = + let fire_single_watch (oldroot, root) watch = + let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in + let perms = lookup_watch_perms oldroot root abspath in +- if List.exists (Perms.has watch.con.perm READ) perms then ++ if Perms.can_fire_watch watch.con.perm perms then + fire_single_watch_unchecked watch + else + let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in +diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in +index 151b65b72d..f843482981 100644 +--- a/tools/ocaml/xenstored/oxenstored.conf.in ++++ b/tools/ocaml/xenstored/oxenstored.conf.in +@@ -44,6 +44,16 @@ conflict-rate-limit-is-aggregate = true + # Activate node permission system + perms-activate = true + ++# Activate the watch permission system ++# When this is enabled unprivileged guests can only get watch events ++# for xenstore entries that they would've been able to read. ++# ++# When this is disabled unprivileged guests may get watch events ++# for xenstore entries that they cannot read. The watch event contains ++# only the entry name, not the value. ++# This restores behaviour prior to XSA-115. ++perms-watch-activate = true ++ + # Activate quota + quota-activate = true + quota-maxentity = 1000 +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index 23b80aba3d..ee7fee6bda 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -20,6 +20,7 @@ let info fmt = Logging.info "perms" fmt + open Stdext + + let activate = ref true ++let watch_activate = ref true + + type permty = READ | WRITE | RDWR | NONE + +@@ -168,5 +169,9 @@ let check connection request node = + (* check if the current connection has the requested perm on the current node *) + let has connection request node = not (lacks connection request node) + ++let can_fire_watch connection perms = ++ not !watch_activate ++ || List.exists (has connection READ) perms ++ + let equiv perm1 perm2 = + (Node.to_string perm1) = (Node.to_string perm2) +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index f7b88065bb..0d355bbcb8 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -95,6 +95,7 @@ let parse_config filename = + ("conflict-max-history-seconds", Config.Set_float Define.conflict_max_history_seconds); + ("conflict-rate-limit-is-aggregate", Config.Set_bool Define.conflict_rate_limit_is_aggregate); + ("perms-activate", Config.Set_bool Perms.activate); ++ ("perms-watch-activate", Config.Set_bool Perms.watch_activate); + ("quota-activate", Config.Set_bool Quota.activate); + ("quota-maxwatch", Config.Set_int Define.maxwatch); + ("quota-transaction", Config.Set_int Define.maxtransaction); diff --git a/xsa322-4.14-c.patch b/xsa322-4.14-c.patch new file mode 100644 index 0000000..5059f24 --- /dev/null +++ b/xsa322-4.14-c.patch @@ -0,0 +1,532 @@ +From: Juergen Gross +Subject: tools/xenstore: revoke access rights for removed domains + +Access rights of Xenstore nodes are per domid. Unfortunately existing +granted access rights are not removed when a domain is being destroyed. +This means that a new domain created with the same domid will inherit +the access rights to Xenstore nodes from the previous domain(s) with +the same domid. + +This can be avoided by adding a generation counter to each domain. +The generation counter of the domain is set to the global generation +counter when a domain structure is being allocated. When reading or +writing a node all permissions of domains which are younger than the +node itself are dropped. This is done by flagging the related entry +as invalid in order to avoid modifying permissions in a way the user +could detect. + +A special case has to be considered: for a new domain the first +Xenstore entries are already written before the domain is officially +introduced in Xenstore. In order not to drop the permissions for the +new domain a domain struct is allocated even before introduction if +the hypervisor is aware of the domain. This requires adding another +bool "introduced" to struct domain in xenstored. In order to avoid +additional padding holes convert the shutdown flag to bool, too. + +As verifying permissions has its price regarding runtime add a new +quota for limiting the number of permissions an unprivileged domain +can set for a node. The default for that new quota is 5. + +This is part of XSA-322. + +Signed-off-by: Juergen Gross +Reviewed-by: Paul Durrant +Acked-by: Julien Grall + +diff --git a/tools/xenstore/include/xenstore_lib.h b/tools/xenstore/include/xenstore_lib.h +index 0ffbae9eb5..4c9b6d1685 100644 +--- a/tools/xenstore/include/xenstore_lib.h ++++ b/tools/xenstore/include/xenstore_lib.h +@@ -34,6 +34,7 @@ enum xs_perm_type { + /* Internal use. */ + XS_PERM_ENOENT_OK = 4, + XS_PERM_OWNER = 8, ++ XS_PERM_IGNORE = 16, + }; + + struct xs_permissions +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 92bfd54cff..505560a5de 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -104,6 +104,7 @@ int quota_nb_entry_per_domain = 1000; + int quota_nb_watch_per_domain = 128; + int quota_max_entry_size = 2048; /* 2K */ + int quota_max_transaction = 10; ++int quota_nb_perms_per_node = 5; + + void trace(const char *fmt, ...) + { +@@ -409,8 +410,13 @@ struct node *read_node(struct connection *conn, const void *ctx, + + /* Permissions are struct xs_permissions. */ + node->perms.p = hdr->perms; ++ if (domain_adjust_node_perms(node)) { ++ talloc_free(node); ++ return NULL; ++ } ++ + /* Data is binary blob (usually ascii, no nul). */ +- node->data = node->perms.p + node->perms.num; ++ node->data = node->perms.p + hdr->num_perms; + /* Children is strings, nul separated. */ + node->children = node->data + node->datalen; + +@@ -426,6 +432,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, + void *p; + struct xs_tdb_record_hdr *hdr; + ++ if (domain_adjust_node_perms(node)) ++ return errno; ++ + data.dsize = sizeof(*hdr) + + node->perms.num * sizeof(node->perms.p[0]) + + node->datalen + node->childlen; +@@ -485,8 +494,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, + return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; + + for (i = 1; i < perms->num; i++) +- if (perms->p[i].id == conn->id +- || (conn->target && perms->p[i].id == conn->target->id)) ++ if (!(perms->p[i].perms & XS_PERM_IGNORE) && ++ (perms->p[i].id == conn->id || ++ (conn->target && perms->p[i].id == conn->target->id))) + return perms->p[i].perms & mask; + + return perms->p[0].perms & mask; +@@ -1248,8 +1258,12 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) + if (perms.num < 2) + return EINVAL; + +- permstr = in->buffer + strlen(in->buffer) + 1; + perms.num--; ++ if (domain_is_unprivileged(conn) && ++ perms.num > quota_nb_perms_per_node) ++ return ENOSPC; ++ ++ permstr = in->buffer + strlen(in->buffer) + 1; + + perms.p = talloc_array(in, struct xs_permissions, perms.num); + if (!perms.p) +@@ -1904,6 +1918,7 @@ static void usage(void) + " -S, --entry-size limit the size of entry per domain, and\n" + " -W, --watch-nb limit the number of watches per domain,\n" + " -t, --transaction limit the number of transaction allowed per domain,\n" ++" -A, --perm-nb limit the number of permissions per node,\n" + " -R, --no-recovery to request that no recovery should be attempted when\n" + " the store is corrupted (debug only),\n" + " -I, --internal-db store database in memory, not on disk\n" +@@ -1924,6 +1939,7 @@ static struct option options[] = { + { "entry-size", 1, NULL, 'S' }, + { "trace-file", 1, NULL, 'T' }, + { "transaction", 1, NULL, 't' }, ++ { "perm-nb", 1, NULL, 'A' }, + { "no-recovery", 0, NULL, 'R' }, + { "internal-db", 0, NULL, 'I' }, + { "verbose", 0, NULL, 'V' }, +@@ -1946,7 +1962,7 @@ int main(int argc, char *argv[]) + int timeout; + + +- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:T:RVW:", options, ++ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:T:RVW:", options, + NULL)) != -1) { + switch (opt) { + case 'D': +@@ -1988,6 +2004,9 @@ int main(int argc, char *argv[]) + case 'W': + quota_nb_watch_per_domain = strtol(optarg, NULL, 10); + break; ++ case 'A': ++ quota_nb_perms_per_node = strtol(optarg, NULL, 10); ++ break; + case 'e': + dom0_event = strtol(optarg, NULL, 10); + break; +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index 9fad470f83..dc635e9be3 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -67,8 +67,14 @@ struct domain + /* The connection associated with this. */ + struct connection *conn; + ++ /* Generation count at domain introduction time. */ ++ uint64_t generation; ++ + /* Have we noticed that this domain is shutdown? */ +- int shutdown; ++ bool shutdown; ++ ++ /* Has domain been officially introduced? */ ++ bool introduced; + + /* number of entry from this domain in the store */ + int nbentry; +@@ -188,6 +194,9 @@ static int destroy_domain(void *_domain) + + list_del(&domain->list); + ++ if (!domain->introduced) ++ return 0; ++ + if (domain->port) { + if (xenevtchn_unbind(xce_handle, domain->port) == -1) + eprintf("> Unbinding port %i failed!\n", domain->port); +@@ -209,21 +218,34 @@ static int destroy_domain(void *_domain) + return 0; + } + ++static bool get_domain_info(unsigned int domid, xc_dominfo_t *dominfo) ++{ ++ return xc_domain_getinfo(*xc_handle, domid, 1, dominfo) == 1 && ++ dominfo->domid == domid; ++} ++ + static void domain_cleanup(void) + { + xc_dominfo_t dominfo; + struct domain *domain; + struct connection *conn; + int notify = 0; ++ bool dom_valid; + + again: + list_for_each_entry(domain, &domains, list) { +- if (xc_domain_getinfo(*xc_handle, domain->domid, 1, +- &dominfo) == 1 && +- dominfo.domid == domain->domid) { ++ dom_valid = get_domain_info(domain->domid, &dominfo); ++ if (!domain->introduced) { ++ if (!dom_valid) { ++ talloc_free(domain); ++ goto again; ++ } ++ continue; ++ } ++ if (dom_valid) { + if ((dominfo.crashed || dominfo.shutdown) + && !domain->shutdown) { +- domain->shutdown = 1; ++ domain->shutdown = true; + notify = 1; + } + if (!dominfo.dying) +@@ -289,58 +311,84 @@ static char *talloc_domain_path(void *context, unsigned int domid) + return talloc_asprintf(context, "/local/domain/%u", domid); + } + +-static struct domain *new_domain(void *context, unsigned int domid, +- int port) ++static struct domain *find_domain_struct(unsigned int domid) ++{ ++ struct domain *i; ++ ++ list_for_each_entry(i, &domains, list) { ++ if (i->domid == domid) ++ return i; ++ } ++ return NULL; ++} ++ ++static struct domain *alloc_domain(void *context, unsigned int domid) + { + struct domain *domain; +- int rc; + + domain = talloc(context, struct domain); +- if (!domain) ++ if (!domain) { ++ errno = ENOMEM; + return NULL; ++ } + +- domain->port = 0; +- domain->shutdown = 0; + domain->domid = domid; +- domain->path = talloc_domain_path(domain, domid); +- if (!domain->path) +- return NULL; ++ domain->generation = generation; ++ domain->introduced = false; + +- wrl_domain_new(domain); ++ talloc_set_destructor(domain, destroy_domain); + + list_add(&domain->list, &domains); +- talloc_set_destructor(domain, destroy_domain); ++ ++ return domain; ++} ++ ++static int new_domain(struct domain *domain, int port) ++{ ++ int rc; ++ ++ domain->port = 0; ++ domain->shutdown = false; ++ domain->path = talloc_domain_path(domain, domain->domid); ++ if (!domain->path) { ++ errno = ENOMEM; ++ return errno; ++ } ++ ++ wrl_domain_new(domain); + + /* Tell kernel we're interested in this event. */ +- rc = xenevtchn_bind_interdomain(xce_handle, domid, port); ++ rc = xenevtchn_bind_interdomain(xce_handle, domain->domid, port); + if (rc == -1) +- return NULL; ++ return errno; + domain->port = rc; + ++ domain->introduced = true; ++ + domain->conn = new_connection(writechn, readchn); +- if (!domain->conn) +- return NULL; ++ if (!domain->conn) { ++ errno = ENOMEM; ++ return errno; ++ } + + domain->conn->domain = domain; +- domain->conn->id = domid; ++ domain->conn->id = domain->domid; + + domain->remote_port = port; + domain->nbentry = 0; + domain->nbwatch = 0; + +- return domain; ++ return 0; + } + + + static struct domain *find_domain_by_domid(unsigned int domid) + { +- struct domain *i; ++ struct domain *d; + +- list_for_each_entry(i, &domains, list) { +- if (i->domid == domid) +- return i; +- } +- return NULL; ++ d = find_domain_struct(domid); ++ ++ return (d && d->introduced) ? d : NULL; + } + + static void domain_conn_reset(struct domain *domain) +@@ -386,15 +434,21 @@ int do_introduce(struct connection *conn, struct buffered_data *in) + if (port <= 0) + return EINVAL; + +- domain = find_domain_by_domid(domid); ++ domain = find_domain_struct(domid); + + if (domain == NULL) { ++ /* Hang domain off "in" until we're finished. */ ++ domain = alloc_domain(in, domid); ++ if (domain == NULL) ++ return ENOMEM; ++ } ++ ++ if (!domain->introduced) { + interface = map_interface(domid); + if (!interface) + return errno; + /* Hang domain off "in" until we're finished. */ +- domain = new_domain(in, domid, port); +- if (!domain) { ++ if (new_domain(domain, port)) { + rc = errno; + unmap_interface(interface); + return rc; +@@ -503,8 +557,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) + if (IS_ERR(domain)) + return -PTR_ERR(domain); + +- domain->shutdown = 0; +- ++ domain->shutdown = false; ++ + send_ack(conn, XS_RESUME); + + return 0; +@@ -647,8 +701,10 @@ static int dom0_init(void) + if (port == -1) + return -1; + +- dom0 = new_domain(NULL, xenbus_master_domid(), port); +- if (dom0 == NULL) ++ dom0 = alloc_domain(NULL, xenbus_master_domid()); ++ if (!dom0) ++ return -1; ++ if (new_domain(dom0, port)) + return -1; + + dom0->interface = xenbus_map(); +@@ -729,6 +785,66 @@ void domain_entry_inc(struct connection *conn, struct node *node) + } + } + ++/* ++ * Check whether a domain was created before or after a specific generation ++ * count (used for testing whether a node permission is older than a domain). ++ * ++ * Return values: ++ * -1: error ++ * 0: domain has higher generation count (it is younger than a node with the ++ * given count), or domain isn't existing any longer ++ * 1: domain is older than the node ++ */ ++static int chk_domain_generation(unsigned int domid, uint64_t gen) ++{ ++ struct domain *d; ++ xc_dominfo_t dominfo; ++ ++ if (!xc_handle && domid == 0) ++ return 1; ++ ++ d = find_domain_struct(domid); ++ if (d) ++ return (d->generation <= gen) ? 1 : 0; ++ ++ if (!get_domain_info(domid, &dominfo)) ++ return 0; ++ ++ d = alloc_domain(NULL, domid); ++ return d ? 1 : -1; ++} ++ ++/* ++ * Remove permissions for no longer existing domains in order to avoid a new ++ * domain with the same domid inheriting the permissions. ++ */ ++int domain_adjust_node_perms(struct node *node) ++{ ++ unsigned int i; ++ int ret; ++ ++ ret = chk_domain_generation(node->perms.p[0].id, node->generation); ++ if (ret < 0) ++ return errno; ++ ++ /* If the owner doesn't exist any longer give it to priv domain. */ ++ if (!ret) ++ node->perms.p[0].id = priv_domid; ++ ++ for (i = 1; i < node->perms.num; i++) { ++ if (node->perms.p[i].perms & XS_PERM_IGNORE) ++ continue; ++ ret = chk_domain_generation(node->perms.p[i].id, ++ node->generation); ++ if (ret < 0) ++ return errno; ++ if (!ret) ++ node->perms.p[i].perms |= XS_PERM_IGNORE; ++ } ++ ++ return 0; ++} ++ + void domain_entry_dec(struct connection *conn, struct node *node) + { + struct domain *d; +diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h +index 259183962a..5e00087206 100644 +--- a/tools/xenstore/xenstored_domain.h ++++ b/tools/xenstore/xenstored_domain.h +@@ -56,6 +56,9 @@ bool domain_can_write(struct connection *conn); + + bool domain_is_unprivileged(struct connection *conn); + ++/* Remove node permissions for no longer existing domains. */ ++int domain_adjust_node_perms(struct node *node); ++ + /* Quota manipulation */ + void domain_entry_inc(struct connection *conn, struct node *); + void domain_entry_dec(struct connection *conn, struct node *); +diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c +index a7d8c5d475..2881f3b2e4 100644 +--- a/tools/xenstore/xenstored_transaction.c ++++ b/tools/xenstore/xenstored_transaction.c +@@ -47,7 +47,12 @@ + * transaction. + * Each time the global generation count is copied to either a node or a + * transaction it is incremented. This ensures all nodes and/or transactions +- * are having a unique generation count. ++ * are having a unique generation count. The increment is done _before_ the ++ * copy as that is needed for checking whether a domain was created before ++ * or after a node has been written (the domain's generation is set with the ++ * actual generation count without incrementing it, in order to support ++ * writing a node for a domain before the domain has been officially ++ * introduced). + * + * Transaction conflicts are detected by checking the generation count of all + * nodes read in the transaction to match with the generation count in the +@@ -161,7 +166,7 @@ struct transaction + }; + + extern int quota_max_transaction; +-static uint64_t generation; ++uint64_t generation; + + static void set_tdb_key(const char *name, TDB_DATA *key) + { +@@ -237,7 +242,7 @@ int access_node(struct connection *conn, struct node *node, + bool introduce = false; + + if (type != NODE_ACCESS_READ) { +- node->generation = generation++; ++ node->generation = ++generation; + if (conn && !conn->transaction) + wrl_apply_debit_direct(conn); + } +@@ -374,7 +379,7 @@ static int finalize_transaction(struct connection *conn, + if (!data.dptr) + goto err; + hdr = (void *)data.dptr; +- hdr->generation = generation++; ++ hdr->generation = ++generation; + ret = tdb_store(tdb_ctx, key, data, + TDB_REPLACE); + talloc_free(data.dptr); +@@ -462,7 +467,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) + INIT_LIST_HEAD(&trans->accessed); + INIT_LIST_HEAD(&trans->changed_domains); + trans->fail = false; +- trans->generation = generation++; ++ trans->generation = ++generation; + + /* Pick an unused transaction identifier. */ + do { +diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h +index 3386bac565..43a162bea3 100644 +--- a/tools/xenstore/xenstored_transaction.h ++++ b/tools/xenstore/xenstored_transaction.h +@@ -27,6 +27,8 @@ enum node_access_type { + + struct transaction; + ++extern uint64_t generation; ++ + int do_transaction_start(struct connection *conn, struct buffered_data *node); + int do_transaction_end(struct connection *conn, struct buffered_data *in); + +diff --git a/tools/xenstore/xs_lib.c b/tools/xenstore/xs_lib.c +index 3e43f8809d..d407d5713a 100644 +--- a/tools/xenstore/xs_lib.c ++++ b/tools/xenstore/xs_lib.c +@@ -152,7 +152,7 @@ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, + bool xs_perm_to_string(const struct xs_permissions *perm, + char *buffer, size_t buf_len) + { +- switch ((int)perm->perms) { ++ switch ((int)perm->perms & ~XS_PERM_IGNORE) { + case XS_PERM_WRITE: + *buffer = 'w'; + break; diff --git a/xsa322-o.patch b/xsa322-o.patch new file mode 100644 index 0000000..75f7c20 --- /dev/null +++ b/xsa322-o.patch @@ -0,0 +1,110 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: clean up permissions for dead domains +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +domain ids are prone to wrapping (15-bits), and with sufficient number +of VMs in a reboot loop it is possible to trigger it. Xenstore entries +may linger after a domain dies, until a toolstack cleans it up. During +this time there is a window where a wrapped domid could access these +xenstore keys (that belonged to another VM). + +To prevent this do a cleanup when a domain dies: + * walk the entire xenstore tree and update permissions for all nodes + * if the dead domain had an ACL entry: remove it + * if the dead domain was the owner: change the owner to Dom0 + +This is done without quota checks or a transaction. Quota checks would +be a no-op (either the domain is dead, or it is Dom0 where they are not +enforced). Transactions are not needed, because this is all done +atomically by oxenstored's single thread. + +The xenstore entries owned by the dead domain are not deleted, because +that could confuse a toolstack / backends that are still bound to it +(or generate unexpected watch events). It is the responsibility of a +toolstack to remove the xenstore entries themselves. + +This is part of XSA-322. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml +index ee7fee6bda..e8a16221f8 100644 +--- a/tools/ocaml/xenstored/perms.ml ++++ b/tools/ocaml/xenstored/perms.ml +@@ -58,6 +58,15 @@ let get_other perms = perms.other + let get_acl perms = perms.acl + let get_owner perm = perm.owner + ++(** [remote_domid ~domid perm] removes all ACLs for [domid] from perm. ++* If [domid] was the owner then it is changed to Dom0. ++* This is used for cleaning up after dead domains. ++* *) ++let remove_domid ~domid perm = ++ let acl = List.filter (fun (acl_domid, _) -> acl_domid <> domid) perm.acl in ++ let owner = if perm.owner = domid then 0 else perm.owner in ++ { perm with acl; owner } ++ + let default0 = create 0 NONE [] + + let perm_of_string s = +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index f99b9e935c..73e04cc18b 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -443,6 +443,7 @@ let do_release con t domains cons data = + let fire_spec_watches = Domains.exist domains domid in + Domains.del domains domid; + Connections.del_domain cons domid; ++ Store.reset_permissions (Transaction.get_store t) domid; + if fire_spec_watches + then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain + else raise Invalid_Cmd_Args +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 6b6e440e98..3b05128f1b 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -89,6 +89,13 @@ let check_owner node connection = + + let rec recurse fct node = fct node; List.iter (recurse fct) node.children + ++(** [recurse_map f tree] applies [f] on each node in the tree recursively *) ++let recurse_map f = ++ let rec walk node = ++ f { node with children = List.rev_map walk node.children |> List.rev } ++ in ++ walk ++ + let unpack node = (Symbol.to_string node.name, node.perms, node.value) + + end +@@ -405,6 +412,15 @@ let setperms store perm path nperms = + Quota.del_entry store.quota old_owner; + Quota.add_entry store.quota new_owner + ++let reset_permissions store domid = ++ Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; ++ store.root <- Node.recurse_map (fun node -> ++ let perms = Perms.Node.remove_domid ~domid node.perms in ++ if perms <> node.perms then ++ Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); ++ { node with perms } ++ ) store.root ++ + type ops = { + store: t; + write: Path.t -> string -> unit; +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index 0d355bbcb8..ff9fbbbac2 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -336,6 +336,7 @@ let _ = + finally (fun () -> + if Some port = eventchn.Event.virq_port then ( + let (notify, deaddom) = Domains.cleanup domains in ++ List.iter (Store.reset_permissions store) deaddom; + List.iter (Connections.del_domain cons) deaddom; + if deaddom <> [] || notify then + Connections.fire_spec_watches diff --git a/xsa323.patch b/xsa323.patch new file mode 100644 index 0000000..aadf5c7 --- /dev/null +++ b/xsa323.patch @@ -0,0 +1,140 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: Fix path length validation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Currently, oxenstored checks the length of paths against 1024, then +prepends "/local/domain/$DOMID/" to relative paths. This allows a domU +to create paths which can't subsequently be read by anyone, even dom0. +This also interferes with listing directories, etc. + +Define a new oxenstored.conf entry: quota-path-max, defaulting to 1024 +as before. For paths that begin with "/local/domain/$DOMID/" check the +relative path length against this quota. For all other paths check the +entire path length. + +This ensures that if the domid changes (and thus the length of a prefix +changes) a path that used to be valid stays valid (e.g. after a +live-migration). It also ensures that regardless how the client tries +to access a path (domid-relative or absolute) it will get consistent +results, since the limit is always applied on the final canonicalized +path. + +Delete the unused Domain.get_path to avoid it being confused with +Connection.get_path (which differs by a trailing slash only). + +Rewrite Util.path_validate to apply the appropriate length restriction +based on whether the path is relative or not. Remove the check for +connection_path being absolute, because it is not guest controlled data. + +This is part of XSA-323. + +Signed-off-by: Andrew Cooper +Signed-off-by: Edwin Török +Acked-by: Christian Lindig + +diff --git a/tools/ocaml/libs/xb/partial.ml b/tools/ocaml/libs/xb/partial.ml +index d4d1c7bdec..b6e2a716e2 100644 +--- a/tools/ocaml/libs/xb/partial.ml ++++ b/tools/ocaml/libs/xb/partial.ml +@@ -28,6 +28,7 @@ external header_of_string_internal: string -> int * int * int * int + = "stub_header_of_string" + + let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) ++let xenstore_rel_path_max = 2048 (* xen/include/public/io/xs_wire.h *) + + let of_string s = + let tid, rid, opint, dlen = header_of_string_internal s in +diff --git a/tools/ocaml/libs/xb/partial.mli b/tools/ocaml/libs/xb/partial.mli +index 359a75e88d..b9216018f5 100644 +--- a/tools/ocaml/libs/xb/partial.mli ++++ b/tools/ocaml/libs/xb/partial.mli +@@ -9,6 +9,7 @@ external header_size : unit -> int = "stub_header_size" + external header_of_string_internal : string -> int * int * int * int + = "stub_header_of_string" + val xenstore_payload_max : int ++val xenstore_rel_path_max : int + val of_string : string -> pkt + val append : pkt -> string -> int -> unit + val to_complete : pkt -> int +diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml +index ea9e1b7620..ebe18b8e31 100644 +--- a/tools/ocaml/xenstored/define.ml ++++ b/tools/ocaml/xenstored/define.ml +@@ -31,6 +31,8 @@ let conflict_rate_limit_is_aggregate = ref true + + let domid_self = 0x7FF0 + ++let path_max = ref Xenbus.Partial.xenstore_rel_path_max ++ + exception Not_a_directory of string + exception Not_a_value of string + exception Already_exist +diff --git a/tools/ocaml/xenstored/domain.ml b/tools/ocaml/xenstored/domain.ml +index aeb185ff7e..81cb59b8f1 100644 +--- a/tools/ocaml/xenstored/domain.ml ++++ b/tools/ocaml/xenstored/domain.ml +@@ -38,7 +38,6 @@ type t = + } + + let is_dom0 d = d.id = 0 +-let get_path dom = "/local/domain/" ^ (sprintf "%u" dom.id) + let get_id domain = domain.id + let get_interface d = d.interface + let get_mfn d = d.mfn +diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in +index f843482981..4ae48e42d4 100644 +--- a/tools/ocaml/xenstored/oxenstored.conf.in ++++ b/tools/ocaml/xenstored/oxenstored.conf.in +@@ -61,6 +61,7 @@ quota-maxsize = 2048 + quota-maxwatch = 100 + quota-transaction = 10 + quota-maxrequests = 1024 ++quota-path-max = 1024 + + # Activate filed base backend + persistent = false +diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml +index e8c9fe4e94..eb79bf0146 100644 +--- a/tools/ocaml/xenstored/utils.ml ++++ b/tools/ocaml/xenstored/utils.ml +@@ -93,7 +93,7 @@ let read_file_single_integer filename = + let path_validate path connection_path = + let len = String.length path in + +- if len = 0 || len > 1024 then raise Define.Invalid_path; ++ if len = 0 then raise Define.Invalid_path; + + let abs_path = + match String.get path 0 with +@@ -101,4 +101,17 @@ let path_validate path connection_path = + | _ -> connection_path ^ path + in + ++ (* Regardless whether client specified absolute or relative path, ++ canonicalize it (above) and, for domain-relative paths, check the ++ length of the relative part. ++ ++ This prevents paths becoming invalid across migrate when the length ++ of the domid changes in @param connection_path. ++ *) ++ let len = String.length abs_path in ++ let on_absolute _ _ = len in ++ let on_relative _ offset = len - offset in ++ let len = Scanf.ksscanf abs_path on_absolute "/local/domain/%d/%n" on_relative in ++ if len > !Define.path_max then raise Define.Invalid_path; ++ + abs_path +diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml +index ff9fbbbac2..39d6d767e4 100644 +--- a/tools/ocaml/xenstored/xenstored.ml ++++ b/tools/ocaml/xenstored/xenstored.ml +@@ -102,6 +102,7 @@ let parse_config filename = + ("quota-maxentity", Config.Set_int Quota.maxent); + ("quota-maxsize", Config.Set_int Quota.maxsize); + ("quota-maxrequests", Config.Set_int Define.maxrequests); ++ ("quota-path-max", Config.Set_int Define.path_max); + ("test-eagain", Config.Set_bool Transaction.test_eagain); + ("persistent", Config.Set_bool Disk.enable); + ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); diff --git a/xsa324.patch b/xsa324.patch new file mode 100644 index 0000000..c5e542d --- /dev/null +++ b/xsa324.patch @@ -0,0 +1,48 @@ +From: Juergen Gross +Subject: tools/xenstore: drop watch event messages exceeding maximum size + +By setting a watch with a very large tag it is possible to trick +xenstored to send watch event messages exceeding the maximum allowed +payload size. This might in turn lead to a crash of xenstored as the +resulting error can cause dereferencing a NULL pointer in case there +is no active request being handled by the guest the watch event is +being sent to. + +Fix that by just dropping such watch events. Additionally modify the +error handling to test the pointer to be not NULL before dereferencing +it. + +This is XSA-324. + +Signed-off-by: Juergen Gross +Acked-by: Julien Grall + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index 33f95dcf3c..3d74dbbb40 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -674,6 +674,9 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, + /* Replies reuse the request buffer, events need a new one. */ + if (type != XS_WATCH_EVENT) { + bdata = conn->in; ++ /* Drop asynchronous responses, e.g. errors for watch events. */ ++ if (!bdata) ++ return; + bdata->inhdr = true; + bdata->used = 0; + conn->in = NULL; +diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c +index 71c108ea99..9ff20690c0 100644 +--- a/tools/xenstore/xenstored_watch.c ++++ b/tools/xenstore/xenstored_watch.c +@@ -92,6 +92,10 @@ static void add_event(struct connection *conn, + } + + len = strlen(name) + 1 + strlen(watch->token) + 1; ++ /* Don't try to send over-long events. */ ++ if (len > XENSTORE_PAYLOAD_MAX) ++ return; ++ + data = talloc_array(ctx, char, len); + if (!data) + return; diff --git a/xsa325-4.14.patch b/xsa325-4.14.patch new file mode 100644 index 0000000..a17f546 --- /dev/null +++ b/xsa325-4.14.patch @@ -0,0 +1,192 @@ +From: Harsha Shamsundara Havanur +Subject: tools/xenstore: Preserve bad client until they are destroyed + +XenStored will kill any connection that it thinks has misbehaved, +this is currently happening in two places: + * In `handle_input()` if the sanity check on the ring and the message + fails. + * In `handle_output()` when failing to write the response in the ring. + +As the domain structure is a child of the connection, XenStored will +destroy its view of the domain when killing the connection. This will +result in sending @releaseDomain event to all the watchers. + +As the watch event doesn't carry which domain has been released, +the watcher (such as XenStored) will generally go through the list of +domains registers and check if one of them is shutting down/dying. +In the case of a client misbehaving, the domain will likely to be +running, so no action will be performed. + +When the domain is effectively destroyed, XenStored will not be aware of +the domain anymore. So the watch event is not going to be sent. +By consequence, the watchers of the event will not release mappings +they may have on the domain. This will result in a zombie domain. + +In order to send @releaseDomain event at the correct time, we want +to keep the domain structure until the domain is effectively +shutting-down/dying. + +We also want to keep the connection around so we could possibly revive +the connection in the future. + +A new flag 'is_ignored' is added to mark whether a connection should be +ignored when checking if there are work to do. Additionally any +transactions, watches, buffers associated to the connection will be +freed as you can't do much with them (restarting the connection will +likely need a reset). + +As a side note, when the device model were running in a stubdomain, a +guest would have been able to introduce a use-after-free because there +is two parents for a guest connection. + +This is XSA-325. + +Reported-by: Pawel Wieczorkiewicz +Signed-off-by: Harsha Shamsundara Havanur +Signed-off-by: Julien Grall +Reviewed-by: Juergen Gross +Reviewed-by: Paul Durrant + +diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c +index af3d17004b3f..27d8f15b6b76 100644 +--- a/tools/xenstore/xenstored_core.c ++++ b/tools/xenstore/xenstored_core.c +@@ -1355,6 +1355,32 @@ static struct { + [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, + }; + ++/* ++ * Keep the connection alive but stop processing any new request or sending ++ * reponse. This is to allow sending @releaseDomain watch event at the correct ++ * moment and/or to allow the connection to restart (not yet implemented). ++ * ++ * All watches, transactions, buffers will be freed. ++ */ ++static void ignore_connection(struct connection *conn) ++{ ++ struct buffered_data *out, *tmp; ++ ++ trace("CONN %p ignored\n", conn); ++ ++ conn->is_ignored = true; ++ conn_delete_all_watches(conn); ++ conn_delete_all_transactions(conn); ++ ++ list_for_each_entry_safe(out, tmp, &conn->out_list, list) { ++ list_del(&out->list); ++ talloc_free(out); ++ } ++ ++ talloc_free(conn->in); ++ conn->in = NULL; ++} ++ + static const char *sockmsg_string(enum xsd_sockmsg_type type) + { + if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) +@@ -1413,8 +1439,10 @@ static void consider_message(struct connection *conn) + assert(conn->in == NULL); + } + +-/* Errors in reading or allocating here mean we get out of sync, so we +- * drop the whole client connection. */ ++/* ++ * Errors in reading or allocating here means we get out of sync, so we mark ++ * the connection as ignored. ++ */ + static void handle_input(struct connection *conn) + { + int bytes; +@@ -1471,14 +1499,14 @@ static void handle_input(struct connection *conn) + return; + + bad_client: +- /* Kill it. */ +- talloc_free(conn); ++ ignore_connection(conn); + } + + static void handle_output(struct connection *conn) + { ++ /* Ignore the connection if an error occured */ + if (!write_messages(conn)) +- talloc_free(conn); ++ ignore_connection(conn); + } + + struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) +@@ -1494,6 +1522,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) + new->write = write; + new->read = read; + new->can_write = true; ++ new->is_ignored = false; + new->transaction_started = 0; + INIT_LIST_HEAD(&new->out_list); + INIT_LIST_HEAD(&new->watches); +@@ -2186,8 +2215,9 @@ int main(int argc, char *argv[]) + if (fds[conn->pollfd_idx].revents + & ~(POLLIN|POLLOUT)) + talloc_free(conn); +- else if (fds[conn->pollfd_idx].revents +- & POLLIN) ++ else if ((fds[conn->pollfd_idx].revents ++ & POLLIN) && ++ !conn->is_ignored) + handle_input(conn); + } + if (talloc_free(conn) == 0) +@@ -2199,8 +2229,9 @@ int main(int argc, char *argv[]) + if (fds[conn->pollfd_idx].revents + & ~(POLLIN|POLLOUT)) + talloc_free(conn); +- else if (fds[conn->pollfd_idx].revents +- & POLLOUT) ++ else if ((fds[conn->pollfd_idx].revents ++ & POLLOUT) && ++ !conn->is_ignored) + handle_output(conn); + } + if (talloc_free(conn) == 0) +diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h +index eb19b71f5f46..196a6fd2b0be 100644 +--- a/tools/xenstore/xenstored_core.h ++++ b/tools/xenstore/xenstored_core.h +@@ -80,6 +80,9 @@ struct connection + /* Is this a read-only connection? */ + bool can_write; + ++ /* Is this connection ignored? */ ++ bool is_ignored; ++ + /* Buffered incoming data. */ + struct buffered_data *in; + +diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c +index dc635e9be30c..d5e1e3e9d42d 100644 +--- a/tools/xenstore/xenstored_domain.c ++++ b/tools/xenstore/xenstored_domain.c +@@ -286,6 +286,10 @@ bool domain_can_read(struct connection *conn) + + if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) + return false; ++ ++ if (conn->is_ignored) ++ return false; ++ + return (intf->req_cons != intf->req_prod); + } + +@@ -303,6 +307,10 @@ bool domain_is_unprivileged(struct connection *conn) + bool domain_can_write(struct connection *conn) + { + struct xenstore_domain_interface *intf = conn->domain->interface; ++ ++ if (conn->is_ignored) ++ return false; ++ + return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE); + } + +-- +2.17.1 + diff --git a/xsa330.patch b/xsa330.patch new file mode 100644 index 0000000..c834516 --- /dev/null +++ b/xsa330.patch @@ -0,0 +1,66 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: delete watch from trie too when resetting + watches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +c/s f8c72b526129 "oxenstored: implement XS_RESET_WATCHES" from Xen 4.6 +introduced reset watches support in oxenstored by mirroring the change +in cxenstored. + +However the OCaml version has some additional data structures to +optimize watch firing, and just resetting the watches in one of the data +structures creates a security bug where a malicious guest kernel can +exceed its watch quota, driving oxenstored into OOM: + * create watches + * reset watches (this still keeps the watches lingering in another data + structure, using memory) + * create some more watches + * loop until oxenstored dies + +The guest kernel doesn't necessarily have to be malicious to trigger +this: + * if control/platform-feature-xs_reset_watches is set + * the guest kexecs (e.g. because it crashes) + * on boot more watches are set up + * this will slowly "leak" memory for watches in oxenstored, driving it + towards OOM. + +This is XSA-330. + +Fixes: f8c72b526129 ("oxenstored: implement XS_RESET_WATCHES") +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml +index 9f9f7ee2f0..6ee3552ec2 100644 +--- a/tools/ocaml/xenstored/connections.ml ++++ b/tools/ocaml/xenstored/connections.ml +@@ -134,6 +134,10 @@ let del_watch cons con path token = + cons.watches <- Trie.set cons.watches key watches; + watch + ++let del_watches cons con = ++ Connection.del_watches con; ++ cons.watches <- Trie.map (del_watches_of_con con) cons.watches ++ + (* path is absolute *) + let fire_watches ?oldroot root cons path recurse = + let key = key_of_path path in +diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml +index 73e04cc18b..437d2dcf9e 100644 +--- a/tools/ocaml/xenstored/process.ml ++++ b/tools/ocaml/xenstored/process.ml +@@ -179,8 +179,8 @@ let do_isintroduced con _t domains _cons data = + if domid = Define.domid_self || Domains.exist domains domid then "T\000" else "F\000" + + (* only in xen >= 4.2 *) +-let do_reset_watches con _t _domains _cons _data = +- Connection.del_watches con; ++let do_reset_watches con _t _domains cons _data = ++ Connections.del_watches cons con; + Connection.del_transactions con + + (* only in >= xen3.3 *) diff --git a/xsa348-4.13-1.patch b/xsa348-4.13-1.patch new file mode 100644 index 0000000..bfc9962 --- /dev/null +++ b/xsa348-4.13-1.patch @@ -0,0 +1,106 @@ +From: Jan Beulich +Subject: x86: replace reset_stack_and_jump_nolp() + +Move the necessary check into check_for_livepatch_work(), rather than +mostly duplicating reset_stack_and_jump() for this purpose. This is to +prevent an inflation of reset_stack_and_jump() flavors. + +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross + +--- sle15sp2.orig/xen/arch/x86/domain.c 2020-10-30 17:22:39.000000000 +0100 ++++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:51:10.894525721 +0100 +@@ -192,7 +192,7 @@ static void noreturn continue_idle_domai + { + /* Idle vcpus might be attached to non-idle units! */ + if ( !is_idle_domain(v->sched_unit->domain) ) +- reset_stack_and_jump_nolp(guest_idle_loop); ++ reset_stack_and_jump(guest_idle_loop); + + reset_stack_and_jump(idle_loop); + } +--- sle15sp2.orig/xen/arch/x86/hvm/svm/svm.c 2020-10-30 17:22:39.000000000 +0100 ++++ sle15sp2/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:51:10.898525723 +0100 +@@ -1032,7 +1032,7 @@ static void noreturn svm_do_resume(struc + + hvm_do_resume(v); + +- reset_stack_and_jump_nolp(svm_asm_do_resume); ++ reset_stack_and_jump(svm_asm_do_resume); + } + + void svm_vmenter_helper(const struct cpu_user_regs *regs) +--- sle15sp2.orig/xen/arch/x86/hvm/vmx/vmcs.c 2020-05-18 18:53:09.000000000 +0200 ++++ sle15sp2/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:51:10.898525723 +0100 +@@ -1889,7 +1889,7 @@ void vmx_do_resume(struct vcpu *v) + if ( host_cr4 != read_cr4() ) + __vmwrite(HOST_CR4, read_cr4()); + +- reset_stack_and_jump_nolp(vmx_asm_do_vmentry); ++ reset_stack_and_jump(vmx_asm_do_vmentry); + } + + static inline unsigned long vmr(unsigned long field) +--- sle15sp2.orig/xen/arch/x86/pv/domain.c 2020-10-30 17:22:39.000000000 +0100 ++++ sle15sp2/xen/arch/x86/pv/domain.c 2020-11-10 17:51:10.898525723 +0100 +@@ -61,7 +61,7 @@ custom_runtime_param("pcid", parse_pcid) + static void noreturn continue_nonidle_domain(struct vcpu *v) + { + check_wakeup_from_wait(); +- reset_stack_and_jump_nolp(ret_from_intr); ++ reset_stack_and_jump(ret_from_intr); + } + + static int setup_compat_l4(struct vcpu *v) +--- sle15sp2.orig/xen/arch/x86/setup.c 2020-05-18 18:53:09.000000000 +0200 ++++ sle15sp2/xen/arch/x86/setup.c 2020-11-10 17:51:10.898525723 +0100 +@@ -631,7 +631,7 @@ static void __init noreturn reinit_bsp_s + stack_base[0] = stack; + memguard_guard_stack(stack); + +- reset_stack_and_jump_nolp(init_done); ++ reset_stack_and_jump(init_done); + } + + /* +--- sle15sp2.orig/xen/common/livepatch.c 2020-05-18 18:53:09.000000000 +0200 ++++ sle15sp2/xen/common/livepatch.c 2020-11-10 17:51:10.898525723 +0100 +@@ -1300,6 +1300,11 @@ void check_for_livepatch_work(void) + s_time_t timeout; + unsigned long flags; + ++ /* Only do any work when invoked in truly idle state. */ ++ if ( system_state != SYS_STATE_active || ++ !is_idle_domain(current->sched_unit->domain) ) ++ return; ++ + /* Fast path: no work to do. */ + if ( !per_cpu(work_to_do, cpu ) ) + return; +--- sle15sp2.orig/xen/include/asm-x86/current.h 2019-12-18 16:18:59.000000000 +0100 ++++ sle15sp2/xen/include/asm-x86/current.h 2020-11-10 17:51:10.902525725 +0100 +@@ -129,22 +129,16 @@ unsigned long get_stack_dump_bottom (uns + # define CHECK_FOR_LIVEPATCH_WORK "" + #endif + +-#define switch_stack_and_jump(fn, instr) \ ++#define reset_stack_and_jump(fn) \ + ({ \ + __asm__ __volatile__ ( \ + "mov %0,%%"__OP"sp;" \ +- instr \ ++ CHECK_FOR_LIVEPATCH_WORK \ + "jmp %c1" \ + : : "r" (guest_cpu_user_regs()), "i" (fn) : "memory" ); \ + unreachable(); \ + }) + +-#define reset_stack_and_jump(fn) \ +- switch_stack_and_jump(fn, CHECK_FOR_LIVEPATCH_WORK) +- +-#define reset_stack_and_jump_nolp(fn) \ +- switch_stack_and_jump(fn, "") +- + /* + * Which VCPU's state is currently running on each CPU? + * This is not necesasrily the same as 'current' as a CPU may be diff --git a/xsa348-4.13-2.patch b/xsa348-4.13-2.patch new file mode 100644 index 0000000..ce6cf48 --- /dev/null +++ b/xsa348-4.13-2.patch @@ -0,0 +1,85 @@ +From: Jan Beulich +Subject: x86: fold guest_idle_loop() into idle_loop() + +The latter can easily be made cover both cases. This is in preparation +of using idle_loop directly for populating idle_csw.tail. + +Take the liberty and also adjust indentation / spacing in involved code. + +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross + +--- sle15sp2.orig/xen/arch/x86/domain.c 2020-11-10 17:51:10.894525721 +0100 ++++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:51:46.354546349 +0100 +@@ -133,14 +133,22 @@ void play_dead(void) + static void idle_loop(void) + { + unsigned int cpu = smp_processor_id(); ++ /* ++ * Idle vcpus might be attached to non-idle units! We don't do any ++ * standard idle work like tasklets or livepatching in this case. ++ */ ++ bool guest = !is_idle_domain(current->sched_unit->domain); + + for ( ; ; ) + { + if ( cpu_is_offline(cpu) ) ++ { ++ ASSERT(!guest); + play_dead(); ++ } + + /* Are we here for running vcpu context tasklets, or for idling? */ +- if ( unlikely(tasklet_work_to_do(cpu)) ) ++ if ( !guest && unlikely(tasklet_work_to_do(cpu)) ) + { + do_tasklet(); + /* Livepatch work is always kicked off via a tasklet. */ +@@ -151,28 +159,14 @@ static void idle_loop(void) + * and then, after it is done, whether softirqs became pending + * while we were scrubbing. + */ +- else if ( !softirq_pending(cpu) && !scrub_free_pages() && +- !softirq_pending(cpu) ) +- pm_idle(); +- do_softirq(); +- } +-} +- +-/* +- * Idle loop for siblings in active schedule units. +- * We don't do any standard idle work like tasklets or livepatching. +- */ +-static void guest_idle_loop(void) +-{ +- unsigned int cpu = smp_processor_id(); +- +- for ( ; ; ) +- { +- ASSERT(!cpu_is_offline(cpu)); +- +- if ( !softirq_pending(cpu) && !scrub_free_pages() && +- !softirq_pending(cpu)) +- sched_guest_idle(pm_idle, cpu); ++ else if ( !softirq_pending(cpu) && !scrub_free_pages() && ++ !softirq_pending(cpu) ) ++ { ++ if ( guest ) ++ sched_guest_idle(pm_idle, cpu); ++ else ++ pm_idle(); ++ } + do_softirq(); + } + } +@@ -190,10 +184,6 @@ void startup_cpu_idle_loop(void) + + static void noreturn continue_idle_domain(struct vcpu *v) + { +- /* Idle vcpus might be attached to non-idle units! */ +- if ( !is_idle_domain(v->sched_unit->domain) ) +- reset_stack_and_jump(guest_idle_loop); +- + reset_stack_and_jump(idle_loop); + } + diff --git a/xsa348-4.13-3.patch b/xsa348-4.13-3.patch new file mode 100644 index 0000000..abd1b22 --- /dev/null +++ b/xsa348-4.13-3.patch @@ -0,0 +1,163 @@ +From: Jan Beulich +Subject: x86: avoid calling {svm,vmx}_do_resume() + +These functions follow the following path: hvm_do_resume() -> +handle_hvm_io_completion() -> hvm_wait_for_io() -> +wait_on_xen_event_channel() -> do_softirq() -> schedule() -> +sched_context_switch() -> continue_running() and hence may +recursively invoke themselves. If this ends up happening a couple of +times, a stack overflow would result. + +Prevent this by also resetting the stack at the +->arch.ctxt_switch->tail() invocations (in both places for consistency) +and thus jumping to the functions instead of calling them. + +This is XSA-348 / CVE-2020-29566. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Juergen Gross + +--- sle15sp2.orig/xen/arch/x86/domain.c 2020-11-10 17:51:46.354546349 +0100 ++++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:56:58.758730088 +0100 +@@ -130,7 +130,7 @@ void play_dead(void) + dead_idle(); + } + +-static void idle_loop(void) ++static void noreturn idle_loop(void) + { + unsigned int cpu = smp_processor_id(); + /* +@@ -182,11 +182,6 @@ void startup_cpu_idle_loop(void) + reset_stack_and_jump(idle_loop); + } + +-static void noreturn continue_idle_domain(struct vcpu *v) +-{ +- reset_stack_and_jump(idle_loop); +-} +- + void init_hypercall_page(struct domain *d, void *ptr) + { + memset(ptr, 0xcc, PAGE_SIZE); +@@ -535,7 +530,7 @@ int arch_domain_create(struct domain *d, + static const struct arch_csw idle_csw = { + .from = paravirt_ctxt_switch_from, + .to = paravirt_ctxt_switch_to, +- .tail = continue_idle_domain, ++ .tail = idle_loop, + }; + + d->arch.ctxt_switch = &idle_csw; +@@ -1833,20 +1828,12 @@ void context_switch(struct vcpu *prev, s + /* Ensure that the vcpu has an up-to-date time base. */ + update_vcpu_system_time(next); + +- /* +- * Schedule tail *should* be a terminal function pointer, but leave a +- * bug frame around just in case it returns, to save going back into the +- * context switching code and leaving a far more subtle crash to diagnose. +- */ +- nextd->arch.ctxt_switch->tail(next); +- BUG(); ++ reset_stack_and_jump_ind(nextd->arch.ctxt_switch->tail); + } + + void continue_running(struct vcpu *same) + { +- /* See the comment above. */ +- same->domain->arch.ctxt_switch->tail(same); +- BUG(); ++ reset_stack_and_jump_ind(same->domain->arch.ctxt_switch->tail); + } + + int __sync_local_execstate(void) +--- sle15sp2.orig/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:51:10.898525723 +0100 ++++ sle15sp2/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:56:58.762730090 +0100 +@@ -987,8 +987,9 @@ static void svm_ctxt_switch_to(struct vc + wrmsr_tsc_aux(v->arch.msrs->tsc_aux); + } + +-static void noreturn svm_do_resume(struct vcpu *v) ++static void noreturn svm_do_resume(void) + { ++ struct vcpu *v = current; + struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb; + bool debug_state = (v->domain->debugger_attached || + v->domain->arch.monitor.software_breakpoint_enabled || +--- sle15sp2.orig/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:51:10.898525723 +0100 ++++ sle15sp2/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:56:58.762730090 +0100 +@@ -1830,8 +1830,9 @@ void vmx_vmentry_failure(void) + domain_crash(curr->domain); + } + +-void vmx_do_resume(struct vcpu *v) ++void vmx_do_resume(void) + { ++ struct vcpu *v = current; + bool_t debug_state; + unsigned long host_cr4; + +--- sle15sp2.orig/xen/arch/x86/pv/domain.c 2020-11-10 17:51:10.898525723 +0100 ++++ sle15sp2/xen/arch/x86/pv/domain.c 2020-11-10 17:56:58.762730090 +0100 +@@ -58,7 +58,7 @@ static int parse_pcid(const char *s) + } + custom_runtime_param("pcid", parse_pcid); + +-static void noreturn continue_nonidle_domain(struct vcpu *v) ++static void noreturn continue_nonidle_domain(void) + { + check_wakeup_from_wait(); + reset_stack_and_jump(ret_from_intr); +--- sle15sp2.orig/xen/include/asm-x86/current.h 2020-11-10 17:51:10.902525725 +0100 ++++ sle15sp2/xen/include/asm-x86/current.h 2020-11-10 17:56:58.762730090 +0100 +@@ -129,16 +129,23 @@ unsigned long get_stack_dump_bottom (uns + # define CHECK_FOR_LIVEPATCH_WORK "" + #endif + +-#define reset_stack_and_jump(fn) \ ++#define switch_stack_and_jump(fn, instr, constr) \ + ({ \ + __asm__ __volatile__ ( \ + "mov %0,%%"__OP"sp;" \ + CHECK_FOR_LIVEPATCH_WORK \ +- "jmp %c1" \ +- : : "r" (guest_cpu_user_regs()), "i" (fn) : "memory" ); \ ++ instr "1" \ ++ : : "r" (guest_cpu_user_regs()), constr (fn) : "memory" ); \ + unreachable(); \ + }) + ++#define reset_stack_and_jump(fn) \ ++ switch_stack_and_jump(fn, "jmp %c", "i") ++ ++/* The constraint may only specify non-call-clobbered registers. */ ++#define reset_stack_and_jump_ind(fn) \ ++ switch_stack_and_jump(fn, "INDIRECT_JMP %", "b") ++ + /* + * Which VCPU's state is currently running on each CPU? + * This is not necesasrily the same as 'current' as a CPU may be +--- sle15sp2.orig/xen/include/asm-x86/domain.h 2020-10-30 17:22:39.000000000 +0100 ++++ sle15sp2/xen/include/asm-x86/domain.h 2020-11-10 17:56:58.762730090 +0100 +@@ -313,7 +313,7 @@ struct arch_domain + const struct arch_csw { + void (*from)(struct vcpu *); + void (*to)(struct vcpu *); +- void (*tail)(struct vcpu *); ++ void noreturn (*tail)(void); + } *ctxt_switch; + + #ifdef CONFIG_HVM +--- sle15sp2.orig/xen/include/asm-x86/hvm/vmx/vmx.h 2019-12-18 16:18:59.000000000 +0100 ++++ sle15sp2/xen/include/asm-x86/hvm/vmx/vmx.h 2020-11-10 17:56:58.762730090 +0100 +@@ -95,7 +95,7 @@ typedef enum { + void vmx_asm_vmexit_handler(struct cpu_user_regs); + void vmx_asm_do_vmentry(void); + void vmx_intr_assist(void); +-void noreturn vmx_do_resume(struct vcpu *); ++void noreturn vmx_do_resume(void); + void vmx_vlapic_msr_changed(struct vcpu *v); + void vmx_realmode_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt); + void vmx_realmode(struct cpu_user_regs *regs); diff --git a/xsa352.patch b/xsa352.patch new file mode 100644 index 0000000..e21d21a --- /dev/null +++ b/xsa352.patch @@ -0,0 +1,42 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: only Dom0 can change node owner +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Otherwise we can give quota away to another domain, either causing it to run +out of quota, or in case of Dom0 use unbounded amounts of memory and bypass +the quota system entirely. + +This was fixed in the C version of xenstored in 2006 (c/s db34d2aaa5f5, +predating the XSA process by 5 years). + +It was also fixed in the mirage version of xenstore in 2012, with a unit test +demonstrating the vulnerability: + + https://github.com/mirage/ocaml-xenstore/commit/6b91f3ac46b885d0530a51d57a9b3a57d64923a7 + https://github.com/mirage/ocaml-xenstore/commit/22ee5417c90b8fda905c38de0d534506152eace6 + +but possibly without realising that the vulnerability still affected the +in-tree oxenstored (added c/s f44af660412 in 2010). + +This is XSA-352. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index 3b05128f1b..5f915f2bbe 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -407,7 +407,8 @@ let setperms store perm path nperms = + | Some node -> + let old_owner = Node.get_owner node in + let new_owner = Perms.Node.get_owner nperms in +- if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota new_owner 0; ++ if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then ++ raise Define.Permission_denied; + store.root <- path_setperms store perm path nperms; + Quota.del_entry store.quota old_owner; + Quota.add_entry store.quota new_owner diff --git a/xsa353.patch b/xsa353.patch new file mode 100644 index 0000000..764f93c --- /dev/null +++ b/xsa353.patch @@ -0,0 +1,89 @@ +From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= +Subject: tools/ocaml/xenstored: do permission checks on xenstore root +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This was lacking in a disappointing number of places. + +The xenstore root node is treated differently from all other nodes, because it +doesn't have a parent, and mutation requires changing the parent. + +Unfortunately this lead to open-coding the special case for root into every +single xenstore operation, and out of all the xenstore operations only read +did a permission check when handling the root node. + +This means that an unprivileged guest can: + + * xenstore-chmod / to its liking and subsequently write new arbitrary nodes + there (subject to quota) + * xenstore-rm -r / deletes almost the entire xenstore tree (xenopsd quickly + refills some, but you are left with a broken system) + * DIRECTORY on / lists all children when called through python + bindings (xenstore-ls stops at /local because it tries to list recursively) + * get-perms on / works too, but that is just a minor information leak + +Add the missing permission checks, but this should really be refactored to do +the root handling and permission checks on the node only once from a single +function, instead of getting it wrong nearly everywhere. + +This is XSA-353. + +Signed-off-by: Edwin Török +Acked-by: Christian Lindig +Reviewed-by: Andrew Cooper + +diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml +index f299ec6461..92b6289b5e 100644 +--- a/tools/ocaml/xenstored/store.ml ++++ b/tools/ocaml/xenstored/store.ml +@@ -273,15 +273,17 @@ let path_rm store perm path = + Node.del_childname node name + with Not_found -> + raise Define.Doesnt_exist in +- if path = [] then ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.WRITE; + Node.del_all_children store.root +- else ++ ) else + Path.apply_modify store.root path do_rm + + let path_setperms store perm path perms = +- if path = [] then ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.WRITE; + Node.set_perms store.root perms +- else ++ ) else + let do_setperms node name = + let c = Node.find node name in + Node.check_owner c perm; +@@ -313,9 +315,10 @@ let read store perm path = + + let ls store perm path = + let children = +- if path = [] then +- (Node.get_children store.root) +- else ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.READ; ++ Node.get_children store.root ++ ) else + let do_ls node name = + let cnode = Node.find node name in + Node.check_perm cnode perm Perms.READ; +@@ -324,9 +327,10 @@ let ls store perm path = + List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) + + let getperms store perm path = +- if path = [] then +- (Node.get_perms store.root) +- else ++ if path = [] then ( ++ Node.check_perm store.root perm Perms.READ; ++ Node.get_perms store.root ++ ) else + let fct n name = + let c = Node.find n name in + Node.check_perm c perm Perms.READ; diff --git a/xsa358-4.14.patch b/xsa358-4.14.patch new file mode 100644 index 0000000..0d56a8f --- /dev/null +++ b/xsa358-4.14.patch @@ -0,0 +1,54 @@ +From: Jan Beulich +Subject: evtchn/FIFO: re-order and synchronize (with) map_control_block() + +For evtchn_fifo_set_pending()'s check of the control block having been +set to be effective, ordering of respective reads and writes needs to be +ensured: The control block pointer needs to be recorded strictly after +the setting of all the queue heads, and it needs checking strictly +before any uses of them (this latter aspect was already guaranteed). + +This is XSA-358 / CVE-2020-29570. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Acked-by: Julien Grall + +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -249,6 +249,10 @@ static void evtchn_fifo_set_pending(stru + goto unlock; + } + ++ /* ++ * This also acts as the read counterpart of the smp_wmb() in ++ * map_control_block(). ++ */ + if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) + goto unlock; + +@@ -474,6 +478,7 @@ static int setup_control_block(struct vc + static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset) + { + void *virt; ++ struct evtchn_fifo_control_block *control_block; + unsigned int i; + int rc; + +@@ -484,10 +489,15 @@ static int map_control_block(struct vcpu + if ( rc < 0 ) + return rc; + +- v->evtchn_fifo->control_block = virt + offset; ++ control_block = virt + offset; + + for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ ) +- v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i]; ++ v->evtchn_fifo->queue[i].head = &control_block->head[i]; ++ ++ /* All queue heads must have been set before setting the control block. */ ++ smp_wmb(); ++ ++ v->evtchn_fifo->control_block = control_block; + + return 0; + } diff --git a/xsa359.patch b/xsa359.patch new file mode 100644 index 0000000..231810b --- /dev/null +++ b/xsa359.patch @@ -0,0 +1,40 @@ +From: Jan Beulich +Subject: evtchn/FIFO: add 2nd smp_rmb() to evtchn_fifo_word_from_port() + +Besides with add_page_to_event_array() the function also needs to +synchronize with evtchn_fifo_init_control() setting both d->evtchn_fifo +and (subsequently) d->evtchn_port_ops. + +This is XSA-359 / CVE-2020-29571. + +Reported-by: Julien Grall +Signed-off-by: Jan Beulich +Reviewed-by: Julien Grall + +--- a/xen/common/event_fifo.c ++++ b/xen/common/event_fifo.c +@@ -55,6 +55,13 @@ static inline event_word_t *evtchn_fifo_ + { + unsigned int p, w; + ++ /* ++ * Callers aren't required to hold d->event_lock, so we need to synchronize ++ * with evtchn_fifo_init_control() setting d->evtchn_port_ops /after/ ++ * d->evtchn_fifo. ++ */ ++ smp_rmb(); ++ + if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) + return NULL; + +@@ -606,6 +613,10 @@ int evtchn_fifo_init_control(struct evtc + if ( rc < 0 ) + goto error; + ++ /* ++ * This call, as a side effect, synchronizes with ++ * evtchn_fifo_word_from_port(). ++ */ + rc = map_control_block(v, gfn, offset); + if ( rc < 0 ) + goto error; From 37608f5d89228f25c60bc76e5654bac2a918f55b Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 21 Jan 2021 21:38:50 +0000 Subject: [PATCH 12/15] IRQ vector leak on x86 [XSA-360] --- xen.spec | 7 +++- xsa360-4.14.patch | 97 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 xsa360-4.14.patch diff --git a/xen.spec b/xen.spec index b516974..508c991 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 5%{?dist} +Release: 6%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -153,6 +153,7 @@ Patch95: xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch Patch96: xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch Patch97: xsa358-4.14.patch Patch98: xsa359.patch +Patch99: xsa360-4.14.patch %if %build_qemutrad @@ -397,6 +398,7 @@ manage Xen virtual machines. %patch96 -p1 %patch97 -p1 %patch98 -p1 +%patch99 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -990,6 +992,9 @@ fi %endif %changelog +* Thu Jan 21 2021 Michael Young - 4.13.2-6 +- IRQ vector leak on x86 [XSA-360] + * Wed Dec 16 2020 Michael Young - 4.13.2-5 - xenstore watch notifications lacking permission checks [XSA-115, CVE-2020-29480] (#1908091) diff --git a/xsa360-4.14.patch b/xsa360-4.14.patch new file mode 100644 index 0000000..1bc185b --- /dev/null +++ b/xsa360-4.14.patch @@ -0,0 +1,97 @@ +From: Roger Pau Monne +Subject: x86/dpci: do not remove pirqs from domain tree on unbind + +A fix for a previous issue removed the pirqs from the domain tree when +they are unbound in order to prevent shared pirqs from triggering a +BUG_ON in __pirq_guest_unbind if they are unbound multiple times. That +caused free_domain_pirqs to no longer unmap the pirqs because they +are gone from the domain pirq tree, thus leaving stale unbound pirqs +after domain destruction if the domain had mapped dpci pirqs after +shutdown. + +Take a different approach to fix the original issue, instead of +removing the pirq from d->pirq_tree clear the flags of the dpci pirq +struct to signal that the pirq is now unbound. This prevents calling +pirq_guest_unbind multiple times for the same pirq without having to +remove it from the domain pirq tree. + +This is XSA-360. + +Fixes: 5b58dad089 ('x86/pass-through: avoid double IRQ unbind during domain cleanup') +Signed-off-by: Roger Pau Monné +Reviewed-by: Jan Beulich + +--- a/xen/arch/x86/irq.c ++++ b/xen/arch/x86/irq.c +@@ -1331,7 +1331,7 @@ void (pirq_cleanup_check)(struct pirq *p + } + + if ( radix_tree_delete(&d->pirq_tree, pirq->pirq) != pirq ) +- BUG_ON(!d->is_dying); ++ BUG(); + } + + /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ +--- a/xen/drivers/passthrough/pci.c ++++ b/xen/drivers/passthrough/pci.c +@@ -862,6 +862,10 @@ static int pci_clean_dpci_irq(struct dom + { + struct dev_intx_gsi_link *digl, *tmp; + ++ if ( !pirq_dpci->flags ) ++ /* Already processed. */ ++ return 0; ++ + pirq_guest_unbind(d, dpci_pirq(pirq_dpci)); + + if ( pt_irq_need_timer(pirq_dpci->flags) ) +@@ -872,15 +876,10 @@ static int pci_clean_dpci_irq(struct dom + list_del(&digl->list); + xfree(digl); + } ++ /* Note the pirq is now unbound. */ ++ pirq_dpci->flags = 0; + +- radix_tree_delete(&d->pirq_tree, dpci_pirq(pirq_dpci)->pirq); +- +- if ( !pt_pirq_softirq_active(pirq_dpci) ) +- return 0; +- +- domain_get_irq_dpci(d)->pending_pirq_dpci = pirq_dpci; +- +- return -ERESTART; ++ return pt_pirq_softirq_active(pirq_dpci) ? -ERESTART : 0; + } + + static int pci_clean_dpci_irqs(struct domain *d) +@@ -897,18 +896,8 @@ static int pci_clean_dpci_irqs(struct do + hvm_irq_dpci = domain_get_irq_dpci(d); + if ( hvm_irq_dpci != NULL ) + { +- int ret = 0; +- +- if ( hvm_irq_dpci->pending_pirq_dpci ) +- { +- if ( pt_pirq_softirq_active(hvm_irq_dpci->pending_pirq_dpci) ) +- ret = -ERESTART; +- else +- hvm_irq_dpci->pending_pirq_dpci = NULL; +- } ++ int ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); + +- if ( !ret ) +- ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); + if ( ret ) + { + spin_unlock(&d->event_lock); +--- a/xen/include/asm-x86/hvm/irq.h ++++ b/xen/include/asm-x86/hvm/irq.h +@@ -160,8 +160,6 @@ struct hvm_irq_dpci { + DECLARE_BITMAP(isairq_map, NR_ISAIRQS); + /* Record of mapped Links */ + uint8_t link_cnt[NR_LINK]; +- /* Clean up: Entry with a softirq invocation pending / in progress. */ +- struct hvm_pirq_dpci *pending_pirq_dpci; + }; + + /* Machine IRQ to guest device/intx mapping. */ From c3863c816efabbb5b2dc109a57b29697000f1ed7 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Wed, 17 Feb 2021 22:17:51 +0000 Subject: [PATCH 13/15] Linux: display frontend "be-alloc" mode is unsupported (comment only) [XSA-363, CVE-2021-26934] (#1929549) arm: The cache may not be cleaned for newly allocated scrubbed pages [XSA-364, CVE-2021-26933] (#1929547) --- xen.spec | 12 ++++++++- xsa363.patch | 22 +++++++++++++++++ xsa364.patch | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 xsa363.patch create mode 100644 xsa364.patch diff --git a/xen.spec b/xen.spec index 508c991..8df8d3d 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 6%{?dist} +Release: 7%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -154,6 +154,8 @@ Patch96: xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch Patch97: xsa358-4.14.patch Patch98: xsa359.patch Patch99: xsa360-4.14.patch +Patch100: xsa363.patch +Patch101: xsa364.patch %if %build_qemutrad @@ -399,6 +401,8 @@ manage Xen virtual machines. %patch97 -p1 %patch98 -p1 %patch99 -p1 +%patch100 -p1 +%patch101 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -992,6 +996,12 @@ fi %endif %changelog +* Wed Feb 17 2021 Michael Young - 4.13.2-7 +- Linux: display frontend "be-alloc" mode is unsupported (comment only) + [XSA-363, CVE-2021-26934] (#1929549) +- arm: The cache may not be cleaned for newly allocated scrubbed pages + [XSA-364, CVE-2021-26933] (#1929547) + * Thu Jan 21 2021 Michael Young - 4.13.2-6 - IRQ vector leak on x86 [XSA-360] diff --git a/xsa363.patch b/xsa363.patch new file mode 100644 index 0000000..c8a3de3 --- /dev/null +++ b/xsa363.patch @@ -0,0 +1,22 @@ +From: Jan Beulich +Subject: SUPPORT.md: PV display frontend is unsupported in "backend allocation" mode + +This wasn't meant to be supported, but wasn't stated this way. + +This is XSA-363. + +Reported-by: Jan Belich +Signed-off-by: Jan Beulich + +--- a/SUPPORT.md ++++ b/SUPPORT.md +@@ -414,7 +414,8 @@ Guest-side driver capable of speaking th + + Guest-side driver capable of speaking the Xen PV display protocol + +- Status, Linux: Supported ++ Status, Linux: Supported (outside of "backend allocation" mode) ++ Status, Linux: Experimental (in "backend allocation" mode) + + ### PV Console (frontend) + diff --git a/xsa364.patch b/xsa364.patch new file mode 100644 index 0000000..2d4b057 --- /dev/null +++ b/xsa364.patch @@ -0,0 +1,69 @@ +From dadb5b4b21c904ce59024c686eb1c55be8f46c52 Mon Sep 17 00:00:00 2001 +From: Julien Grall +Date: Thu, 21 Jan 2021 10:16:08 +0000 +Subject: [PATCH] xen/page_alloc: Only flush the page to RAM once we know they + are scrubbed + +At the moment, each page are flushed to RAM just after the allocator +found some free pages. However, this is happening before check if the +page was scrubbed. + +As a consequence, on Arm, a guest may be able to access the old content +of the scrubbed pages if it has cache disabled (default at boot) and +the content didn't reach the Point of Coherency. + +The flush is now moved after we know the content of the page will not +change. This also has the benefit to reduce the amount of work happening +with the heap_lock held. + +This is XSA-364. + +Fixes: 307c3be3ccb2 ("mm: Don't scrub pages while holding heap lock in alloc_heap_pages()") +Signed-off-by: Julien Grall +Reviewed-by: Jan Beulich +--- + xen/common/page_alloc.c | 14 +++++++++----- + 1 file changed, 9 insertions(+), 5 deletions(-) + +diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c +index 02ac1fa613e7..1744e6faa5c4 100644 +--- a/xen/common/page_alloc.c ++++ b/xen/common/page_alloc.c +@@ -924,6 +924,7 @@ static struct page_info *alloc_heap_pages( + bool need_tlbflush = false; + uint32_t tlbflush_timestamp = 0; + unsigned int dirty_cnt = 0; ++ mfn_t mfn; + + /* Make sure there are enough bits in memflags for nodeID. */ + BUILD_BUG_ON((_MEMF_bits - _MEMF_node) < (8 * sizeof(nodeid_t))); +@@ -1022,11 +1023,6 @@ static struct page_info *alloc_heap_pages( + pg[i].u.inuse.type_info = 0; + page_set_owner(&pg[i], NULL); + +- /* Ensure cache and RAM are consistent for platforms where the +- * guest can control its own visibility of/through the cache. +- */ +- flush_page_to_ram(mfn_x(page_to_mfn(&pg[i])), +- !(memflags & MEMF_no_icache_flush)); + } + + spin_unlock(&heap_lock); +@@ -1062,6 +1058,14 @@ static struct page_info *alloc_heap_pages( + if ( need_tlbflush ) + filtered_flush_tlb_mask(tlbflush_timestamp); + ++ /* ++ * Ensure cache and RAM are consistent for platforms where the guest ++ * can control its own visibility of/through the cache. ++ */ ++ mfn = page_to_mfn(pg); ++ for ( i = 0; i < (1U << order); i++ ) ++ flush_page_to_ram(mfn_x(mfn) + i, !(memflags & MEMF_no_icache_flush)); ++ + return pg; + } + +-- +2.17.1 + From 7490b76e1994be34262038fd69eb6b9ba5821612 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Thu, 18 Mar 2021 21:32:03 +0000 Subject: [PATCH 14/15] HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) --- xen.spec | 7 ++- xsa368-4.13.patch | 112 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 xsa368-4.13.patch diff --git a/xen.spec b/xen.spec index 8df8d3d..64e1e84 100644 --- a/xen.spec +++ b/xen.spec @@ -58,7 +58,7 @@ Summary: Xen is a virtual machine monitor Name: xen Version: 4.13.2 -Release: 7%{?dist} +Release: 8%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -156,6 +156,7 @@ Patch98: xsa359.patch Patch99: xsa360-4.14.patch Patch100: xsa363.patch Patch101: xsa364.patch +Patch102: xsa368-4.13.patch %if %build_qemutrad @@ -403,6 +404,7 @@ manage Xen virtual machines. %patch99 -p1 %patch100 -p1 %patch101 -p1 +%patch102 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -996,6 +998,9 @@ fi %endif %changelog +* Thu Mar 18 2021 Michael Young - 4.13.2-8 +- HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) + * Wed Feb 17 2021 Michael Young - 4.13.2-7 - Linux: display frontend "be-alloc" mode is unsupported (comment only) [XSA-363, CVE-2021-26934] (#1929549) diff --git a/xsa368-4.13.patch b/xsa368-4.13.patch new file mode 100644 index 0000000..7c1d162 --- /dev/null +++ b/xsa368-4.13.patch @@ -0,0 +1,112 @@ +From a733fcca97d4e0d7503198ba1dd739a5d7a00dac Mon Sep 17 00:00:00 2001 +From: Anthony PERARD +Date: Wed, 24 Feb 2021 18:39:20 +0000 +Subject: [PATCH] libxl: Fix domain soft reset state handling + +In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used +without been properly initialised and disposed of. This lead do a +abort() in libxl due to the `dsps.qmp' state been used before been +initialised: + libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. + +Once initialised, `dsps' also needs to be disposed of as the `qmp' +state might still be in the `Connected' state in the callback for +libxl__domain_suspend_device_model(). So this patch adds +libxl__domain_suspend_dispose() which can be called from the two +places where we need to dispose of `dsps'. + +Reported-by: Olaf Hering +Signed-off-by: Anthony PERARD +Reviewed-by: Ian Jackson +Tested-by: Olaf Hering +--- + tools/libxl/libxl_create.c | 11 ++++++++--- + tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- + tools/libxl/libxl_internal.h | 2 ++ + 3 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c +index 32d45dcef0..651ad18d2d 100644 +--- a/tools/libxl/libxl_create.c ++++ b/tools/libxl/libxl_create.c +@@ -1974,9 +1974,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + state->console_tty = libxl__strdup(gc, console_tty); + + dss->ao = ao; +- dss->domid = dss->dsps.domid = domid_soft_reset; +- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", +- domid_soft_reset); ++ dss->domid = domid_soft_reset; + + rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, + &srs->toolstack_len); +@@ -1986,6 +1984,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, + } + + dss->dsps.ao = ao; ++ dss->dsps.domid = domid_soft_reset; ++ dss->dsps.live = false; ++ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); ++ if (rc) ++ goto out; + dss->dsps.callback_device_model_done = soft_reset_dm_suspended; + libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ + +@@ -2004,6 +2007,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, + CONTAINER_OF(dsps, *srs, dss.dsps); + libxl__app_domain_create_state *cdcs = &srs->cdcs; + ++ libxl__domain_suspend_dispose(gc, dsps); ++ + /* + * Ask all backends to disconnect by removing the domain from + * xenstore. On the creation path the domain will be introduced to +diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c +index 25d1571895..2a280f69a1 100644 +--- a/tools/libxl/libxl_dom_suspend.c ++++ b/tools/libxl/libxl_dom_suspend.c +@@ -67,6 +67,16 @@ out: + return rc; + } + ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps) ++{ ++ libxl__xswait_stop(gc, &dsps->pvcontrol); ++ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); ++ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); ++ libxl__ev_time_deregister(gc, &dsps->guest_timeout); ++ libxl__ev_qmp_dispose(gc, &dsps->qmp); ++} ++ + /*----- callbacks, called by xc_domain_save -----*/ + + void libxl__domain_suspend_device_model(libxl__egc *egc, +@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, + { + EGC_GC; + assert(!libxl__xswait_inuse(&dsps->pvcontrol)); +- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); +- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); +- libxl__ev_time_deregister(gc, &dsps->guest_timeout); +- libxl__ev_qmp_dispose(gc, &dsps->qmp); ++ libxl__domain_suspend_dispose(gc, dsps); + dsps->callback_common_done(egc, dsps, rc); + } + +diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h +index 247518a7ac..5b4795908b 100644 +--- a/tools/libxl/libxl_internal.h ++++ b/tools/libxl/libxl_internal.h +@@ -3569,6 +3569,8 @@ struct libxl__domain_suspend_state { + int libxl__domain_suspend_init(libxl__egc *egc, + libxl__domain_suspend_state *dsps, + libxl_domain_type type); ++void libxl__domain_suspend_dispose(libxl__gc *gc, ++ libxl__domain_suspend_state *dsps); + + /* calls dsps->callback_device_model_done when done + * may synchronously calls this callback */ +-- +2.30.1 + From f72515dd8bd2ed8ecfb5ad7397f49e0af3f25b72 Mon Sep 17 00:00:00 2001 From: Michael Young Date: Mon, 29 Mar 2021 22:30:31 +0100 Subject: [PATCH 15/15] update to xen-4.13.3 --- .gitignore | 2 +- sources | 2 +- xen.canonicalize.patch | 10 +- xen.gcc10.fixes.patch | 65 -- ...729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch | 240 ------- ...52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch | 592 ------------------ ...b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch | 97 --- xen.hypervisor.config | 2 + xen.ocaml.4.10.patch | 26 +- xen.spec | 93 +-- ...llow-removing-child-of-a-node-exceed.patch | 157 ----- ...e-ignore-transaction-id-for-un-watch.patch | 86 --- ...ix-node-accounting-after-failed-node.patch | 104 --- ...simplify-and-rename-check_event_node.patch | 55 -- ...heck-privilege-for-XS_IS_DOMAIN_INTR.patch | 115 ---- ...6-tools-xenstore-rework-node-removal.patch | 217 ------- ...ire-watches-only-when-removing-a-spe.patch | 118 ---- ...store-introduce-node_perms-structure.patch | 289 --------- ...llow-special-watches-for-privileged-.patch | 237 ------- ...void-watch-events-for-nodes-without-.patch | 375 ----------- ...tored-ignore-transaction-id-for-un-w.patch | 43 -- ...tored-check-privilege-for-XS_IS_DOMA.patch | 30 - ...s-ocaml-xenstored-unify-watch-firing.patch | 29 - ...tored-introduce-permissions-for-spec.patch | 117 ---- ...tored-avoid-watch-events-for-nodes-w.patch | 406 ------------ ...tored-add-xenstored.conf-flag-to-tur.patch | 84 --- xsa322-4.14-c.patch | 532 ---------------- xsa322-o.patch | 110 ---- xsa323.patch | 140 ----- xsa324.patch | 48 -- xsa325-4.14.patch | 192 ------ xsa330.patch | 66 -- xsa335-qemu.patch | 84 --- xsa335-trad.patch | 45 -- xsa348-4.13-1.patch | 106 ---- xsa348-4.13-2.patch | 85 --- xsa348-4.13-3.patch | 163 ----- xsa351-arm.patch | 58 -- xsa351-x86-4.13-1.patch | 155 ----- xsa351-x86-4.13-2.patch | 128 ---- xsa352.patch | 42 -- xsa353.patch | 89 --- xsa355.patch | 23 - xsa358-4.14.patch | 54 -- xsa359.patch | 40 -- xsa360-4.14.patch | 97 --- xsa364.patch | 69 -- xsa368-4.13.patch | 112 ---- 48 files changed, 19 insertions(+), 6010 deletions(-) delete mode 100644 xen.gcc10.fixes.patch delete mode 100644 xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch delete mode 100644 xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch delete mode 100644 xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch delete mode 100644 xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch delete mode 100644 xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch delete mode 100644 xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch delete mode 100644 xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch delete mode 100644 xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch delete mode 100644 xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch delete mode 100644 xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch delete mode 100644 xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch delete mode 100644 xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch delete mode 100644 xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch delete mode 100644 xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch delete mode 100644 xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch delete mode 100644 xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch delete mode 100644 xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch delete mode 100644 xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch delete mode 100644 xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch delete mode 100644 xsa322-4.14-c.patch delete mode 100644 xsa322-o.patch delete mode 100644 xsa323.patch delete mode 100644 xsa324.patch delete mode 100644 xsa325-4.14.patch delete mode 100644 xsa330.patch delete mode 100644 xsa335-qemu.patch delete mode 100644 xsa335-trad.patch delete mode 100644 xsa348-4.13-1.patch delete mode 100644 xsa348-4.13-2.patch delete mode 100644 xsa348-4.13-3.patch delete mode 100644 xsa351-arm.patch delete mode 100644 xsa351-x86-4.13-1.patch delete mode 100644 xsa351-x86-4.13-2.patch delete mode 100644 xsa352.patch delete mode 100644 xsa353.patch delete mode 100644 xsa355.patch delete mode 100644 xsa358-4.14.patch delete mode 100644 xsa359.patch delete mode 100644 xsa360-4.14.patch delete mode 100644 xsa364.patch delete mode 100644 xsa368-4.13.patch diff --git a/.gitignore b/.gitignore index 21b852b..d414712 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ lwip-1.3.0.tar.gz pciutils-2.2.9.tar.bz2 zlib-1.2.3.tar.gz polarssl-1.1.4-gpl.tgz -/xen-4.13.2.tar.gz +/xen-4.13.3.tar.gz diff --git a/sources b/sources index ea02e58..cc9d5b9 100644 --- a/sources +++ b/sources @@ -4,4 +4,4 @@ SHA512 (newlib-1.16.0.tar.gz) = 40eb96bbc6736a16b6399e0cdb73e853d0d90b685c967e77 SHA512 (zlib-1.2.3.tar.gz) = 021b958fcd0d346c4ba761bcf0cc40f3522de6186cf5a0a6ea34a70504ce9622b1c2626fce40675bc8282cf5f5ade18473656abc38050f72f5d6480507a2106e SHA512 (polarssl-1.1.4-gpl.tgz) = 88da614e4d3f4409c4fd3bb3e44c7587ba051e3fed4e33d526069a67e8180212e1ea22da984656f50e290049f60ddca65383e5983c0f8884f648d71f698303ad SHA512 (pciutils-2.2.9.tar.bz2) = 2b3d98d027e46d8c08037366dde6f0781ca03c610ef2b380984639e4ef39899ed8d8b8e4cd9c9dc54df101279b95879bd66bfd4d04ad07fef41e847ea7ae32b5 -SHA512 (xen-4.13.2.tar.gz) = cd3092281c97e9421e303aa288aac04dcccd5536ba7c0ff4d51fbf3d07b5ffacfe3456ba06f5cf63577dafbf8cf3a5d9825ceb5e9ef8ca1427900cc3e57b50a3 +SHA512 (xen-4.13.3.tar.gz) = 622127d824b9c49b57282a887fb404e0bad05ff60bccade82e4e0e9b5ad975ff9aa1fba83392e6d8379e9a15340e8ae9785c0913eb11027816e4600432eea6b6 diff --git a/xen.canonicalize.patch b/xen.canonicalize.patch index 43ccf02..0130355 100644 --- a/xen.canonicalize.patch +++ b/xen.canonicalize.patch @@ -38,17 +38,17 @@ return get_node(conn, ctx, *canonical_name, perm); } ---- xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.h.orig 2017-04-12 16:18:57.000000000 +0100 -+++ xen-4.9.0-rc1.2/tools/xenstore/xenstored_core.h 2017-04-13 21:20:29.146368478 +0100 -@@ -148,7 +148,7 @@ +--- xen-4.13.3/tools/xenstore/xenstored_core.h.orig 2021-03-22 16:57:42.000000000 +0000 ++++ xen-4.13.3/tools/xenstore/xenstored_core.h 2021-03-29 19:56:36.642394283 +0100 +@@ -153,7 +153,7 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); /* Canonicalize this path if possible. */ -char *canonicalize(struct connection *conn, const void *ctx, const char *node); +char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); - /* Write a node to the tdb data base. */ - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); + /* Get access permissions. */ + enum xs_perm_type perm_for_conn(struct connection *conn, --- xen-4.8.0/tools/console/testsuite/console-dom0.c.orig 2016-12-05 12:03:27.000000000 +0000 +++ xen-4.8.0/tools/console/testsuite/console-dom0.c 2017-02-26 21:52:24.554678631 +0000 @@ -18,7 +18,7 @@ diff --git a/xen.gcc10.fixes.patch b/xen.gcc10.fixes.patch deleted file mode 100644 index 3159bd7..0000000 --- a/xen.gcc10.fixes.patch +++ /dev/null @@ -1,65 +0,0 @@ ---- xen-4.13.0/tools/xenstore/utils.h.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/xenstore/utils.h 2020-01-21 21:13:05.108957447 +0000 -@@ -24,7 +24,7 @@ - void barf(const char *fmt, ...) __attribute__((noreturn)); - void barf_perror(const char *fmt, ...) __attribute__((noreturn)); - --void (*xprintf)(const char *fmt, ...); -+extern void (*xprintf)(const char *fmt, ...); - - #define eprintf(_fmt, _args...) xprintf("[ERR] %s" _fmt, __FUNCTION__, ##_args) - ---- xen-4.13.0/tools/xenstore/xenstored_core.h.orig 2020-01-21 21:15:19.243931307 +0000 -+++ xen-4.13.0/tools/xenstore/xenstored_core.h 2020-01-21 21:38:35.340617819 +0000 -@@ -204,7 +204,7 @@ - /* Open a pipe for signal handling */ - void init_pipe(int reopen_log_pipe[2]); - --xengnttab_handle **xgt_handle; -+extern xengnttab_handle **xgt_handle; - - int remember_string(struct hashtable *hash, const char *str); - ---- xen-4.13.0/tools/libxl/libxlu_pci.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/libxl/libxlu_pci.c 2020-01-21 21:56:26.812212916 +0000 -@@ -48,7 +48,7 @@ - int xlu_pci_parse_bdf(XLU_Config *cfg, libxl_device_pci *pcidev, const char *str) - { - unsigned state = STATE_DOMAIN; -- unsigned dom, bus, dev, func, vslot = 0; -+ unsigned dom = 0, bus = 0, dev = 0, func = 0, vslot = 0; - char *buf2, *tok, *ptr, *end, *optkey = NULL; - - if ( NULL == (buf2 = ptr = strdup(str)) ) ---- xen-4.13.0/tools/libxl/libxl_utils.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/libxl/libxl_utils.c 2020-01-21 22:34:52.096300774 +0000 -@@ -1259,7 +1259,7 @@ - } - memset(un, 0, sizeof(struct sockaddr_un)); - un->sun_family = AF_UNIX; -- strncpy(un->sun_path, path, sizeof(un->sun_path)); -+ strncpy(un->sun_path, path, sizeof(un->sun_path)-1); - return 0; - } - ---- xen-4.13.0/tools/debugger/kdd/kdd.h.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/debugger/kdd/kdd.h 2020-01-21 23:35:55.458605582 +0000 -@@ -323,7 +323,7 @@ - kdd_msg msg; - kdd_reg reg; - kdd_stc stc; -- uint8_t payload[0]; -+ uint8_t payload[65535]; - }; - } PACKED kdd_pkt; - ---- xen-4.13.0/tools/xenpmd/Makefile.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/xenpmd/Makefile 2020-01-22 22:13:16.564873608 +0000 -@@ -3,6 +3,7 @@ - - CFLAGS += -Werror - CFLAGS += $(CFLAGS_libxenstore) -+CFLAGS += -Wno-error=format-truncation - - LDLIBS += $(LDLIBS_libxenstore) - diff --git a/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch b/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch deleted file mode 100644 index b3a5ad4..0000000 --- a/xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch +++ /dev/null @@ -1,240 +0,0 @@ -From 74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:40:24 +0100 -Subject: [PATCH] xen/events: rework fifo queue locking - -Two cpus entering evtchn_fifo_set_pending() for the same event channel -can race in case the first one gets interrupted after setting -EVTCHN_FIFO_PENDING and when the other one manages to set -EVTCHN_FIFO_LINKED before the first one is testing that bit. This can -lead to evtchn_check_pollers() being called before the event is put -properly into the queue, resulting eventually in the guest not seeing -the event pending and thus blocking forever afterwards. - -Note that commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel -lock") made the race just more obvious, while the fifo event channel -implementation had this race forever since the introduction and use of -per-channel locks, when an unmask operation was running in parallel with -an event channel send operation. - -Using a spinlock for the per event channel lock had turned out -problematic due to some paths needing to take the lock are called with -interrupts off, so the lock would need to disable interrupts, which in -turn broke some use cases related to vm events. - -For avoiding this race the queue locking in evtchn_fifo_set_pending() -needs to be reworked to cover the test of EVTCHN_FIFO_PENDING, -EVTCHN_FIFO_MASKED and EVTCHN_FIFO_LINKED, too. Additionally when an -event channel needs to change queues both queues need to be locked -initially, in order to avoid having a window with no lock held at all. - -Reported-by: Jan Beulich -Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -Fixes: de6acb78bf0e137c ("evtchn: use a per-event channel lock for sending events") -Signed-off-by: Juergen Gross -Reviewed-by: Jan Beulich -master commit: 71ac522909e9302350a88bc378be99affa87067c -master date: 2020-11-30 14:05:39 +0100 ---- - xen/common/event_fifo.c | 128 ++++++++++++++++++++++------------------ - 1 file changed, 70 insertions(+), 58 deletions(-) - -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 2037b24196..2f5e868b7a 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -66,38 +66,6 @@ static void evtchn_fifo_init(struct domain *d, struct evtchn *evtchn) - d->domain_id, evtchn->port); - } - --static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, -- struct evtchn *evtchn, -- unsigned long *flags) --{ -- struct vcpu *v; -- struct evtchn_fifo_queue *q, *old_q; -- unsigned int try; -- union evtchn_fifo_lastq lastq; -- -- for ( try = 0; try < 3; try++ ) -- { -- lastq.raw = read_atomic(&evtchn->fifo_lastq); -- v = d->vcpu[lastq.last_vcpu_id]; -- old_q = &v->evtchn_fifo->queue[lastq.last_priority]; -- -- spin_lock_irqsave(&old_q->lock, *flags); -- -- v = d->vcpu[lastq.last_vcpu_id]; -- q = &v->evtchn_fifo->queue[lastq.last_priority]; -- -- if ( old_q == q ) -- return old_q; -- -- spin_unlock_irqrestore(&old_q->lock, *flags); -- } -- -- gprintk(XENLOG_WARNING, -- "dom%d port %d lost event (too many queue changes)\n", -- d->domain_id, evtchn->port); -- return NULL; --} -- - static int try_set_link(event_word_t *word, event_word_t *w, uint32_t link) - { - event_word_t new, old; -@@ -169,6 +137,9 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - event_word_t *word; - unsigned long flags; - bool_t was_pending; -+ struct evtchn_fifo_queue *q, *old_q; -+ unsigned int try; -+ bool linked = true; - - port = evtchn->port; - word = evtchn_fifo_word_from_port(d, port); -@@ -183,17 +154,67 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - return; - } - -+ /* -+ * Lock all queues related to the event channel (in case of a queue change -+ * this might be two). -+ * It is mandatory to do that before setting and testing the PENDING bit -+ * and to hold the current queue lock until the event has been put into the -+ * list of pending events in order to avoid waking up a guest without the -+ * event being visibly pending in the guest. -+ */ -+ for ( try = 0; try < 3; try++ ) -+ { -+ union evtchn_fifo_lastq lastq; -+ const struct vcpu *old_v; -+ -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ old_v = d->vcpu[lastq.last_vcpu_id]; -+ -+ q = &v->evtchn_fifo->queue[evtchn->priority]; -+ old_q = &old_v->evtchn_fifo->queue[lastq.last_priority]; -+ -+ if ( q == old_q ) -+ spin_lock_irqsave(&q->lock, flags); -+ else if ( q < old_q ) -+ { -+ spin_lock_irqsave(&q->lock, flags); -+ spin_lock(&old_q->lock); -+ } -+ else -+ { -+ spin_lock_irqsave(&old_q->lock, flags); -+ spin_lock(&q->lock); -+ } -+ -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ old_v = d->vcpu[lastq.last_vcpu_id]; -+ if ( q == &v->evtchn_fifo->queue[evtchn->priority] && -+ old_q == &old_v->evtchn_fifo->queue[lastq.last_priority] ) -+ break; -+ -+ if ( q != old_q ) -+ spin_unlock(&old_q->lock); -+ spin_unlock_irqrestore(&q->lock, flags); -+ } -+ - was_pending = guest_test_and_set_bit(d, EVTCHN_FIFO_PENDING, word); - -+ /* If we didn't get the lock bail out. */ -+ if ( try == 3 ) -+ { -+ gprintk(XENLOG_WARNING, -+ "%pd port %u lost event (too many queue changes)\n", -+ d, evtchn->port); -+ goto done; -+ } -+ - /* - * Link the event if it unmasked and not already linked. - */ - if ( !guest_test_bit(d, EVTCHN_FIFO_MASKED, word) && - !guest_test_bit(d, EVTCHN_FIFO_LINKED, word) ) - { -- struct evtchn_fifo_queue *q, *old_q; - event_word_t *tail_word; -- bool_t linked = 0; - - /* - * Control block not mapped. The guest must not unmask an -@@ -204,25 +225,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - { - printk(XENLOG_G_WARNING - "%pv has no FIFO event channel control block\n", v); -- goto done; -+ goto unlock; - } - -- /* -- * No locking around getting the queue. This may race with -- * changing the priority but we are allowed to signal the -- * event once on the old priority. -- */ -- q = &v->evtchn_fifo->queue[evtchn->priority]; -- -- old_q = lock_old_queue(d, evtchn, &flags); -- if ( !old_q ) -- goto done; -- - if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) -- { -- spin_unlock_irqrestore(&old_q->lock, flags); -- goto done; -- } -+ goto unlock; - - /* - * If this event was a tail, the old queue is now empty and -@@ -241,8 +248,8 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - lastq.last_priority = q->priority; - write_atomic(&evtchn->fifo_lastq, lastq.raw); - -- spin_unlock_irqrestore(&old_q->lock, flags); -- spin_lock_irqsave(&q->lock, flags); -+ spin_unlock(&old_q->lock); -+ old_q = q; - } - - /* -@@ -255,6 +262,7 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - * If the queue is empty (i.e., we haven't linked to the new - * event), head must be updated. - */ -+ linked = false; - if ( q->tail ) - { - tail_word = evtchn_fifo_word_from_port(d, q->tail); -@@ -263,15 +271,19 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - if ( !linked ) - write_atomic(q->head, port); - q->tail = port; -+ } - -- spin_unlock_irqrestore(&q->lock, flags); -+ unlock: -+ if ( q != old_q ) -+ spin_unlock(&old_q->lock); -+ spin_unlock_irqrestore(&q->lock, flags); - -- if ( !linked -- && !guest_test_and_set_bit(d, q->priority, -- &v->evtchn_fifo->control_block->ready) ) -- vcpu_mark_events_pending(v); -- } - done: -+ if ( !linked && -+ !guest_test_and_set_bit(d, q->priority, -+ &v->evtchn_fifo->control_block->ready) ) -+ vcpu_mark_events_pending(v); -+ - if ( !was_pending ) - evtchn_check_pollers(d, port); - } --- -2.20.1 - diff --git a/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch b/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch deleted file mode 100644 index 86bcfbd..0000000 --- a/xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch +++ /dev/null @@ -1,592 +0,0 @@ -From 7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:36:36 +0100 -Subject: [PATCH] xen/evtchn: rework per event channel lock - -Currently the lock for a single event channel needs to be taken with -interrupts off, which causes deadlocks in some cases. - -Rework the per event channel lock to be non-blocking for the case of -sending an event and removing the need for disabling interrupts for -taking the lock. - -The lock is needed for avoiding races between event channel state -changes (creation, closing, binding) against normal operations (set -pending, [un]masking, priority changes). - -Use a rwlock, but with some restrictions: - -- Changing the state of an event channel (creation, closing, binding) - needs to use write_lock(), with ASSERT()ing that the lock is taken as - writer only when the state of the event channel is either before or - after the locked region appropriate (either free or unbound). - -- Sending an event needs to use read_trylock() mostly, in case of not - obtaining the lock the operation is omitted. This is needed as - sending an event can happen with interrupts off (at least in some - cases). - -- Dumping the event channel state for debug purposes is using - read_trylock(), too, in order to avoid blocking in case the lock is - taken as writer for a long time. - -- All other cases can use read_lock(). - -Fixes: e045199c7c9c54 ("evtchn: address races with evtchn_reset()") -Signed-off-by: Juergen Gross -Reviewed-by: Jan Beulich -Acked-by: Julien Grall - -xen/events: fix build - -Commit 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -introduced a build failure for NDEBUG builds. - -Fixes: 5f2df45ead7c1195 ("xen/evtchn: rework per event channel lock") -Signed-off-by: Juergen Gross -Signed-off-by: Jan Beulich -master commit: 5f2df45ead7c1195142f68b7923047a1e9479d54 -master date: 2020-11-10 14:36:15 +0100 -master commit: 53bacb86f496fdb11560d9e3b361bca7de60d268 -master date: 2020-11-11 08:56:21 +0100 ---- - xen/arch/x86/irq.c | 6 +- - xen/arch/x86/pv/shim.c | 9 +-- - xen/common/event_channel.c | 141 ++++++++++++++++++++++--------------- - xen/include/xen/event.h | 27 +++++-- - xen/include/xen/sched.h | 5 +- - 5 files changed, 116 insertions(+), 72 deletions(-) - -diff --git a/xen/arch/x86/irq.c b/xen/arch/x86/irq.c -index bb95583742..f05defbd7d 100644 ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -2481,14 +2481,12 @@ static void dump_irqs(unsigned char key) - pirq = domain_irq_to_pirq(d, irq); - info = pirq_info(d, pirq); - evtchn = evtchn_from_port(d, info->evtchn); -- local_irq_disable(); -- if ( spin_trylock(&evtchn->lock) ) -+ if ( evtchn_read_trylock(evtchn) ) - { - pending = evtchn_is_pending(d, evtchn); - masked = evtchn_is_masked(d, evtchn); -- spin_unlock(&evtchn->lock); -+ evtchn_read_unlock(evtchn); - } -- local_irq_enable(); - printk("d%d:%3d(%c%c%c)%c", - d->domain_id, pirq, "-P?"[pending], - "-M?"[masked], info->masked ? 'M' : '-', -diff --git a/xen/arch/x86/pv/shim.c b/xen/arch/x86/pv/shim.c -index bbecbfa16f..36a7e30605 100644 ---- a/xen/arch/x86/pv/shim.c -+++ b/xen/arch/x86/pv/shim.c -@@ -660,11 +660,12 @@ void pv_shim_inject_evtchn(unsigned int port) - if ( port_is_valid(guest, port) ) - { - struct evtchn *chn = evtchn_from_port(guest, port); -- unsigned long flags; - -- spin_lock_irqsave(&chn->lock, flags); -- evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -- spin_unlock_irqrestore(&chn->lock, flags); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(guest, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - } - } - -diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c -index 12f666cb79..181e5abaa6 100644 ---- a/xen/common/event_channel.c -+++ b/xen/common/event_channel.c -@@ -50,6 +50,40 @@ - - #define consumer_is_xen(e) (!!(e)->xen_consumer) - -+/* -+ * Lock an event channel exclusively. This is allowed only when the channel is -+ * free or unbound either when taking or when releasing the lock, as any -+ * concurrent operation on the event channel using evtchn_read_trylock() will -+ * just assume the event channel is free or unbound at the moment when the -+ * evtchn_read_trylock() returns false. -+ */ -+static inline void evtchn_write_lock(struct evtchn *evtchn) -+{ -+ write_lock(&evtchn->lock); -+ -+#ifndef NDEBUG -+ evtchn->old_state = evtchn->state; -+#endif -+} -+ -+static inline unsigned int old_state(const struct evtchn *evtchn) -+{ -+#ifndef NDEBUG -+ return evtchn->old_state; -+#else -+ return ECS_RESERVED; /* Just to allow things to build. */ -+#endif -+} -+ -+static inline void evtchn_write_unlock(struct evtchn *evtchn) -+{ -+ /* Enforce lock discipline. */ -+ ASSERT(old_state(evtchn) == ECS_FREE || old_state(evtchn) == ECS_UNBOUND || -+ evtchn->state == ECS_FREE || evtchn->state == ECS_UNBOUND); -+ -+ write_unlock(&evtchn->lock); -+} -+ - /* - * The function alloc_unbound_xen_event_channel() allows an arbitrary - * notifier function to be specified. However, very few unique functions -@@ -131,7 +165,7 @@ static struct evtchn *alloc_evtchn_bucket(struct domain *d, unsigned int port) - return NULL; - } - chn[i].port = port + i; -- spin_lock_init(&chn[i].lock); -+ rwlock_init(&chn[i].lock); - } - return chn; - } -@@ -249,7 +283,6 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - int port; - domid_t dom = alloc->dom; - long rc; -- unsigned long flags; - - d = rcu_lock_domain_by_any_id(dom); - if ( d == NULL ) -@@ -265,14 +298,14 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - if ( rc ) - goto out; - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_UNBOUND; - if ( (chn->u.unbound.remote_domid = alloc->remote_dom) == DOMID_SELF ) - chn->u.unbound.remote_domid = current->domain->domain_id; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - alloc->port = port; - -@@ -285,32 +318,26 @@ static long evtchn_alloc_unbound(evtchn_alloc_unbound_t *alloc) - } - - --static unsigned long double_evtchn_lock(struct evtchn *lchn, -- struct evtchn *rchn) -+static void double_evtchn_lock(struct evtchn *lchn, struct evtchn *rchn) - { -- unsigned long flags; -- - if ( lchn <= rchn ) - { -- spin_lock_irqsave(&lchn->lock, flags); -+ evtchn_write_lock(lchn); - if ( lchn != rchn ) -- spin_lock(&rchn->lock); -+ evtchn_write_lock(rchn); - } - else - { -- spin_lock_irqsave(&rchn->lock, flags); -- spin_lock(&lchn->lock); -+ evtchn_write_lock(rchn); -+ evtchn_write_lock(lchn); - } -- -- return flags; - } - --static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn, -- unsigned long flags) -+static void double_evtchn_unlock(struct evtchn *lchn, struct evtchn *rchn) - { - if ( lchn != rchn ) -- spin_unlock(&lchn->lock); -- spin_unlock_irqrestore(&rchn->lock, flags); -+ evtchn_write_unlock(lchn); -+ evtchn_write_unlock(rchn); - } - - static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) -@@ -320,7 +347,6 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - int lport, rport = bind->remote_port; - domid_t rdom = bind->remote_dom; - long rc; -- unsigned long flags; - - if ( rdom == DOMID_SELF ) - rdom = current->domain->domain_id; -@@ -356,7 +382,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - if ( rc ) - goto out; - -- flags = double_evtchn_lock(lchn, rchn); -+ double_evtchn_lock(lchn, rchn); - - lchn->u.interdomain.remote_dom = rd; - lchn->u.interdomain.remote_port = rport; -@@ -373,7 +399,7 @@ static long evtchn_bind_interdomain(evtchn_bind_interdomain_t *bind) - */ - evtchn_port_set_pending(ld, lchn->notify_vcpu_id, lchn); - -- double_evtchn_unlock(lchn, rchn, flags); -+ double_evtchn_unlock(lchn, rchn); - - bind->local_port = lport; - -@@ -396,7 +422,6 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) - struct domain *d = current->domain; - int virq = bind->virq, vcpu = bind->vcpu; - int rc = 0; -- unsigned long flags; - - if ( (virq < 0) || (virq >= ARRAY_SIZE(v->virq_to_evtchn)) ) - return -EINVAL; -@@ -434,14 +459,14 @@ int evtchn_bind_virq(evtchn_bind_virq_t *bind, evtchn_port_t port) - - chn = evtchn_from_port(d, port); - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_VIRQ; - chn->notify_vcpu_id = vcpu; - chn->u.virq = virq; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - v->virq_to_evtchn[virq] = bind->port = port; - -@@ -458,7 +483,6 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) - struct domain *d = current->domain; - int port, vcpu = bind->vcpu; - long rc = 0; -- unsigned long flags; - - if ( domain_vcpu(d, vcpu) == NULL ) - return -ENOENT; -@@ -470,13 +494,13 @@ static long evtchn_bind_ipi(evtchn_bind_ipi_t *bind) - - chn = evtchn_from_port(d, port); - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_IPI; - chn->notify_vcpu_id = vcpu; - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - bind->port = port; - -@@ -520,7 +544,6 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) - struct pirq *info; - int port = 0, pirq = bind->pirq; - long rc; -- unsigned long flags; - - if ( (pirq < 0) || (pirq >= d->nr_pirqs) ) - return -EINVAL; -@@ -553,14 +576,14 @@ static long evtchn_bind_pirq(evtchn_bind_pirq_t *bind) - goto out; - } - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_PIRQ; - chn->u.pirq.irq = pirq; - link_pirq_port(port, chn, v); - evtchn_port_init(d, chn); - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - bind->port = port; - -@@ -581,7 +604,6 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - struct evtchn *chn1, *chn2; - int port2; - long rc = 0; -- unsigned long flags; - - again: - spin_lock(&d1->event_lock); -@@ -681,14 +703,14 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - BUG_ON(chn2->state != ECS_INTERDOMAIN); - BUG_ON(chn2->u.interdomain.remote_dom != d1); - -- flags = double_evtchn_lock(chn1, chn2); -+ double_evtchn_lock(chn1, chn2); - - evtchn_free(d1, chn1); - - chn2->state = ECS_UNBOUND; - chn2->u.unbound.remote_domid = d1->domain_id; - -- double_evtchn_unlock(chn1, chn2, flags); -+ double_evtchn_unlock(chn1, chn2); - - goto out; - -@@ -696,9 +718,9 @@ int evtchn_close(struct domain *d1, int port1, bool guest) - BUG(); - } - -- spin_lock_irqsave(&chn1->lock, flags); -+ evtchn_write_lock(chn1); - evtchn_free(d1, chn1); -- spin_unlock_irqrestore(&chn1->lock, flags); -+ evtchn_write_unlock(chn1); - - out: - if ( d2 != NULL ) -@@ -718,7 +740,6 @@ int evtchn_send(struct domain *ld, unsigned int lport) - struct evtchn *lchn, *rchn; - struct domain *rd; - int rport, ret = 0; -- unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - return -EINVAL; -@@ -731,7 +752,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) - - lchn = evtchn_from_port(ld, lport); - -- spin_lock_irqsave(&lchn->lock, flags); -+ evtchn_read_lock(lchn); - - /* Guest cannot send via a Xen-attached event channel. */ - if ( unlikely(consumer_is_xen(lchn)) ) -@@ -766,7 +787,7 @@ int evtchn_send(struct domain *ld, unsigned int lport) - } - - out: -- spin_unlock_irqrestore(&lchn->lock, flags); -+ evtchn_read_unlock(lchn); - - return ret; - } -@@ -793,9 +814,11 @@ void send_guest_vcpu_virq(struct vcpu *v, uint32_t virq) - - d = v->domain; - chn = evtchn_from_port(d, port); -- spin_lock(&chn->lock); -- evtchn_port_set_pending(d, v->vcpu_id, chn); -- spin_unlock(&chn->lock); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, v->vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -824,9 +847,11 @@ void send_guest_global_virq(struct domain *d, uint32_t virq) - goto out; - - chn = evtchn_from_port(d, port); -- spin_lock(&chn->lock); -- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -- spin_unlock(&chn->lock); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - - out: - spin_unlock_irqrestore(&v->virq_lock, flags); -@@ -836,7 +861,6 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) - { - int port; - struct evtchn *chn; -- unsigned long flags; - - /* - * PV guests: It should not be possible to race with __evtchn_close(). The -@@ -851,9 +875,11 @@ void send_guest_pirq(struct domain *d, const struct pirq *pirq) - } - - chn = evtchn_from_port(d, port); -- spin_lock_irqsave(&chn->lock, flags); -- evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -- spin_unlock_irqrestore(&chn->lock, flags); -+ if ( evtchn_read_trylock(chn) ) -+ { -+ evtchn_port_set_pending(d, chn->notify_vcpu_id, chn); -+ evtchn_read_unlock(chn); -+ } - } - - static struct domain *global_virq_handlers[NR_VIRQS] __read_mostly; -@@ -1050,15 +1076,17 @@ int evtchn_unmask(unsigned int port) - { - struct domain *d = current->domain; - struct evtchn *evtchn; -- unsigned long flags; - - if ( unlikely(!port_is_valid(d, port)) ) - return -EINVAL; - - evtchn = evtchn_from_port(d, port); -- spin_lock_irqsave(&evtchn->lock, flags); -+ -+ evtchn_read_lock(evtchn); -+ - evtchn_port_unmask(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ evtchn_read_unlock(evtchn); - - return 0; - } -@@ -1304,7 +1332,6 @@ int alloc_unbound_xen_event_channel( - { - struct evtchn *chn; - int port, rc; -- unsigned long flags; - - spin_lock(&ld->event_lock); - -@@ -1317,14 +1344,14 @@ int alloc_unbound_xen_event_channel( - if ( rc ) - goto out; - -- spin_lock_irqsave(&chn->lock, flags); -+ evtchn_write_lock(chn); - - chn->state = ECS_UNBOUND; - chn->xen_consumer = get_xen_consumer(notification_fn); - chn->notify_vcpu_id = lvcpu; - chn->u.unbound.remote_domid = remote_domid; - -- spin_unlock_irqrestore(&chn->lock, flags); -+ evtchn_write_unlock(chn); - - write_atomic(&ld->xen_evtchns, ld->xen_evtchns + 1); - -@@ -1356,7 +1383,6 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - { - struct evtchn *lchn, *rchn; - struct domain *rd; -- unsigned long flags; - - if ( !port_is_valid(ld, lport) ) - { -@@ -1371,7 +1397,8 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - - lchn = evtchn_from_port(ld, lport); - -- spin_lock_irqsave(&lchn->lock, flags); -+ if ( !evtchn_read_trylock(lchn) ) -+ return; - - if ( likely(lchn->state == ECS_INTERDOMAIN) ) - { -@@ -1381,7 +1408,7 @@ void notify_via_xen_event_channel(struct domain *ld, int lport) - evtchn_port_set_pending(rd, rchn->notify_vcpu_id, rchn); - } - -- spin_unlock_irqrestore(&lchn->lock, flags); -+ evtchn_read_unlock(lchn); - } - - void evtchn_check_pollers(struct domain *d, unsigned int port) -diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h -index fa93a3684a..6588333f42 100644 ---- a/xen/include/xen/event.h -+++ b/xen/include/xen/event.h -@@ -111,6 +111,21 @@ static inline unsigned int max_evtchns(const struct domain *d) - : BITS_PER_EVTCHN_WORD(d) * BITS_PER_EVTCHN_WORD(d); - } - -+static inline void evtchn_read_lock(struct evtchn *evtchn) -+{ -+ read_lock(&evtchn->lock); -+} -+ -+static inline bool evtchn_read_trylock(struct evtchn *evtchn) -+{ -+ return read_trylock(&evtchn->lock); -+} -+ -+static inline void evtchn_read_unlock(struct evtchn *evtchn) -+{ -+ read_unlock(&evtchn->lock); -+} -+ - static inline bool_t port_is_valid(struct domain *d, unsigned int p) - { - if ( p >= read_atomic(&d->valid_evtchns) ) -@@ -244,11 +259,10 @@ static inline bool evtchn_port_is_pending(struct domain *d, evtchn_port_t port) - { - struct evtchn *evtchn = evtchn_from_port(d, port); - bool rc; -- unsigned long flags; - -- spin_lock_irqsave(&evtchn->lock, flags); -+ evtchn_read_lock(evtchn); - rc = evtchn_is_pending(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ evtchn_read_unlock(evtchn); - - return rc; - } -@@ -263,11 +277,12 @@ static inline bool evtchn_port_is_masked(struct domain *d, evtchn_port_t port) - { - struct evtchn *evtchn = evtchn_from_port(d, port); - bool rc; -- unsigned long flags; - -- spin_lock_irqsave(&evtchn->lock, flags); -+ evtchn_read_lock(evtchn); -+ - rc = evtchn_is_masked(d, evtchn); -- spin_unlock_irqrestore(&evtchn->lock, flags); -+ -+ evtchn_read_unlock(evtchn); - - return rc; - } -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index 8bb5bd7b38..89a9df5a63 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -83,7 +83,7 @@ extern domid_t hardware_domid; - - struct evtchn - { -- spinlock_t lock; -+ rwlock_t lock; - #define ECS_FREE 0 /* Channel is available for use. */ - #define ECS_RESERVED 1 /* Channel is reserved. */ - #define ECS_UNBOUND 2 /* Channel is waiting to bind to a remote domain. */ -@@ -112,6 +112,9 @@ struct evtchn - u16 virq; /* state == ECS_VIRQ */ - } u; - u8 priority; -+#ifndef NDEBUG -+ u8 old_state; /* State when taking lock in write mode. */ -+#endif - u8 last_priority; - u16 last_vcpu_id; - #ifdef CONFIG_XSM --- -2.20.1 - diff --git a/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch b/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch deleted file mode 100644 index 0d84566..0000000 --- a/xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch +++ /dev/null @@ -1,97 +0,0 @@ -From d064b6581bbddf9ef4a8817a2f077dd2a0afa1da Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Tue, 1 Dec 2020 15:39:02 +0100 -Subject: [PATCH] xen/events: access last_priority and last_vcpu_id together - -The queue for a fifo event is depending on the vcpu_id and the -priority of the event. When sending an event it might happen the -event needs to change queues and the old queue needs to be kept for -keeping the links between queue elements intact. For this purpose -the event channel contains last_priority and last_vcpu_id values -elements for being able to identify the old queue. - -In order to avoid races always access last_priority and last_vcpu_id -with a single atomic operation avoiding any inconsistencies. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -master commit: 1277cb9dc5e966f1faf665bcded02b7533e38078 -master date: 2020-11-24 11:23:42 +0100 ---- - xen/common/event_fifo.c | 25 +++++++++++++++++++------ - xen/include/xen/sched.h | 3 +-- - 2 files changed, 20 insertions(+), 8 deletions(-) - -diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c -index 27ab3a1c3f..2037b24196 100644 ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -21,6 +21,14 @@ - - #include - -+union evtchn_fifo_lastq { -+ uint32_t raw; -+ struct { -+ uint8_t last_priority; -+ uint16_t last_vcpu_id; -+ }; -+}; -+ - static inline event_word_t *evtchn_fifo_word_from_port(const struct domain *d, - unsigned int port) - { -@@ -65,16 +73,18 @@ static struct evtchn_fifo_queue *lock_old_queue(const struct domain *d, - struct vcpu *v; - struct evtchn_fifo_queue *q, *old_q; - unsigned int try; -+ union evtchn_fifo_lastq lastq; - - for ( try = 0; try < 3; try++ ) - { -- v = d->vcpu[evtchn->last_vcpu_id]; -- old_q = &v->evtchn_fifo->queue[evtchn->last_priority]; -+ lastq.raw = read_atomic(&evtchn->fifo_lastq); -+ v = d->vcpu[lastq.last_vcpu_id]; -+ old_q = &v->evtchn_fifo->queue[lastq.last_priority]; - - spin_lock_irqsave(&old_q->lock, *flags); - -- v = d->vcpu[evtchn->last_vcpu_id]; -- q = &v->evtchn_fifo->queue[evtchn->last_priority]; -+ v = d->vcpu[lastq.last_vcpu_id]; -+ q = &v->evtchn_fifo->queue[lastq.last_priority]; - - if ( old_q == q ) - return old_q; -@@ -225,8 +235,11 @@ static void evtchn_fifo_set_pending(struct vcpu *v, struct evtchn *evtchn) - /* Moved to a different queue? */ - if ( old_q != q ) - { -- evtchn->last_vcpu_id = v->vcpu_id; -- evtchn->last_priority = q->priority; -+ union evtchn_fifo_lastq lastq = { }; -+ -+ lastq.last_vcpu_id = v->vcpu_id; -+ lastq.last_priority = q->priority; -+ write_atomic(&evtchn->fifo_lastq, lastq.raw); - - spin_unlock_irqrestore(&old_q->lock, flags); - spin_lock_irqsave(&q->lock, flags); -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index 89a9df5a63..8bf1b90261 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -115,8 +115,7 @@ struct evtchn - #ifndef NDEBUG - u8 old_state; /* State when taking lock in write mode. */ - #endif -- u8 last_priority; -- u16 last_vcpu_id; -+ u32 fifo_lastq; /* Data for fifo events identifying last queue. */ - #ifdef CONFIG_XSM - union { - #ifdef XSM_NEED_GENERIC_EVTCHN_SSID --- -2.20.1 - diff --git a/xen.hypervisor.config b/xen.hypervisor.config index cf6aa02..4f2bb09 100644 --- a/xen.hypervisor.config +++ b/xen.hypervisor.config @@ -103,8 +103,10 @@ CONFIG_HARDEN_BRANCH_PREDICTOR=y CONFIG_ARM64_ERRATUM_827319=y CONFIG_ARM64_ERRATUM_824069=y CONFIG_ARM64_ERRATUM_819472=y +CONFIG_ARM64_ERRATUM_843419=y CONFIG_ARM64_ERRATUM_832075=y CONFIG_ARM64_ERRATUM_834220=y +CONFIG_ARM_ERRATUM_858921=y CONFIG_ARM64_HARDEN_BRANCH_PREDICTOR=y CONFIG_ALL_PLAT=y # CONFIG_QEMU is not set diff --git a/xen.ocaml.4.10.patch b/xen.ocaml.4.10.patch index a24ffdd..f6692d0 100644 --- a/xen.ocaml.4.10.patch +++ b/xen.ocaml.4.10.patch @@ -13,7 +13,7 @@ ret = xc_vcpu_getcontext(_H(xch), _D(domid), Int_val(cpu), &ctxt); context = caml_alloc_string(sizeof(ctxt)); -- memcpy(String_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); +- memcpy((char *) String_val(context), &ctxt.c, sizeof(ctxt.c)); + memcpy((char *) Bp_val(context), (char *) &ctxt.c, sizeof(ctxt.c)); CAMLreturn(context); @@ -22,7 +22,7 @@ conring_size = size; ring = caml_alloc_string(count); -- memcpy(String_val(ring), str, count); +- memcpy((char *) String_val(ring), str, count); + memcpy((char *) Bp_val(ring), str, count); free(str); @@ -45,33 +45,13 @@ r = xc_cpuid_set(_H(xch), _D(domid), c_input, (const char **)c_config, out_config); ---- xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 -+++ xen-4.13.0/tools/ocaml/libs/xb/xs_ring_stubs.c 2020-01-21 23:51:35.473330934 +0000 -@@ -44,7 +44,7 @@ - CAMLlocal1(ml_result); - - struct mmap_interface *interface = GET_C_STRUCT(ml_interface); -- char *buffer = String_val(ml_buffer); -+ char *buffer = (char *) Bp_val(ml_buffer); - int len = Int_val(ml_len); - int result; - -@@ -103,7 +103,7 @@ - CAMLlocal1(ml_result); - - struct mmap_interface *interface = GET_C_STRUCT(ml_interface); -- char *buffer = String_val(ml_buffer); -+ char *buffer = (char *) Bp_val(ml_buffer); - int len = Int_val(ml_len); - int result; - --- xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c.orig 2019-12-17 14:23:09.000000000 +0000 +++ xen-4.13.0/tools/ocaml/libs/xb/xenbus_stubs.c 2020-01-22 00:04:09.443168991 +0000 @@ -65,7 +65,7 @@ }; ret = caml_alloc_string(sizeof(struct xsd_sockmsg)); -- memcpy(String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); +- memcpy((char *) String_val(ret), &xsd, sizeof(struct xsd_sockmsg)); + memcpy((char *) Bp_val(ret), &xsd, sizeof(struct xsd_sockmsg)); CAMLreturn(ret); diff --git a/xen.spec b/xen.spec index 64e1e84..7a5d044 100644 --- a/xen.spec +++ b/xen.spec @@ -57,8 +57,8 @@ Summary: Xen is a virtual machine monitor Name: xen -Version: 4.13.2 -Release: 8%{?dist} +Version: 4.13.3 +Release: 1%{?dist} License: GPLv2+ and LGPLv2+ and BSD URL: http://xen.org/ Source0: https://downloads.xenproject.org/release/xen/%{version}/xen-%{version}.tar.gz @@ -113,50 +113,8 @@ Patch40: xen.drop.brctl.patch Patch41: xen.python.env.patch Patch42: xen.gcc9.fixes.patch Patch44: xen.ocaml.4.10.patch -Patch45: xen.gcc10.fixes.patch -Patch60: xsa335-qemu.patch -Patch61: xsa335-trad.patch -Patch62: xsa351-arm.patch -Patch63: xsa351-x86-4.13-1.patch -Patch64: xsa351-x86-4.13-2.patch Patch65: zstd-dom0.patch -Patch66: xsa355.patch -Patch67: xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch -Patch68: xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch -Patch69: xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch -Patch70: xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch -Patch71: xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch -Patch72: xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch -Patch73: xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch -Patch74: xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch -Patch75: xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch -Patch76: xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch -Patch77: xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch -Patch78: xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch -Patch79: xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch -Patch80: xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch -Patch81: xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch -Patch82: xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch -Patch83: xsa322-4.14-c.patch -Patch84: xsa322-o.patch -Patch85: xsa323.patch -Patch86: xsa324.patch -Patch87: xsa325-4.14.patch -Patch88: xsa330.patch -Patch89: xsa348-4.13-1.patch -Patch90: xsa348-4.13-2.patch -Patch91: xsa348-4.13-3.patch -Patch92: xsa352.patch -Patch93: xsa353.patch -Patch94: xen.git-7d6f52d47b3d3ec9f0bec8b34e7f1a0e639849e8.patch -Patch95: xen.git-d064b6581bbddf9ef4a8817a2f077dd2a0afa1da.patch -Patch96: xen.git-74c5729bb33d25eb2c6a5ea1fd9936d9798bb74c.patch -Patch97: xsa358-4.14.patch -Patch98: xsa359.patch -Patch99: xsa360-4.14.patch Patch100: xsa363.patch -Patch101: xsa364.patch -Patch102: xsa368-4.13.patch %if %build_qemutrad @@ -362,49 +320,8 @@ manage Xen virtual machines. %patch41 -p1 %patch42 -p1 %patch44 -p1 -%patch45 -p1 -%patch61 -p1 -%patch62 -p1 -%patch63 -p1 -%patch64 -p1 %patch65 -p1 -%patch66 -p1 -%patch67 -p1 -%patch68 -p1 -%patch69 -p1 -%patch70 -p1 -%patch71 -p1 -%patch72 -p1 -%patch73 -p1 -%patch74 -p1 -%patch75 -p1 -%patch76 -p1 -%patch77 -p1 -%patch78 -p1 -%patch79 -p1 -%patch80 -p1 -%patch81 -p1 -%patch82 -p1 -%patch83 -p1 -%patch84 -p1 -%patch85 -p1 -%patch86 -p1 -%patch87 -p1 -%patch88 -p1 -%patch89 -p1 -%patch90 -p1 -%patch91 -p1 -%patch92 -p1 -%patch93 -p1 -%patch94 -p1 -%patch95 -p1 -%patch96 -p1 -%patch97 -p1 -%patch98 -p1 -%patch99 -p1 %patch100 -p1 -%patch101 -p1 -%patch102 -p1 # qemu-xen-traditional patches pushd tools/qemu-xen-traditional @@ -421,7 +338,6 @@ popd # qemu-xen patches pushd tools/qemu-xen -%patch60 -p1 popd # stubdom sources @@ -998,6 +914,11 @@ fi %endif %changelog +* Mon Mar 29 2021 Michael Young - 4.13.3-1 +- update to 4.13.3 + remove patches now included or superceded upstream + adjust xen.hypervisor.config + * Thu Mar 18 2021 Michael Young - 4.13.2-8 - HVM soft-reset crashes toolstack [XSA-368, CVE-2021-28687] (#1940610) diff --git a/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch b/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch deleted file mode 100644 index ee2c1a7..0000000 --- a/xsa115-4.13-c-0001-tools-xenstore-allow-removing-child-of-a-node-exceed.patch +++ /dev/null @@ -1,157 +0,0 @@ -From e92f3dfeaae21a335e666c9247954424e34e5c56 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:37 +0200 -Subject: [PATCH 01/10] tools/xenstore: allow removing child of a node - exceeding quota - -An unprivileged user of Xenstore is not allowed to write nodes with a -size exceeding a global quota, while privileged users like dom0 are -allowed to write such nodes. The size of a node is the needed space -to store all node specific data, this includes the names of all -children of the node. - -When deleting a node its parent has to be modified by removing the -name of the to be deleted child from it. - -This results in the strange situation that an unprivileged owner of a -node might not succeed in deleting that node in case its parent is -exceeding the quota of that unprivileged user (it might have been -written by dom0), as the user is not allowed to write the updated -parent node. - -Fix that by not checking the quota when writing a node for the -purpose of removing a child's name only. - -The same applies to transaction handling: a node being read during a -transaction is written to the transaction specific area and it should -not be tested for exceeding the quota, as it might not be owned by -the reader and presumably the original write would have failed if the -node is owned by the reader. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 20 +++++++++++--------- - tools/xenstore/xenstored_core.h | 3 ++- - tools/xenstore/xenstored_transaction.c | 2 +- - 3 files changed, 14 insertions(+), 11 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 97ceabf9642d..b43e1018babd 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -417,7 +417,8 @@ static struct node *read_node(struct connection *conn, const void *ctx, - return node; - } - --int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) -+int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -+ bool no_quota_check) - { - TDB_DATA data; - void *p; -@@ -427,7 +428,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) - + node->num_perms*sizeof(node->perms[0]) - + node->datalen + node->childlen; - -- if (domain_is_unprivileged(conn) && -+ if (!no_quota_check && domain_is_unprivileged(conn) && - data.dsize >= quota_max_entry_size) { - errno = ENOSPC; - return errno; -@@ -455,14 +456,15 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node) - return 0; - } - --static int write_node(struct connection *conn, struct node *node) -+static int write_node(struct connection *conn, struct node *node, -+ bool no_quota_check) - { - TDB_DATA key; - - if (access_node(conn, node, NODE_ACCESS_WRITE, &key)) - return errno; - -- return write_node_raw(conn, &key, node); -+ return write_node_raw(conn, &key, node, no_quota_check); - } - - static enum xs_perm_type perm_for_conn(struct connection *conn, -@@ -999,7 +1001,7 @@ static struct node *create_node(struct connection *conn, const void *ctx, - /* We write out the nodes down, setting destructor in case - * something goes wrong. */ - for (i = node; i; i = i->parent) { -- if (write_node(conn, i)) { -+ if (write_node(conn, i, false)) { - domain_entry_dec(conn, i); - return NULL; - } -@@ -1039,7 +1041,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) - } else { - node->data = in->buffer + offset; - node->datalen = datalen; -- if (write_node(conn, node)) -+ if (write_node(conn, node, false)) - return errno; - } - -@@ -1115,7 +1117,7 @@ static int remove_child_entry(struct connection *conn, struct node *node, - size_t childlen = strlen(node->children + offset); - memdel(node->children, offset, childlen + 1, node->childlen); - node->childlen -= childlen + 1; -- return write_node(conn, node); -+ return write_node(conn, node, true); - } - - -@@ -1254,7 +1256,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - node->num_perms = num; - domain_entry_inc(conn, node); - -- if (write_node(conn, node)) -+ if (write_node(conn, node, false)) - return errno; - - fire_watches(conn, in, name, false); -@@ -1514,7 +1516,7 @@ static void manual_node(const char *name, const char *child) - if (child) - node->childlen = strlen(child) + 1; - -- if (write_node(NULL, node)) -+ if (write_node(NULL, node, false)) - barf_perror("Could not create initial node %s", name); - talloc_free(node); - } -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 56a279cfbb47..3cb1c235a101 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -149,7 +149,8 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); - char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); - - /* Write a node to the tdb data base. */ --int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node); -+int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, -+ bool no_quota_check); - - /* Get this node, checking we have permissions. */ - struct node *get_node(struct connection *conn, -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index 2824f7b359b8..e87897573469 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -276,7 +276,7 @@ int access_node(struct connection *conn, struct node *node, - i->check_gen = true; - if (node->generation != NO_GENERATION) { - set_tdb_key(trans_name, &local_key); -- ret = write_node_raw(conn, &local_key, node); -+ ret = write_node_raw(conn, &local_key, node, true); - if (ret) - goto err; - i->ta_node = true; --- -2.17.1 - diff --git a/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch b/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch deleted file mode 100644 index d143818..0000000 --- a/xsa115-4.13-c-0002-tools-xenstore-ignore-transaction-id-for-un-watch.patch +++ /dev/null @@ -1,86 +0,0 @@ -From e8076f73de65c4816f69d6ebf75839c706145fcd Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:38 +0200 -Subject: [PATCH 02/10] tools/xenstore: ignore transaction id for [un]watch - -Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH -commands as it is documented in docs/misc/xenstore.txt, it is tested -for validity today. - -Really ignore the transaction id for XS_WATCH and XS_UNWATCH. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 26 ++++++++++++++++---------- - 1 file changed, 16 insertions(+), 10 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index b43e1018babd..bb2f9fd4e76e 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1268,13 +1268,17 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - static struct { - const char *str; - int (*func)(struct connection *conn, struct buffered_data *in); -+ unsigned int flags; -+#define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ - } const wire_funcs[XS_TYPE_COUNT] = { - [XS_CONTROL] = { "CONTROL", do_control }, - [XS_DIRECTORY] = { "DIRECTORY", send_directory }, - [XS_READ] = { "READ", do_read }, - [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, -- [XS_WATCH] = { "WATCH", do_watch }, -- [XS_UNWATCH] = { "UNWATCH", do_unwatch }, -+ [XS_WATCH] = -+ { "WATCH", do_watch, XS_FLAG_NOTID }, -+ [XS_UNWATCH] = -+ { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, - [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, - [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, - [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, -@@ -1296,7 +1300,7 @@ static struct { - - static const char *sockmsg_string(enum xsd_sockmsg_type type) - { -- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].str) -+ if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) - return wire_funcs[type].str; - - return "**UNKNOWN**"; -@@ -1311,7 +1315,14 @@ static void process_message(struct connection *conn, struct buffered_data *in) - enum xsd_sockmsg_type type = in->hdr.msg.type; - int ret; - -- trans = transaction_lookup(conn, in->hdr.msg.tx_id); -+ if ((unsigned int)type >= XS_TYPE_COUNT || !wire_funcs[type].func) { -+ eprintf("Client unknown operation %i", type); -+ send_error(conn, ENOSYS); -+ return; -+ } -+ -+ trans = (wire_funcs[type].flags & XS_FLAG_NOTID) -+ ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); - if (IS_ERR(trans)) { - send_error(conn, -PTR_ERR(trans)); - return; -@@ -1320,12 +1331,7 @@ static void process_message(struct connection *conn, struct buffered_data *in) - assert(conn->transaction == NULL); - conn->transaction = trans; - -- if ((unsigned)type < XS_TYPE_COUNT && wire_funcs[type].func) -- ret = wire_funcs[type].func(conn, in); -- else { -- eprintf("Client unknown operation %i", type); -- ret = ENOSYS; -- } -+ ret = wire_funcs[type].func(conn, in); - if (ret) - send_error(conn, ret); - --- -2.17.1 - diff --git a/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch b/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch deleted file mode 100644 index 2c2304a..0000000 --- a/xsa115-4.13-c-0003-tools-xenstore-fix-node-accounting-after-failed-node.patch +++ /dev/null @@ -1,104 +0,0 @@ -From b8c6dbb67ebb449126023446a7d209eedf966537 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:39 +0200 -Subject: [PATCH 03/10] tools/xenstore: fix node accounting after failed node - creation - -When a node creation fails the number of nodes of the domain should be -the same as before the failed node creation. In case of failure when -trying to create a node requiring to create one or more intermediate -nodes as well (e.g. when /a/b/c/d is to be created, but /a/b isn't -existing yet) it might happen that the number of nodes of the creating -domain is not reset to the value it had before. - -So move the quota accounting out of construct_node() and into the node -write loop in create_node() in order to be able to undo the accounting -in case of an error in the intermediate node destructor. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Paul Durrant -Acked-by: Julien Grall ---- - tools/xenstore/xenstored_core.c | 37 ++++++++++++++++++++++----------- - 1 file changed, 25 insertions(+), 12 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index bb2f9fd4e76e..db9b9ca7957d 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -925,11 +925,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - if (!parent) - return NULL; - -- if (domain_entry(conn) >= quota_nb_entry_per_domain) { -- errno = ENOSPC; -- return NULL; -- } -- - /* Add child to parent. */ - base = basename(name); - baselen = strlen(base) + 1; -@@ -962,7 +957,6 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - node->children = node->data = NULL; - node->childlen = node->datalen = 0; - node->parent = parent; -- domain_entry_inc(conn, node); - return node; - - nomem: -@@ -982,6 +976,9 @@ static int destroy_node(void *_node) - key.dsize = strlen(node->name); - - tdb_delete(tdb_ctx, key); -+ -+ domain_entry_dec(talloc_parent(node), node); -+ - return 0; - } - -@@ -998,18 +995,34 @@ static struct node *create_node(struct connection *conn, const void *ctx, - node->data = data; - node->datalen = datalen; - -- /* We write out the nodes down, setting destructor in case -- * something goes wrong. */ -+ /* -+ * We write out the nodes bottom up. -+ * All new created nodes will have i->parent set, while the final -+ * node will be already existing and won't have i->parent set. -+ * New nodes are subject to quota handling. -+ * Initially set a destructor for all new nodes removing them from -+ * TDB again and undoing quota accounting for the case of an error -+ * during the write loop. -+ */ - for (i = node; i; i = i->parent) { -- if (write_node(conn, i, false)) { -- domain_entry_dec(conn, i); -+ /* i->parent is set for each new node, so check quota. */ -+ if (i->parent && -+ domain_entry(conn) >= quota_nb_entry_per_domain) { -+ errno = ENOSPC; - return NULL; - } -- talloc_set_destructor(i, destroy_node); -+ if (write_node(conn, i, false)) -+ return NULL; -+ -+ /* Account for new node, set destructor for error case. */ -+ if (i->parent) { -+ domain_entry_inc(conn, i); -+ talloc_set_destructor(i, destroy_node); -+ } - } - - /* OK, now remove destructors so they stay around */ -- for (i = node; i; i = i->parent) -+ for (i = node; i->parent; i = i->parent) - talloc_set_destructor(i, NULL); - return node; - } --- -2.17.1 - diff --git a/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch b/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch deleted file mode 100644 index 2424384..0000000 --- a/xsa115-4.13-c-0004-tools-xenstore-simplify-and-rename-check_event_node.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 318aa75bd0c05423e717ad0b64adb204282025db Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:40 +0200 -Subject: [PATCH 04/10] tools/xenstore: simplify and rename check_event_node() - -There is no path which allows to call check_event_node() without a -event name. So don't let the result depend on the name being NULL and -add an assert() covering that case. - -Rename the function to check_special_event() to better match the -semantics. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_watch.c | 12 +++++------- - 1 file changed, 5 insertions(+), 7 deletions(-) - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 7dedca60dfd6..f2f1bed47cc6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -47,13 +47,11 @@ struct watch - char *node; - }; - --static bool check_event_node(const char *node) -+static bool check_special_event(const char *name) - { -- if (!node || !strstarts(node, "@")) { -- errno = EINVAL; -- return false; -- } -- return true; -+ assert(name); -+ -+ return strstarts(name, "@"); - } - - /* Is child a subnode of parent, or equal? */ -@@ -87,7 +85,7 @@ static void add_event(struct connection *conn, - unsigned int len; - char *data; - -- if (!check_event_node(name)) { -+ if (!check_special_event(name)) { - /* Can this conn load node, or see that it doesn't exist? */ - struct node *node = get_node(conn, ctx, name, XS_PERM_READ); - /* --- -2.17.1 - diff --git a/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch b/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch deleted file mode 100644 index a7695e2..0000000 --- a/xsa115-4.13-c-0005-tools-xenstore-check-privilege-for-XS_IS_DOMAIN_INTR.patch +++ /dev/null @@ -1,115 +0,0 @@ -From c625fae44aedc246776b52eb1173cf847a3d4d80 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:41 +0200 -Subject: [PATCH 05/10] tools/xenstore: check privilege for - XS_IS_DOMAIN_INTRODUCED - -The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for -privileged domains only (the only user in the tree is the xenpaging -daemon). - -Instead of having the privilege test for each command introduce a -per-command flag for that purpose. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 24 ++++++++++++++++++------ - tools/xenstore/xenstored_domain.c | 7 ++----- - 2 files changed, 20 insertions(+), 11 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index db9b9ca7957d..6afd58431111 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1283,8 +1283,10 @@ static struct { - int (*func)(struct connection *conn, struct buffered_data *in); - unsigned int flags; - #define XS_FLAG_NOTID (1U << 0) /* Ignore transaction id. */ -+#define XS_FLAG_PRIV (1U << 1) /* Privileged domain only. */ - } const wire_funcs[XS_TYPE_COUNT] = { -- [XS_CONTROL] = { "CONTROL", do_control }, -+ [XS_CONTROL] = -+ { "CONTROL", do_control, XS_FLAG_PRIV }, - [XS_DIRECTORY] = { "DIRECTORY", send_directory }, - [XS_READ] = { "READ", do_read }, - [XS_GET_PERMS] = { "GET_PERMS", do_get_perms }, -@@ -1294,8 +1296,10 @@ static struct { - { "UNWATCH", do_unwatch, XS_FLAG_NOTID }, - [XS_TRANSACTION_START] = { "TRANSACTION_START", do_transaction_start }, - [XS_TRANSACTION_END] = { "TRANSACTION_END", do_transaction_end }, -- [XS_INTRODUCE] = { "INTRODUCE", do_introduce }, -- [XS_RELEASE] = { "RELEASE", do_release }, -+ [XS_INTRODUCE] = -+ { "INTRODUCE", do_introduce, XS_FLAG_PRIV }, -+ [XS_RELEASE] = -+ { "RELEASE", do_release, XS_FLAG_PRIV }, - [XS_GET_DOMAIN_PATH] = { "GET_DOMAIN_PATH", do_get_domain_path }, - [XS_WRITE] = { "WRITE", do_write }, - [XS_MKDIR] = { "MKDIR", do_mkdir }, -@@ -1304,9 +1308,11 @@ static struct { - [XS_WATCH_EVENT] = { "WATCH_EVENT", NULL }, - [XS_ERROR] = { "ERROR", NULL }, - [XS_IS_DOMAIN_INTRODUCED] = -- { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced }, -- [XS_RESUME] = { "RESUME", do_resume }, -- [XS_SET_TARGET] = { "SET_TARGET", do_set_target }, -+ { "IS_DOMAIN_INTRODUCED", do_is_domain_introduced, XS_FLAG_PRIV }, -+ [XS_RESUME] = -+ { "RESUME", do_resume, XS_FLAG_PRIV }, -+ [XS_SET_TARGET] = -+ { "SET_TARGET", do_set_target, XS_FLAG_PRIV }, - [XS_RESET_WATCHES] = { "RESET_WATCHES", do_reset_watches }, - [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, - }; -@@ -1334,6 +1340,12 @@ static void process_message(struct connection *conn, struct buffered_data *in) - return; - } - -+ if ((wire_funcs[type].flags & XS_FLAG_PRIV) && -+ domain_is_unprivileged(conn)) { -+ send_error(conn, EACCES); -+ return; -+ } -+ - trans = (wire_funcs[type].flags & XS_FLAG_NOTID) - ? NULL : transaction_lookup(conn, in->hdr.msg.tx_id); - if (IS_ERR(trans)) { -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 1eae703ef680..0e2926e2a3d0 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -377,7 +377,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) - return EINVAL; - -- if (domain_is_unprivileged(conn) || !conn->can_write) -+ if (!conn->can_write) - return EACCES; - - domid = atoi(vec[0]); -@@ -445,7 +445,7 @@ int do_set_target(struct connection *conn, struct buffered_data *in) - if (get_strings(in, vec, ARRAY_SIZE(vec)) < ARRAY_SIZE(vec)) - return EINVAL; - -- if (domain_is_unprivileged(conn) || !conn->can_write) -+ if (!conn->can_write) - return EACCES; - - domid = atoi(vec[0]); -@@ -480,9 +480,6 @@ static struct domain *onearg_domain(struct connection *conn, - if (!domid) - return ERR_PTR(-EINVAL); - -- if (domain_is_unprivileged(conn)) -- return ERR_PTR(-EACCES); -- - return find_connected_domain(domid); - } - --- -2.17.1 - diff --git a/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch b/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch deleted file mode 100644 index fc6c2b6..0000000 --- a/xsa115-4.13-c-0006-tools-xenstore-rework-node-removal.patch +++ /dev/null @@ -1,217 +0,0 @@ -From 461c880600175c06e23a63e62d9f1ccab755d708 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:42 +0200 -Subject: [PATCH 06/10] tools/xenstore: rework node removal - -Today a Xenstore node is being removed by deleting it from the parent -first and then deleting itself and all its children. This results in -stale entries remaining in the data base in case e.g. a memory -allocation is failing during processing. This would result in the -rather strange behavior to be able to read a node (as its still in the -data base) while not being visible in the tree view of Xenstore. - -Fix that by deleting the nodes from the leaf side instead of starting -at the root. - -As fire_watches() is now called from _rm() the ctx parameter needs a -const attribute. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 99 ++++++++++++++++---------------- - tools/xenstore/xenstored_watch.c | 4 +- - tools/xenstore/xenstored_watch.h | 2 +- - 3 files changed, 54 insertions(+), 51 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 6afd58431111..1cb729a2cd5f 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1087,74 +1087,76 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - return 0; - } - --static void delete_node(struct connection *conn, struct node *node) --{ -- unsigned int i; -- char *name; -- -- /* Delete self, then delete children. If we crash, then the worst -- that can happen is the children will continue to take up space, but -- will otherwise be unreachable. */ -- delete_node_single(conn, node); -- -- /* Delete children, too. */ -- for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { -- struct node *child; -- -- name = talloc_asprintf(node, "%s/%s", node->name, -- node->children + i); -- child = name ? read_node(conn, node, name) : NULL; -- if (child) { -- delete_node(conn, child); -- } -- else { -- trace("delete_node: Error deleting child '%s/%s'!\n", -- node->name, node->children + i); -- /* Skip it, we've already deleted the parent. */ -- } -- talloc_free(name); -- } --} -- -- - /* Delete memory using memmove. */ - static void memdel(void *mem, unsigned off, unsigned len, unsigned total) - { - memmove(mem + off, mem + off + len, total - off - len); - } - -- --static int remove_child_entry(struct connection *conn, struct node *node, -- size_t offset) -+static void remove_child_entry(struct connection *conn, struct node *node, -+ size_t offset) - { - size_t childlen = strlen(node->children + offset); -+ - memdel(node->children, offset, childlen + 1, node->childlen); - node->childlen -= childlen + 1; -- return write_node(conn, node, true); -+ if (write_node(conn, node, true)) -+ corrupt(conn, "Can't update parent node '%s'", node->name); - } - -- --static int delete_child(struct connection *conn, -- struct node *node, const char *childname) -+static void delete_child(struct connection *conn, -+ struct node *node, const char *childname) - { - unsigned int i; - - for (i = 0; i < node->childlen; i += strlen(node->children+i) + 1) { - if (streq(node->children+i, childname)) { -- return remove_child_entry(conn, node, i); -+ remove_child_entry(conn, node, i); -+ return; - } - } - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); -- return ENOENT; - } - -+static int delete_node(struct connection *conn, struct node *parent, -+ struct node *node) -+{ -+ char *name; -+ -+ /* Delete children. */ -+ while (node->childlen) { -+ struct node *child; -+ -+ name = talloc_asprintf(node, "%s/%s", node->name, -+ node->children); -+ child = name ? read_node(conn, node, name) : NULL; -+ if (child) { -+ if (delete_node(conn, node, child)) -+ return errno; -+ } else { -+ trace("delete_node: Error deleting child '%s/%s'!\n", -+ node->name, node->children); -+ /* Quit deleting. */ -+ errno = ENOMEM; -+ return errno; -+ } -+ talloc_free(name); -+ } -+ -+ delete_node_single(conn, node); -+ delete_child(conn, parent, basename(node->name)); -+ talloc_free(node); -+ -+ return 0; -+} - - static int _rm(struct connection *conn, const void *ctx, struct node *node, - const char *name) - { -- /* Delete from parent first, then if we crash, the worst that can -- happen is the child will continue to take up space, but will -- otherwise be unreachable. */ -+ /* -+ * Deleting node by node, so the result is always consistent even in -+ * case of a failure. -+ */ - struct node *parent; - char *parentname = get_parent(ctx, name); - -@@ -1165,11 +1167,13 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - if (!parent) - return (errno == ENOMEM) ? ENOMEM : EINVAL; - -- if (delete_child(conn, parent, basename(name))) -- return EINVAL; -- -- delete_node(conn, node); -- return 0; -+ /* -+ * Fire the watches now, when we can still see the node permissions. -+ * This fine as we are single threaded and the next possible read will -+ * be handled only after the node has been really removed. -+ */ -+ fire_watches(conn, ctx, name, true); -+ return delete_node(conn, parent, node); - } - - -@@ -1207,7 +1211,6 @@ static int do_rm(struct connection *conn, struct buffered_data *in) - if (ret) - return ret; - -- fire_watches(conn, in, name, true); - send_ack(conn, XS_RM); - - return 0; -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f2f1bed47cc6..f0bbfe7a6dc6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -77,7 +77,7 @@ static bool is_child(const char *child, const char *parent) - * Temporary memory allocations are done with ctx. - */ - static void add_event(struct connection *conn, -- void *ctx, -+ const void *ctx, - struct watch *watch, - const char *name) - { -@@ -121,7 +121,7 @@ static void add_event(struct connection *conn, - * Check whether any watch events are to be sent. - * Temporary memory allocations are done with ctx. - */ --void fire_watches(struct connection *conn, void *ctx, const char *name, -+void fire_watches(struct connection *conn, const void *ctx, const char *name, - bool recurse) - { - struct connection *i; -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index c72ea6a68542..54d4ea7e0d41 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -25,7 +25,7 @@ int do_watch(struct connection *conn, struct buffered_data *in); - int do_unwatch(struct connection *conn, struct buffered_data *in); - - /* Fire all watches: recurse means all the children are affected (ie. rm). */ --void fire_watches(struct connection *conn, void *tmp, const char *name, -+void fire_watches(struct connection *conn, const void *tmp, const char *name, - bool recurse); - - void conn_delete_all_watches(struct connection *conn); --- -2.17.1 - diff --git a/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch b/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch deleted file mode 100644 index 739a02f..0000000 --- a/xsa115-4.13-c-0007-tools-xenstore-fire-watches-only-when-removing-a-spe.patch +++ /dev/null @@ -1,118 +0,0 @@ -From 6ca2e14b43aecc79effc1a0cd528a4aceef44d42 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:43 +0200 -Subject: [PATCH 07/10] tools/xenstore: fire watches only when removing a - specific node - -Instead of firing all watches for removing a subtree in one go, do so -only when the related node is being removed. - -The watches for the top-most node being removed include all watches -including that node, while watches for nodes below that are only fired -if they are matching exactly. This avoids firing any watch more than -once when removing a subtree. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 11 ++++++----- - tools/xenstore/xenstored_watch.c | 13 ++++++++----- - tools/xenstore/xenstored_watch.h | 4 ++-- - 3 files changed, 16 insertions(+), 12 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 1cb729a2cd5f..d7c025616ead 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1118,8 +1118,8 @@ static void delete_child(struct connection *conn, - corrupt(conn, "Can't find child '%s' in %s", childname, node->name); - } - --static int delete_node(struct connection *conn, struct node *parent, -- struct node *node) -+static int delete_node(struct connection *conn, const void *ctx, -+ struct node *parent, struct node *node) - { - char *name; - -@@ -1131,7 +1131,7 @@ static int delete_node(struct connection *conn, struct node *parent, - node->children); - child = name ? read_node(conn, node, name) : NULL; - if (child) { -- if (delete_node(conn, node, child)) -+ if (delete_node(conn, ctx, node, child)) - return errno; - } else { - trace("delete_node: Error deleting child '%s/%s'!\n", -@@ -1143,6 +1143,7 @@ static int delete_node(struct connection *conn, struct node *parent, - talloc_free(name); - } - -+ fire_watches(conn, ctx, node->name, true); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1172,8 +1173,8 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - * This fine as we are single threaded and the next possible read will - * be handled only after the node has been really removed. - */ -- fire_watches(conn, ctx, name, true); -- return delete_node(conn, parent, node); -+ fire_watches(conn, ctx, name, false); -+ return delete_node(conn, ctx, parent, node); - } - - -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f0bbfe7a6dc6..3836675459fa 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -122,7 +122,7 @@ static void add_event(struct connection *conn, - * Temporary memory allocations are done with ctx. - */ - void fire_watches(struct connection *conn, const void *ctx, const char *name, -- bool recurse) -+ bool exact) - { - struct connection *i; - struct watch *watch; -@@ -134,10 +134,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - list_for_each_entry(watch, &i->watches, list) { -- if (is_child(name, watch->node)) -- add_event(i, ctx, watch, name); -- else if (recurse && is_child(watch->node, name)) -- add_event(i, ctx, watch, watch->node); -+ if (exact) { -+ if (streq(name, watch->node)) -+ add_event(i, ctx, watch, name); -+ } else { -+ if (is_child(name, watch->node)) -+ add_event(i, ctx, watch, name); -+ } - } - } - } -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 54d4ea7e0d41..1b3c80d3dda1 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -24,9 +24,9 @@ - int do_watch(struct connection *conn, struct buffered_data *in); - int do_unwatch(struct connection *conn, struct buffered_data *in); - --/* Fire all watches: recurse means all the children are affected (ie. rm). */ -+/* Fire all watches: !exact means all the children are affected (ie. rm). */ - void fire_watches(struct connection *conn, const void *tmp, const char *name, -- bool recurse); -+ bool exact); - - void conn_delete_all_watches(struct connection *conn); - --- -2.17.1 - diff --git a/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch b/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch deleted file mode 100644 index 17ba0b3..0000000 --- a/xsa115-4.13-c-0008-tools-xenstore-introduce-node_perms-structure.patch +++ /dev/null @@ -1,289 +0,0 @@ -From 2d4f410899bf59e112c107f371c3d164f8a592f8 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:44 +0200 -Subject: [PATCH 08/10] tools/xenstore: introduce node_perms structure - -There are several places in xenstored using a permission array and the -size of that array. Introduce a new struct node_perms containing both. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Acked-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 79 +++++++++++++++---------------- - tools/xenstore/xenstored_core.h | 8 +++- - tools/xenstore/xenstored_domain.c | 12 ++--- - 3 files changed, 50 insertions(+), 49 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index d7c025616ead..fe9943113b9f 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -401,14 +401,14 @@ static struct node *read_node(struct connection *conn, const void *ctx, - /* Datalen, childlen, number of permissions */ - hdr = (void *)data.dptr; - node->generation = hdr->generation; -- node->num_perms = hdr->num_perms; -+ node->perms.num = hdr->num_perms; - node->datalen = hdr->datalen; - node->childlen = hdr->childlen; - - /* Permissions are struct xs_permissions. */ -- node->perms = hdr->perms; -+ node->perms.p = hdr->perms; - /* Data is binary blob (usually ascii, no nul). */ -- node->data = node->perms + node->num_perms; -+ node->data = node->perms.p + node->perms.num; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -@@ -425,7 +425,7 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - struct xs_tdb_record_hdr *hdr; - - data.dsize = sizeof(*hdr) -- + node->num_perms*sizeof(node->perms[0]) -+ + node->perms.num * sizeof(node->perms.p[0]) - + node->datalen + node->childlen; - - if (!no_quota_check && domain_is_unprivileged(conn) && -@@ -437,12 +437,13 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - data.dptr = talloc_size(node, data.dsize); - hdr = (void *)data.dptr; - hdr->generation = node->generation; -- hdr->num_perms = node->num_perms; -+ hdr->num_perms = node->perms.num; - hdr->datalen = node->datalen; - hdr->childlen = node->childlen; - -- memcpy(hdr->perms, node->perms, node->num_perms*sizeof(node->perms[0])); -- p = hdr->perms + node->num_perms; -+ memcpy(hdr->perms, node->perms.p, -+ node->perms.num * sizeof(*node->perms.p)); -+ p = hdr->perms + node->perms.num; - memcpy(p, node->data, node->datalen); - p += node->datalen; - memcpy(p, node->children, node->childlen); -@@ -468,8 +469,7 @@ static int write_node(struct connection *conn, struct node *node, - } - - static enum xs_perm_type perm_for_conn(struct connection *conn, -- struct xs_permissions *perms, -- unsigned int num) -+ const struct node_perms *perms) - { - unsigned int i; - enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; -@@ -478,16 +478,16 @@ static enum xs_perm_type perm_for_conn(struct connection *conn, - mask &= ~XS_PERM_WRITE; - - /* Owners and tools get it all... */ -- if (!domain_is_unprivileged(conn) || perms[0].id == conn->id -- || (conn->target && perms[0].id == conn->target->id)) -+ if (!domain_is_unprivileged(conn) || perms->p[0].id == conn->id -+ || (conn->target && perms->p[0].id == conn->target->id)) - return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; - -- for (i = 1; i < num; i++) -- if (perms[i].id == conn->id -- || (conn->target && perms[i].id == conn->target->id)) -- return perms[i].perms & mask; -+ for (i = 1; i < perms->num; i++) -+ if (perms->p[i].id == conn->id -+ || (conn->target && perms->p[i].id == conn->target->id)) -+ return perms->p[i].perms & mask; - -- return perms[0].perms & mask; -+ return perms->p[0].perms & mask; - } - - /* -@@ -534,7 +534,7 @@ static int ask_parents(struct connection *conn, const void *ctx, - return 0; - } - -- *perm = perm_for_conn(conn, node->perms, node->num_perms); -+ *perm = perm_for_conn(conn, &node->perms); - return 0; - } - -@@ -580,8 +580,7 @@ struct node *get_node(struct connection *conn, - node = read_node(conn, ctx, name); - /* If we don't have permission, we don't have node. */ - if (node) { -- if ((perm_for_conn(conn, node->perms, node->num_perms) & perm) -- != perm) { -+ if ((perm_for_conn(conn, &node->perms) & perm) != perm) { - errno = EACCES; - node = NULL; - } -@@ -757,16 +756,15 @@ const char *onearg(struct buffered_data *in) - return in->buffer; - } - --static char *perms_to_strings(const void *ctx, -- struct xs_permissions *perms, unsigned int num, -+static char *perms_to_strings(const void *ctx, const struct node_perms *perms, - unsigned int *len) - { - unsigned int i; - char *strings = NULL; - char buffer[MAX_STRLEN(unsigned int) + 1]; - -- for (*len = 0, i = 0; i < num; i++) { -- if (!xs_perm_to_string(&perms[i], buffer, sizeof(buffer))) -+ for (*len = 0, i = 0; i < perms->num; i++) { -+ if (!xs_perm_to_string(&perms->p[i], buffer, sizeof(buffer))) - return NULL; - - strings = talloc_realloc(ctx, strings, char, -@@ -945,13 +943,13 @@ static struct node *construct_node(struct connection *conn, const void *ctx, - goto nomem; - - /* Inherit permissions, except unprivileged domains own what they create */ -- node->num_perms = parent->num_perms; -- node->perms = talloc_memdup(node, parent->perms, -- node->num_perms * sizeof(node->perms[0])); -- if (!node->perms) -+ node->perms.num = parent->perms.num; -+ node->perms.p = talloc_memdup(node, parent->perms.p, -+ node->perms.num * sizeof(*node->perms.p)); -+ if (!node->perms.p) - goto nomem; - if (domain_is_unprivileged(conn)) -- node->perms[0].id = conn->id; -+ node->perms.p[0].id = conn->id; - - /* No children, no data */ - node->children = node->data = NULL; -@@ -1228,7 +1226,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - if (!node) - return errno; - -- strings = perms_to_strings(node, node->perms, node->num_perms, &len); -+ strings = perms_to_strings(node, &node->perms, &len); - if (!strings) - return errno; - -@@ -1239,13 +1237,12 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - - static int do_set_perms(struct connection *conn, struct buffered_data *in) - { -- unsigned int num; -- struct xs_permissions *perms; -+ struct node_perms perms; - char *name, *permstr; - struct node *node; - -- num = xs_count_strings(in->buffer, in->used); -- if (num < 2) -+ perms.num = xs_count_strings(in->buffer, in->used); -+ if (perms.num < 2) - return EINVAL; - - /* First arg is node name. */ -@@ -1256,21 +1253,21 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - return errno; - - permstr = in->buffer + strlen(in->buffer) + 1; -- num--; -+ perms.num--; - -- perms = talloc_array(node, struct xs_permissions, num); -- if (!perms) -+ perms.p = talloc_array(node, struct xs_permissions, perms.num); -+ if (!perms.p) - return ENOMEM; -- if (!xs_strings_to_perms(perms, num, permstr)) -+ if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - - /* Unprivileged domains may not change the owner. */ -- if (domain_is_unprivileged(conn) && perms[0].id != node->perms[0].id) -+ if (domain_is_unprivileged(conn) && -+ perms.p[0].id != node->perms.p[0].id) - return EPERM; - - domain_entry_dec(conn, node); - node->perms = perms; -- node->num_perms = num; - domain_entry_inc(conn, node); - - if (write_node(conn, node, false)) -@@ -1545,8 +1542,8 @@ static void manual_node(const char *name, const char *child) - barf_perror("Could not allocate initial node %s", name); - - node->name = name; -- node->perms = &perms; -- node->num_perms = 1; -+ node->perms.p = &perms; -+ node->perms.num = 1; - node->children = (char *)child; - if (child) - node->childlen = strlen(child) + 1; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 3cb1c235a101..193d93142636 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -109,6 +109,11 @@ struct connection - }; - extern struct list_head connections; - -+struct node_perms { -+ unsigned int num; -+ struct xs_permissions *p; -+}; -+ - struct node { - const char *name; - -@@ -120,8 +125,7 @@ struct node { - #define NO_GENERATION ~((uint64_t)0) - - /* Permissions. */ -- unsigned int num_perms; -- struct xs_permissions *perms; -+ struct node_perms perms; - - /* Contents. */ - unsigned int datalen; -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 0e2926e2a3d0..dc51cdfa9aa7 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -657,12 +657,12 @@ void domain_entry_inc(struct connection *conn, struct node *node) - if (!conn) - return; - -- if (node->perms && node->perms[0].id != conn->id) { -+ if (node->perms.p && node->perms.p[0].id != conn->id) { - if (conn->transaction) { - transaction_entry_inc(conn->transaction, -- node->perms[0].id); -+ node->perms.p[0].id); - } else { -- d = find_domain_by_domid(node->perms[0].id); -+ d = find_domain_by_domid(node->perms.p[0].id); - if (d) - d->nbentry++; - } -@@ -683,12 +683,12 @@ void domain_entry_dec(struct connection *conn, struct node *node) - if (!conn) - return; - -- if (node->perms && node->perms[0].id != conn->id) { -+ if (node->perms.p && node->perms.p[0].id != conn->id) { - if (conn->transaction) { - transaction_entry_dec(conn->transaction, -- node->perms[0].id); -+ node->perms.p[0].id); - } else { -- d = find_domain_by_domid(node->perms[0].id); -+ d = find_domain_by_domid(node->perms.p[0].id); - if (d && d->nbentry) - d->nbentry--; - } --- -2.17.1 - diff --git a/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch b/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch deleted file mode 100644 index 7804103..0000000 --- a/xsa115-4.13-c-0009-tools-xenstore-allow-special-watches-for-privileged-.patch +++ /dev/null @@ -1,237 +0,0 @@ -From cddf74031b3c8a108e8fd7db0bf56e9c2809d3e2 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:45 +0200 -Subject: [PATCH 09/10] tools/xenstore: allow special watches for privileged - callers only - -The special watches "@introduceDomain" and "@releaseDomain" should be -allowed for privileged callers only, as they allow to gain information -about presence of other guests on the host. So send watch events for -those watches via privileged connections only. - -In order to allow for disaggregated setups where e.g. driver domains -need to make use of those special watches add support for calling -"set permissions" for those special nodes, too. - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - docs/misc/xenstore.txt | 5 +++ - tools/xenstore/xenstored_core.c | 27 ++++++++------ - tools/xenstore/xenstored_core.h | 2 ++ - tools/xenstore/xenstored_domain.c | 60 +++++++++++++++++++++++++++++++ - tools/xenstore/xenstored_domain.h | 5 +++ - tools/xenstore/xenstored_watch.c | 4 +++ - 6 files changed, 93 insertions(+), 10 deletions(-) - -diff --git a/docs/misc/xenstore.txt b/docs/misc/xenstore.txt -index 6f8569d5760f..32969eb3fecd 100644 ---- a/docs/misc/xenstore.txt -+++ b/docs/misc/xenstore.txt -@@ -170,6 +170,9 @@ SET_PERMS ||+? - n no access - See http://wiki.xen.org/wiki/XenBus section - `Permissions' for details of the permissions system. -+ It is possible to set permissions for the special watch paths -+ "@introduceDomain" and "@releaseDomain" to enable receiving those -+ watches in unprivileged domains. - - ---------- Watches ---------- - -@@ -194,6 +197,8 @@ WATCH ||? - @releaseDomain occurs on any domain crash or - shutdown, and also on RELEASE - and domain destruction -+ events are sent to privileged callers or explicitly -+ via SET_PERMS enabled domains only. - - When a watch is first set up it is triggered once straight - away, with equal to . Watches may be triggered -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index fe9943113b9f..720bec269dd3 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -468,8 +468,8 @@ static int write_node(struct connection *conn, struct node *node, - return write_node_raw(conn, &key, node, no_quota_check); - } - --static enum xs_perm_type perm_for_conn(struct connection *conn, -- const struct node_perms *perms) -+enum xs_perm_type perm_for_conn(struct connection *conn, -+ const struct node_perms *perms) - { - unsigned int i; - enum xs_perm_type mask = XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER; -@@ -1245,22 +1245,29 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (perms.num < 2) - return EINVAL; - -- /* First arg is node name. */ -- /* We must own node to do this (tools can do this too). */ -- node = get_node_canonicalized(conn, in, in->buffer, &name, -- XS_PERM_WRITE | XS_PERM_OWNER); -- if (!node) -- return errno; -- - permstr = in->buffer + strlen(in->buffer) + 1; - perms.num--; - -- perms.p = talloc_array(node, struct xs_permissions, perms.num); -+ perms.p = talloc_array(in, struct xs_permissions, perms.num); - if (!perms.p) - return ENOMEM; - if (!xs_strings_to_perms(perms.p, perms.num, permstr)) - return errno; - -+ /* First arg is node name. */ -+ if (strstarts(in->buffer, "@")) { -+ if (set_perms_special(conn, in->buffer, &perms)) -+ return errno; -+ send_ack(conn, XS_SET_PERMS); -+ return 0; -+ } -+ -+ /* We must own node to do this (tools can do this too). */ -+ node = get_node_canonicalized(conn, in, in->buffer, &name, -+ XS_PERM_WRITE | XS_PERM_OWNER); -+ if (!node) -+ return errno; -+ - /* Unprivileged domains may not change the owner. */ - if (domain_is_unprivileged(conn) && - perms.p[0].id != node->perms.p[0].id) -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index 193d93142636..f3da6bbc943d 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -165,6 +165,8 @@ struct node *get_node(struct connection *conn, - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); - void check_store(void); - void corrupt(struct connection *conn, const char *fmt, ...); -+enum xs_perm_type perm_for_conn(struct connection *conn, -+ const struct node_perms *perms); - - /* Is this a valid node name? */ - bool is_valid_nodename(const char *node); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index dc51cdfa9aa7..7afabe0ae084 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -41,6 +41,9 @@ static evtchn_port_t virq_port; - - xenevtchn_handle *xce_handle = NULL; - -+static struct node_perms dom_release_perms; -+static struct node_perms dom_introduce_perms; -+ - struct domain - { - struct list_head list; -@@ -589,6 +592,59 @@ void restore_existing_connections(void) - { - } - -+static int set_dom_perms_default(struct node_perms *perms) -+{ -+ perms->num = 1; -+ perms->p = talloc_array(NULL, struct xs_permissions, perms->num); -+ if (!perms->p) -+ return -1; -+ perms->p->id = 0; -+ perms->p->perms = XS_PERM_NONE; -+ -+ return 0; -+} -+ -+static struct node_perms *get_perms_special(const char *name) -+{ -+ if (!strcmp(name, "@releaseDomain")) -+ return &dom_release_perms; -+ if (!strcmp(name, "@introduceDomain")) -+ return &dom_introduce_perms; -+ return NULL; -+} -+ -+int set_perms_special(struct connection *conn, const char *name, -+ struct node_perms *perms) -+{ -+ struct node_perms *p; -+ -+ p = get_perms_special(name); -+ if (!p) -+ return EINVAL; -+ -+ if ((perm_for_conn(conn, p) & (XS_PERM_WRITE | XS_PERM_OWNER)) != -+ (XS_PERM_WRITE | XS_PERM_OWNER)) -+ return EACCES; -+ -+ p->num = perms->num; -+ talloc_free(p->p); -+ p->p = perms->p; -+ talloc_steal(NULL, perms->p); -+ -+ return 0; -+} -+ -+bool check_perms_special(const char *name, struct connection *conn) -+{ -+ struct node_perms *p; -+ -+ p = get_perms_special(name); -+ if (!p) -+ return false; -+ -+ return perm_for_conn(conn, p) & XS_PERM_READ; -+} -+ - static int dom0_init(void) - { - evtchn_port_t port; -@@ -610,6 +666,10 @@ static int dom0_init(void) - - xenevtchn_notify(xce_handle, dom0->port); - -+ if (set_dom_perms_default(&dom_release_perms) || -+ set_dom_perms_default(&dom_introduce_perms)) -+ return -1; -+ - return 0; - } - -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 56ae01597475..259183962a9c 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -65,6 +65,11 @@ void domain_watch_inc(struct connection *conn); - void domain_watch_dec(struct connection *conn); - int domain_watch(struct connection *conn); - -+/* Special node permission handling. */ -+int set_perms_special(struct connection *conn, const char *name, -+ struct node_perms *perms); -+bool check_perms_special(const char *name, struct connection *conn); -+ - /* Write rate limiting */ - - #define WRL_FACTOR 1000 /* for fixed-point arithmetic */ -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 3836675459fa..f4e289362eb6 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -133,6 +133,10 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { -+ /* introduce/release domain watches */ -+ if (check_special_event(name) && !check_perms_special(name, i)) -+ continue; -+ - list_for_each_entry(watch, &i->watches, list) { - if (exact) { - if (streq(name, watch->node)) --- -2.17.1 - diff --git a/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch b/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch deleted file mode 100644 index 8ad1aca..0000000 --- a/xsa115-4.13-c-0010-tools-xenstore-avoid-watch-events-for-nodes-without-.patch +++ /dev/null @@ -1,375 +0,0 @@ -From e57b7687b43b033fe45e755e285efbe67bc71921 Mon Sep 17 00:00:00 2001 -From: Juergen Gross -Date: Thu, 11 Jun 2020 16:12:46 +0200 -Subject: [PATCH 10/10] tools/xenstore: avoid watch events for nodes without - access - -Today watch events are sent regardless of the access rights of the -node the event is sent for. This enables any guest to e.g. setup a -watch for "/" in order to have a detailed record of all Xenstore -modifications. - -Modify that by sending only watch events for nodes that the watcher -has a chance to see otherwise (either via direct reads or by querying -the children of a node). This includes cases where the visibility of -a node for a watcher is changing (permissions being removed). - -This is part of XSA-115. - -Signed-off-by: Juergen Gross -[julieng: Handle rebase conflict] -Reviewed-by: Julien Grall -Reviewed-by: Paul Durrant ---- - tools/xenstore/xenstored_core.c | 28 +++++----- - tools/xenstore/xenstored_core.h | 15 ++++-- - tools/xenstore/xenstored_domain.c | 6 +-- - tools/xenstore/xenstored_transaction.c | 21 +++++++- - tools/xenstore/xenstored_watch.c | 75 +++++++++++++++++++------- - tools/xenstore/xenstored_watch.h | 2 +- - 6 files changed, 104 insertions(+), 43 deletions(-) - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 720bec269dd3..1c2845454560 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -358,8 +358,8 @@ static void initialize_fds(int sock, int *p_sock_pollfd_idx, - * If it fails, returns NULL and sets errno. - * Temporary memory allocations will be done with ctx. - */ --static struct node *read_node(struct connection *conn, const void *ctx, -- const char *name) -+struct node *read_node(struct connection *conn, const void *ctx, -+ const char *name) - { - TDB_DATA key, data; - struct xs_tdb_record_hdr *hdr; -@@ -494,7 +494,7 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - * Get name of node parent. - * Temporary memory allocations are done with ctx. - */ --static char *get_parent(const void *ctx, const char *node) -+char *get_parent(const void *ctx, const char *node) - { - char *parent; - char *slash = strrchr(node + 1, '/'); -@@ -566,10 +566,10 @@ static int errno_from_parents(struct connection *conn, const void *ctx, - * If it fails, returns NULL and sets errno. - * Temporary memory allocations are done with ctx. - */ --struct node *get_node(struct connection *conn, -- const void *ctx, -- const char *name, -- enum xs_perm_type perm) -+static struct node *get_node(struct connection *conn, -+ const void *ctx, -+ const char *name, -+ enum xs_perm_type perm) - { - struct node *node; - -@@ -1056,7 +1056,7 @@ static int do_write(struct connection *conn, struct buffered_data *in) - return errno; - } - -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, NULL); - send_ack(conn, XS_WRITE); - - return 0; -@@ -1078,7 +1078,7 @@ static int do_mkdir(struct connection *conn, struct buffered_data *in) - node = create_node(conn, in, name, NULL, 0); - if (!node) - return errno; -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, NULL); - } - send_ack(conn, XS_MKDIR); - -@@ -1141,7 +1141,7 @@ static int delete_node(struct connection *conn, const void *ctx, - talloc_free(name); - } - -- fire_watches(conn, ctx, node->name, true); -+ fire_watches(conn, ctx, node->name, node, true, NULL); - delete_node_single(conn, node); - delete_child(conn, parent, basename(node->name)); - talloc_free(node); -@@ -1165,13 +1165,14 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, - parent = read_node(conn, ctx, parentname); - if (!parent) - return (errno == ENOMEM) ? ENOMEM : EINVAL; -+ node->parent = parent; - - /* - * Fire the watches now, when we can still see the node permissions. - * This fine as we are single threaded and the next possible read will - * be handled only after the node has been really removed. - */ -- fire_watches(conn, ctx, name, false); -+ fire_watches(conn, ctx, name, node, false, NULL); - return delete_node(conn, ctx, parent, node); - } - -@@ -1237,7 +1238,7 @@ static int do_get_perms(struct connection *conn, struct buffered_data *in) - - static int do_set_perms(struct connection *conn, struct buffered_data *in) - { -- struct node_perms perms; -+ struct node_perms perms, old_perms; - char *name, *permstr; - struct node *node; - -@@ -1273,6 +1274,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - perms.p[0].id != node->perms.p[0].id) - return EPERM; - -+ old_perms = node->perms; - domain_entry_dec(conn, node); - node->perms = perms; - domain_entry_inc(conn, node); -@@ -1280,7 +1282,7 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (write_node(conn, node, false)) - return errno; - -- fire_watches(conn, in, name, false); -+ fire_watches(conn, in, name, node, false, &old_perms); - send_ack(conn, XS_SET_PERMS); - - return 0; -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index f3da6bbc943d..e050b27cbdde 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -152,15 +152,17 @@ void send_ack(struct connection *conn, enum xsd_sockmsg_type type); - /* Canonicalize this path if possible. */ - char *xenstore_canonicalize(struct connection *conn, const void *ctx, const char *node); - -+/* Get access permissions. */ -+enum xs_perm_type perm_for_conn(struct connection *conn, -+ const struct node_perms *perms); -+ - /* Write a node to the tdb data base. */ - int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - bool no_quota_check); - --/* Get this node, checking we have permissions. */ --struct node *get_node(struct connection *conn, -- const void *ctx, -- const char *name, -- enum xs_perm_type perm); -+/* Get a node from the tdb data base. */ -+struct node *read_node(struct connection *conn, const void *ctx, -+ const char *name); - - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read); - void check_store(void); -@@ -171,6 +173,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - /* Is this a valid node name? */ - bool is_valid_nodename(const char *node); - -+/* Get name of parent node. */ -+char *get_parent(const void *ctx, const char *node); -+ - /* Tracing infrastructure. */ - void trace_create(const void *data, const char *type); - void trace_destroy(const void *data, const char *type); -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 7afabe0ae084..711a11b18ad6 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -206,7 +206,7 @@ static int destroy_domain(void *_domain) - unmap_interface(domain->interface); - } - -- fire_watches(NULL, domain, "@releaseDomain", false); -+ fire_watches(NULL, domain, "@releaseDomain", NULL, false, NULL); - - wrl_domain_destroy(domain); - -@@ -244,7 +244,7 @@ static void domain_cleanup(void) - } - - if (notify) -- fire_watches(NULL, NULL, "@releaseDomain", false); -+ fire_watches(NULL, NULL, "@releaseDomain", NULL, false, NULL); - } - - /* We scan all domains rather than use the information given here. */ -@@ -410,7 +410,7 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - /* Now domain belongs to its connection. */ - talloc_steal(domain->conn, domain); - -- fire_watches(NULL, in, "@introduceDomain", false); -+ fire_watches(NULL, in, "@introduceDomain", NULL, false, NULL); - } else if ((domain->mfn == mfn) && (domain->conn != conn)) { - /* Use XS_INTRODUCE for recreating the xenbus event-channel. */ - if (domain->port) -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index e87897573469..a7d8c5d475ec 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -114,6 +114,9 @@ struct accessed_node - /* Generation count (or NO_GENERATION) for conflict checking. */ - uint64_t generation; - -+ /* Original node permissions. */ -+ struct node_perms perms; -+ - /* Generation count checking required? */ - bool check_gen; - -@@ -260,6 +263,15 @@ int access_node(struct connection *conn, struct node *node, - i->node = talloc_strdup(i, node->name); - if (!i->node) - goto nomem; -+ if (node->generation != NO_GENERATION && node->perms.num) { -+ i->perms.p = talloc_array(i, struct xs_permissions, -+ node->perms.num); -+ if (!i->perms.p) -+ goto nomem; -+ i->perms.num = node->perms.num; -+ memcpy(i->perms.p, node->perms.p, -+ i->perms.num * sizeof(*i->perms.p)); -+ } - - introduce = true; - i->ta_node = false; -@@ -368,9 +380,14 @@ static int finalize_transaction(struct connection *conn, - talloc_free(data.dptr); - if (ret) - goto err; -- } else if (tdb_delete(tdb_ctx, key)) -+ fire_watches(conn, trans, i->node, NULL, false, -+ i->perms.p ? &i->perms : NULL); -+ } else { -+ fire_watches(conn, trans, i->node, NULL, false, -+ i->perms.p ? &i->perms : NULL); -+ if (tdb_delete(tdb_ctx, key)) - goto err; -- fire_watches(conn, trans, i->node, false); -+ } - } - - if (i->ta_node && tdb_delete(tdb_ctx, ta_key)) -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index f4e289362eb6..71c108ea99f1 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -85,22 +85,6 @@ static void add_event(struct connection *conn, - unsigned int len; - char *data; - -- if (!check_special_event(name)) { -- /* Can this conn load node, or see that it doesn't exist? */ -- struct node *node = get_node(conn, ctx, name, XS_PERM_READ); -- /* -- * XXX We allow EACCES here because otherwise a non-dom0 -- * backend driver cannot watch for disappearance of a frontend -- * xenstore directory. When the directory disappears, we -- * revert to permissions of the parent directory for that path, -- * which will typically disallow access for the backend. -- * But this breaks device-channel teardown! -- * Really we should fix this better... -- */ -- if (!node && errno != ENOENT && errno != EACCES) -- return; -- } -- - if (watch->relative_path) { - name += strlen(watch->relative_path); - if (*name == '/') /* Could be "" */ -@@ -117,12 +101,60 @@ static void add_event(struct connection *conn, - talloc_free(data); - } - -+/* -+ * Check permissions of a specific watch to fire: -+ * Either the node itself or its parent have to be readable by the connection -+ * the watch has been setup for. In case a watch event is created due to -+ * changed permissions we need to take the old permissions into account, too. -+ */ -+static bool watch_permitted(struct connection *conn, const void *ctx, -+ const char *name, struct node *node, -+ struct node_perms *perms) -+{ -+ enum xs_perm_type perm; -+ struct node *parent; -+ char *parent_name; -+ -+ if (perms) { -+ perm = perm_for_conn(conn, perms); -+ if (perm & XS_PERM_READ) -+ return true; -+ } -+ -+ if (!node) { -+ node = read_node(conn, ctx, name); -+ if (!node) -+ return false; -+ } -+ -+ perm = perm_for_conn(conn, &node->perms); -+ if (perm & XS_PERM_READ) -+ return true; -+ -+ parent = node->parent; -+ if (!parent) { -+ parent_name = get_parent(ctx, node->name); -+ if (!parent_name) -+ return false; -+ parent = read_node(conn, ctx, parent_name); -+ if (!parent) -+ return false; -+ } -+ -+ perm = perm_for_conn(conn, &parent->perms); -+ -+ return perm & XS_PERM_READ; -+} -+ - /* - * Check whether any watch events are to be sent. - * Temporary memory allocations are done with ctx. -+ * We need to take the (potential) old permissions of the node into account -+ * as a watcher losing permissions to access a node should receive the -+ * watch event, too. - */ - void fire_watches(struct connection *conn, const void *ctx, const char *name, -- bool exact) -+ struct node *node, bool exact, struct node_perms *perms) - { - struct connection *i; - struct watch *watch; -@@ -134,8 +166,13 @@ void fire_watches(struct connection *conn, const void *ctx, const char *name, - /* Create an event for each watch. */ - list_for_each_entry(i, &connections, list) { - /* introduce/release domain watches */ -- if (check_special_event(name) && !check_perms_special(name, i)) -- continue; -+ if (check_special_event(name)) { -+ if (!check_perms_special(name, i)) -+ continue; -+ } else { -+ if (!watch_permitted(i, ctx, name, node, perms)) -+ continue; -+ } - - list_for_each_entry(watch, &i->watches, list) { - if (exact) { -diff --git a/tools/xenstore/xenstored_watch.h b/tools/xenstore/xenstored_watch.h -index 1b3c80d3dda1..03094374f379 100644 ---- a/tools/xenstore/xenstored_watch.h -+++ b/tools/xenstore/xenstored_watch.h -@@ -26,7 +26,7 @@ int do_unwatch(struct connection *conn, struct buffered_data *in); - - /* Fire all watches: !exact means all the children are affected (ie. rm). */ - void fire_watches(struct connection *conn, const void *tmp, const char *name, -- bool exact); -+ struct node *node, bool exact, struct node_perms *perms); - - void conn_delete_all_watches(struct connection *conn); - --- -2.17.1 - diff --git a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch b/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch deleted file mode 100644 index 0072c68..0000000 --- a/xsa115-o-0001-tools-ocaml-xenstored-ignore-transaction-id-for-un-w.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: ignore transaction id for [un]watch -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Instead of ignoring the transaction id for XS_WATCH and XS_UNWATCH -commands as it is documented in docs/misc/xenstore.txt, it is tested -for validity today. - -Really ignore the transaction id for XS_WATCH and XS_UNWATCH. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index ff5c9484fc..2fa6798e3b 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -498,12 +498,19 @@ let retain_op_in_history ty = - | Xenbus.Xb.Op.Reset_watches - | Xenbus.Xb.Op.Invalid -> false - -+let maybe_ignore_transaction = function -+ | Xenbus.Xb.Op.Watch | Xenbus.Xb.Op.Unwatch -> fun tid -> -+ if tid <> Transaction.none then -+ debug "Ignoring transaction ID %d for watch/unwatch" tid; -+ Transaction.none -+ | _ -> fun x -> x -+ - (** - * Nothrow guarantee. - *) - let process_packet ~store ~cons ~doms ~con ~req = - let ty = req.Packet.ty in -- let tid = req.Packet.tid in -+ let tid = maybe_ignore_transaction ty req.Packet.tid in - let rid = req.Packet.rid in - try - let fct = function_of_type ty in diff --git a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch b/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch deleted file mode 100644 index 26033c7..0000000 --- a/xsa115-o-0002-tools-ocaml-xenstored-check-privilege-for-XS_IS_DOMA.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: check privilege for XS_IS_DOMAIN_INTRODUCED -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The Xenstore command XS_IS_DOMAIN_INTRODUCED should be possible for privileged -domains only (the only user in the tree is the xenpaging daemon). - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 2fa6798e3b..fd79ef564f 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -166,7 +166,9 @@ let do_setperms con t _domains _cons data = - let do_error _con _t _domains _cons _data = - raise Define.Unknown_operation - --let do_isintroduced _con _t domains _cons data = -+let do_isintroduced con _t domains _cons data = -+ if not (Connection.is_dom0 con) -+ then raise Define.Permission_denied; - let domid = - match (split None '\000' data) with - | domid :: _ -> int_of_string domid diff --git a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch b/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch deleted file mode 100644 index fea94a9..0000000 --- a/xsa115-o-0003-tools-ocaml-xenstored-unify-watch-firing.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: unify watch firing -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This will make it easier insert additional checks in a follow-up patch. -All watches are now fired from a single function. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 24750ada43..e5df62d9e7 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -210,8 +210,7 @@ let fire_watch watch path = - end else - path - in -- let data = Utils.join_by_null [ new_path; watch.token; "" ] in -- send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data -+ fire_single_watch { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = diff --git a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch b/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch deleted file mode 100644 index 76f98e9..0000000 --- a/xsa115-o-0004-tools-ocaml-xenstored-introduce-permissions-for-spec.patch +++ /dev/null @@ -1,117 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: introduce permissions for special watches -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The special watches "@introduceDomain" and "@releaseDomain" should be -allowed for privileged callers only, as they allow to gain information -about presence of other guests on the host. So send watch events for -those watches via privileged connections only. - -Start to address this by treating the special watches as regular nodes -in the tree, which gives them normal semantics for permissions. A later -change will restrict the handling, so that they can't be listed, etc. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index fd79ef564f..e528d1ecb2 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -420,7 +420,7 @@ let do_introduce con _t domains cons data = - else try - let ndom = Domains.create domains domid mfn port in - Connections.add_domain cons ndom; -- Connections.fire_spec_watches cons "@introduceDomain"; -+ Connections.fire_spec_watches cons Store.Path.introduce_domain; - ndom - with _ -> raise Invalid_Cmd_Args - in -@@ -439,7 +439,7 @@ let do_release con _t domains cons data = - Domains.del domains domid; - Connections.del_domain cons domid; - if fire_spec_watches -- then Connections.fire_spec_watches cons "@releaseDomain" -+ then Connections.fire_spec_watches cons Store.Path.release_domain - else raise Invalid_Cmd_Args - - let do_resume con _t domains _cons data = -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 92b6289b5e..52b88b3ee1 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -214,6 +214,11 @@ let rec lookup node path fct = - - let apply rnode path fct = - lookup rnode path fct -+ -+let introduce_domain = "@introduceDomain" -+let release_domain = "@releaseDomain" -+let specials = List.map of_string [ introduce_domain; release_domain ] -+ - end - - (* The Store.t type *) -diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml -index b252db799b..e8c9fe4e94 100644 ---- a/tools/ocaml/xenstored/utils.ml -+++ b/tools/ocaml/xenstored/utils.ml -@@ -88,19 +88,17 @@ let read_file_single_integer filename = - Unix.close fd; - int_of_string (Bytes.sub_string buf 0 sz) - --let path_complete path connection_path = -- if String.get path 0 <> '/' then -- connection_path ^ path -- else -- path -- -+(* @path may be guest data and needs its length validating. @connection_path -+ * is generated locally in xenstored and always of the form "/local/domain/$N/" *) - let path_validate path connection_path = -- if String.length path = 0 || String.length path > 1024 then -- raise Define.Invalid_path -- else -- let cpath = path_complete path connection_path in -- if String.get cpath 0 <> '/' then -- raise Define.Invalid_path -- else -- cpath -+ let len = String.length path in -+ -+ if len = 0 || len > 1024 then raise Define.Invalid_path; -+ -+ let abs_path = -+ match String.get path 0 with -+ | '/' | '@' -> path -+ | _ -> connection_path ^ path -+ in - -+ abs_path -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 7e7824761b..8d0c50bfa4 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -286,6 +286,8 @@ let _ = - let quit = ref false in - - Logging.init_xenstored_log(); -+ List.iter (fun path -> -+ Store.write store Perms.Connection.full_rights path "") Store.Path.specials; - - let filename = Paths.xen_run_stored ^ "/db" in - if cf.restart && Sys.file_exists filename then ( -@@ -335,7 +337,7 @@ let _ = - let (notify, deaddom) = Domains.cleanup domains in - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then -- Connections.fire_spec_watches cons "@releaseDomain" -+ Connections.fire_spec_watches cons Store.Path.release_domain - ) - else - let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch b/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch deleted file mode 100644 index 866d415..0000000 --- a/xsa115-o-0005-tools-ocaml-xenstored-avoid-watch-events-for-nodes-w.patch +++ /dev/null @@ -1,406 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: avoid watch events for nodes without access -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Today watch events are sent regardless of the access rights of the -node the event is sent for. This enables any guest to e.g. setup a -watch for "/" in order to have a detailed record of all Xenstore -modifications. - -Modify that by sending only watch events for nodes that the watcher -has a chance to see otherwise (either via direct reads or by querying -the children of a node). This includes cases where the visibility of -a node for a watcher is changing (permissions being removed). - -Permissions for nodes are looked up either in the old (pre -transaction/command) or current trees (post transaction). If -permissions are changed multiple times in a transaction only the final -version is checked, because considering a transaction atomic the -individual permission changes would not be noticable to an outside -observer. - -Two trees are only needed for set_perms: here we can either notice the -node disappearing (if we loose permission), appearing -(if we gain permission), or changing (if we preserve permission). - -RM needs to only look at the old tree: in the new tree the node would be -gone, or could have different permissions if it was recreated (the -recreation would get its own watch fired). - -Inside a tree we lookup the watch path's parent, and then the watch path -child itself. This gets us 4 sets of permissions in worst case, and if -either of these allows a watch, then we permit it to fire. The -permission lookups are done without logging the failures, otherwise we'd -get confusing errors about permission denied for some paths, but a watch -still firing. The actual result is logged in xenstored-access log: - - 'w event ...' as usual if watch was fired - 'w notfired...' if the watch was not fired, together with path and - permission set to help in troubleshooting - -Adding a watch bypasses permission checks and always fires the watch -once immediately. This is consistent with the specification, and no -information is gained (the watch is fired both if the path exists or -doesn't, and both if you have or don't have access, i.e. it reflects the -path a domain gave it back to that domain). - -There are some semantic changes here: - - * Write+rm in a single transaction of the same path is unobservable - now via watches: both before and after a transaction the path - doesn't exist, thus both tree lookups come up with the empty - permission set, and noone, not even Dom0 can see this. This is - consistent with transaction atomicity though. - * Similar to above if we temporarily grant and then revoke permission - on a path any watches fired inbetween are ignored as well - * There is a new log event (w notfired) which shows the permission set - of the path, and the path. - * Watches on paths that a domain doesn't have access to are now not - seen, which is the purpose of the security fix. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index e5df62d9e7..644a448f2e 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -196,11 +196,36 @@ let list_watches con = - con.watches [] in - List.concat ll - --let fire_single_watch watch = -+let dbg fmt = Logging.debug "connection" fmt -+let info fmt = Logging.info "connection" fmt -+ -+let lookup_watch_perm path = function -+| None -> [] -+| Some root -> -+ try Store.Path.apply root path @@ fun parent name -> -+ Store.Node.get_perms parent :: -+ try [Store.Node.get_perms (Store.Node.find parent name)] -+ with Not_found -> [] -+ with Define.Invalid_path | Not_found -> [] -+ -+let lookup_watch_perms oldroot root path = -+ lookup_watch_perm path oldroot @ lookup_watch_perm path (Some root) -+ -+let fire_single_watch_unchecked watch = - let data = Utils.join_by_null [watch.path; watch.token; ""] in - send_reply watch.con Transaction.none 0 Xenbus.Xb.Op.Watchevent data - --let fire_watch watch path = -+let fire_single_watch (oldroot, root) watch = -+ let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in -+ let perms = lookup_watch_perms oldroot root abspath in -+ if List.exists (Perms.has watch.con.perm READ) perms then -+ fire_single_watch_unchecked watch -+ else -+ let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in -+ let con = get_domstr watch.con in -+ Logging.watch_not_fired ~con perms (Store.Path.to_string abspath) -+ -+let fire_watch roots watch path = - let new_path = - if watch.is_relative && path.[0] = '/' - then begin -@@ -210,7 +235,7 @@ let fire_watch watch path = - end else - path - in -- fire_single_watch { watch with path = new_path } -+ fire_single_watch roots { watch with path = new_path } - - (* Search for a valid unused transaction id. *) - let rec valid_transaction_id con proposed_id = -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index f2c4318c88..9f9f7ee2f0 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -135,25 +135,26 @@ let del_watch cons con path token = - watch - - (* path is absolute *) --let fire_watches cons path recurse = -+let fire_watches ?oldroot root cons path recurse = - let key = key_of_path path in - let path = Store.Path.to_string path in -+ let roots = oldroot, root in - let fire_watch _ = function - | None -> () -- | Some watches -> List.iter (fun w -> Connection.fire_watch w path) watches -+ | Some watches -> List.iter (fun w -> Connection.fire_watch roots w path) watches - in - let fire_rec _x = function - | None -> () - | Some watches -> -- List.iter (fun w -> Connection.fire_single_watch w) watches -+ List.iter (Connection.fire_single_watch roots) watches - in - Trie.iter_path fire_watch cons.watches key; - if recurse then - Trie.iter fire_rec (Trie.sub cons.watches key) - --let fire_spec_watches cons specpath = -+let fire_spec_watches root cons specpath = - iter cons (fun con -> -- List.iter (fun w -> Connection.fire_single_watch w) (Connection.get_watches con specpath)) -+ List.iter (Connection.fire_single_watch (None, root)) (Connection.get_watches con specpath)) - - let set_target cons domain target_domain = - let con = find_domain cons domain in -diff --git a/tools/ocaml/xenstored/logging.ml b/tools/ocaml/xenstored/logging.ml -index c5cba79e92..1ede131329 100644 ---- a/tools/ocaml/xenstored/logging.ml -+++ b/tools/ocaml/xenstored/logging.ml -@@ -161,6 +161,8 @@ let xenstored_log_nb_lines = ref 13215 - let xenstored_log_nb_chars = ref (-1) - let xenstored_logger = ref (None: logger option) - -+let debug_enabled () = !xenstored_log_level = Debug -+ - let set_xenstored_log_destination s = - xenstored_log_destination := log_destination_of_string s - -@@ -204,6 +206,7 @@ type access_type = - | Commit - | Newconn - | Endconn -+ | Watch_not_fired - | XbOp of Xenbus.Xb.Op.operation - - let string_of_tid ~con tid = -@@ -217,6 +220,7 @@ let string_of_access_type = function - | Commit -> "commit " - | Newconn -> "newconn " - | Endconn -> "endconn " -+ | Watch_not_fired -> "w notfired" - - | XbOp op -> match op with - | Xenbus.Xb.Op.Debug -> "debug " -@@ -331,3 +335,7 @@ let xb_answer ~tid ~con ~ty data = - | _ -> false, Debug - in - if print then access_logging ~tid ~con ~data (XbOp ty) ~level -+ -+let watch_not_fired ~con perms path = -+ let data = Printf.sprintf "EPERM perms=[%s] path=%s" perms path in -+ access_logging ~tid:0 ~con ~data Watch_not_fired ~level:Info -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index 3ea193ea14..23b80aba3d 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -79,9 +79,9 @@ let of_string s = - let string_of_perm perm = - Printf.sprintf "%c%u" (char_of_permty (snd perm)) (fst perm) - --let to_string permvec = -+let to_string ?(sep="\000") permvec = - let l = ((permvec.owner, permvec.other) :: permvec.acl) in -- String.concat "\000" (List.map string_of_perm l) -+ String.concat sep (List.map string_of_perm l) - - end - -@@ -132,8 +132,8 @@ let check_owner (connection:Connection.t) (node:Node.t) = - then Connection.is_owner connection (Node.get_owner node) - else true - --(* check if the current connection has the requested perm on the current node *) --let check (connection:Connection.t) request (node:Node.t) = -+(* check if the current connection lacks the requested perm on the current node *) -+let lacks (connection:Connection.t) request (node:Node.t) = - let check_acl domainid = - let perm = - if List.mem_assoc domainid (Node.get_acl node) -@@ -154,11 +154,19 @@ let check (connection:Connection.t) request (node:Node.t) = - info "Permission denied: Domain %d has write only access" domainid; - false - in -- if !activate -+ !activate - && not (Connection.is_dom0 connection) - && not (check_owner connection node) - && not (List.exists check_acl (Connection.get_owners connection)) -+ -+(* check if the current connection has the requested perm on the current node. -+* Raises an exception if it doesn't. *) -+let check connection request node = -+ if lacks connection request node - then raise Define.Permission_denied - -+(* check if the current connection has the requested perm on the current node *) -+let has connection request node = not (lacks connection request node) -+ - let equiv perm1 perm2 = - (Node.to_string perm1) = (Node.to_string perm2) -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index e528d1ecb2..f99b9e935c 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -56,15 +56,17 @@ let split_one_path data con = - | path :: "" :: [] -> Store.Path.create path (Connection.get_path con) - | _ -> raise Invalid_Cmd_Args - --let process_watch ops cons = -+let process_watch t cons = -+ let oldroot = t.Transaction.oldroot in -+ let newroot = Store.get_root t.store in -+ let ops = Transaction.get_paths t |> List.rev in - let do_op_watch op cons = -- let recurse = match (fst op) with -- | Xenbus.Xb.Op.Write -> false -- | Xenbus.Xb.Op.Mkdir -> false -- | Xenbus.Xb.Op.Rm -> true -- | Xenbus.Xb.Op.Setperms -> false -+ let recurse, oldroot, root = match (fst op) with -+ | Xenbus.Xb.Op.Write|Xenbus.Xb.Op.Mkdir -> false, None, newroot -+ | Xenbus.Xb.Op.Rm -> true, None, oldroot -+ | Xenbus.Xb.Op.Setperms -> false, Some oldroot, newroot - | _ -> raise (Failure "huh ?") in -- Connections.fire_watches cons (snd op) recurse in -+ Connections.fire_watches ?oldroot root cons (snd op) recurse in - List.iter (fun op -> do_op_watch op cons) ops - - let create_implicit_path t perm path = -@@ -205,7 +207,7 @@ let reply_ack fct con t doms cons data = - fct con t doms cons data; - Packet.Ack (fun () -> - if Transaction.get_id t = Transaction.none then -- process_watch (Transaction.get_paths t) cons -+ process_watch t cons - ) - - let reply_data fct con t doms cons data = -@@ -353,14 +355,17 @@ let transaction_replay c t doms cons = - ignore @@ Connection.end_transaction c tid None - ) - --let do_watch con _t _domains cons data = -+let do_watch con t _domains cons data = - let (node, token) = - match (split None '\000' data) with - | [node; token; ""] -> node, token - | _ -> raise Invalid_Cmd_Args - in - let watch = Connections.add_watch cons con node token in -- Packet.Ack (fun () -> Connection.fire_single_watch watch) -+ Packet.Ack (fun () -> -+ (* xenstore.txt says this watch is fired immediately, -+ implying even if path doesn't exist or is unreadable *) -+ Connection.fire_single_watch_unchecked watch) - - let do_unwatch con _t _domains cons data = - let (node, token) = -@@ -391,7 +396,7 @@ let do_transaction_end con t domains cons data = - if not success then - raise Transaction_again; - if commit then begin -- process_watch (List.rev (Transaction.get_paths t)) cons; -+ process_watch t cons; - match t.Transaction.ty with - | Transaction.No -> - () (* no need to record anything *) -@@ -399,7 +404,7 @@ let do_transaction_end con t domains cons data = - record_commit ~con ~tid:id ~before:oldstore ~after:cstore - end - --let do_introduce con _t domains cons data = -+let do_introduce con t domains cons data = - if not (Connection.is_dom0 con) - then raise Define.Permission_denied; - let (domid, mfn, port) = -@@ -420,14 +425,14 @@ let do_introduce con _t domains cons data = - else try - let ndom = Domains.create domains domid mfn port in - Connections.add_domain cons ndom; -- Connections.fire_spec_watches cons Store.Path.introduce_domain; -+ Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.introduce_domain; - ndom - with _ -> raise Invalid_Cmd_Args - in - if (Domain.get_remote_port dom) <> port || (Domain.get_mfn dom) <> mfn then - raise Domain_not_match - --let do_release con _t domains cons data = -+let do_release con t domains cons data = - if not (Connection.is_dom0 con) - then raise Define.Permission_denied; - let domid = -@@ -439,7 +444,7 @@ let do_release con _t domains cons data = - Domains.del domains domid; - Connections.del_domain cons domid; - if fire_spec_watches -- then Connections.fire_spec_watches cons Store.Path.release_domain -+ then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain - else raise Invalid_Cmd_Args - - let do_resume con _t domains _cons data = -@@ -507,6 +512,8 @@ let maybe_ignore_transaction = function - Transaction.none - | _ -> fun x -> x - -+ -+let () = Printexc.record_backtrace true - (** - * Nothrow guarantee. - *) -@@ -548,7 +555,8 @@ let process_packet ~store ~cons ~doms ~con ~req = - (* Put the response on the wire *) - send_response ty con t rid response - with exn -> -- error "process packet: %s" (Printexc.to_string exn); -+ let bt = Printexc.get_backtrace () in -+ error "process packet: %s. %s" (Printexc.to_string exn) bt; - Connection.send_error con tid rid "EIO" - - let do_input store cons doms con = -diff --git a/tools/ocaml/xenstored/transaction.ml b/tools/ocaml/xenstored/transaction.ml -index 963734a653..25bc8c3b4a 100644 ---- a/tools/ocaml/xenstored/transaction.ml -+++ b/tools/ocaml/xenstored/transaction.ml -@@ -82,6 +82,7 @@ type t = { - start_count: int64; - store: Store.t; (* This is the store that we change in write operations. *) - quota: Quota.t; -+ oldroot: Store.Node.t; - mutable paths: (Xenbus.Xb.Op.operation * Store.Path.t) list; - mutable operations: (Packet.request * Packet.response) list; - mutable read_lowpath: Store.Path.t option; -@@ -123,6 +124,7 @@ let make ?(internal=false) id store = - start_count = !counter; - store = if id = none then store else Store.copy store; - quota = Quota.copy store.Store.quota; -+ oldroot = Store.get_root store; - paths = []; - operations = []; - read_lowpath = None; -@@ -137,6 +139,8 @@ let make ?(internal=false) id store = - let get_store t = t.store - let get_paths t = t.paths - -+let get_root t = Store.get_root t.store -+ - let is_read_only t = t.paths = [] - let add_wop t ty path = t.paths <- (ty, path) :: t.paths - let add_operation ~perm t request response = -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 8d0c50bfa4..f7b88065bb 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -337,7 +337,9 @@ let _ = - let (notify, deaddom) = Domains.cleanup domains in - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then -- Connections.fire_spec_watches cons Store.Path.release_domain -+ Connections.fire_spec_watches -+ (Store.get_root store) -+ cons Store.Path.release_domain - ) - else - let c = Connections.find_domain_by_port cons port in diff --git a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch b/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch deleted file mode 100644 index d1fa8b2..0000000 --- a/xsa115-o-0006-tools-ocaml-xenstored-add-xenstored.conf-flag-to-tur.patch +++ /dev/null @@ -1,84 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: add xenstored.conf flag to turn off watch - permission checks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There are flags to turn off quotas and the permission system, so add one -that turns off the newly introduced watch permission checks as well. - -This is part of XSA-115. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connection.ml b/tools/ocaml/xenstored/connection.ml -index 644a448f2e..fa0d3c4d92 100644 ---- a/tools/ocaml/xenstored/connection.ml -+++ b/tools/ocaml/xenstored/connection.ml -@@ -218,7 +218,7 @@ let fire_single_watch_unchecked watch = - let fire_single_watch (oldroot, root) watch = - let abspath = get_watch_path watch.con watch.path |> Store.Path.of_string in - let perms = lookup_watch_perms oldroot root abspath in -- if List.exists (Perms.has watch.con.perm READ) perms then -+ if Perms.can_fire_watch watch.con.perm perms then - fire_single_watch_unchecked watch - else - let perms = perms |> List.map (Perms.Node.to_string ~sep:" ") |> String.concat ", " in -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index 151b65b72d..f843482981 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -44,6 +44,16 @@ conflict-rate-limit-is-aggregate = true - # Activate node permission system - perms-activate = true - -+# Activate the watch permission system -+# When this is enabled unprivileged guests can only get watch events -+# for xenstore entries that they would've been able to read. -+# -+# When this is disabled unprivileged guests may get watch events -+# for xenstore entries that they cannot read. The watch event contains -+# only the entry name, not the value. -+# This restores behaviour prior to XSA-115. -+perms-watch-activate = true -+ - # Activate quota - quota-activate = true - quota-maxentity = 1000 -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index 23b80aba3d..ee7fee6bda 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -20,6 +20,7 @@ let info fmt = Logging.info "perms" fmt - open Stdext - - let activate = ref true -+let watch_activate = ref true - - type permty = READ | WRITE | RDWR | NONE - -@@ -168,5 +169,9 @@ let check connection request node = - (* check if the current connection has the requested perm on the current node *) - let has connection request node = not (lacks connection request node) - -+let can_fire_watch connection perms = -+ not !watch_activate -+ || List.exists (has connection READ) perms -+ - let equiv perm1 perm2 = - (Node.to_string perm1) = (Node.to_string perm2) -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index f7b88065bb..0d355bbcb8 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -95,6 +95,7 @@ let parse_config filename = - ("conflict-max-history-seconds", Config.Set_float Define.conflict_max_history_seconds); - ("conflict-rate-limit-is-aggregate", Config.Set_bool Define.conflict_rate_limit_is_aggregate); - ("perms-activate", Config.Set_bool Perms.activate); -+ ("perms-watch-activate", Config.Set_bool Perms.watch_activate); - ("quota-activate", Config.Set_bool Quota.activate); - ("quota-maxwatch", Config.Set_int Define.maxwatch); - ("quota-transaction", Config.Set_int Define.maxtransaction); diff --git a/xsa322-4.14-c.patch b/xsa322-4.14-c.patch deleted file mode 100644 index 5059f24..0000000 --- a/xsa322-4.14-c.patch +++ /dev/null @@ -1,532 +0,0 @@ -From: Juergen Gross -Subject: tools/xenstore: revoke access rights for removed domains - -Access rights of Xenstore nodes are per domid. Unfortunately existing -granted access rights are not removed when a domain is being destroyed. -This means that a new domain created with the same domid will inherit -the access rights to Xenstore nodes from the previous domain(s) with -the same domid. - -This can be avoided by adding a generation counter to each domain. -The generation counter of the domain is set to the global generation -counter when a domain structure is being allocated. When reading or -writing a node all permissions of domains which are younger than the -node itself are dropped. This is done by flagging the related entry -as invalid in order to avoid modifying permissions in a way the user -could detect. - -A special case has to be considered: for a new domain the first -Xenstore entries are already written before the domain is officially -introduced in Xenstore. In order not to drop the permissions for the -new domain a domain struct is allocated even before introduction if -the hypervisor is aware of the domain. This requires adding another -bool "introduced" to struct domain in xenstored. In order to avoid -additional padding holes convert the shutdown flag to bool, too. - -As verifying permissions has its price regarding runtime add a new -quota for limiting the number of permissions an unprivileged domain -can set for a node. The default for that new quota is 5. - -This is part of XSA-322. - -Signed-off-by: Juergen Gross -Reviewed-by: Paul Durrant -Acked-by: Julien Grall - -diff --git a/tools/xenstore/include/xenstore_lib.h b/tools/xenstore/include/xenstore_lib.h -index 0ffbae9eb5..4c9b6d1685 100644 ---- a/tools/xenstore/include/xenstore_lib.h -+++ b/tools/xenstore/include/xenstore_lib.h -@@ -34,6 +34,7 @@ enum xs_perm_type { - /* Internal use. */ - XS_PERM_ENOENT_OK = 4, - XS_PERM_OWNER = 8, -+ XS_PERM_IGNORE = 16, - }; - - struct xs_permissions -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 92bfd54cff..505560a5de 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -104,6 +104,7 @@ int quota_nb_entry_per_domain = 1000; - int quota_nb_watch_per_domain = 128; - int quota_max_entry_size = 2048; /* 2K */ - int quota_max_transaction = 10; -+int quota_nb_perms_per_node = 5; - - void trace(const char *fmt, ...) - { -@@ -409,8 +410,13 @@ struct node *read_node(struct connection *conn, const void *ctx, - - /* Permissions are struct xs_permissions. */ - node->perms.p = hdr->perms; -+ if (domain_adjust_node_perms(node)) { -+ talloc_free(node); -+ return NULL; -+ } -+ - /* Data is binary blob (usually ascii, no nul). */ -- node->data = node->perms.p + node->perms.num; -+ node->data = node->perms.p + hdr->num_perms; - /* Children is strings, nul separated. */ - node->children = node->data + node->datalen; - -@@ -426,6 +432,9 @@ int write_node_raw(struct connection *conn, TDB_DATA *key, struct node *node, - void *p; - struct xs_tdb_record_hdr *hdr; - -+ if (domain_adjust_node_perms(node)) -+ return errno; -+ - data.dsize = sizeof(*hdr) - + node->perms.num * sizeof(node->perms.p[0]) - + node->datalen + node->childlen; -@@ -485,8 +494,9 @@ enum xs_perm_type perm_for_conn(struct connection *conn, - return (XS_PERM_READ|XS_PERM_WRITE|XS_PERM_OWNER) & mask; - - for (i = 1; i < perms->num; i++) -- if (perms->p[i].id == conn->id -- || (conn->target && perms->p[i].id == conn->target->id)) -+ if (!(perms->p[i].perms & XS_PERM_IGNORE) && -+ (perms->p[i].id == conn->id || -+ (conn->target && perms->p[i].id == conn->target->id))) - return perms->p[i].perms & mask; - - return perms->p[0].perms & mask; -@@ -1248,8 +1258,12 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) - if (perms.num < 2) - return EINVAL; - -- permstr = in->buffer + strlen(in->buffer) + 1; - perms.num--; -+ if (domain_is_unprivileged(conn) && -+ perms.num > quota_nb_perms_per_node) -+ return ENOSPC; -+ -+ permstr = in->buffer + strlen(in->buffer) + 1; - - perms.p = talloc_array(in, struct xs_permissions, perms.num); - if (!perms.p) -@@ -1904,6 +1918,7 @@ static void usage(void) - " -S, --entry-size limit the size of entry per domain, and\n" - " -W, --watch-nb limit the number of watches per domain,\n" - " -t, --transaction limit the number of transaction allowed per domain,\n" -+" -A, --perm-nb limit the number of permissions per node,\n" - " -R, --no-recovery to request that no recovery should be attempted when\n" - " the store is corrupted (debug only),\n" - " -I, --internal-db store database in memory, not on disk\n" -@@ -1924,6 +1939,7 @@ static struct option options[] = { - { "entry-size", 1, NULL, 'S' }, - { "trace-file", 1, NULL, 'T' }, - { "transaction", 1, NULL, 't' }, -+ { "perm-nb", 1, NULL, 'A' }, - { "no-recovery", 0, NULL, 'R' }, - { "internal-db", 0, NULL, 'I' }, - { "verbose", 0, NULL, 'V' }, -@@ -1946,7 +1962,7 @@ int main(int argc, char *argv[]) - int timeout; - - -- while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:T:RVW:", options, -+ while ((opt = getopt_long(argc, argv, "DE:F:HNPS:t:A:T:RVW:", options, - NULL)) != -1) { - switch (opt) { - case 'D': -@@ -1988,6 +2004,9 @@ int main(int argc, char *argv[]) - case 'W': - quota_nb_watch_per_domain = strtol(optarg, NULL, 10); - break; -+ case 'A': -+ quota_nb_perms_per_node = strtol(optarg, NULL, 10); -+ break; - case 'e': - dom0_event = strtol(optarg, NULL, 10); - break; -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index 9fad470f83..dc635e9be3 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -67,8 +67,14 @@ struct domain - /* The connection associated with this. */ - struct connection *conn; - -+ /* Generation count at domain introduction time. */ -+ uint64_t generation; -+ - /* Have we noticed that this domain is shutdown? */ -- int shutdown; -+ bool shutdown; -+ -+ /* Has domain been officially introduced? */ -+ bool introduced; - - /* number of entry from this domain in the store */ - int nbentry; -@@ -188,6 +194,9 @@ static int destroy_domain(void *_domain) - - list_del(&domain->list); - -+ if (!domain->introduced) -+ return 0; -+ - if (domain->port) { - if (xenevtchn_unbind(xce_handle, domain->port) == -1) - eprintf("> Unbinding port %i failed!\n", domain->port); -@@ -209,21 +218,34 @@ static int destroy_domain(void *_domain) - return 0; - } - -+static bool get_domain_info(unsigned int domid, xc_dominfo_t *dominfo) -+{ -+ return xc_domain_getinfo(*xc_handle, domid, 1, dominfo) == 1 && -+ dominfo->domid == domid; -+} -+ - static void domain_cleanup(void) - { - xc_dominfo_t dominfo; - struct domain *domain; - struct connection *conn; - int notify = 0; -+ bool dom_valid; - - again: - list_for_each_entry(domain, &domains, list) { -- if (xc_domain_getinfo(*xc_handle, domain->domid, 1, -- &dominfo) == 1 && -- dominfo.domid == domain->domid) { -+ dom_valid = get_domain_info(domain->domid, &dominfo); -+ if (!domain->introduced) { -+ if (!dom_valid) { -+ talloc_free(domain); -+ goto again; -+ } -+ continue; -+ } -+ if (dom_valid) { - if ((dominfo.crashed || dominfo.shutdown) - && !domain->shutdown) { -- domain->shutdown = 1; -+ domain->shutdown = true; - notify = 1; - } - if (!dominfo.dying) -@@ -289,58 +311,84 @@ static char *talloc_domain_path(void *context, unsigned int domid) - return talloc_asprintf(context, "/local/domain/%u", domid); - } - --static struct domain *new_domain(void *context, unsigned int domid, -- int port) -+static struct domain *find_domain_struct(unsigned int domid) -+{ -+ struct domain *i; -+ -+ list_for_each_entry(i, &domains, list) { -+ if (i->domid == domid) -+ return i; -+ } -+ return NULL; -+} -+ -+static struct domain *alloc_domain(void *context, unsigned int domid) - { - struct domain *domain; -- int rc; - - domain = talloc(context, struct domain); -- if (!domain) -+ if (!domain) { -+ errno = ENOMEM; - return NULL; -+ } - -- domain->port = 0; -- domain->shutdown = 0; - domain->domid = domid; -- domain->path = talloc_domain_path(domain, domid); -- if (!domain->path) -- return NULL; -+ domain->generation = generation; -+ domain->introduced = false; - -- wrl_domain_new(domain); -+ talloc_set_destructor(domain, destroy_domain); - - list_add(&domain->list, &domains); -- talloc_set_destructor(domain, destroy_domain); -+ -+ return domain; -+} -+ -+static int new_domain(struct domain *domain, int port) -+{ -+ int rc; -+ -+ domain->port = 0; -+ domain->shutdown = false; -+ domain->path = talloc_domain_path(domain, domain->domid); -+ if (!domain->path) { -+ errno = ENOMEM; -+ return errno; -+ } -+ -+ wrl_domain_new(domain); - - /* Tell kernel we're interested in this event. */ -- rc = xenevtchn_bind_interdomain(xce_handle, domid, port); -+ rc = xenevtchn_bind_interdomain(xce_handle, domain->domid, port); - if (rc == -1) -- return NULL; -+ return errno; - domain->port = rc; - -+ domain->introduced = true; -+ - domain->conn = new_connection(writechn, readchn); -- if (!domain->conn) -- return NULL; -+ if (!domain->conn) { -+ errno = ENOMEM; -+ return errno; -+ } - - domain->conn->domain = domain; -- domain->conn->id = domid; -+ domain->conn->id = domain->domid; - - domain->remote_port = port; - domain->nbentry = 0; - domain->nbwatch = 0; - -- return domain; -+ return 0; - } - - - static struct domain *find_domain_by_domid(unsigned int domid) - { -- struct domain *i; -+ struct domain *d; - -- list_for_each_entry(i, &domains, list) { -- if (i->domid == domid) -- return i; -- } -- return NULL; -+ d = find_domain_struct(domid); -+ -+ return (d && d->introduced) ? d : NULL; - } - - static void domain_conn_reset(struct domain *domain) -@@ -386,15 +434,21 @@ int do_introduce(struct connection *conn, struct buffered_data *in) - if (port <= 0) - return EINVAL; - -- domain = find_domain_by_domid(domid); -+ domain = find_domain_struct(domid); - - if (domain == NULL) { -+ /* Hang domain off "in" until we're finished. */ -+ domain = alloc_domain(in, domid); -+ if (domain == NULL) -+ return ENOMEM; -+ } -+ -+ if (!domain->introduced) { - interface = map_interface(domid); - if (!interface) - return errno; - /* Hang domain off "in" until we're finished. */ -- domain = new_domain(in, domid, port); -- if (!domain) { -+ if (new_domain(domain, port)) { - rc = errno; - unmap_interface(interface); - return rc; -@@ -503,8 +557,8 @@ int do_resume(struct connection *conn, struct buffered_data *in) - if (IS_ERR(domain)) - return -PTR_ERR(domain); - -- domain->shutdown = 0; -- -+ domain->shutdown = false; -+ - send_ack(conn, XS_RESUME); - - return 0; -@@ -647,8 +701,10 @@ static int dom0_init(void) - if (port == -1) - return -1; - -- dom0 = new_domain(NULL, xenbus_master_domid(), port); -- if (dom0 == NULL) -+ dom0 = alloc_domain(NULL, xenbus_master_domid()); -+ if (!dom0) -+ return -1; -+ if (new_domain(dom0, port)) - return -1; - - dom0->interface = xenbus_map(); -@@ -729,6 +785,66 @@ void domain_entry_inc(struct connection *conn, struct node *node) - } - } - -+/* -+ * Check whether a domain was created before or after a specific generation -+ * count (used for testing whether a node permission is older than a domain). -+ * -+ * Return values: -+ * -1: error -+ * 0: domain has higher generation count (it is younger than a node with the -+ * given count), or domain isn't existing any longer -+ * 1: domain is older than the node -+ */ -+static int chk_domain_generation(unsigned int domid, uint64_t gen) -+{ -+ struct domain *d; -+ xc_dominfo_t dominfo; -+ -+ if (!xc_handle && domid == 0) -+ return 1; -+ -+ d = find_domain_struct(domid); -+ if (d) -+ return (d->generation <= gen) ? 1 : 0; -+ -+ if (!get_domain_info(domid, &dominfo)) -+ return 0; -+ -+ d = alloc_domain(NULL, domid); -+ return d ? 1 : -1; -+} -+ -+/* -+ * Remove permissions for no longer existing domains in order to avoid a new -+ * domain with the same domid inheriting the permissions. -+ */ -+int domain_adjust_node_perms(struct node *node) -+{ -+ unsigned int i; -+ int ret; -+ -+ ret = chk_domain_generation(node->perms.p[0].id, node->generation); -+ if (ret < 0) -+ return errno; -+ -+ /* If the owner doesn't exist any longer give it to priv domain. */ -+ if (!ret) -+ node->perms.p[0].id = priv_domid; -+ -+ for (i = 1; i < node->perms.num; i++) { -+ if (node->perms.p[i].perms & XS_PERM_IGNORE) -+ continue; -+ ret = chk_domain_generation(node->perms.p[i].id, -+ node->generation); -+ if (ret < 0) -+ return errno; -+ if (!ret) -+ node->perms.p[i].perms |= XS_PERM_IGNORE; -+ } -+ -+ return 0; -+} -+ - void domain_entry_dec(struct connection *conn, struct node *node) - { - struct domain *d; -diff --git a/tools/xenstore/xenstored_domain.h b/tools/xenstore/xenstored_domain.h -index 259183962a..5e00087206 100644 ---- a/tools/xenstore/xenstored_domain.h -+++ b/tools/xenstore/xenstored_domain.h -@@ -56,6 +56,9 @@ bool domain_can_write(struct connection *conn); - - bool domain_is_unprivileged(struct connection *conn); - -+/* Remove node permissions for no longer existing domains. */ -+int domain_adjust_node_perms(struct node *node); -+ - /* Quota manipulation */ - void domain_entry_inc(struct connection *conn, struct node *); - void domain_entry_dec(struct connection *conn, struct node *); -diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c -index a7d8c5d475..2881f3b2e4 100644 ---- a/tools/xenstore/xenstored_transaction.c -+++ b/tools/xenstore/xenstored_transaction.c -@@ -47,7 +47,12 @@ - * transaction. - * Each time the global generation count is copied to either a node or a - * transaction it is incremented. This ensures all nodes and/or transactions -- * are having a unique generation count. -+ * are having a unique generation count. The increment is done _before_ the -+ * copy as that is needed for checking whether a domain was created before -+ * or after a node has been written (the domain's generation is set with the -+ * actual generation count without incrementing it, in order to support -+ * writing a node for a domain before the domain has been officially -+ * introduced). - * - * Transaction conflicts are detected by checking the generation count of all - * nodes read in the transaction to match with the generation count in the -@@ -161,7 +166,7 @@ struct transaction - }; - - extern int quota_max_transaction; --static uint64_t generation; -+uint64_t generation; - - static void set_tdb_key(const char *name, TDB_DATA *key) - { -@@ -237,7 +242,7 @@ int access_node(struct connection *conn, struct node *node, - bool introduce = false; - - if (type != NODE_ACCESS_READ) { -- node->generation = generation++; -+ node->generation = ++generation; - if (conn && !conn->transaction) - wrl_apply_debit_direct(conn); - } -@@ -374,7 +379,7 @@ static int finalize_transaction(struct connection *conn, - if (!data.dptr) - goto err; - hdr = (void *)data.dptr; -- hdr->generation = generation++; -+ hdr->generation = ++generation; - ret = tdb_store(tdb_ctx, key, data, - TDB_REPLACE); - talloc_free(data.dptr); -@@ -462,7 +467,7 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) - INIT_LIST_HEAD(&trans->accessed); - INIT_LIST_HEAD(&trans->changed_domains); - trans->fail = false; -- trans->generation = generation++; -+ trans->generation = ++generation; - - /* Pick an unused transaction identifier. */ - do { -diff --git a/tools/xenstore/xenstored_transaction.h b/tools/xenstore/xenstored_transaction.h -index 3386bac565..43a162bea3 100644 ---- a/tools/xenstore/xenstored_transaction.h -+++ b/tools/xenstore/xenstored_transaction.h -@@ -27,6 +27,8 @@ enum node_access_type { - - struct transaction; - -+extern uint64_t generation; -+ - int do_transaction_start(struct connection *conn, struct buffered_data *node); - int do_transaction_end(struct connection *conn, struct buffered_data *in); - -diff --git a/tools/xenstore/xs_lib.c b/tools/xenstore/xs_lib.c -index 3e43f8809d..d407d5713a 100644 ---- a/tools/xenstore/xs_lib.c -+++ b/tools/xenstore/xs_lib.c -@@ -152,7 +152,7 @@ bool xs_strings_to_perms(struct xs_permissions *perms, unsigned int num, - bool xs_perm_to_string(const struct xs_permissions *perm, - char *buffer, size_t buf_len) - { -- switch ((int)perm->perms) { -+ switch ((int)perm->perms & ~XS_PERM_IGNORE) { - case XS_PERM_WRITE: - *buffer = 'w'; - break; diff --git a/xsa322-o.patch b/xsa322-o.patch deleted file mode 100644 index 75f7c20..0000000 --- a/xsa322-o.patch +++ /dev/null @@ -1,110 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: clean up permissions for dead domains -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -domain ids are prone to wrapping (15-bits), and with sufficient number -of VMs in a reboot loop it is possible to trigger it. Xenstore entries -may linger after a domain dies, until a toolstack cleans it up. During -this time there is a window where a wrapped domid could access these -xenstore keys (that belonged to another VM). - -To prevent this do a cleanup when a domain dies: - * walk the entire xenstore tree and update permissions for all nodes - * if the dead domain had an ACL entry: remove it - * if the dead domain was the owner: change the owner to Dom0 - -This is done without quota checks or a transaction. Quota checks would -be a no-op (either the domain is dead, or it is Dom0 where they are not -enforced). Transactions are not needed, because this is all done -atomically by oxenstored's single thread. - -The xenstore entries owned by the dead domain are not deleted, because -that could confuse a toolstack / backends that are still bound to it -(or generate unexpected watch events). It is the responsibility of a -toolstack to remove the xenstore entries themselves. - -This is part of XSA-322. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/xenstored/perms.ml b/tools/ocaml/xenstored/perms.ml -index ee7fee6bda..e8a16221f8 100644 ---- a/tools/ocaml/xenstored/perms.ml -+++ b/tools/ocaml/xenstored/perms.ml -@@ -58,6 +58,15 @@ let get_other perms = perms.other - let get_acl perms = perms.acl - let get_owner perm = perm.owner - -+(** [remote_domid ~domid perm] removes all ACLs for [domid] from perm. -+* If [domid] was the owner then it is changed to Dom0. -+* This is used for cleaning up after dead domains. -+* *) -+let remove_domid ~domid perm = -+ let acl = List.filter (fun (acl_domid, _) -> acl_domid <> domid) perm.acl in -+ let owner = if perm.owner = domid then 0 else perm.owner in -+ { perm with acl; owner } -+ - let default0 = create 0 NONE [] - - let perm_of_string s = -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index f99b9e935c..73e04cc18b 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -443,6 +443,7 @@ let do_release con t domains cons data = - let fire_spec_watches = Domains.exist domains domid in - Domains.del domains domid; - Connections.del_domain cons domid; -+ Store.reset_permissions (Transaction.get_store t) domid; - if fire_spec_watches - then Connections.fire_spec_watches (Transaction.get_root t) cons Store.Path.release_domain - else raise Invalid_Cmd_Args -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 6b6e440e98..3b05128f1b 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -89,6 +89,13 @@ let check_owner node connection = - - let rec recurse fct node = fct node; List.iter (recurse fct) node.children - -+(** [recurse_map f tree] applies [f] on each node in the tree recursively *) -+let recurse_map f = -+ let rec walk node = -+ f { node with children = List.rev_map walk node.children |> List.rev } -+ in -+ walk -+ - let unpack node = (Symbol.to_string node.name, node.perms, node.value) - - end -@@ -405,6 +412,15 @@ let setperms store perm path nperms = - Quota.del_entry store.quota old_owner; - Quota.add_entry store.quota new_owner - -+let reset_permissions store domid = -+ Logging.info "store|node" "Cleaning up xenstore ACLs for domid %d" domid; -+ store.root <- Node.recurse_map (fun node -> -+ let perms = Perms.Node.remove_domid ~domid node.perms in -+ if perms <> node.perms then -+ Logging.debug "store|node" "Changed permissions for node %s" (Node.get_name node); -+ { node with perms } -+ ) store.root -+ - type ops = { - store: t; - write: Path.t -> string -> unit; -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index 0d355bbcb8..ff9fbbbac2 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -336,6 +336,7 @@ let _ = - finally (fun () -> - if Some port = eventchn.Event.virq_port then ( - let (notify, deaddom) = Domains.cleanup domains in -+ List.iter (Store.reset_permissions store) deaddom; - List.iter (Connections.del_domain cons) deaddom; - if deaddom <> [] || notify then - Connections.fire_spec_watches diff --git a/xsa323.patch b/xsa323.patch deleted file mode 100644 index aadf5c7..0000000 --- a/xsa323.patch +++ /dev/null @@ -1,140 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: Fix path length validation -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Currently, oxenstored checks the length of paths against 1024, then -prepends "/local/domain/$DOMID/" to relative paths. This allows a domU -to create paths which can't subsequently be read by anyone, even dom0. -This also interferes with listing directories, etc. - -Define a new oxenstored.conf entry: quota-path-max, defaulting to 1024 -as before. For paths that begin with "/local/domain/$DOMID/" check the -relative path length against this quota. For all other paths check the -entire path length. - -This ensures that if the domid changes (and thus the length of a prefix -changes) a path that used to be valid stays valid (e.g. after a -live-migration). It also ensures that regardless how the client tries -to access a path (domid-relative or absolute) it will get consistent -results, since the limit is always applied on the final canonicalized -path. - -Delete the unused Domain.get_path to avoid it being confused with -Connection.get_path (which differs by a trailing slash only). - -Rewrite Util.path_validate to apply the appropriate length restriction -based on whether the path is relative or not. Remove the check for -connection_path being absolute, because it is not guest controlled data. - -This is part of XSA-323. - -Signed-off-by: Andrew Cooper -Signed-off-by: Edwin Török -Acked-by: Christian Lindig - -diff --git a/tools/ocaml/libs/xb/partial.ml b/tools/ocaml/libs/xb/partial.ml -index d4d1c7bdec..b6e2a716e2 100644 ---- a/tools/ocaml/libs/xb/partial.ml -+++ b/tools/ocaml/libs/xb/partial.ml -@@ -28,6 +28,7 @@ external header_of_string_internal: string -> int * int * int * int - = "stub_header_of_string" - - let xenstore_payload_max = 4096 (* xen/include/public/io/xs_wire.h *) -+let xenstore_rel_path_max = 2048 (* xen/include/public/io/xs_wire.h *) - - let of_string s = - let tid, rid, opint, dlen = header_of_string_internal s in -diff --git a/tools/ocaml/libs/xb/partial.mli b/tools/ocaml/libs/xb/partial.mli -index 359a75e88d..b9216018f5 100644 ---- a/tools/ocaml/libs/xb/partial.mli -+++ b/tools/ocaml/libs/xb/partial.mli -@@ -9,6 +9,7 @@ external header_size : unit -> int = "stub_header_size" - external header_of_string_internal : string -> int * int * int * int - = "stub_header_of_string" - val xenstore_payload_max : int -+val xenstore_rel_path_max : int - val of_string : string -> pkt - val append : pkt -> string -> int -> unit - val to_complete : pkt -> int -diff --git a/tools/ocaml/xenstored/define.ml b/tools/ocaml/xenstored/define.ml -index ea9e1b7620..ebe18b8e31 100644 ---- a/tools/ocaml/xenstored/define.ml -+++ b/tools/ocaml/xenstored/define.ml -@@ -31,6 +31,8 @@ let conflict_rate_limit_is_aggregate = ref true - - let domid_self = 0x7FF0 - -+let path_max = ref Xenbus.Partial.xenstore_rel_path_max -+ - exception Not_a_directory of string - exception Not_a_value of string - exception Already_exist -diff --git a/tools/ocaml/xenstored/domain.ml b/tools/ocaml/xenstored/domain.ml -index aeb185ff7e..81cb59b8f1 100644 ---- a/tools/ocaml/xenstored/domain.ml -+++ b/tools/ocaml/xenstored/domain.ml -@@ -38,7 +38,6 @@ type t = - } - - let is_dom0 d = d.id = 0 --let get_path dom = "/local/domain/" ^ (sprintf "%u" dom.id) - let get_id domain = domain.id - let get_interface d = d.interface - let get_mfn d = d.mfn -diff --git a/tools/ocaml/xenstored/oxenstored.conf.in b/tools/ocaml/xenstored/oxenstored.conf.in -index f843482981..4ae48e42d4 100644 ---- a/tools/ocaml/xenstored/oxenstored.conf.in -+++ b/tools/ocaml/xenstored/oxenstored.conf.in -@@ -61,6 +61,7 @@ quota-maxsize = 2048 - quota-maxwatch = 100 - quota-transaction = 10 - quota-maxrequests = 1024 -+quota-path-max = 1024 - - # Activate filed base backend - persistent = false -diff --git a/tools/ocaml/xenstored/utils.ml b/tools/ocaml/xenstored/utils.ml -index e8c9fe4e94..eb79bf0146 100644 ---- a/tools/ocaml/xenstored/utils.ml -+++ b/tools/ocaml/xenstored/utils.ml -@@ -93,7 +93,7 @@ let read_file_single_integer filename = - let path_validate path connection_path = - let len = String.length path in - -- if len = 0 || len > 1024 then raise Define.Invalid_path; -+ if len = 0 then raise Define.Invalid_path; - - let abs_path = - match String.get path 0 with -@@ -101,4 +101,17 @@ let path_validate path connection_path = - | _ -> connection_path ^ path - in - -+ (* Regardless whether client specified absolute or relative path, -+ canonicalize it (above) and, for domain-relative paths, check the -+ length of the relative part. -+ -+ This prevents paths becoming invalid across migrate when the length -+ of the domid changes in @param connection_path. -+ *) -+ let len = String.length abs_path in -+ let on_absolute _ _ = len in -+ let on_relative _ offset = len - offset in -+ let len = Scanf.ksscanf abs_path on_absolute "/local/domain/%d/%n" on_relative in -+ if len > !Define.path_max then raise Define.Invalid_path; -+ - abs_path -diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml -index ff9fbbbac2..39d6d767e4 100644 ---- a/tools/ocaml/xenstored/xenstored.ml -+++ b/tools/ocaml/xenstored/xenstored.ml -@@ -102,6 +102,7 @@ let parse_config filename = - ("quota-maxentity", Config.Set_int Quota.maxent); - ("quota-maxsize", Config.Set_int Quota.maxsize); - ("quota-maxrequests", Config.Set_int Define.maxrequests); -+ ("quota-path-max", Config.Set_int Define.path_max); - ("test-eagain", Config.Set_bool Transaction.test_eagain); - ("persistent", Config.Set_bool Disk.enable); - ("xenstored-log-file", Config.String Logging.set_xenstored_log_destination); diff --git a/xsa324.patch b/xsa324.patch deleted file mode 100644 index c5e542d..0000000 --- a/xsa324.patch +++ /dev/null @@ -1,48 +0,0 @@ -From: Juergen Gross -Subject: tools/xenstore: drop watch event messages exceeding maximum size - -By setting a watch with a very large tag it is possible to trick -xenstored to send watch event messages exceeding the maximum allowed -payload size. This might in turn lead to a crash of xenstored as the -resulting error can cause dereferencing a NULL pointer in case there -is no active request being handled by the guest the watch event is -being sent to. - -Fix that by just dropping such watch events. Additionally modify the -error handling to test the pointer to be not NULL before dereferencing -it. - -This is XSA-324. - -Signed-off-by: Juergen Gross -Acked-by: Julien Grall - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index 33f95dcf3c..3d74dbbb40 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -674,6 +674,9 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, - /* Replies reuse the request buffer, events need a new one. */ - if (type != XS_WATCH_EVENT) { - bdata = conn->in; -+ /* Drop asynchronous responses, e.g. errors for watch events. */ -+ if (!bdata) -+ return; - bdata->inhdr = true; - bdata->used = 0; - conn->in = NULL; -diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c -index 71c108ea99..9ff20690c0 100644 ---- a/tools/xenstore/xenstored_watch.c -+++ b/tools/xenstore/xenstored_watch.c -@@ -92,6 +92,10 @@ static void add_event(struct connection *conn, - } - - len = strlen(name) + 1 + strlen(watch->token) + 1; -+ /* Don't try to send over-long events. */ -+ if (len > XENSTORE_PAYLOAD_MAX) -+ return; -+ - data = talloc_array(ctx, char, len); - if (!data) - return; diff --git a/xsa325-4.14.patch b/xsa325-4.14.patch deleted file mode 100644 index a17f546..0000000 --- a/xsa325-4.14.patch +++ /dev/null @@ -1,192 +0,0 @@ -From: Harsha Shamsundara Havanur -Subject: tools/xenstore: Preserve bad client until they are destroyed - -XenStored will kill any connection that it thinks has misbehaved, -this is currently happening in two places: - * In `handle_input()` if the sanity check on the ring and the message - fails. - * In `handle_output()` when failing to write the response in the ring. - -As the domain structure is a child of the connection, XenStored will -destroy its view of the domain when killing the connection. This will -result in sending @releaseDomain event to all the watchers. - -As the watch event doesn't carry which domain has been released, -the watcher (such as XenStored) will generally go through the list of -domains registers and check if one of them is shutting down/dying. -In the case of a client misbehaving, the domain will likely to be -running, so no action will be performed. - -When the domain is effectively destroyed, XenStored will not be aware of -the domain anymore. So the watch event is not going to be sent. -By consequence, the watchers of the event will not release mappings -they may have on the domain. This will result in a zombie domain. - -In order to send @releaseDomain event at the correct time, we want -to keep the domain structure until the domain is effectively -shutting-down/dying. - -We also want to keep the connection around so we could possibly revive -the connection in the future. - -A new flag 'is_ignored' is added to mark whether a connection should be -ignored when checking if there are work to do. Additionally any -transactions, watches, buffers associated to the connection will be -freed as you can't do much with them (restarting the connection will -likely need a reset). - -As a side note, when the device model were running in a stubdomain, a -guest would have been able to introduce a use-after-free because there -is two parents for a guest connection. - -This is XSA-325. - -Reported-by: Pawel Wieczorkiewicz -Signed-off-by: Harsha Shamsundara Havanur -Signed-off-by: Julien Grall -Reviewed-by: Juergen Gross -Reviewed-by: Paul Durrant - -diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c -index af3d17004b3f..27d8f15b6b76 100644 ---- a/tools/xenstore/xenstored_core.c -+++ b/tools/xenstore/xenstored_core.c -@@ -1355,6 +1355,32 @@ static struct { - [XS_DIRECTORY_PART] = { "DIRECTORY_PART", send_directory_part }, - }; - -+/* -+ * Keep the connection alive but stop processing any new request or sending -+ * reponse. This is to allow sending @releaseDomain watch event at the correct -+ * moment and/or to allow the connection to restart (not yet implemented). -+ * -+ * All watches, transactions, buffers will be freed. -+ */ -+static void ignore_connection(struct connection *conn) -+{ -+ struct buffered_data *out, *tmp; -+ -+ trace("CONN %p ignored\n", conn); -+ -+ conn->is_ignored = true; -+ conn_delete_all_watches(conn); -+ conn_delete_all_transactions(conn); -+ -+ list_for_each_entry_safe(out, tmp, &conn->out_list, list) { -+ list_del(&out->list); -+ talloc_free(out); -+ } -+ -+ talloc_free(conn->in); -+ conn->in = NULL; -+} -+ - static const char *sockmsg_string(enum xsd_sockmsg_type type) - { - if ((unsigned int)type < ARRAY_SIZE(wire_funcs) && wire_funcs[type].str) -@@ -1413,8 +1439,10 @@ static void consider_message(struct connection *conn) - assert(conn->in == NULL); - } - --/* Errors in reading or allocating here mean we get out of sync, so we -- * drop the whole client connection. */ -+/* -+ * Errors in reading or allocating here means we get out of sync, so we mark -+ * the connection as ignored. -+ */ - static void handle_input(struct connection *conn) - { - int bytes; -@@ -1471,14 +1499,14 @@ static void handle_input(struct connection *conn) - return; - - bad_client: -- /* Kill it. */ -- talloc_free(conn); -+ ignore_connection(conn); - } - - static void handle_output(struct connection *conn) - { -+ /* Ignore the connection if an error occured */ - if (!write_messages(conn)) -- talloc_free(conn); -+ ignore_connection(conn); - } - - struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) -@@ -1494,6 +1522,7 @@ struct connection *new_connection(connwritefn_t *write, connreadfn_t *read) - new->write = write; - new->read = read; - new->can_write = true; -+ new->is_ignored = false; - new->transaction_started = 0; - INIT_LIST_HEAD(&new->out_list); - INIT_LIST_HEAD(&new->watches); -@@ -2186,8 +2215,9 @@ int main(int argc, char *argv[]) - if (fds[conn->pollfd_idx].revents - & ~(POLLIN|POLLOUT)) - talloc_free(conn); -- else if (fds[conn->pollfd_idx].revents -- & POLLIN) -+ else if ((fds[conn->pollfd_idx].revents -+ & POLLIN) && -+ !conn->is_ignored) - handle_input(conn); - } - if (talloc_free(conn) == 0) -@@ -2199,8 +2229,9 @@ int main(int argc, char *argv[]) - if (fds[conn->pollfd_idx].revents - & ~(POLLIN|POLLOUT)) - talloc_free(conn); -- else if (fds[conn->pollfd_idx].revents -- & POLLOUT) -+ else if ((fds[conn->pollfd_idx].revents -+ & POLLOUT) && -+ !conn->is_ignored) - handle_output(conn); - } - if (talloc_free(conn) == 0) -diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h -index eb19b71f5f46..196a6fd2b0be 100644 ---- a/tools/xenstore/xenstored_core.h -+++ b/tools/xenstore/xenstored_core.h -@@ -80,6 +80,9 @@ struct connection - /* Is this a read-only connection? */ - bool can_write; - -+ /* Is this connection ignored? */ -+ bool is_ignored; -+ - /* Buffered incoming data. */ - struct buffered_data *in; - -diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c -index dc635e9be30c..d5e1e3e9d42d 100644 ---- a/tools/xenstore/xenstored_domain.c -+++ b/tools/xenstore/xenstored_domain.c -@@ -286,6 +286,10 @@ bool domain_can_read(struct connection *conn) - - if (domain_is_unprivileged(conn) && conn->domain->wrl_credit < 0) - return false; -+ -+ if (conn->is_ignored) -+ return false; -+ - return (intf->req_cons != intf->req_prod); - } - -@@ -303,6 +307,10 @@ bool domain_is_unprivileged(struct connection *conn) - bool domain_can_write(struct connection *conn) - { - struct xenstore_domain_interface *intf = conn->domain->interface; -+ -+ if (conn->is_ignored) -+ return false; -+ - return ((intf->rsp_prod - intf->rsp_cons) != XENSTORE_RING_SIZE); - } - --- -2.17.1 - diff --git a/xsa330.patch b/xsa330.patch deleted file mode 100644 index c834516..0000000 --- a/xsa330.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: delete watch from trie too when resetting - watches -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -c/s f8c72b526129 "oxenstored: implement XS_RESET_WATCHES" from Xen 4.6 -introduced reset watches support in oxenstored by mirroring the change -in cxenstored. - -However the OCaml version has some additional data structures to -optimize watch firing, and just resetting the watches in one of the data -structures creates a security bug where a malicious guest kernel can -exceed its watch quota, driving oxenstored into OOM: - * create watches - * reset watches (this still keeps the watches lingering in another data - structure, using memory) - * create some more watches - * loop until oxenstored dies - -The guest kernel doesn't necessarily have to be malicious to trigger -this: - * if control/platform-feature-xs_reset_watches is set - * the guest kexecs (e.g. because it crashes) - * on boot more watches are set up - * this will slowly "leak" memory for watches in oxenstored, driving it - towards OOM. - -This is XSA-330. - -Fixes: f8c72b526129 ("oxenstored: implement XS_RESET_WATCHES") -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/connections.ml b/tools/ocaml/xenstored/connections.ml -index 9f9f7ee2f0..6ee3552ec2 100644 ---- a/tools/ocaml/xenstored/connections.ml -+++ b/tools/ocaml/xenstored/connections.ml -@@ -134,6 +134,10 @@ let del_watch cons con path token = - cons.watches <- Trie.set cons.watches key watches; - watch - -+let del_watches cons con = -+ Connection.del_watches con; -+ cons.watches <- Trie.map (del_watches_of_con con) cons.watches -+ - (* path is absolute *) - let fire_watches ?oldroot root cons path recurse = - let key = key_of_path path in -diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml -index 73e04cc18b..437d2dcf9e 100644 ---- a/tools/ocaml/xenstored/process.ml -+++ b/tools/ocaml/xenstored/process.ml -@@ -179,8 +179,8 @@ let do_isintroduced con _t domains _cons data = - if domid = Define.domid_self || Domains.exist domains domid then "T\000" else "F\000" - - (* only in xen >= 4.2 *) --let do_reset_watches con _t _domains _cons _data = -- Connection.del_watches con; -+let do_reset_watches con _t _domains cons _data = -+ Connections.del_watches cons con; - Connection.del_transactions con - - (* only in >= xen3.3 *) diff --git a/xsa335-qemu.patch b/xsa335-qemu.patch deleted file mode 100644 index 5617502..0000000 --- a/xsa335-qemu.patch +++ /dev/null @@ -1,84 +0,0 @@ -From c5bd2924c6d6a5bcbffb8b5e7798a88970131c07 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Mon, 17 Aug 2020 08:34:22 +0200 -Subject: [PATCH] usb: fix setup_len init (CVE-2020-14364) - -Store calculated setup_len in a local variable, verify it, and only -write it to the struct (USBDevice->setup_len) in case it passed the -sanity checks. - -This prevents other code (do_token_{in,out} functions specifically) -from working with invalid USBDevice->setup_len values and overrunning -the USBDevice->setup_buf[] buffer. - -Fixes: CVE-2020-14364 -Signed-off-by: Gerd Hoffmann ---- - hw/usb/core.c | 16 ++++++++++------ - 1 file changed, 10 insertions(+), 6 deletions(-) - -diff --git a/hw/usb/core.c b/hw/usb/core.c -index 5abd128b6bc5..5234dcc73fea 100644 ---- a/hw/usb/core.c -+++ b/hw/usb/core.c -@@ -129,6 +129,7 @@ void usb_wakeup(USBEndpoint *ep, unsigned int stream) - static void do_token_setup(USBDevice *s, USBPacket *p) - { - int request, value, index; -+ unsigned int setup_len; - - if (p->iov.size != 8) { - p->status = USB_RET_STALL; -@@ -138,14 +139,15 @@ static void do_token_setup(USBDevice *s, USBPacket *p) - usb_packet_copy(p, s->setup_buf, p->iov.size); - s->setup_index = 0; - p->actual_length = 0; -- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -- if (s->setup_len > sizeof(s->data_buf)) { -+ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -+ if (setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", -- s->setup_len, sizeof(s->data_buf)); -+ setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } -+ s->setup_len = setup_len; - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; -@@ -259,26 +261,28 @@ static void do_token_out(USBDevice *s, USBPacket *p) - static void do_parameter(USBDevice *s, USBPacket *p) - { - int i, request, value, index; -+ unsigned int setup_len; - - for (i = 0; i < 8; i++) { - s->setup_buf[i] = p->parameter >> (i*8); - } - - s->setup_state = SETUP_STATE_PARAM; -- s->setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; - s->setup_index = 0; - - request = (s->setup_buf[0] << 8) | s->setup_buf[1]; - value = (s->setup_buf[3] << 8) | s->setup_buf[2]; - index = (s->setup_buf[5] << 8) | s->setup_buf[4]; - -- if (s->setup_len > sizeof(s->data_buf)) { -+ setup_len = (s->setup_buf[7] << 8) | s->setup_buf[6]; -+ if (setup_len > sizeof(s->data_buf)) { - fprintf(stderr, - "usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n", -- s->setup_len, sizeof(s->data_buf)); -+ setup_len, sizeof(s->data_buf)); - p->status = USB_RET_STALL; - return; - } -+ s->setup_len = setup_len; - - if (p->pid == USB_TOKEN_OUT) { - usb_packet_copy(p, s->data_buf, s->setup_len); --- -2.18.4 diff --git a/xsa335-trad.patch b/xsa335-trad.patch deleted file mode 100644 index 1310b84..0000000 --- a/xsa335-trad.patch +++ /dev/null @@ -1,45 +0,0 @@ -From a62cdd675bc6a8053f6797b6add29b2853b081e3 Mon Sep 17 00:00:00 2001 -From: Ian Jackson -Date: Wed, 19 Aug 2020 18:31:45 +0100 -Subject: [PATCH] SUPPORT.md: Desupport qemu trad except stub dm - -While investigating XSA-335 we discovered that many upstream security -fixes were missing. It is not practical to backport them. There is -no good reason to be running this very ancient version of qemu, except -that it is the only way to run a stub dm which is currently supported -by upstream. - -Signed-off-by: Ian Jackson ---- - SUPPORT.md | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - -diff --git a/SUPPORT.md b/SUPPORT.md -index 1479055c45..b0939052e2 100644 ---- a/SUPPORT.md -+++ b/SUPPORT.md -@@ -758,6 +758,21 @@ See the section **Blkback** for image formats supported by QEMU. - - Status: Supported, not security supported - -+### qemu-xen-traditional ### -+ -+The Xen Project provides an old version of qemu with modifications -+which enable use as a device model stub domain. The old version is -+normally selected by default only in a stub dm configuration, but it -+can be requested explicitly in other configurations, for example in -+`xl` with `device_model_version="QEMU_XEN_TRADITIONAL"`. -+ -+ Status, Device Model Stub Domains: Supported, with caveats -+ Status, as host process device model: No security support, not recommended -+ -+qemu-xen-traditional is security supported only for those available -+devices which are supported for mainstream QEMU (see above), with -+trusted driver domains (see Device Model Stub Domains). -+ - ## Virtual Firmware - - ### x86/HVM iPXE --- -2.20.1 - diff --git a/xsa348-4.13-1.patch b/xsa348-4.13-1.patch deleted file mode 100644 index bfc9962..0000000 --- a/xsa348-4.13-1.patch +++ /dev/null @@ -1,106 +0,0 @@ -From: Jan Beulich -Subject: x86: replace reset_stack_and_jump_nolp() - -Move the necessary check into check_for_livepatch_work(), rather than -mostly duplicating reset_stack_and_jump() for this purpose. This is to -prevent an inflation of reset_stack_and_jump() flavors. - -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- sle15sp2.orig/xen/arch/x86/domain.c 2020-10-30 17:22:39.000000000 +0100 -+++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:51:10.894525721 +0100 -@@ -192,7 +192,7 @@ static void noreturn continue_idle_domai - { - /* Idle vcpus might be attached to non-idle units! */ - if ( !is_idle_domain(v->sched_unit->domain) ) -- reset_stack_and_jump_nolp(guest_idle_loop); -+ reset_stack_and_jump(guest_idle_loop); - - reset_stack_and_jump(idle_loop); - } ---- sle15sp2.orig/xen/arch/x86/hvm/svm/svm.c 2020-10-30 17:22:39.000000000 +0100 -+++ sle15sp2/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:51:10.898525723 +0100 -@@ -1032,7 +1032,7 @@ static void noreturn svm_do_resume(struc - - hvm_do_resume(v); - -- reset_stack_and_jump_nolp(svm_asm_do_resume); -+ reset_stack_and_jump(svm_asm_do_resume); - } - - void svm_vmenter_helper(const struct cpu_user_regs *regs) ---- sle15sp2.orig/xen/arch/x86/hvm/vmx/vmcs.c 2020-05-18 18:53:09.000000000 +0200 -+++ sle15sp2/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:51:10.898525723 +0100 -@@ -1889,7 +1889,7 @@ void vmx_do_resume(struct vcpu *v) - if ( host_cr4 != read_cr4() ) - __vmwrite(HOST_CR4, read_cr4()); - -- reset_stack_and_jump_nolp(vmx_asm_do_vmentry); -+ reset_stack_and_jump(vmx_asm_do_vmentry); - } - - static inline unsigned long vmr(unsigned long field) ---- sle15sp2.orig/xen/arch/x86/pv/domain.c 2020-10-30 17:22:39.000000000 +0100 -+++ sle15sp2/xen/arch/x86/pv/domain.c 2020-11-10 17:51:10.898525723 +0100 -@@ -61,7 +61,7 @@ custom_runtime_param("pcid", parse_pcid) - static void noreturn continue_nonidle_domain(struct vcpu *v) - { - check_wakeup_from_wait(); -- reset_stack_and_jump_nolp(ret_from_intr); -+ reset_stack_and_jump(ret_from_intr); - } - - static int setup_compat_l4(struct vcpu *v) ---- sle15sp2.orig/xen/arch/x86/setup.c 2020-05-18 18:53:09.000000000 +0200 -+++ sle15sp2/xen/arch/x86/setup.c 2020-11-10 17:51:10.898525723 +0100 -@@ -631,7 +631,7 @@ static void __init noreturn reinit_bsp_s - stack_base[0] = stack; - memguard_guard_stack(stack); - -- reset_stack_and_jump_nolp(init_done); -+ reset_stack_and_jump(init_done); - } - - /* ---- sle15sp2.orig/xen/common/livepatch.c 2020-05-18 18:53:09.000000000 +0200 -+++ sle15sp2/xen/common/livepatch.c 2020-11-10 17:51:10.898525723 +0100 -@@ -1300,6 +1300,11 @@ void check_for_livepatch_work(void) - s_time_t timeout; - unsigned long flags; - -+ /* Only do any work when invoked in truly idle state. */ -+ if ( system_state != SYS_STATE_active || -+ !is_idle_domain(current->sched_unit->domain) ) -+ return; -+ - /* Fast path: no work to do. */ - if ( !per_cpu(work_to_do, cpu ) ) - return; ---- sle15sp2.orig/xen/include/asm-x86/current.h 2019-12-18 16:18:59.000000000 +0100 -+++ sle15sp2/xen/include/asm-x86/current.h 2020-11-10 17:51:10.902525725 +0100 -@@ -129,22 +129,16 @@ unsigned long get_stack_dump_bottom (uns - # define CHECK_FOR_LIVEPATCH_WORK "" - #endif - --#define switch_stack_and_jump(fn, instr) \ -+#define reset_stack_and_jump(fn) \ - ({ \ - __asm__ __volatile__ ( \ - "mov %0,%%"__OP"sp;" \ -- instr \ -+ CHECK_FOR_LIVEPATCH_WORK \ - "jmp %c1" \ - : : "r" (guest_cpu_user_regs()), "i" (fn) : "memory" ); \ - unreachable(); \ - }) - --#define reset_stack_and_jump(fn) \ -- switch_stack_and_jump(fn, CHECK_FOR_LIVEPATCH_WORK) -- --#define reset_stack_and_jump_nolp(fn) \ -- switch_stack_and_jump(fn, "") -- - /* - * Which VCPU's state is currently running on each CPU? - * This is not necesasrily the same as 'current' as a CPU may be diff --git a/xsa348-4.13-2.patch b/xsa348-4.13-2.patch deleted file mode 100644 index ce6cf48..0000000 --- a/xsa348-4.13-2.patch +++ /dev/null @@ -1,85 +0,0 @@ -From: Jan Beulich -Subject: x86: fold guest_idle_loop() into idle_loop() - -The latter can easily be made cover both cases. This is in preparation -of using idle_loop directly for populating idle_csw.tail. - -Take the liberty and also adjust indentation / spacing in involved code. - -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- sle15sp2.orig/xen/arch/x86/domain.c 2020-11-10 17:51:10.894525721 +0100 -+++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:51:46.354546349 +0100 -@@ -133,14 +133,22 @@ void play_dead(void) - static void idle_loop(void) - { - unsigned int cpu = smp_processor_id(); -+ /* -+ * Idle vcpus might be attached to non-idle units! We don't do any -+ * standard idle work like tasklets or livepatching in this case. -+ */ -+ bool guest = !is_idle_domain(current->sched_unit->domain); - - for ( ; ; ) - { - if ( cpu_is_offline(cpu) ) -+ { -+ ASSERT(!guest); - play_dead(); -+ } - - /* Are we here for running vcpu context tasklets, or for idling? */ -- if ( unlikely(tasklet_work_to_do(cpu)) ) -+ if ( !guest && unlikely(tasklet_work_to_do(cpu)) ) - { - do_tasklet(); - /* Livepatch work is always kicked off via a tasklet. */ -@@ -151,28 +159,14 @@ static void idle_loop(void) - * and then, after it is done, whether softirqs became pending - * while we were scrubbing. - */ -- else if ( !softirq_pending(cpu) && !scrub_free_pages() && -- !softirq_pending(cpu) ) -- pm_idle(); -- do_softirq(); -- } --} -- --/* -- * Idle loop for siblings in active schedule units. -- * We don't do any standard idle work like tasklets or livepatching. -- */ --static void guest_idle_loop(void) --{ -- unsigned int cpu = smp_processor_id(); -- -- for ( ; ; ) -- { -- ASSERT(!cpu_is_offline(cpu)); -- -- if ( !softirq_pending(cpu) && !scrub_free_pages() && -- !softirq_pending(cpu)) -- sched_guest_idle(pm_idle, cpu); -+ else if ( !softirq_pending(cpu) && !scrub_free_pages() && -+ !softirq_pending(cpu) ) -+ { -+ if ( guest ) -+ sched_guest_idle(pm_idle, cpu); -+ else -+ pm_idle(); -+ } - do_softirq(); - } - } -@@ -190,10 +184,6 @@ void startup_cpu_idle_loop(void) - - static void noreturn continue_idle_domain(struct vcpu *v) - { -- /* Idle vcpus might be attached to non-idle units! */ -- if ( !is_idle_domain(v->sched_unit->domain) ) -- reset_stack_and_jump(guest_idle_loop); -- - reset_stack_and_jump(idle_loop); - } - diff --git a/xsa348-4.13-3.patch b/xsa348-4.13-3.patch deleted file mode 100644 index abd1b22..0000000 --- a/xsa348-4.13-3.patch +++ /dev/null @@ -1,163 +0,0 @@ -From: Jan Beulich -Subject: x86: avoid calling {svm,vmx}_do_resume() - -These functions follow the following path: hvm_do_resume() -> -handle_hvm_io_completion() -> hvm_wait_for_io() -> -wait_on_xen_event_channel() -> do_softirq() -> schedule() -> -sched_context_switch() -> continue_running() and hence may -recursively invoke themselves. If this ends up happening a couple of -times, a stack overflow would result. - -Prevent this by also resetting the stack at the -->arch.ctxt_switch->tail() invocations (in both places for consistency) -and thus jumping to the functions instead of calling them. - -This is XSA-348 / CVE-2020-29566. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Juergen Gross - ---- sle15sp2.orig/xen/arch/x86/domain.c 2020-11-10 17:51:46.354546349 +0100 -+++ sle15sp2/xen/arch/x86/domain.c 2020-11-10 17:56:58.758730088 +0100 -@@ -130,7 +130,7 @@ void play_dead(void) - dead_idle(); - } - --static void idle_loop(void) -+static void noreturn idle_loop(void) - { - unsigned int cpu = smp_processor_id(); - /* -@@ -182,11 +182,6 @@ void startup_cpu_idle_loop(void) - reset_stack_and_jump(idle_loop); - } - --static void noreturn continue_idle_domain(struct vcpu *v) --{ -- reset_stack_and_jump(idle_loop); --} -- - void init_hypercall_page(struct domain *d, void *ptr) - { - memset(ptr, 0xcc, PAGE_SIZE); -@@ -535,7 +530,7 @@ int arch_domain_create(struct domain *d, - static const struct arch_csw idle_csw = { - .from = paravirt_ctxt_switch_from, - .to = paravirt_ctxt_switch_to, -- .tail = continue_idle_domain, -+ .tail = idle_loop, - }; - - d->arch.ctxt_switch = &idle_csw; -@@ -1833,20 +1828,12 @@ void context_switch(struct vcpu *prev, s - /* Ensure that the vcpu has an up-to-date time base. */ - update_vcpu_system_time(next); - -- /* -- * Schedule tail *should* be a terminal function pointer, but leave a -- * bug frame around just in case it returns, to save going back into the -- * context switching code and leaving a far more subtle crash to diagnose. -- */ -- nextd->arch.ctxt_switch->tail(next); -- BUG(); -+ reset_stack_and_jump_ind(nextd->arch.ctxt_switch->tail); - } - - void continue_running(struct vcpu *same) - { -- /* See the comment above. */ -- same->domain->arch.ctxt_switch->tail(same); -- BUG(); -+ reset_stack_and_jump_ind(same->domain->arch.ctxt_switch->tail); - } - - int __sync_local_execstate(void) ---- sle15sp2.orig/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:51:10.898525723 +0100 -+++ sle15sp2/xen/arch/x86/hvm/svm/svm.c 2020-11-10 17:56:58.762730090 +0100 -@@ -987,8 +987,9 @@ static void svm_ctxt_switch_to(struct vc - wrmsr_tsc_aux(v->arch.msrs->tsc_aux); - } - --static void noreturn svm_do_resume(struct vcpu *v) -+static void noreturn svm_do_resume(void) - { -+ struct vcpu *v = current; - struct vmcb_struct *vmcb = v->arch.hvm.svm.vmcb; - bool debug_state = (v->domain->debugger_attached || - v->domain->arch.monitor.software_breakpoint_enabled || ---- sle15sp2.orig/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:51:10.898525723 +0100 -+++ sle15sp2/xen/arch/x86/hvm/vmx/vmcs.c 2020-11-10 17:56:58.762730090 +0100 -@@ -1830,8 +1830,9 @@ void vmx_vmentry_failure(void) - domain_crash(curr->domain); - } - --void vmx_do_resume(struct vcpu *v) -+void vmx_do_resume(void) - { -+ struct vcpu *v = current; - bool_t debug_state; - unsigned long host_cr4; - ---- sle15sp2.orig/xen/arch/x86/pv/domain.c 2020-11-10 17:51:10.898525723 +0100 -+++ sle15sp2/xen/arch/x86/pv/domain.c 2020-11-10 17:56:58.762730090 +0100 -@@ -58,7 +58,7 @@ static int parse_pcid(const char *s) - } - custom_runtime_param("pcid", parse_pcid); - --static void noreturn continue_nonidle_domain(struct vcpu *v) -+static void noreturn continue_nonidle_domain(void) - { - check_wakeup_from_wait(); - reset_stack_and_jump(ret_from_intr); ---- sle15sp2.orig/xen/include/asm-x86/current.h 2020-11-10 17:51:10.902525725 +0100 -+++ sle15sp2/xen/include/asm-x86/current.h 2020-11-10 17:56:58.762730090 +0100 -@@ -129,16 +129,23 @@ unsigned long get_stack_dump_bottom (uns - # define CHECK_FOR_LIVEPATCH_WORK "" - #endif - --#define reset_stack_and_jump(fn) \ -+#define switch_stack_and_jump(fn, instr, constr) \ - ({ \ - __asm__ __volatile__ ( \ - "mov %0,%%"__OP"sp;" \ - CHECK_FOR_LIVEPATCH_WORK \ -- "jmp %c1" \ -- : : "r" (guest_cpu_user_regs()), "i" (fn) : "memory" ); \ -+ instr "1" \ -+ : : "r" (guest_cpu_user_regs()), constr (fn) : "memory" ); \ - unreachable(); \ - }) - -+#define reset_stack_and_jump(fn) \ -+ switch_stack_and_jump(fn, "jmp %c", "i") -+ -+/* The constraint may only specify non-call-clobbered registers. */ -+#define reset_stack_and_jump_ind(fn) \ -+ switch_stack_and_jump(fn, "INDIRECT_JMP %", "b") -+ - /* - * Which VCPU's state is currently running on each CPU? - * This is not necesasrily the same as 'current' as a CPU may be ---- sle15sp2.orig/xen/include/asm-x86/domain.h 2020-10-30 17:22:39.000000000 +0100 -+++ sle15sp2/xen/include/asm-x86/domain.h 2020-11-10 17:56:58.762730090 +0100 -@@ -313,7 +313,7 @@ struct arch_domain - const struct arch_csw { - void (*from)(struct vcpu *); - void (*to)(struct vcpu *); -- void (*tail)(struct vcpu *); -+ void noreturn (*tail)(void); - } *ctxt_switch; - - #ifdef CONFIG_HVM ---- sle15sp2.orig/xen/include/asm-x86/hvm/vmx/vmx.h 2019-12-18 16:18:59.000000000 +0100 -+++ sle15sp2/xen/include/asm-x86/hvm/vmx/vmx.h 2020-11-10 17:56:58.762730090 +0100 -@@ -95,7 +95,7 @@ typedef enum { - void vmx_asm_vmexit_handler(struct cpu_user_regs); - void vmx_asm_do_vmentry(void); - void vmx_intr_assist(void); --void noreturn vmx_do_resume(struct vcpu *); -+void noreturn vmx_do_resume(void); - void vmx_vlapic_msr_changed(struct vcpu *v); - void vmx_realmode_emulate_one(struct hvm_emulate_ctxt *hvmemul_ctxt); - void vmx_realmode(struct cpu_user_regs *regs); diff --git a/xsa351-arm.patch b/xsa351-arm.patch deleted file mode 100644 index d0d1941..0000000 --- a/xsa351-arm.patch +++ /dev/null @@ -1,58 +0,0 @@ -From: Julien Grall -Subject: xen/arm: Always trap AMU system registers - -The Activity Monitors Unit (AMU) has been introduced by ARMv8.4. It is -considered to be unsafe to be expose to guests as they might expose -information about code executed by other guests or the host. - -Arm provided a way to trap all the AMU system registers by setting -CPTR_EL2.TAM to 1. - -Unfortunately, on older revision of the specification, the bit 30 (now -CPTR_EL1.TAM) was RES0. Because of that, Xen is setting it to 0 and -therefore the system registers would be exposed to the guest when it is -run on processors with AMU. - -As the bit is mark as UNKNOWN at boot in Armv8.4, the only safe solution -for us is to always set CPTR_EL1.TAM to 1. - -Guest trying to access the AMU system registers will now receive an -undefined instruction. Unfortunately, this means that even well-behaved -guest may fail to boot because we don't sanitize the ID registers. - -This is a known issues with other Armv8.0+ features (e.g. SVE, Pointer -Auth). This will taken care separately. - -This is part of XSA-351 (or XSA-93 re-born). - -Signed-off-by: Julien Grall -Reviewed-by: Andre Przywara -Reviewed-by: Stefano Stabellini -Reviewed-by: Bertrand Marquis - -diff --git a/xen/arch/arm/traps.c b/xen/arch/arm/traps.c -index a36f145e67..22bd1bd4c6 100644 ---- a/xen/arch/arm/traps.c -+++ b/xen/arch/arm/traps.c -@@ -151,7 +151,8 @@ void init_traps(void) - * On ARM64 the TCPx bits which we set here (0..9,12,13) are all - * RES1, i.e. they would trap whether we did this write or not. - */ -- WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | HCPTR_TTA, -+ WRITE_SYSREG((HCPTR_CP_MASK & ~(HCPTR_CP(10) | HCPTR_CP(11))) | -+ HCPTR_TTA | HCPTR_TAM, - CPTR_EL2); - - /* -diff --git a/xen/include/asm-arm/processor.h b/xen/include/asm-arm/processor.h -index 3ca67f8157..d3d12a9d19 100644 ---- a/xen/include/asm-arm/processor.h -+++ b/xen/include/asm-arm/processor.h -@@ -351,6 +351,7 @@ - #define VTCR_RES1 (_AC(1,UL)<<31) - - /* HCPTR Hyp. Coprocessor Trap Register */ -+#define HCPTR_TAM ((_AC(1,U)<<30)) - #define HCPTR_TTA ((_AC(1,U)<<20)) /* Trap trace registers */ - #define HCPTR_CP(x) ((_AC(1,U)<<(x))) /* Trap Coprocessor x */ - #define HCPTR_CP_MASK ((_AC(1,U)<<14)-1) diff --git a/xsa351-x86-4.13-1.patch b/xsa351-x86-4.13-1.patch deleted file mode 100644 index b1fa25e..0000000 --- a/xsa351-x86-4.13-1.patch +++ /dev/null @@ -1,155 +0,0 @@ -From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= -Subject: x86/msr: fix handling of MSR_IA32_PERF_{STATUS/CTL} -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Currently a PV hardware domain can also be given control over the CPU -frequency, and such guest is allowed to write to MSR_IA32_PERF_CTL. -However since commit 322ec7c89f6 the default behavior has been changed -to reject accesses to not explicitly handled MSRs, preventing PV -guests that manage CPU frequency from reading -MSR_IA32_PERF_{STATUS/CTL}. - -Additionally some HVM guests (Windows at least) will attempt to read -MSR_IA32_PERF_CTL and will panic if given back a #GP fault: - - vmx.c:3035:d8v0 RDMSR 0x00000199 unimplemented - d8v0 VIRIDIAN CRASH: 3b c0000096 fffff806871c1651 ffffda0253683720 0 - -Move the handling of MSR_IA32_PERF_{STATUS/CTL} to the common MSR -handling shared between HVM and PV guests, and add an explicit case -for reads to MSR_IA32_PERF_{STATUS/CTL}. - -Restore previous behavior and allow PV guests with the required -permissions to read the contents of the mentioned MSRs. Non privileged -guests will get 0 when trying to read those registers, as writes to -MSR_IA32_PERF_CTL by such guest will already be silently dropped. - -Fixes: 322ec7c89f6 ('x86/pv: disallow access to unknown MSRs') -Fixes: 84e848fd7a1 ('x86/hvm: disallow access to unknown MSRs') -Signed-off-by: Roger Pau Monné -Signed-off-by: Andrew Cooper -Reviewed-by: Roger Pau Monné -Reviewed-by: Jan Beulich -(cherry picked from commit 3059178798a23ba870ff86ff54d442a07e6651fc) - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 875ac39d30..8c969197aa 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -208,6 +208,25 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - *val = msrs->misc_features_enables.raw; - break; - -+ /* -+ * These MSRs are not enumerated in CPUID. They have been around -+ * since the Pentium 4, and implemented by other vendors. -+ * -+ * Some versions of Windows try reading these before setting up a #GP -+ * handler, and Linux has several unguarded reads as well. Provide -+ * RAZ semantics, in general, but permit a cpufreq controller dom0 to -+ * have full access. -+ */ -+ case MSR_IA32_PERF_STATUS: -+ case MSR_IA32_PERF_CTL: -+ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) -+ goto gp_fault; -+ -+ *val = 0; -+ if ( likely(!is_cpufreq_controller(d)) || rdmsr_safe(msr, *val) == 0 ) -+ break; -+ goto gp_fault; -+ - case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: - if ( !is_hvm_domain(d) || v != curr ) - goto gp_fault; -@@ -305,6 +324,7 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_INTEL_CORE_THREAD_COUNT: - case MSR_INTEL_PLATFORM_INFO: - case MSR_ARCH_CAPABILITIES: -+ case MSR_IA32_PERF_STATUS: - /* Read-only */ - case MSR_TSX_FORCE_ABORT: - case MSR_TSX_CTRL: -@@ -411,6 +431,21 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - break; - } - -+ /* -+ * This MSR is not enumerated in CPUID. It has been around since the -+ * Pentium 4, and implemented by other vendors. -+ * -+ * To match the RAZ semantics, implement as write-discard, except for -+ * a cpufreq controller dom0 which has full access. -+ */ -+ case MSR_IA32_PERF_CTL: -+ if ( !(cp->x86_vendor & (X86_VENDOR_INTEL | X86_VENDOR_CENTAUR)) ) -+ goto gp_fault; -+ -+ if ( likely(!is_cpufreq_controller(d)) || wrmsr_safe(msr, val) == 0 ) -+ break; -+ goto gp_fault; -+ - case MSR_X2APIC_FIRST ... MSR_X2APIC_LAST: - if ( !is_hvm_domain(d) || v != curr ) - goto gp_fault; -diff --git a/xen/arch/x86/pv/emul-priv-op.c b/xen/arch/x86/pv/emul-priv-op.c -index 42258c6bf1..6dc4f92a84 100644 ---- a/xen/arch/x86/pv/emul-priv-op.c -+++ b/xen/arch/x86/pv/emul-priv-op.c -@@ -776,12 +776,6 @@ static inline uint64_t guest_misc_enable(uint64_t val) - return val; - } - --static inline bool is_cpufreq_controller(const struct domain *d) --{ -- return ((cpufreq_controller == FREQCTL_dom0_kernel) && -- is_hardware_domain(d)); --} -- - static int read_msr(unsigned int reg, uint64_t *val, - struct x86_emulate_ctxt *ctxt) - { -@@ -1026,14 +1020,6 @@ static int write_msr(unsigned int reg, uint64_t val, - return X86EMUL_OKAY; - break; - -- case MSR_IA32_PERF_CTL: -- if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) -- break; -- if ( likely(!is_cpufreq_controller(currd)) || -- wrmsr_safe(reg, val) == 0 ) -- return X86EMUL_OKAY; -- break; -- - case MSR_IA32_THERM_CONTROL: - case MSR_IA32_ENERGY_PERF_BIAS: - if ( boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ) -diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h -index d6e27fc4b8..8bb5bd7b38 100644 ---- a/xen/include/xen/sched.h -+++ b/xen/include/xen/sched.h -@@ -1057,6 +1057,22 @@ extern enum cpufreq_controller { - FREQCTL_none, FREQCTL_dom0_kernel, FREQCTL_xen - } cpufreq_controller; - -+static always_inline bool is_cpufreq_controller(const struct domain *d) -+{ -+ /* -+ * A PV dom0 can be nominated as the cpufreq controller, instead of using -+ * Xen's cpufreq driver, at which point dom0 gets direct access to certain -+ * MSRs. -+ * -+ * This interface only works when dom0 is identity pinned and has the same -+ * number of vCPUs as pCPUs on the system. -+ * -+ * It would be far better to paravirtualise the interface. -+ */ -+ return (is_pv_domain(d) && is_hardware_domain(d) && -+ cpufreq_controller == FREQCTL_dom0_kernel); -+} -+ - #define CPUPOOLID_NONE -1 - - struct cpupool *cpupool_get_by_id(int poolid); diff --git a/xsa351-x86-4.13-2.patch b/xsa351-x86-4.13-2.patch deleted file mode 100644 index fee25fb..0000000 --- a/xsa351-x86-4.13-2.patch +++ /dev/null @@ -1,128 +0,0 @@ -From: Andrew Cooper -Subject: x86/msr: Disallow guest access to the RAPL MSRs - -Researchers have demonstrated using the RAPL interface to perform a -differential power analysis attack to recover AES keys used by other cores in -the system. - -Furthermore, even privileged guests cannot use this interface correctly, due -to MSR scope and vcpu scheduling issues. The interface would want to be -paravirtualised to be used sensibly. - -Disallow access to the RAPL MSRs completely, as well as other MSRs which -potentially access fine grain power information. - -This is part of XSA-351. - -Signed-off-by: Andrew Cooper -Reviewed-by: Jan Beulich - -diff --git a/xen/arch/x86/msr.c b/xen/arch/x86/msr.c -index 8c969197aa..8ab6949a8e 100644 ---- a/xen/arch/x86/msr.c -+++ b/xen/arch/x86/msr.c -@@ -152,11 +152,20 @@ int guest_rdmsr(struct vcpu *v, uint32_t msr, uint64_t *val) - case MSR_TSX_CTRL: - case MSR_MCU_OPT_CTRL: - case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): -+ case MSR_RAPL_POWER_UNIT: -+ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: -+ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: -+ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: -+ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: -+ case MSR_PLATFORM_ENERGY_COUNTER: -+ case MSR_PLATFORM_POWER_LIMIT: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: - case MSR_AMD64_LWP_CFG: - case MSR_AMD64_LWP_CBADDR: -+ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: -+ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: - /* Not offered to guests. */ - goto gp_fault; - -@@ -330,11 +339,20 @@ int guest_wrmsr(struct vcpu *v, uint32_t msr, uint64_t val) - case MSR_TSX_CTRL: - case MSR_MCU_OPT_CTRL: - case MSR_RTIT_OUTPUT_BASE ... MSR_RTIT_ADDR_B(7): -+ case MSR_RAPL_POWER_UNIT: -+ case MSR_PKG_POWER_LIMIT ... MSR_PKG_POWER_INFO: -+ case MSR_DRAM_POWER_LIMIT ... MSR_DRAM_POWER_INFO: -+ case MSR_PP0_POWER_LIMIT ... MSR_PP0_POLICY: -+ case MSR_PP1_POWER_LIMIT ... MSR_PP1_POLICY: -+ case MSR_PLATFORM_ENERGY_COUNTER: -+ case MSR_PLATFORM_POWER_LIMIT: - case MSR_U_CET: - case MSR_S_CET: - case MSR_PL0_SSP ... MSR_INTERRUPT_SSP_TABLE: - case MSR_AMD64_LWP_CFG: - case MSR_AMD64_LWP_CBADDR: -+ case MSR_F15H_CU_POWER ... MSR_F15H_CU_MAX_POWER: -+ case MSR_AMD_RAPL_POWER_UNIT ... MSR_AMD_PKG_ENERGY_STATUS: - /* Not offered to guests. */ - goto gp_fault; - -diff --git a/xen/include/asm-x86/msr-index.h b/xen/include/asm-x86/msr-index.h -index 0eb6855614..ba9e90af21 100644 ---- a/xen/include/asm-x86/msr-index.h -+++ b/xen/include/asm-x86/msr-index.h -@@ -96,6 +96,38 @@ - /* Lower 6 bits define the format of the address in the LBR stack */ - #define MSR_IA32_PERF_CAP_LBR_FORMAT 0x3f - -+/* -+ * Intel Runtime Average Power Limiting (RAPL) interface. Power plane base -+ * addresses (MSR_*_POWER_LIMIT) are model specific, but have so-far been -+ * consistent since their introduction in SandyBridge. -+ * -+ * Offsets of functionality from the power plane base is architectural, but -+ * not all power planes support all functionality. -+ */ -+#define MSR_RAPL_POWER_UNIT 0x00000606 -+ -+#define MSR_PKG_POWER_LIMIT 0x00000610 -+#define MSR_PKG_ENERGY_STATUS 0x00000611 -+#define MSR_PKG_PERF_STATUS 0x00000613 -+#define MSR_PKG_POWER_INFO 0x00000614 -+ -+#define MSR_DRAM_POWER_LIMIT 0x00000618 -+#define MSR_DRAM_ENERGY_STATUS 0x00000619 -+#define MSR_DRAM_PERF_STATUS 0x0000061b -+#define MSR_DRAM_POWER_INFO 0x0000061c -+ -+#define MSR_PP0_POWER_LIMIT 0x00000638 -+#define MSR_PP0_ENERGY_STATUS 0x00000639 -+#define MSR_PP0_POLICY 0x0000063a -+ -+#define MSR_PP1_POWER_LIMIT 0x00000640 -+#define MSR_PP1_ENERGY_STATUS 0x00000641 -+#define MSR_PP1_POLICY 0x00000642 -+ -+/* Intel Platform-wide power interface. */ -+#define MSR_PLATFORM_ENERGY_COUNTER 0x0000064d -+#define MSR_PLATFORM_POWER_LIMIT 0x0000065c -+ - #define MSR_IA32_BNDCFGS 0x00000d90 - #define IA32_BNDCFGS_ENABLE 0x00000001 - #define IA32_BNDCFGS_PRESERVE 0x00000002 -@@ -236,6 +268,8 @@ - #define MSR_K8_VM_CR 0xc0010114 - #define MSR_K8_VM_HSAVE_PA 0xc0010117 - -+#define MSR_F15H_CU_POWER 0xc001007a -+#define MSR_F15H_CU_MAX_POWER 0xc001007b - #define MSR_AMD_FAM15H_EVNTSEL0 0xc0010200 - #define MSR_AMD_FAM15H_PERFCTR0 0xc0010201 - #define MSR_AMD_FAM15H_EVNTSEL1 0xc0010202 -@@ -249,6 +283,10 @@ - #define MSR_AMD_FAM15H_EVNTSEL5 0xc001020a - #define MSR_AMD_FAM15H_PERFCTR5 0xc001020b - -+#define MSR_AMD_RAPL_POWER_UNIT 0xc0010299 -+#define MSR_AMD_CORE_ENERGY_STATUS 0xc001029a -+#define MSR_AMD_PKG_ENERGY_STATUS 0xc001029b -+ - #define MSR_AMD_L7S0_FEATURE_MASK 0xc0011002 - #define MSR_AMD_THRM_FEATURE_MASK 0xc0011003 - #define MSR_K8_FEATURE_MASK 0xc0011004 diff --git a/xsa352.patch b/xsa352.patch deleted file mode 100644 index e21d21a..0000000 --- a/xsa352.patch +++ /dev/null @@ -1,42 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: only Dom0 can change node owner -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Otherwise we can give quota away to another domain, either causing it to run -out of quota, or in case of Dom0 use unbounded amounts of memory and bypass -the quota system entirely. - -This was fixed in the C version of xenstored in 2006 (c/s db34d2aaa5f5, -predating the XSA process by 5 years). - -It was also fixed in the mirage version of xenstore in 2012, with a unit test -demonstrating the vulnerability: - - https://github.com/mirage/ocaml-xenstore/commit/6b91f3ac46b885d0530a51d57a9b3a57d64923a7 - https://github.com/mirage/ocaml-xenstore/commit/22ee5417c90b8fda905c38de0d534506152eace6 - -but possibly without realising that the vulnerability still affected the -in-tree oxenstored (added c/s f44af660412 in 2010). - -This is XSA-352. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index 3b05128f1b..5f915f2bbe 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -407,7 +407,8 @@ let setperms store perm path nperms = - | Some node -> - let old_owner = Node.get_owner node in - let new_owner = Perms.Node.get_owner nperms in -- if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then Quota.check store.quota new_owner 0; -+ if not ((old_owner = new_owner) || (Perms.Connection.is_dom0 perm)) then -+ raise Define.Permission_denied; - store.root <- path_setperms store perm path nperms; - Quota.del_entry store.quota old_owner; - Quota.add_entry store.quota new_owner diff --git a/xsa353.patch b/xsa353.patch deleted file mode 100644 index 764f93c..0000000 --- a/xsa353.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: =?UTF-8?q?Edwin=20T=C3=B6r=C3=B6k?= -Subject: tools/ocaml/xenstored: do permission checks on xenstore root -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This was lacking in a disappointing number of places. - -The xenstore root node is treated differently from all other nodes, because it -doesn't have a parent, and mutation requires changing the parent. - -Unfortunately this lead to open-coding the special case for root into every -single xenstore operation, and out of all the xenstore operations only read -did a permission check when handling the root node. - -This means that an unprivileged guest can: - - * xenstore-chmod / to its liking and subsequently write new arbitrary nodes - there (subject to quota) - * xenstore-rm -r / deletes almost the entire xenstore tree (xenopsd quickly - refills some, but you are left with a broken system) - * DIRECTORY on / lists all children when called through python - bindings (xenstore-ls stops at /local because it tries to list recursively) - * get-perms on / works too, but that is just a minor information leak - -Add the missing permission checks, but this should really be refactored to do -the root handling and permission checks on the node only once from a single -function, instead of getting it wrong nearly everywhere. - -This is XSA-353. - -Signed-off-by: Edwin Török -Acked-by: Christian Lindig -Reviewed-by: Andrew Cooper - -diff --git a/tools/ocaml/xenstored/store.ml b/tools/ocaml/xenstored/store.ml -index f299ec6461..92b6289b5e 100644 ---- a/tools/ocaml/xenstored/store.ml -+++ b/tools/ocaml/xenstored/store.ml -@@ -273,15 +273,17 @@ let path_rm store perm path = - Node.del_childname node name - with Not_found -> - raise Define.Doesnt_exist in -- if path = [] then -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.WRITE; - Node.del_all_children store.root -- else -+ ) else - Path.apply_modify store.root path do_rm - - let path_setperms store perm path perms = -- if path = [] then -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.WRITE; - Node.set_perms store.root perms -- else -+ ) else - let do_setperms node name = - let c = Node.find node name in - Node.check_owner c perm; -@@ -313,9 +315,10 @@ let read store perm path = - - let ls store perm path = - let children = -- if path = [] then -- (Node.get_children store.root) -- else -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.READ; -+ Node.get_children store.root -+ ) else - let do_ls node name = - let cnode = Node.find node name in - Node.check_perm cnode perm Perms.READ; -@@ -324,9 +327,10 @@ let ls store perm path = - List.rev (List.map (fun n -> Symbol.to_string n.Node.name) children) - - let getperms store perm path = -- if path = [] then -- (Node.get_perms store.root) -- else -+ if path = [] then ( -+ Node.check_perm store.root perm Perms.READ; -+ Node.get_perms store.root -+ ) else - let fct n name = - let c = Node.find n name in - Node.check_perm c perm Perms.READ; diff --git a/xsa355.patch b/xsa355.patch deleted file mode 100644 index 491dd05..0000000 --- a/xsa355.patch +++ /dev/null @@ -1,23 +0,0 @@ -From: Jan Beulich -Subject: memory: fix off-by-one in XSA-346 change - -The comparison against ARRAY_SIZE() needs to be >= in order to avoid -overrunning the pages[] array. - -This is XSA-355. - -Fixes: 5777a3742d88 ("IOMMU: hold page ref until after deferred TLB flush") -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/memory.c -+++ b/xen/common/memory.c -@@ -854,7 +854,7 @@ int xenmem_add_to_physmap(struct domain - ++extra.ppage; - - /* Check for continuation if it's not the last iteration. */ -- if ( (++done > ARRAY_SIZE(pages) && extra.ppage) || -+ if ( (++done >= ARRAY_SIZE(pages) && extra.ppage) || - (xatp->size > done && hypercall_preempt_check()) ) - { - rc = start + done; diff --git a/xsa358-4.14.patch b/xsa358-4.14.patch deleted file mode 100644 index 0d56a8f..0000000 --- a/xsa358-4.14.patch +++ /dev/null @@ -1,54 +0,0 @@ -From: Jan Beulich -Subject: evtchn/FIFO: re-order and synchronize (with) map_control_block() - -For evtchn_fifo_set_pending()'s check of the control block having been -set to be effective, ordering of respective reads and writes needs to be -ensured: The control block pointer needs to be recorded strictly after -the setting of all the queue heads, and it needs checking strictly -before any uses of them (this latter aspect was already guaranteed). - -This is XSA-358 / CVE-2020-29570. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Acked-by: Julien Grall - ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -249,6 +249,10 @@ static void evtchn_fifo_set_pending(stru - goto unlock; - } - -+ /* -+ * This also acts as the read counterpart of the smp_wmb() in -+ * map_control_block(). -+ */ - if ( guest_test_and_set_bit(d, EVTCHN_FIFO_LINKED, word) ) - goto unlock; - -@@ -474,6 +478,7 @@ static int setup_control_block(struct vc - static int map_control_block(struct vcpu *v, uint64_t gfn, uint32_t offset) - { - void *virt; -+ struct evtchn_fifo_control_block *control_block; - unsigned int i; - int rc; - -@@ -484,10 +489,15 @@ static int map_control_block(struct vcpu - if ( rc < 0 ) - return rc; - -- v->evtchn_fifo->control_block = virt + offset; -+ control_block = virt + offset; - - for ( i = 0; i <= EVTCHN_FIFO_PRIORITY_MIN; i++ ) -- v->evtchn_fifo->queue[i].head = &v->evtchn_fifo->control_block->head[i]; -+ v->evtchn_fifo->queue[i].head = &control_block->head[i]; -+ -+ /* All queue heads must have been set before setting the control block. */ -+ smp_wmb(); -+ -+ v->evtchn_fifo->control_block = control_block; - - return 0; - } diff --git a/xsa359.patch b/xsa359.patch deleted file mode 100644 index 231810b..0000000 --- a/xsa359.patch +++ /dev/null @@ -1,40 +0,0 @@ -From: Jan Beulich -Subject: evtchn/FIFO: add 2nd smp_rmb() to evtchn_fifo_word_from_port() - -Besides with add_page_to_event_array() the function also needs to -synchronize with evtchn_fifo_init_control() setting both d->evtchn_fifo -and (subsequently) d->evtchn_port_ops. - -This is XSA-359 / CVE-2020-29571. - -Reported-by: Julien Grall -Signed-off-by: Jan Beulich -Reviewed-by: Julien Grall - ---- a/xen/common/event_fifo.c -+++ b/xen/common/event_fifo.c -@@ -55,6 +55,13 @@ static inline event_word_t *evtchn_fifo_ - { - unsigned int p, w; - -+ /* -+ * Callers aren't required to hold d->event_lock, so we need to synchronize -+ * with evtchn_fifo_init_control() setting d->evtchn_port_ops /after/ -+ * d->evtchn_fifo. -+ */ -+ smp_rmb(); -+ - if ( unlikely(port >= d->evtchn_fifo->num_evtchns) ) - return NULL; - -@@ -606,6 +613,10 @@ int evtchn_fifo_init_control(struct evtc - if ( rc < 0 ) - goto error; - -+ /* -+ * This call, as a side effect, synchronizes with -+ * evtchn_fifo_word_from_port(). -+ */ - rc = map_control_block(v, gfn, offset); - if ( rc < 0 ) - goto error; diff --git a/xsa360-4.14.patch b/xsa360-4.14.patch deleted file mode 100644 index 1bc185b..0000000 --- a/xsa360-4.14.patch +++ /dev/null @@ -1,97 +0,0 @@ -From: Roger Pau Monne -Subject: x86/dpci: do not remove pirqs from domain tree on unbind - -A fix for a previous issue removed the pirqs from the domain tree when -they are unbound in order to prevent shared pirqs from triggering a -BUG_ON in __pirq_guest_unbind if they are unbound multiple times. That -caused free_domain_pirqs to no longer unmap the pirqs because they -are gone from the domain pirq tree, thus leaving stale unbound pirqs -after domain destruction if the domain had mapped dpci pirqs after -shutdown. - -Take a different approach to fix the original issue, instead of -removing the pirq from d->pirq_tree clear the flags of the dpci pirq -struct to signal that the pirq is now unbound. This prevents calling -pirq_guest_unbind multiple times for the same pirq without having to -remove it from the domain pirq tree. - -This is XSA-360. - -Fixes: 5b58dad089 ('x86/pass-through: avoid double IRQ unbind during domain cleanup') -Signed-off-by: Roger Pau Monné -Reviewed-by: Jan Beulich - ---- a/xen/arch/x86/irq.c -+++ b/xen/arch/x86/irq.c -@@ -1331,7 +1331,7 @@ void (pirq_cleanup_check)(struct pirq *p - } - - if ( radix_tree_delete(&d->pirq_tree, pirq->pirq) != pirq ) -- BUG_ON(!d->is_dying); -+ BUG(); - } - - /* Flush all ready EOIs from the top of this CPU's pending-EOI stack. */ ---- a/xen/drivers/passthrough/pci.c -+++ b/xen/drivers/passthrough/pci.c -@@ -862,6 +862,10 @@ static int pci_clean_dpci_irq(struct dom - { - struct dev_intx_gsi_link *digl, *tmp; - -+ if ( !pirq_dpci->flags ) -+ /* Already processed. */ -+ return 0; -+ - pirq_guest_unbind(d, dpci_pirq(pirq_dpci)); - - if ( pt_irq_need_timer(pirq_dpci->flags) ) -@@ -872,15 +876,10 @@ static int pci_clean_dpci_irq(struct dom - list_del(&digl->list); - xfree(digl); - } -+ /* Note the pirq is now unbound. */ -+ pirq_dpci->flags = 0; - -- radix_tree_delete(&d->pirq_tree, dpci_pirq(pirq_dpci)->pirq); -- -- if ( !pt_pirq_softirq_active(pirq_dpci) ) -- return 0; -- -- domain_get_irq_dpci(d)->pending_pirq_dpci = pirq_dpci; -- -- return -ERESTART; -+ return pt_pirq_softirq_active(pirq_dpci) ? -ERESTART : 0; - } - - static int pci_clean_dpci_irqs(struct domain *d) -@@ -897,18 +896,8 @@ static int pci_clean_dpci_irqs(struct do - hvm_irq_dpci = domain_get_irq_dpci(d); - if ( hvm_irq_dpci != NULL ) - { -- int ret = 0; -- -- if ( hvm_irq_dpci->pending_pirq_dpci ) -- { -- if ( pt_pirq_softirq_active(hvm_irq_dpci->pending_pirq_dpci) ) -- ret = -ERESTART; -- else -- hvm_irq_dpci->pending_pirq_dpci = NULL; -- } -+ int ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - -- if ( !ret ) -- ret = pt_pirq_iterate(d, pci_clean_dpci_irq, NULL); - if ( ret ) - { - spin_unlock(&d->event_lock); ---- a/xen/include/asm-x86/hvm/irq.h -+++ b/xen/include/asm-x86/hvm/irq.h -@@ -160,8 +160,6 @@ struct hvm_irq_dpci { - DECLARE_BITMAP(isairq_map, NR_ISAIRQS); - /* Record of mapped Links */ - uint8_t link_cnt[NR_LINK]; -- /* Clean up: Entry with a softirq invocation pending / in progress. */ -- struct hvm_pirq_dpci *pending_pirq_dpci; - }; - - /* Machine IRQ to guest device/intx mapping. */ diff --git a/xsa364.patch b/xsa364.patch deleted file mode 100644 index 2d4b057..0000000 --- a/xsa364.patch +++ /dev/null @@ -1,69 +0,0 @@ -From dadb5b4b21c904ce59024c686eb1c55be8f46c52 Mon Sep 17 00:00:00 2001 -From: Julien Grall -Date: Thu, 21 Jan 2021 10:16:08 +0000 -Subject: [PATCH] xen/page_alloc: Only flush the page to RAM once we know they - are scrubbed - -At the moment, each page are flushed to RAM just after the allocator -found some free pages. However, this is happening before check if the -page was scrubbed. - -As a consequence, on Arm, a guest may be able to access the old content -of the scrubbed pages if it has cache disabled (default at boot) and -the content didn't reach the Point of Coherency. - -The flush is now moved after we know the content of the page will not -change. This also has the benefit to reduce the amount of work happening -with the heap_lock held. - -This is XSA-364. - -Fixes: 307c3be3ccb2 ("mm: Don't scrub pages while holding heap lock in alloc_heap_pages()") -Signed-off-by: Julien Grall -Reviewed-by: Jan Beulich ---- - xen/common/page_alloc.c | 14 +++++++++----- - 1 file changed, 9 insertions(+), 5 deletions(-) - -diff --git a/xen/common/page_alloc.c b/xen/common/page_alloc.c -index 02ac1fa613e7..1744e6faa5c4 100644 ---- a/xen/common/page_alloc.c -+++ b/xen/common/page_alloc.c -@@ -924,6 +924,7 @@ static struct page_info *alloc_heap_pages( - bool need_tlbflush = false; - uint32_t tlbflush_timestamp = 0; - unsigned int dirty_cnt = 0; -+ mfn_t mfn; - - /* Make sure there are enough bits in memflags for nodeID. */ - BUILD_BUG_ON((_MEMF_bits - _MEMF_node) < (8 * sizeof(nodeid_t))); -@@ -1022,11 +1023,6 @@ static struct page_info *alloc_heap_pages( - pg[i].u.inuse.type_info = 0; - page_set_owner(&pg[i], NULL); - -- /* Ensure cache and RAM are consistent for platforms where the -- * guest can control its own visibility of/through the cache. -- */ -- flush_page_to_ram(mfn_x(page_to_mfn(&pg[i])), -- !(memflags & MEMF_no_icache_flush)); - } - - spin_unlock(&heap_lock); -@@ -1062,6 +1058,14 @@ static struct page_info *alloc_heap_pages( - if ( need_tlbflush ) - filtered_flush_tlb_mask(tlbflush_timestamp); - -+ /* -+ * Ensure cache and RAM are consistent for platforms where the guest -+ * can control its own visibility of/through the cache. -+ */ -+ mfn = page_to_mfn(pg); -+ for ( i = 0; i < (1U << order); i++ ) -+ flush_page_to_ram(mfn_x(mfn) + i, !(memflags & MEMF_no_icache_flush)); -+ - return pg; - } - --- -2.17.1 - diff --git a/xsa368-4.13.patch b/xsa368-4.13.patch deleted file mode 100644 index 7c1d162..0000000 --- a/xsa368-4.13.patch +++ /dev/null @@ -1,112 +0,0 @@ -From a733fcca97d4e0d7503198ba1dd739a5d7a00dac Mon Sep 17 00:00:00 2001 -From: Anthony PERARD -Date: Wed, 24 Feb 2021 18:39:20 +0000 -Subject: [PATCH] libxl: Fix domain soft reset state handling - -In do_domain_soft_reset(), a `libxl__domain_suspend_state' is used -without been properly initialised and disposed of. This lead do a -abort() in libxl due to the `dsps.qmp' state been used before been -initialised: - libxl__ev_qmp_send: Assertion `ev->state == qmp_state_disconnected || ev->state == qmp_state_connected' failed. - -Once initialised, `dsps' also needs to be disposed of as the `qmp' -state might still be in the `Connected' state in the callback for -libxl__domain_suspend_device_model(). So this patch adds -libxl__domain_suspend_dispose() which can be called from the two -places where we need to dispose of `dsps'. - -Reported-by: Olaf Hering -Signed-off-by: Anthony PERARD -Reviewed-by: Ian Jackson -Tested-by: Olaf Hering ---- - tools/libxl/libxl_create.c | 11 ++++++++--- - tools/libxl/libxl_dom_suspend.c | 15 +++++++++++---- - tools/libxl/libxl_internal.h | 2 ++ - 3 files changed, 21 insertions(+), 7 deletions(-) - -diff --git a/tools/libxl/libxl_create.c b/tools/libxl/libxl_create.c -index 32d45dcef0..651ad18d2d 100644 ---- a/tools/libxl/libxl_create.c -+++ b/tools/libxl/libxl_create.c -@@ -1974,9 +1974,7 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - state->console_tty = libxl__strdup(gc, console_tty); - - dss->ao = ao; -- dss->domid = dss->dsps.domid = domid_soft_reset; -- dss->dsps.dm_savefile = GCSPRINTF(LIBXL_DEVICE_MODEL_SAVE_FILE".%d", -- domid_soft_reset); -+ dss->domid = domid_soft_reset; - - rc = libxl__save_emulator_xenstore_data(dss, &srs->toolstack_buf, - &srs->toolstack_len); -@@ -1986,6 +1984,11 @@ static int do_domain_soft_reset(libxl_ctx *ctx, - } - - dss->dsps.ao = ao; -+ dss->dsps.domid = domid_soft_reset; -+ dss->dsps.live = false; -+ rc = libxl__domain_suspend_init(egc, &dss->dsps, d_config->b_info.type); -+ if (rc) -+ goto out; - dss->dsps.callback_device_model_done = soft_reset_dm_suspended; - libxl__domain_suspend_device_model(egc, &dss->dsps); /* must be last */ - -@@ -2004,6 +2007,8 @@ static void soft_reset_dm_suspended(libxl__egc *egc, - CONTAINER_OF(dsps, *srs, dss.dsps); - libxl__app_domain_create_state *cdcs = &srs->cdcs; - -+ libxl__domain_suspend_dispose(gc, dsps); -+ - /* - * Ask all backends to disconnect by removing the domain from - * xenstore. On the creation path the domain will be introduced to -diff --git a/tools/libxl/libxl_dom_suspend.c b/tools/libxl/libxl_dom_suspend.c -index 25d1571895..2a280f69a1 100644 ---- a/tools/libxl/libxl_dom_suspend.c -+++ b/tools/libxl/libxl_dom_suspend.c -@@ -67,6 +67,16 @@ out: - return rc; - } - -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps) -+{ -+ libxl__xswait_stop(gc, &dsps->pvcontrol); -+ libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -+ libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -+ libxl__ev_time_deregister(gc, &dsps->guest_timeout); -+ libxl__ev_qmp_dispose(gc, &dsps->qmp); -+} -+ - /*----- callbacks, called by xc_domain_save -----*/ - - void libxl__domain_suspend_device_model(libxl__egc *egc, -@@ -388,10 +398,7 @@ static void domain_suspend_common_done(libxl__egc *egc, - { - EGC_GC; - assert(!libxl__xswait_inuse(&dsps->pvcontrol)); -- libxl__ev_evtchn_cancel(gc, &dsps->guest_evtchn); -- libxl__ev_xswatch_deregister(gc, &dsps->guest_watch); -- libxl__ev_time_deregister(gc, &dsps->guest_timeout); -- libxl__ev_qmp_dispose(gc, &dsps->qmp); -+ libxl__domain_suspend_dispose(gc, dsps); - dsps->callback_common_done(egc, dsps, rc); - } - -diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h -index 247518a7ac..5b4795908b 100644 ---- a/tools/libxl/libxl_internal.h -+++ b/tools/libxl/libxl_internal.h -@@ -3569,6 +3569,8 @@ struct libxl__domain_suspend_state { - int libxl__domain_suspend_init(libxl__egc *egc, - libxl__domain_suspend_state *dsps, - libxl_domain_type type); -+void libxl__domain_suspend_dispose(libxl__gc *gc, -+ libxl__domain_suspend_state *dsps); - - /* calls dsps->callback_device_model_done when done - * may synchronously calls this callback */ --- -2.30.1 -