linux-user/mmap.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+)
When old_size is zero and old_address refers to a shareable mapping,
mremap() should create a new mapping of the same pages according to the
mremap(2) man page. The MREMAP_MAYMOVE flag must be specified in this case.
Previously, QEMU's target_mremap() rejected this valid case with EFAULT
during the initial validation, before checking for the special
old_size == 0 behaviour.
This patch adds proper handling for old_size == 0:
- Validates that MREMAP_MAYMOVE flag is set (required by man spec)
- Passes the call through to the host mremap()
- Creates a new mapping without invalidating the original, with both
being valid and sharing the same physical memory frames.
- Ensures the new mapping address falls within the valid guest address
region before returning it to the guest.
Tested with the reproducer from the issue on qemu-riscv64, qemu-hppa,
and qemu-aarch64.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3105
Signed-off-by: Razvan Ghiorghe <razvanghiorghe16@gmail.com>
Hi Heldge,
Thank you for testing and detailed feedback!
V3 fixes h2g_valid assertion failure on hppa by using mmap_find_vma()
to ensure the new mapping stays within the valid guest adress region
Tested on qemu-riscv64, qemu-hppa and qemu-aarch64.
---
linux-user/mmap.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 07175e11d5..b41308840a 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -1117,6 +1117,58 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
errno = EINVAL;
return -1;
}
+
+ if (!old_size) {
+ if (!(flags & MREMAP_MAYMOVE)) {
+ errno = EINVAL;
+ return -1;
+ }
+ mmap_lock();
+ if (flags & MREMAP_FIXED) {
+ host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
+ flags, g2h_untagged(new_addr));
+ } else {
+ /*
+ * We ensure that the new mapping stands in the
+ * region of guest mappable addresses.
+ */
+ abi_ulong mmap_start;
+
+ mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
+
+ if (mmap_start == -1) {
+ errno = ENOMEM;
+ mmap_unlock();
+ return -1;
+ }
+
+ host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
+ flags | MREMAP_FIXED, g2h_untagged(mmap_start));
+
+ new_addr = mmap_start;
+ }
+
+ if (host_addr == MAP_FAILED) {
+ mmap_unlock();
+ return -1;
+ }
+
+ if (flags & MREMAP_FIXED) {
+ new_addr = h2g(host_addr);
+ }
+
+ prot = page_get_flags(old_addr);
+ /*
+ * For old_size zero, there is nothing to clear at old_addr.
+ * Only set the flags for the new mapping. They both are valid.
+ */
+ page_set_flags(new_addr, new_addr + new_size - 1,
+ prot | PAGE_VALID, PAGE_VALID);
+ shm_region_rm_complete(new_addr, new_addr + new_size - 1);
+ mmap_unlock();
+ return new_addr;
+ }
+
if (!guest_range_valid_untagged(old_addr, old_size)) {
errno = EFAULT;
return -1;
--
2.43.0
On 3/10/26 00:30, Razvan Ghiorghe wrote:
> When old_size is zero and old_address refers to a shareable mapping,
> mremap() should create a new mapping of the same pages according to the
> mremap(2) man page. The MREMAP_MAYMOVE flag must be specified in this case.
>
> Previously, QEMU's target_mremap() rejected this valid case with EFAULT
> during the initial validation, before checking for the special
> old_size == 0 behaviour.
>
> This patch adds proper handling for old_size == 0:
> - Validates that MREMAP_MAYMOVE flag is set (required by man spec)
> - Passes the call through to the host mremap()
> - Creates a new mapping without invalidating the original, with both
> being valid and sharing the same physical memory frames.
> - Ensures the new mapping address falls within the valid guest address
> region before returning it to the guest.
>
> Tested with the reproducer from the issue on qemu-riscv64, qemu-hppa,
> and qemu-aarch64.
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3105
> Signed-off-by: Razvan Ghiorghe <razvanghiorghe16@gmail.com>
>
> Hi Heldge,
> Thank you for testing and detailed feedback!
> V3 fixes h2g_valid assertion failure on hppa by using mmap_find_vma()
> to ensure the new mapping stays within the valid guest adress region
>
> Tested on qemu-riscv64, qemu-hppa and qemu-aarch64.
Seems correct.
Tested-by: Helge Deller <deller@gmx.de>
Reviewed-by: Helge Deller <deller@gmx.de>
Helge
> ---
> linux-user/mmap.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 52 insertions(+)
>
> diff --git a/linux-user/mmap.c b/linux-user/mmap.c
> index 07175e11d5..b41308840a 100644
> --- a/linux-user/mmap.c
> +++ b/linux-user/mmap.c
> @@ -1117,6 +1117,58 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
> errno = EINVAL;
> return -1;
> }
> +
> + if (!old_size) {
> + if (!(flags & MREMAP_MAYMOVE)) {
> + errno = EINVAL;
> + return -1;
> + }
> + mmap_lock();
> + if (flags & MREMAP_FIXED) {
> + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
> + flags, g2h_untagged(new_addr));
> + } else {
> + /*
> + * We ensure that the new mapping stands in the
> + * region of guest mappable addresses.
> + */
> + abi_ulong mmap_start;
> +
> + mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
> +
> + if (mmap_start == -1) {
> + errno = ENOMEM;
> + mmap_unlock();
> + return -1;
> + }
> +
> + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
> + flags | MREMAP_FIXED, g2h_untagged(mmap_start));
> +
> + new_addr = mmap_start;
> + }
> +
> + if (host_addr == MAP_FAILED) {
> + mmap_unlock();
> + return -1;
> + }
> +
> + if (flags & MREMAP_FIXED) {
> + new_addr = h2g(host_addr);
> + }
> +
> + prot = page_get_flags(old_addr);
> + /*
> + * For old_size zero, there is nothing to clear at old_addr.
> + * Only set the flags for the new mapping. They both are valid.
> + */
> + page_set_flags(new_addr, new_addr + new_size - 1,
> + prot | PAGE_VALID, PAGE_VALID);
> + shm_region_rm_complete(new_addr, new_addr + new_size - 1);
> + mmap_unlock();
> + return new_addr;
> + }
> +
> if (!guest_range_valid_untagged(old_addr, old_size)) {
> errno = EFAULT;
> return -1;
© 2016 - 2026 Red Hat, Inc.