diff mbox series

[3/4] linux-user: Fix mmap2() syscall on 32-bit targets to allow file mapping beyond 4GB

Message ID 20230707131928.89500-4-deller@gmx.de (mailing list archive)
State New, archived
Headers show
Series linux-user: Fix fcntl64() and accept4() for 32-bit targets | expand

Commit Message

Helge Deller July 7, 2023, 1:19 p.m. UTC
The mmap2() syscall allows 32-bit guests to specify the offset into a
file in page units (instead of bytes, as done by mmap(2)).
On physical machines this allows 32-bit applications to map such parts
of large files which are stored beyond the 4GB limit.

Allow the same behaviour when emulating 32-bit guests with qemu.

For that switch the mmap2() function to always take an abi_ullong
(64-bit) offset parameter for target_mmap() and mmap_frag() to avoid an
arithmetical overflow when shifing a 32-bit offset parameter by
12 bits (=PAGE_SHIFT) and thus possibly overflow the abi_ulong (32-bit)
type.

Signed-off-by: Helge Deller <deller@gmx.de>
---
 linux-user/mmap.c      | 9 +++++----
 linux-user/syscall.c   | 2 +-
 linux-user/user-mmap.h | 2 +-
 3 files changed, 7 insertions(+), 6 deletions(-)

--
2.41.0

Comments

Richard Henderson July 7, 2023, 7:47 p.m. UTC | #1
On 7/7/23 14:19, Helge Deller wrote:
> The mmap2() syscall allows 32-bit guests to specify the offset into a
> file in page units (instead of bytes, as done by mmap(2)).
> On physical machines this allows 32-bit applications to map such parts
> of large files which are stored beyond the 4GB limit.
> 
> Allow the same behaviour when emulating 32-bit guests with qemu.
> 
> For that switch the mmap2() function to always take an abi_ullong
> (64-bit) offset parameter for target_mmap() and mmap_frag() to avoid an
> arithmetical overflow when shifing a 32-bit offset parameter by
> 12 bits (=PAGE_SHIFT) and thus possibly overflow the abi_ulong (32-bit)
> type.
> 
> Signed-off-by: Helge Deller<deller@gmx.de>
> ---
>   linux-user/mmap.c      | 9 +++++----
>   linux-user/syscall.c   | 2 +-
>   linux-user/user-mmap.h | 2 +-
>   3 files changed, 7 insertions(+), 6 deletions(-)

https://patchew.org/QEMU/20230630132159.376995-1-richard.henderson@linaro.org/20230630132159.376995-12-richard.henderson@linaro.org/

Wherein I use the host off_t (which must be 64-bits).
(I'm pretty sure there's an older similar patch, but I couldn't find it.)


r~
Helge Deller July 7, 2023, 8:04 p.m. UTC | #2
On 7/7/23 21:47, Richard Henderson wrote:
> On 7/7/23 14:19, Helge Deller wrote:
>> The mmap2() syscall allows 32-bit guests to specify the offset into a
>> file in page units (instead of bytes, as done by mmap(2)).
>> On physical machines this allows 32-bit applications to map such parts
>> of large files which are stored beyond the 4GB limit.
>>
>> Allow the same behaviour when emulating 32-bit guests with qemu.
>>
>> For that switch the mmap2() function to always take an abi_ullong
>> (64-bit) offset parameter for target_mmap() and mmap_frag() to avoid an
>> arithmetical overflow when shifing a 32-bit offset parameter by
>> 12 bits (=PAGE_SHIFT) and thus possibly overflow the abi_ulong (32-bit)
>> type.
>>
>> Signed-off-by: Helge Deller<deller@gmx.de>
>> ---
>>   linux-user/mmap.c      | 9 +++++----
>>   linux-user/syscall.c   | 2 +-
>>   linux-user/user-mmap.h | 2 +-
>>   3 files changed, 7 insertions(+), 6 deletions(-)
>
> https://patchew.org/QEMU/20230630132159.376995-1-richard.henderson@linaro.org/20230630132159.376995-12-richard.henderson@linaro.org/
>
> Wherein I use the host off_t (which must be 64-bits).

I like your patch.
But wouldn't it be better to use off64_t instead of off_t just to make
clear that this is a 64bit int?

And this part:
-                          arg5, arg6 << MMAP_SHIFT);
+                          arg5, (off_t)(abi_ulong)arg6 << MMAP_SHIFT);
maybe should become (with brackets): ?
+                          arg5, ((off64_t)(abi_ulong)arg6) << MMAP_SHIFT);

