linux-user: Use 'last' instead of 'end' in target_mmap

Complete the transition within the mmap functions to a formulation
that does not overflow at the end of the address space.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-Id: <20230707204054.8792-20-richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2023-07-07 21:40:47 +01:00
parent 4c13048e02
commit f9cd8f5eca

View File

@ -456,8 +456,8 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
int flags, int fd, off_t offset)
{
abi_ulong ret, end, real_start, real_end, retaddr, host_len,
passthrough_start = -1, passthrough_end = -1;
abi_ulong ret, last, real_start, real_last, retaddr, host_len;
abi_ulong passthrough_start = -1, passthrough_last = 0;
int page_flags;
off_t host_offset;
@ -581,29 +581,30 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
host_start += offset - host_offset;
}
start = h2g(host_start);
last = start + len - 1;
passthrough_start = start;
passthrough_end = start + len;
passthrough_last = last;
} else {
if (start & ~TARGET_PAGE_MASK) {
errno = EINVAL;
goto fail;
}
end = start + len;
real_end = HOST_PAGE_ALIGN(end);
last = start + len - 1;
real_last = HOST_PAGE_ALIGN(last) - 1;
/*
* Test if requested memory area fits target address space
* It can fail only on 64-bit host with 32-bit target.
* On any other target/host host mmap() handles this error correctly.
*/
if (end < start || !guest_range_valid_untagged(start, len)) {
if (last < start || !guest_range_valid_untagged(start, len)) {
errno = ENOMEM;
goto fail;
}
/* Validate that the chosen range is empty. */
if ((flags & MAP_FIXED_NOREPLACE)
&& !page_check_range_empty(start, end - 1)) {
&& !page_check_range_empty(start, last)) {
errno = EEXIST;
goto fail;
}
@ -642,9 +643,9 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
/* handle the start of the mapping */
if (start > real_start) {
if (real_end == real_start + qemu_host_page_size) {
if (real_last == real_start + qemu_host_page_size - 1) {
/* one single host page */
if (!mmap_frag(real_start, start, end - 1,
if (!mmap_frag(real_start, start, last,
target_prot, flags, fd, offset)) {
goto fail;
}
@ -658,18 +659,18 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
real_start += qemu_host_page_size;
}
/* handle the end of the mapping */
if (end < real_end) {
if (!mmap_frag(real_end - qemu_host_page_size,
real_end - qemu_host_page_size, end - 1,
if (last < real_last) {
abi_ulong real_page = real_last - qemu_host_page_size + 1;
if (!mmap_frag(real_page, real_page, last,
target_prot, flags, fd,
offset + real_end - qemu_host_page_size - start)) {
offset + real_page - start)) {
goto fail;
}
real_end -= qemu_host_page_size;
real_last -= qemu_host_page_size;
}
/* map the middle (easier) */
if (real_start < real_end) {
if (real_start < real_last) {
void *p;
off_t offset1;
@ -678,13 +679,13 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
} else {
offset1 = offset + real_start - start;
}
p = mmap(g2h_untagged(real_start), real_end - real_start,
p = mmap(g2h_untagged(real_start), real_last - real_start + 1,
target_to_host_prot(target_prot), flags, fd, offset1);
if (p == MAP_FAILED) {
goto fail;
}
passthrough_start = real_start;
passthrough_end = real_end;
passthrough_last = real_last;
}
}
the_end1:
@ -692,16 +693,16 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
page_flags |= PAGE_ANON;
}
page_flags |= PAGE_RESET;
if (passthrough_start == passthrough_end) {
page_set_flags(start, start + len - 1, page_flags);
if (passthrough_start > passthrough_last) {
page_set_flags(start, last, page_flags);
} else {
if (start < passthrough_start) {
page_set_flags(start, passthrough_start - 1, page_flags);
}
page_set_flags(passthrough_start, passthrough_end - 1,
page_set_flags(passthrough_start, passthrough_last,
page_flags | PAGE_PASSTHROUGH);
if (passthrough_end < start + len) {
page_set_flags(passthrough_end, start + len - 1, page_flags);
if (passthrough_last < last) {
page_set_flags(passthrough_last + 1, last, page_flags);
}
}
the_end: