HVM: sanitize DOMCTL_gethvmcontext_partial handling Have the caller indicate its buffer size, provide a means to query the needed size, don't ignore the upper halves of type code and instance, and don't copy partial data. Signed-off-by: Jan Beulich Acked-by: Wei Liu --- v2: Re-base. --- a/tools/libxc/xc_domain.c +++ b/tools/libxc/xc_domain.c @@ -496,6 +496,7 @@ int xc_domain_hvm_getcontext_partial(xc_ domctl.domain = (domid_t) domid; domctl.u.hvmcontext_partial.type = typecode; domctl.u.hvmcontext_partial.instance = instance; + domctl.u.hvmcontext_partial.bufsz = size; set_xen_guest_handle(domctl.u.hvmcontext_partial.buffer, ctxt_buf); ret = do_domctl(xch, &domctl); --- a/xen/arch/x86/domctl.c +++ b/xen/arch/x86/domctl.c @@ -590,8 +590,12 @@ long arch_do_domctl( domain_pause(d); ret = hvm_save_one(d, domctl->u.hvmcontext_partial.type, domctl->u.hvmcontext_partial.instance, - domctl->u.hvmcontext_partial.buffer); + domctl->u.hvmcontext_partial.buffer, + &domctl->u.hvmcontext_partial.bufsz); domain_unpause(d); + + if ( !ret ) + copyback = true; break; case XEN_DOMCTL_set_address_size: --- a/xen/common/hvm/save.c +++ b/xen/common/hvm/save.c @@ -76,8 +76,8 @@ size_t hvm_save_size(struct domain *d) /* Extract a single instance of a save record, by marshalling all * records of that type and copying out the one we need. */ -int hvm_save_one(struct domain *d, uint16_t typecode, uint16_t instance, - XEN_GUEST_HANDLE_64(uint8) handle) +int hvm_save_one(struct domain *d, unsigned int typecode, unsigned int instance, + XEN_GUEST_HANDLE_64(uint8) handle, uint64_t *bufsz) { int rv = -ENOENT; size_t sz = 0; @@ -117,16 +117,20 @@ int hvm_save_one(struct domain *d, uint1 desc = (void *)(ctxt.data + off); /* Move past header */ off += sizeof(*desc); + if ( ctxt.cur < desc->length || + off > ctxt.cur - desc->length ) + break; if ( instance == desc->instance ) { - uint32_t copy_length = desc->length; - - if ( ctxt.cur < copy_length || - off > ctxt.cur - copy_length ) - copy_length = ctxt.cur - off; rv = 0; - if ( copy_to_guest(handle, ctxt.data + off, copy_length) ) + if ( guest_handle_is_null(handle) ) + *bufsz = desc->length; + else if ( *bufsz < desc->length ) + rv = -ENOBUFS; + else if ( copy_to_guest(handle, ctxt.data + off, desc->length) ) rv = -EFAULT; + else + *bufsz = desc->length; break; } } --- a/xen/include/public/domctl.h +++ b/xen/include/public/domctl.h @@ -746,6 +746,7 @@ DEFINE_XEN_GUEST_HANDLE(xen_domctl_debug typedef struct xen_domctl_hvmcontext_partial { uint32_t type; /* IN: Type of record required */ uint32_t instance; /* IN: Instance of that type */ + uint64_aligned_t bufsz; /* IN: size of buffer */ XEN_GUEST_HANDLE_64(uint8) buffer; /* OUT: buffer to write record into */ } xen_domctl_hvmcontext_partial_t; DEFINE_XEN_GUEST_HANDLE(xen_domctl_hvmcontext_partial_t); --- a/xen/include/xen/hvm/save.h +++ b/xen/include/xen/hvm/save.h @@ -132,8 +132,8 @@ __initcall(__hvm_register_##_x##_save_an /* Entry points for saving and restoring HVM domain state */ size_t hvm_save_size(struct domain *d); int hvm_save(struct domain *d, hvm_domain_context_t *h); -int hvm_save_one(struct domain *d, uint16_t typecode, uint16_t instance, - XEN_GUEST_HANDLE_64(uint8) handle); +int hvm_save_one(struct domain *d, unsigned int typecode, unsigned int instance, + XEN_GUEST_HANDLE_64(uint8) handle, uint64_t *bufsz); int hvm_load(struct domain *d, hvm_domain_context_t *h); /* Arch-specific definitions. */