In any case I'm fine if your or my patch could be appled.

Helge
Richard Henderson July 7, 2023, 8:06 p.m. UTC | #3
On 7/7/23 21:04, Helge Deller wrote:
> On 7/7/23 21:47, Richard Henderson wrote:
>> On 7/7/23 14:19, Helge Deller wrote:
>>> The mmap2() syscall allows 32-bit guests to specify the offset into a
>>> file in page units (instead of bytes, as done by mmap(2)).
>>> On physical machines this allows 32-bit applications to map such parts
>>> of large files which are stored beyond the 4GB limit.
>>>
>>> Allow the same behaviour when emulating 32-bit guests with qemu.
>>>
>>> For that switch the mmap2() function to always take an abi_ullong
>>> (64-bit) offset parameter for target_mmap() and mmap_frag() to avoid an
>>> arithmetical overflow when shifing a 32-bit offset parameter by
>>> 12 bits (=PAGE_SHIFT) and thus possibly overflow the abi_ulong (32-bit)
>>> type.
>>>
>>> Signed-off-by: Helge Deller<deller@gmx.de>
>>> ---
>>>   linux-user/mmap.c      | 9 +++++----
>>>   linux-user/syscall.c   | 2 +-
>>>   linux-user/user-mmap.h | 2 +-
>>>   3 files changed, 7 insertions(+), 6 deletions(-)
>>
>> https://patchew.org/QEMU/20230630132159.376995-1-richard.henderson@linaro.org/20230630132159.376995-12-richard.henderson@linaro.org/
>>
>> Wherein I use the host off_t (which must be 64-bits).
> 
> I like your patch.
> But wouldn't it be better to use off64_t instead of off_t just to make
> clear that this is a 64bit int?

No, I don't think so.  That's the point of _FILE_OFFSET_BITS=64.

> And this part:
> -                          arg5, arg6 << MMAP_SHIFT);
> +                          arg5, (off_t)(abi_ulong)arg6 << MMAP_SHIFT);
> maybe should become (with brackets): ?
> +                          arg5, ((off64_t)(abi_ulong)arg6) << MMAP_SHIFT);

Why would you add useless parenthesis?
At some point everyone should know C operator precedence...


r~
diff mbox series

Patch

diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index 2692936773..2750146758 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -192,7 +192,7 @@  error:
 /* map an incomplete host page */
 static int mmap_frag(abi_ulong real_start,
                      abi_ulong start, abi_ulong end,
-                     int prot, int flags, int fd, abi_ulong offset)
+                     int prot, int flags, int fd, abi_ullong offset)
 {
     abi_ulong real_end, addr;
     void *host_start;
@@ -436,10 +436,11 @@  abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)

 /* NOTE: all the constants are the HOST ones */
 abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
-                     int flags, int fd, abi_ulong offset)
+                     int flags, int fd, abi_ullong offset)
 {
-    abi_ulong ret, end, real_start, real_end, retaddr, host_offset, host_len,
+    abi_ulong ret, end, real_start, real_end, retaddr, host_len,
               passthrough_start = -1, passthrough_end = -1;
+    abi_ullong host_offset;
     int page_flags, host_prot;

     mmap_lock();
@@ -627,7 +628,7 @@  abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
         /* map the middle (easier) */
         if (real_start < real_end) {
             void *p;
-            unsigned long offset1;
+            off_t offset1;
             if (flags & MAP_ANONYMOUS)
                 offset1 = 0;
             else
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index 9e9317237d..5ebc502f71 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -10427,7 +10427,7 @@  static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
 #endif
         ret = target_mmap(arg1, arg2, arg3,
                           target_to_host_bitmask(arg4, mmap_flags_tbl),
-                          arg5, arg6 << MMAP_SHIFT);
+                          arg5, ((abi_ullong)arg6) << MMAP_SHIFT);
         return get_errno(ret);
 #endif
     case TARGET_NR_munmap:
diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h
index 480ce1c114..72e99000d9 100644
--- a/linux-user/user-mmap.h
+++ b/linux-user/user-mmap.h
@@ -20,7 +20,7 @@ 

 int target_mprotect(abi_ulong start, abi_ulong len, int prot);
 abi_long target_mmap(abi_ulong start, abi_ulong len, int prot,
-                     int flags, int fd, abi_ulong offset);
+                     int flags, int fd, abi_ullong offset);
 int target_munmap(abi_ulong start, abi_ulong len);
 abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
                        abi_ulong new_size, unsigned long flags,