diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c index 8d3d52034a6d..d05ee0da55c5 100644 --- a/xen/arch/x86/domain.c +++ b/xen/arch/x86/domain.c @@ -1088,7 +1088,7 @@ int arch_set_info_guest( if ( is_pv_domain(d) ) { for ( i = 0; i < ARRAY_SIZE(v->arch.dr); i++ ) - if ( !access_ok(c(debugreg[i]), sizeof(long)) ) + if ( !breakpoint_addr_ok(c(debugreg[i])) ) return -EINVAL; /* * Prior to Xen 4.11, dr5 was used to hold the emulated-only diff --git a/xen/arch/x86/include/asm/debugreg.h b/xen/arch/x86/include/asm/debugreg.h index 39ba312b84ee..b6454cc04e71 100644 --- a/xen/arch/x86/include/asm/debugreg.h +++ b/xen/arch/x86/include/asm/debugreg.h @@ -76,6 +76,25 @@ __val; \ }) +/* + * Architecturally, %dr{0..3} can have any arbitrary value. However, Xen + * can't allow the guest to breakpoint the Xen address range, so we limit the + * guest to the lower canonical half, or above the Xen range in the higher + * canonical half. + * + * Breakpoint lengths are specified to mask the low order address bits, + * meaning all breakpoints are naturally aligned. With %dr7, the widest + * breakpoint is 8 bytes. With DBEXT, the widest breakpoint is 4G. Both of + * the Xen boundaries have >4G alignment. + * + * In principle we should account for HYPERVISOR_COMPAT_VIRT_START(d), but + * 64bit Xen has never enforced this for compat guests, and there's no problem + * (to Xen) if the guest breakpoints it's alias of the M2P. Skipping this + * aspect simplifies the logic, and causes us not to reject a migrating guest + * which operated fine on prior versions of Xen. + */ +#define breakpoint_addr_ok(a) __addr_ok(a) + struct vcpu; long set_debugreg(struct vcpu *, unsigned int reg, unsigned long value); void activate_debugregs(const struct vcpu *); diff --git a/xen/arch/x86/pv/misc-hypercalls.c b/xen/arch/x86/pv/misc-hypercalls.c index 99f502812868..b529f00ea127 100644 --- a/xen/arch/x86/pv/misc-hypercalls.c +++ b/xen/arch/x86/pv/misc-hypercalls.c @@ -61,7 +61,7 @@ long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value) switch ( reg ) { case 0 ... 3: - if ( !access_ok(value, sizeof(long)) ) + if ( !breakpoint_addr_ok(value) ) return -EPERM; v->arch.dr[reg] = value;