@samitouri / QOSamiQemu / commits / 5e5b278d2b

linux-user: fix mremap with old_size=0 for shared mappings

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> Tested-by: Helge Deller <deller@gmx.de> Reviewed-by: Helge Deller <deller@gmx.de> Signed-off-by: Helge Deller <deller@gmx.de>

Razvan Ghiorghe committed Mar 10, 2026 at 01:30 UTC 5e5b278d2b1b81fc2b5ca09dba4848f81cd3a718
1 file changed +52
linux-user/mmap.c
+52
@@ -1120,6 +1120,58 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
1120 errno = EINVAL;
1121 return -1;
1122 }
1123 +
1124 + if (!old_size) {
1125 + if (!(flags & MREMAP_MAYMOVE)) {
1126 + errno = EINVAL;
1127 + return -1;
1128 + }
1129 + mmap_lock();
1130 + if (flags & MREMAP_FIXED) {
1131 + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
1132 + flags, g2h_untagged(new_addr));
1133 + } else {
1134 + /*
1135 + * We ensure that the new mapping stands in the
1136 + * region of guest mappable addresses.
1137 + */
1138 + abi_ulong mmap_start;
1139 +
1140 + mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
1141 +
1142 + if (mmap_start == -1) {
1143 + errno = ENOMEM;
1144 + mmap_unlock();
1145 + return -1;
1146 + }
1147 +
1148 + host_addr = mremap(g2h_untagged(old_addr), old_size, new_size,
1149 + flags | MREMAP_FIXED, g2h_untagged(mmap_start));
1150 +
1151 + new_addr = mmap_start;
1152 + }
1153 +
1154 + if (host_addr == MAP_FAILED) {
1155 + mmap_unlock();
1156 + return -1;
1157 + }
1158 +
1159 + if (flags & MREMAP_FIXED) {
1160 + new_addr = h2g(host_addr);
1161 + }
1162 +
1163 + prot = page_get_flags(old_addr);
1164 + /*
1165 + * For old_size zero, there is nothing to clear at old_addr.
1166 + * Only set the flags for the new mapping. They both are valid.
1167 + */
1168 + page_set_flags(new_addr, new_addr + new_size - 1,
1169 + prot | PAGE_VALID, PAGE_VALID);
1170 + shm_region_rm_complete(new_addr, new_addr + new_size - 1);
1171 + mmap_unlock();
1172 + return new_addr;
1173 + }
1174 +
1175 if (!guest_range_valid_untagged(old_addr, old_size)) {
1176 errno = EFAULT;
1177 return -1;