On Tue, 23 Apr 2024 at 16:16, Paolo Bonzini <[email protected]> wrote:
>
> From: Chao Peng <[email protected]>
>
> Upon an KVM_EXIT_MEMORY_FAULT exit, userspace needs to do the memory
> conversion on the RAMBlock to turn the memory into desired attribute,
> switching between private and shared.
>
> Currently only KVM_MEMORY_EXIT_FLAG_PRIVATE in flags is valid when
> KVM_EXIT_MEMORY_FAULT happens.
>
> Note, KVM_EXIT_MEMORY_FAULT makes sense only when the RAMBlock has
> guest_memfd memory backend.
>
> Note, KVM_EXIT_MEMORY_FAULT returns with -EFAULT, so special handling is
> added.
>
> When page is converted from shared to private, the original shared
> memory can be discarded via ram_block_discard_range(). Note, shared
> memory can be discarded only when it's not back'ed by hugetlb because
> hugetlb is supposed to be pre-allocated and no need for discarding.
>
> Signed-off-by: Chao Peng <[email protected]>
> Co-developed-by: Xiaoyao Li <[email protected]>
> Signed-off-by: Xiaoyao Li <[email protected]>
>
> Message-ID: <[email protected]>
> Signed-off-by: Paolo Bonzini <[email protected]>
Hi; Coverity points out an issue with this code (CID 1544114):
> +int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private)
> +{
> + MemoryRegionSection section;
> + ram_addr_t offset;
offset here is not initialized...
> + MemoryRegion *mr;
> + RAMBlock *rb;
> + void *addr;
> + int ret = -1;
> +
> + trace_kvm_convert_memory(start, size, to_private ? "shared_to_private" :
> "private_to_shared");
> +
> + if (!QEMU_PTR_IS_ALIGNED(start, qemu_real_host_page_size()) ||
> + !QEMU_PTR_IS_ALIGNED(size, qemu_real_host_page_size())) {
> + return -1;
> + }
> +
> + if (!size) {
> + return -1;
> + }
> +
> + section = memory_region_find(get_system_memory(), start, size);
> + mr = section.mr;
> + if (!mr) {
> + return -1;
> + }
> +
> + if (!memory_region_has_guest_memfd(mr)) {
> + error_report("Converting non guest_memfd backed memory region "
> + "(0x%"HWADDR_PRIx" ,+ 0x%"HWADDR_PRIx") to %s",
> + start, size, to_private ? "private" : "shared");
> + goto out_unref;
> + }
> +
> + if (to_private) {
> + ret = kvm_set_memory_attributes_private(start, size);
> + } else {
> + ret = kvm_set_memory_attributes_shared(start, size);
> + }
> + if (ret) {
> + goto out_unref;
> + }
> +
> + addr = memory_region_get_ram_ptr(mr) + section.offset_within_region;
> + rb = qemu_ram_block_from_host(addr, false, &offset);
...and this call to qemu_ram_block_from_host() will only initialize
offset if it does not fail (i.e. doesn't return NULL)...
> +
> + if (to_private) {
> + if (rb->page_size != qemu_real_host_page_size()) {
...but here we assume rb is not NULL...
> + /*
> + * shared memory is backed by hugetlb, which is supposed to be
> + * pre-allocated and doesn't need to be discarded
> + */
> + goto out_unref;
> + }
> + ret = ram_block_discard_range(rb, offset, size);
> + } else {
> + ret = ram_block_discard_guest_memfd_range(rb, offset, size);
...and here we use offset assuming it has been initialized.
I think this code should either handle the case where
qemu_ram_block_from_host() fails, or, if it is impossible
for it to fail in this situation, add an assert() and a
comment about why we know it can't fail.
> + }
> +
> +out_unref:
> + memory_region_unref(mr);
> + return ret;
> +}
thanks
-- PMM