From: Alexander Ofitserov oficerovas@altlinux.org
Syzkaller hit 'WARNING: kmalloc bug in kvm_arch_prepare_memory_region' bug.
This bug is not a vulnerability and is reproduced only when running with root privileges for stable 5.10 kernel.
Bug fixed by backported commits in next two patches.
------------[ cut here ]------------ WARNING: CPU: 1 PID: 315 at mm/util.c:618 kvmalloc_node+0x163/0x170 mm/util.c:618 Modules linked in: ide_cd_mod cdrom ide_gd_mod ata_generic pata_acpi ata_piix libata scsi_mod ide_pci_generic ppdev joydev kvm_amd ccp kvm irqbypass crct10dif_pclmul crc32_pclmul crc32c_intel ghash_clmulni_intel aesni_intel crypto_simd bochs_drm cryptd af_packet drm_vram_helper glue_helper drm_ttm_helper ttm psmouse evdev pcspkr drm_kms_helper input_leds piix cec ide_core rc_core intel_agp i2c_piix4 serio_raw intel_gtt parport_pc parport floppy tiny_power_button qemu_fw_cfg button sch_fq_codel drm fuse dm_mod binfmt_misc efi_pstore virtio_rng rng_core ip_tables x_tables autofs4 CPU: 1 PID: 315 Comm: syz-executor319 Not tainted 5.10.198-std-def-alt1 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.0-alt1 04/01/2014 RIP: 0010:kvmalloc_node+0x163/0x170 mm/util.c:618 Code: ed 41 81 cd 00 20 01 00 e9 6c ff ff ff e8 f5 f1 d0 ff 81 e5 00 20 00 00 31 ff 89 ee e8 16 ee d0 ff 85 ed 75 cc e8 dd f1 d0 ff <0f> 0b e9 e4 fe ff ff 66 0f 1f 44 00 00 0f 1f 44 00 00 55 48 89 fd RSP: 0018:ffff8881034c78a0 EFLAGS: 00010293 RAX: 0000000000000000 RBX: 0000000010101640 RCX: ffffffff817bff9a RDX: ffff8881185e0240 RSI: ffffffff817bffa3 RDI: 0000000000000005 RBP: 0000000000000000 R08: 0000000000000001 R09: ffff8881034c7ac8 R10: 0000000000000000 R11: 0000000000000001 R12: 000000008080b200 R13: 0000000000000000 R14: 00000000ffffffff R15: ffff8881034c7ab0 FS: 00007f4ee2a3d740(0000) GS:ffff888140280000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020000008 CR3: 0000000130906000 CR4: 0000000000750ee0 PKRU: 55555554 Call Trace: kvm_arch_prepare_memory_region+0x141/0x5c0 [kvm] kvm_set_memslot+0x522/0x13d0 [kvm] __kvm_set_memory_region+0xb32/0xfe0 [kvm] kvm_vm_ioctl+0x6b7/0x1cd0 [kvm] vfs_ioctl fs/ioctl.c:48 [inline] __do_sys_ioctl fs/ioctl.c:753 [inline] __se_sys_ioctl fs/ioctl.c:739 [inline] __x64_sys_ioctl+0x19f/0x210 fs/ioctl.c:739 do_syscall_64+0x33/0x40 arch/x86/entry/common.c:46 entry_SYSCALL_64_after_hwframe+0x62/0xc7 RIP: 0033:0x7f4ee2b3ad49 Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ef 70 0d 00 f7 d8 64 89 01 48 RSP: 002b:00007fffa9df3428 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 000056092b108330 RCX: 00007f4ee2b3ad49 RDX: 00000000200000c0 RSI: 000000004020ae46 RDI: 0000000000000004 RBP: 0000000000000000 R08: 000056092b108330 R09: 000056092b108330 R10: 000056092b108330 R11: 0000000000000246 R12: 000056092b108240 R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000 irq event stamp: 19075 hardirqs last enabled at (19085): [<ffffffff813489c0>] console_unlock+0xa40/0xca0 kernel/printk/printk.c:2561 hardirqs last disabled at (19092): [<ffffffff81348643>] console_unlock+0x6c3/0xca0 kernel/printk/printk.c:2476 softirqs last enabled at (19108): [<ffffffff836011d2>] asm_call_irq_on_stack+0x12/0x20 softirqs last disabled at (19101): [<ffffffff836011d2>] asm_call_irq_on_stack+0x12/0x20 ---[ end trace c4463afe05024b06 ]---
Syzkaller reproducer: # {Threaded:false Repeat:false RepeatTimes:0 Procs:1 Slowdown:1 Sandbox: Leak:false NetInjection:false NetDevices:false NetReset:false Cgroups:false BinfmtMisc:false CloseFDs:false KCSAN:false DevlinkPCI:false USB:false VhciInjection:false Wifi:false IEEE802154:false Sysctl:false UseTmpDir:false HandleSegv:false Repro:false Trace:false LegacyOptions:{Collide:false Fault:false FaultCall:0 FaultNth:0}} r0 = openat$kvm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0) connect$pppl2tp(0xffffffffffffffff, &(0x7f00000000c0)=@pppol2tp={0x18, 0x1, {0x0, 0xffffffffffffffff, {0x2, 0x0, @rand_addr=0x64010101}}}, 0x26) r1 = ioctl$KVM_CREATE_VM(r0, 0xae01, 0x0) ioctl$KVM_CREATE_DEVICE(r1, 0x4020ae46, &(0x7f00000000c0))
C reproducer: // autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/syscall.h> #include <sys/types.h> #include <unistd.h>
uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
int main(void) { syscall(__NR_mmap, 0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul); syscall(__NR_mmap, 0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul); syscall(__NR_mmap, 0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul); intptr_t res = 0; memcpy((void*)0x20000000, "/dev/kvm\000", 9); res = syscall(__NR_openat, 0xffffffffffffff9cul, 0x20000000ul, 0ul, 0ul); if (res != -1) r[0] = res; *(uint16_t*)0x200000c0 = 0x18; *(uint32_t*)0x200000c2 = 1; *(uint32_t*)0x200000c6 = 0; *(uint32_t*)0x200000ca = -1; *(uint16_t*)0x200000ce = 2; *(uint16_t*)0x200000d0 = htobe16(0); *(uint32_t*)0x200000d2 = htobe32(0x64010101); *(uint16_t*)0x200000de = 0; *(uint16_t*)0x200000e0 = 0; *(uint16_t*)0x200000e2 = 0; *(uint16_t*)0x200000e4 = 0; syscall(__NR_connect, -1, 0x200000c0ul, 0x26ul); res = syscall(__NR_ioctl, r[0], 0xae01, 0ul); if (res != -1) r[1] = res; *(uint32_t*)0x200000c0 = 0; *(uint32_t*)0x200000c8 = 0; syscall(__NR_ioctl, r[1], 0x4020ae46, 0x200000c0ul); return 0; }
[PATCH 1/2] mm: vmalloc: introduce array allocation functions [PATCH 2/2] KVM: use __vcalloc for very large allocations
From: Alexander Ofitserov oficerovas@altlinux.org
From: Paolo Bonzini pbonzini@redhat.com
commit a8749a35c399 ("mm: vmalloc: introduce array allocation functions")
Linux has dozens of occurrences of vmalloc(array_size()) and vzalloc(array_size()). Allow to simplify the code by providing vmalloc_array and vcalloc, as well as the underscored variants that let the caller specify the GFP flags.
Acked-by: Michal Hocko mhocko@suse.com Signed-off-by: Paolo Bonzini pbonzini@redhat.com Signed-off-by: Alexander Ofitserov oficerovas@altlinux.org --- include/linux/vmalloc.h | 5 +++++ mm/util.c | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 76dad53a410ac..0fd47f2f39eb0 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -112,6 +112,11 @@ extern void *__vmalloc_node_range(unsigned long size, unsigned long align, void *__vmalloc_node(unsigned long size, unsigned long align, gfp_t gfp_mask, int node, const void *caller);
+extern void *__vmalloc_array(size_t n, size_t size, gfp_t flags); +extern void *vmalloc_array(size_t n, size_t size); +extern void *__vcalloc(size_t n, size_t size, gfp_t flags); +extern void *vcalloc(size_t n, size_t size); + extern void vfree(const void *addr); extern void vfree_atomic(const void *addr);
diff --git a/mm/util.c b/mm/util.c index 25bfda774f6fd..7fd3c2bb3e4f5 100644 --- a/mm/util.c +++ b/mm/util.c @@ -686,6 +686,56 @@ static inline void *__page_rmapping(struct page *page) return (void *)mapping; }
+/** + * __vmalloc_array - allocate memory for a virtually contiguous array. + * @n: number of elements. + * @size: element size. + * @flags: the type of memory to allocate (see kmalloc). + */ +void *__vmalloc_array(size_t n, size_t size, gfp_t flags) +{ + size_t bytes; + + if (unlikely(check_mul_overflow(n, size, &bytes))) + return NULL; + return __vmalloc(bytes, flags); +} +EXPORT_SYMBOL(__vmalloc_array); + +/** + * vmalloc_array - allocate memory for a virtually contiguous array. + * @n: number of elements. + * @size: element size. + */ +void *vmalloc_array(size_t n, size_t size) +{ + return __vmalloc_array(n, size, GFP_KERNEL); +} +EXPORT_SYMBOL(vmalloc_array); + +/** + * __vcalloc - allocate and zero memory for a virtually contiguous array. + * @n: number of elements. + * @size: element size. + * @flags: the type of memory to allocate (see kmalloc). + */ +void *__vcalloc(size_t n, size_t size, gfp_t flags) +{ + return __vmalloc_array(n, size, flags | __GFP_ZERO); +} +EXPORT_SYMBOL(__vcalloc); + +/** + * vcalloc - allocate and zero memory for a virtually contiguous array. + * @n: number of elements. + * @size: element size. + */ +void *vcalloc(size_t n, size_t size) +{ + return __vmalloc_array(n, size, GFP_KERNEL | __GFP_ZERO); +} +EXPORT_SYMBOL(vcalloc); + /* Neutral page->mapping pointer to address_space or anon_vma or other */ void *page_rmapping(struct page *page) {
From: oficerovas@altlinux.org
Sent: 26 January 2024 09:55
commit a8749a35c399 ("mm: vmalloc: introduce array allocation functions")
Linux has dozens of occurrences of vmalloc(array_size()) and vzalloc(array_size()). Allow to simplify the code by providing vmalloc_array and vcalloc, as well as the underscored variants that let the caller specify the GFP flags.
Acked-by: Michal Hocko mhocko@suse.com Signed-off-by: Paolo Bonzini pbonzini@redhat.com Signed-off-by: Alexander Ofitserov oficerovas@altlinux.org
include/linux/vmalloc.h | 5 +++++ mm/util.c | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 76dad53a410ac..0fd47f2f39eb0 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -112,6 +112,11 @@ extern void *__vmalloc_node_range(unsigned long size, unsigned long align, void *__vmalloc_node(unsigned long size, unsigned long align, gfp_t gfp_mask, int node, const void *caller);
+extern void *__vmalloc_array(size_t n, size_t size, gfp_t flags); +extern void *vmalloc_array(size_t n, size_t size); +extern void *__vcalloc(size_t n, size_t size, gfp_t flags); +extern void *vcalloc(size_t n, size_t size);
Symbols starting __ should really be ones that are part of the implementation and not publicly visible.
...
+/**
- __vmalloc_array - allocate memory for a virtually contiguous array.
- @n: number of elements.
- @size: element size.
- @flags: the type of memory to allocate (see kmalloc).
- */
+void *__vmalloc_array(size_t n, size_t size, gfp_t flags) +{
- size_t bytes;
- if (unlikely(check_mul_overflow(n, size, &bytes)))
return NULL;
- return __vmalloc(bytes, flags);
+} +EXPORT_SYMBOL(__vmalloc_array);
+/**
- vmalloc_array - allocate memory for a virtually contiguous array.
- @n: number of elements.
- @size: element size.
- */
+void *vmalloc_array(size_t n, size_t size) +{
- return __vmalloc_array(n, size, GFP_KERNEL);
+} +EXPORT_SYMBOL(vmalloc_array);
and that should just be an inline wrapper on the function above.
David
- Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
On Fri, Jan 26, 2024 at 3:00 PM David Laight David.Laight@aculab.com wrote:
From: oficerovas@altlinux.org
Sent: 26 January 2024 09:55
commit a8749a35c399 ("mm: vmalloc: introduce array allocation functions")
Linux has dozens of occurrences of vmalloc(array_size()) and vzalloc(array_size()). Allow to simplify the code by providing vmalloc_array and vcalloc, as well as the underscored variants that let the caller specify the GFP flags.
Acked-by: Michal Hocko mhocko@suse.com Signed-off-by: Paolo Bonzini pbonzini@redhat.com Signed-off-by: Alexander Ofitserov oficerovas@altlinux.org
include/linux/vmalloc.h | 5 +++++ mm/util.c | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+)
diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 76dad53a410ac..0fd47f2f39eb0 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -112,6 +112,11 @@ extern void *__vmalloc_node_range(unsigned long size, unsigned long align, void *__vmalloc_node(unsigned long size, unsigned long align, gfp_t gfp_mask, int node, const void *caller);
+extern void *__vmalloc_array(size_t n, size_t size, gfp_t flags); +extern void *vmalloc_array(size_t n, size_t size); +extern void *__vcalloc(size_t n, size_t size, gfp_t flags); +extern void *vcalloc(size_t n, size_t size);
Symbols starting __ should really be ones that are part of the implementation and not publicly visible.
This is a patch that already exists in master.
Paolo
On Fri, Jan 26, 2024 at 12:55:13PM +0300, oficerovas@altlinux.org wrote:
From: Alexander Ofitserov oficerovas@altlinux.org
From: Paolo Bonzini pbonzini@redhat.com
Nit, duplicate From: lines :(
I'll fix it up by hand, watch out for that next time please.
thanks,
greg k-h
From: Alexander Ofitserov oficerovas@altlinux.org
From: Paolo Bonzini pbonzini@redhat.com
commit 37b2a6510a48 ("KVM: use __vcalloc for very large allocations")
Allocations whose size is related to the memslot size can be arbitrarily large. Do not use kvzalloc/kvcalloc, as those are limited to "not crazy" sizes that fit in 32 bits.
URL: https://lore.kernel.org/lkml/20220711090606.962822924@linuxfoundation.org/ Reviewed-by: David Hildenbrand david@redhat.com Signed-off-by: Paolo Bonzini pbonzini@redhat.com Signed-off-by: Alexander Ofitserov oficerovas@altlinux.org --- arch/powerpc/kvm/book3s_hv_uvmem.c | 2 +- arch/x86/kvm/mmu/page_track.c | 2 +- arch/x86/kvm/x86.c | 4 ++-- virt/kvm/kvm_main.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/kvm/book3s_hv_uvmem.c b/arch/powerpc/kvm/book3s_hv_uvmem.c index 3dd58b4ee33e5..5f6b3f80023de 100644 --- a/arch/powerpc/kvm/book3s_hv_uvmem.c +++ b/arch/powerpc/kvm/book3s_hv_uvmem.c @@ -250,7 +250,7 @@ int kvmppc_uvmem_slot_init(struct kvm *kvm, const struct kvm_memory_slot *slot) p = kzalloc(sizeof(*p), GFP_KERNEL); if (!p) return -ENOMEM; - p->pfns = vzalloc(array_size(slot->npages, sizeof(*p->pfns))); + p->pfns = vcalloc(slot->npages, sizeof(*p->pfns)); if (!p->pfns) { kfree(p); return -ENOMEM; diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 81cf4babbd0b4..3c379335ea477 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -35,7 +35,7 @@ int kvm_page_track_create_memslot(struct kvm_memory_slot *slot,
for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { slot->arch.gfn_track[i] = - kvcalloc(npages, sizeof(*slot->arch.gfn_track[i]), + __vcalloc(npages, sizeof(*slot->arch.gfn_track[i]), GFP_KERNEL_ACCOUNT); if (!slot->arch.gfn_track[i]) goto track_free; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 13e4699a0744f..6c2bf7cd7aec6 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10826,14 +10826,14 @@ static int kvm_alloc_memslot_metadata(struct kvm_memory_slot *slot, slot->base_gfn, level) + 1;
slot->arch.rmap[i] = - kvcalloc(lpages, sizeof(*slot->arch.rmap[i]), + __vcalloc(lpages, sizeof(*slot->arch.rmap[i]), GFP_KERNEL_ACCOUNT); if (!slot->arch.rmap[i]) goto out_free; if (i == 0) continue;
- linfo = kvcalloc(lpages, sizeof(*linfo), GFP_KERNEL_ACCOUNT); + linfo = __vcalloc(lpages, sizeof(*linfo), GFP_KERNEL_ACCOUNT); if (!linfo) goto out_free;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 356fd5d1a4285..b7638c3c9eb7d 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1008,9 +1008,9 @@ static int kvm_vm_release(struct inode *inode, struct file *filp) */ static int kvm_alloc_dirty_bitmap(struct kvm_memory_slot *memslot) { - unsigned long dirty_bytes = 2 * kvm_dirty_bitmap_bytes(memslot); + unsigned long dirty_bytes = kvm_dirty_bitmap_bytes(memslot);
- memslot->dirty_bitmap = kvzalloc(dirty_bytes, GFP_KERNEL_ACCOUNT); + memslot->dirty_bitmap = __vcalloc(2, dirty_bytes, GFP_KERNEL_ACCOUNT); if (!memslot->dirty_bitmap) return -ENOMEM;
On Fri, Jan 26, 2024 at 11:01 AM oficerovas@altlinux.org wrote:
From: Alexander Ofitserov oficerovas@altlinux.org
Syzkaller hit 'WARNING: kmalloc bug in kvm_arch_prepare_memory_region' bug.
This bug is not a vulnerability and is reproduced only when running with root privileges for stable 5.10 kernel.
Bug fixed by backported commits in next two patches. [PATCH 1/2] mm: vmalloc: introduce array allocation functions [PATCH 2/2] KVM: use __vcalloc for very large allocations
Acked-by: Paolo Bonzini pbonzini@redhat.com
-- 2.42.1
On Fri, Jan 26, 2024 at 07:04:51PM +0100, Paolo Bonzini wrote:
On Fri, Jan 26, 2024 at 11:01 AM oficerovas@altlinux.org wrote:
From: Alexander Ofitserov oficerovas@altlinux.org
Syzkaller hit 'WARNING: kmalloc bug in kvm_arch_prepare_memory_region' bug.
This bug is not a vulnerability and is reproduced only when running with root privileges for stable 5.10 kernel.
Bug fixed by backported commits in next two patches. [PATCH 1/2] mm: vmalloc: introduce array allocation functions [PATCH 2/2] KVM: use __vcalloc for very large allocations
Acked-by: Paolo Bonzini pbonzini@redhat.com
Thanks for the review, both now queued up.
greg k-h
linux-stable-mirror@lists.linaro.org