| From 2ee2492513f9685cb716dc1cb4cf5b580da43e07 Mon Sep 17 00:00:00 2001 |
| From: Bandan Das <bsd@redhat.com> |
| Date: Wed, 25 Jan 2017 03:36:07 +0100 |
| Subject: [PATCH 01/11] memory: Allow access only upto the maximum alignment |
| for memory_region_* functions |
| |
| RH-Author: Bandan Das <bsd@redhat.com> |
| Message-id: <jpgefzrn74o.fsf@linux.bootlegged.copy> |
| Patchwork-id: 73367 |
| O-Subject: [RHEL-7.4 qemu-kvm PATCH] memory: Allow access only upto the maximum alignment for memory_region_* functions |
| Bugzilla: 1342768 |
| RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com> |
| RH-Acked-by: Laurent Vivier <lvivier@redhat.com> |
| RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1342768 |
| Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=12437870 |
| Upstream: N/A, upstream doesn't exhibit this behavior |
| |
| Currently, there is no check in memory_region_iorange_* functions for whether |
| the size requested is greater than the maximum alignment. This causes |
| an abort with a specific version of the Linux kernel (4.7.0-RC1): |
| /usr/libexec/qemu-kvm -kernel ~/vmlinuz-4.7.0-rc1 --enable-kvm -m 1G -vnc :2 -monitor stdio |
| |
| 0 0x00007fb057cb65f7 in raise () from /lib64/libc.so.6 |
| 1 0x00007fb057cb7ce8 in abort () from /lib64/libc.so.6 |
| 2 0x00007fb05eca5537 in acpi_gpe_ioport_readb () |
| 3 0x00007fb05eca5ff0 in gpe_readb () |
| 4 0x00007fb05ede6f4c in memory_region_read_accessor () |
| 5 0x00007fb05ede6993 in access_with_adjusted_size () |
| 6 0x00007fb05ede7ce8 in memory_region_iorange_read () |
| 7 0x00007fb05ede2ac7 in ioport_readl_thunk () |
| 8 0x00007fb05ede3141 in cpu_inl () |
| 9 0x00007fb05ede5c49 in kvm_cpu_exec () |
| 10 0x00007fb05ed98485 in qemu_kvm_cpu_thread_fn () |
| 11 0x00007fb05bcc9dc5 in start_thread () from /lib64/libpthread.so.0 |
| 12 0x00007fb057d77ced in clone () from /lib64/libc.so.6 |
| |
| This happens because guest code tries to read(l=4) from 0xafe2 |
| with GPE base being 0xafe0 which causes the abort in |
| acpi_gpe_ioport_get_ptr() to trigger. This change adds a |
| memory_access_size() which is similar to the one in upstream that |
| forces size to be equal to the maximum alignment if it's greater. |
| It also keeps the other checks present in upstream for safety and |
| is called from the memory_region_read/write functions before |
| calling the call specific access functions. |
| |
| Signed-off-by: Bandan Das <bsd@redhat.com> |
| Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com> |
| |
| memory.c | 44 ++++++++++++++++++++++++++++++++++++++++---- |
| 1 file changed, 40 insertions(+), 4 deletions(-) |
| |
| diff --git a/memory.c b/memory.c |
| index 7bd6e87..573ecdd 100644 |
| |
| |
| @@ -381,6 +381,33 @@ static const MemoryRegionPortio *find_portio(MemoryRegion *mr, uint64_t offset, |
| return NULL; |
| } |
| |
| +static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) |
| +{ |
| + unsigned access_size_max = mr->ops->valid.max_access_size; |
| + |
| + /* Regions are assumed to support 1-4 byte accesses unless |
| + otherwise specified. */ |
| + if (access_size_max == 0) { |
| + access_size_max = 4; |
| + } |
| + |
| + /* Bound the maximum access by the alignment of the address. */ |
| + if (!mr->ops->impl.unaligned) { |
| + unsigned align_size_max = addr & -addr; |
| + if (align_size_max != 0 && align_size_max < access_size_max) { |
| + access_size_max = align_size_max; |
| + } |
| + } |
| + |
| + /* Don't attempt accesses larger than the maximum. */ |
| + if (l > access_size_max) { |
| + l = access_size_max; |
| + } |
| + l = pow2floor(l); |
| + |
| + return l; |
| +} |
| + |
| static void memory_region_iorange_read(IORange *iorange, |
| uint64_t offset, |
| unsigned width, |
| @@ -389,6 +416,7 @@ static void memory_region_iorange_read(IORange *iorange, |
| MemoryRegionIORange *mrio |
| = container_of(iorange, MemoryRegionIORange, iorange); |
| MemoryRegion *mr = mrio->mr; |
| + unsigned l; |
| |
| offset += mrio->offset; |
| if (mr->ops->old_portio) { |
| @@ -407,7 +435,8 @@ static void memory_region_iorange_read(IORange *iorange, |
| return; |
| } |
| *data = 0; |
| - access_with_adjusted_size(offset, data, width, |
| + l = memory_access_size(mr, width, offset); |
| + access_with_adjusted_size(offset, data, l, |
| mr->ops->impl.min_access_size, |
| mr->ops->impl.max_access_size, |
| memory_region_read_accessor, mr); |
| @@ -421,6 +450,7 @@ static void memory_region_iorange_write(IORange *iorange, |
| MemoryRegionIORange *mrio |
| = container_of(iorange, MemoryRegionIORange, iorange); |
| MemoryRegion *mr = mrio->mr; |
| + unsigned l; |
| |
| offset += mrio->offset; |
| if (mr->ops->old_portio) { |
| @@ -437,7 +467,8 @@ static void memory_region_iorange_write(IORange *iorange, |
| } |
| return; |
| } |
| - access_with_adjusted_size(offset, &data, width, |
| + l = memory_access_size(mr, width, offset); |
| + access_with_adjusted_size(offset, &data, l, |
| mr->ops->impl.min_access_size, |
| mr->ops->impl.max_access_size, |
| memory_region_write_accessor, mr); |
| @@ -850,6 +881,7 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr, |
| unsigned size) |
| { |
| uint64_t data = 0; |
| + unsigned l; |
| |
| if (!memory_region_access_valid(mr, addr, size, false)) { |
| return -1U; /* FIXME: better signalling */ |
| @@ -859,8 +891,9 @@ static uint64_t memory_region_dispatch_read1(MemoryRegion *mr, |
| return mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr); |
| } |
| |
| + l = memory_access_size(mr, size, addr); |
| /* FIXME: support unaligned access */ |
| - access_with_adjusted_size(addr, &data, size, |
| + access_with_adjusted_size(addr, &data, l, |
| mr->ops->impl.min_access_size, |
| mr->ops->impl.max_access_size, |
| memory_region_read_accessor, mr); |
| @@ -902,6 +935,8 @@ static void memory_region_dispatch_write(MemoryRegion *mr, |
| uint64_t data, |
| unsigned size) |
| { |
| + unsigned l; |
| + |
| if (!memory_region_access_valid(mr, addr, size, true)) { |
| return; /* FIXME: better signalling */ |
| } |
| @@ -913,8 +948,9 @@ static void memory_region_dispatch_write(MemoryRegion *mr, |
| return; |
| } |
| |
| + l = memory_access_size(mr, size, addr); |
| /* FIXME: support unaligned access */ |
| - access_with_adjusted_size(addr, &data, size, |
| + access_with_adjusted_size(addr, &data, l, |
| mr->ops->impl.min_access_size, |
| mr->ops->impl.max_access_size, |
| memory_region_write_accessor, mr); |
| -- |
| 1.8.3.1 |
| |