Bug 24699 - mmap64 with very large offset broken on MIPS64 n32
Summary: mmap64 with very large offset broken on MIPS64 n32
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.26
: P2 normal
Target Milestone: 2.30
Assignee: Adhemerval Zanella
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-06-18 18:43 UTC by Thomas De Schampheleire
Modified: 2019-07-15 12:27 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Thomas De Schampheleire 2019-06-18 18:43:07 UTC
Corresponding mail discussion:
https://www.sourceware.org/ml/libc-alpha/2019-06/msg00280.html

Bug opened as this is a user-visible problem, as discussed with Florian Weimer.

---

In glibc 2.26, a change was made to the behavior of mmap64, as a fix
for bug #21270 (mmap64 silently truncates large offset values), via
commit 158d5fa0e1906e7810bdc6ecb7bf598dcc3cd17d.
Practically, with the new behavior, doing mmap64 with an offset larger
than 1<<44 now fails with errno=EINVAL. The reasoning in this bug is
that offsets greater than 1<<44  are silently truncated, and so better
to fail early.

However, I have several cases in embedded applications where such
mmap64 with large offset values is performed, and it worked correctly
(< 2.26). In these applications, a physical memory region is mapped
into the virtual address space, by performing an mmap64 on /dev/mem.
The offset is very large because this is accessing devices high up in
the memory range.

This is on MIPS64 with the n32 ABI.

Example code is:

#define _GNU_SOURCE
#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE
#define _FILE_OFFSET_BITS 64
#include <stdio.h>
#include <sys/mman.h>
#include <errno.h>
#include <stdint.h>
#include <fcntl.h>
#include <inttypes.h>

int main(void)
{
    off_t physical_base = 0x100001a000000ull;
    size_t length = 0x1000;
    volatile uint32_t* virtual_base;

    int fd = open("/dev/mem",O_RDWR);
    if (fd < 0) {
        perror("cannot open /dev/mem");
        return 1;
    }

    errno = 0;
    virtual_base = (volatile uint32_t*) mmap64(0, length,
PROT_READ|PROT_WRITE, MAP_SHARED, fd, physical_base);
    if (virtual_base == (volatile uint32_t *)-1) {
        printf("Mapping failed, errno=%d\n", errno);
        return 1;
    }
    printf("Virtual address: %p\n", virtual_base);
    return 0;
}



On a toolchain using gcc 4.7, glibc 2.16, the above gives a valid
virtual address.
On a toolchain using gcc 7.3, glibc 2.27, the mapping fails with
errno=22 (EINVAL) and virtual_base=-1 (0xffffffff).
Comment 1 cvs-commit@gcc.gnu.org 2019-07-10 19:54:36 UTC
The master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=a008c76b56e4f958cf5a0d6f67d29fade89421b7

commit a008c76b56e4f958cf5a0d6f67d29fade89421b7
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Jun 18 14:09:09 2019 -0300

    posix: Fix large mmap64 offset for mips64n32 (BZ#24699)
    
    The fix for BZ#21270 (commit 158d5fa0e19) added a mask to avoid offset larger
    than 1^44 to be used along __NR_mmap2.  However mips64n32 users __NR_mmap,
    as mips64n64, but still defines off_t as old non-LFS type (other ILP32, such
    x32, defines off_t being equal to off64_t).  This leads to use the same
    mask meant only for __NR_mmap2 call for __NR_mmap, thus limiting the maximum
    offset it can use with mmap64.
    
    This patch fixes by setting the high mask only for __NR_mmap2 usage. The
    posix/tst-mmap-offset.c already tests it and also fails for mips64n32. The
    patch also change the test to check for an arch-specific header that defines
    the maximum supported offset.
    
    Checked on x86_64-linux-gnu, i686-linux-gnu, and I also tests tst-mmap-offset
    on qemu simulated mips64 with kernel 3.2.0 kernel for both mips-linux-gnu and
    mips64-n32-linux-gnu.
    
    	[BZ #24699]
    	* posix/tst-mmap-offset.c: Mention BZ #24699.
    	(do_test_bz21270): Rename to do_test_large_offset and use
    	mmap64_maximum_offset to check for maximum expected offset value.
    	* sysdeps/generic/mmap_info.h: New file.
    	* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
    	* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
    	__NR_mmap2 is used.
Comment 2 Adhemerval Zanella 2019-07-10 19:55:06 UTC
Fixed on 2.30.
Comment 3 cvs-commit@gcc.gnu.org 2019-07-15 12:23:20 UTC
The release/2.26/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=54194d8b4d097fd3a9cb9528a0ad686622d6c182

commit 54194d8b4d097fd3a9cb9528a0ad686622d6c182
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Jun 18 14:09:09 2019 -0300

    posix: Fix large mmap64 offset for mips64n32 (BZ#24699)
    
    The fix for BZ#21270 (commit 158d5fa0e19) added a mask to avoid offset larger
    than 1^44 to be used along __NR_mmap2.  However mips64n32 users __NR_mmap,
    as mips64n64, but still defines off_t as old non-LFS type (other ILP32, such
    x32, defines off_t being equal to off64_t).  This leads to use the same
    mask meant only for __NR_mmap2 call for __NR_mmap, thus limiting the maximum
    offset it can use with mmap64.
    
    This patch fixes by setting the high mask only for __NR_mmap2 usage. The
    posix/tst-mmap-offset.c already tests it and also fails for mips64n32. The
    patch also change the test to check for an arch-specific header that defines
    the maximum supported offset.
    
    Checked on x86_64-linux-gnu, i686-linux-gnu, and I also tests tst-mmap-offset
    on qemu simulated mips64 with kernel 3.2.0 kernel for both mips-linux-gnu and
    mips64-n32-linux-gnu.
    
    	[BZ #24699]
    	* posix/tst-mmap-offset.c: Mention BZ #24699.
    	(do_test_bz21270): Rename to do_test_large_offset and use
    	mmap64_maximum_offset to check for maximum expected offset value.
    	* sysdeps/generic/mmap_info.h: New file.
    	* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
    	* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
    	__NR_mmap2 is used.
    
    (cherry picked from commit a008c76b56e4f958cf5a0d6f67d29fade89421b7)
Comment 4 cvs-commit@gcc.gnu.org 2019-07-15 12:25:32 UTC
The release/2.27/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=22bd3ab40e83ec0049bfb3bdba3b9ebdb1a6ab20

commit 22bd3ab40e83ec0049bfb3bdba3b9ebdb1a6ab20
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Jun 18 14:09:09 2019 -0300

    posix: Fix large mmap64 offset for mips64n32 (BZ#24699)
    
    The fix for BZ#21270 (commit 158d5fa0e19) added a mask to avoid offset larger
    than 1^44 to be used along __NR_mmap2.  However mips64n32 users __NR_mmap,
    as mips64n64, but still defines off_t as old non-LFS type (other ILP32, such
    x32, defines off_t being equal to off64_t).  This leads to use the same
    mask meant only for __NR_mmap2 call for __NR_mmap, thus limiting the maximum
    offset it can use with mmap64.
    
    This patch fixes by setting the high mask only for __NR_mmap2 usage. The
    posix/tst-mmap-offset.c already tests it and also fails for mips64n32. The
    patch also change the test to check for an arch-specific header that defines
    the maximum supported offset.
    
    Checked on x86_64-linux-gnu, i686-linux-gnu, and I also tests tst-mmap-offset
    on qemu simulated mips64 with kernel 3.2.0 kernel for both mips-linux-gnu and
    mips64-n32-linux-gnu.
    
    	[BZ #24699]
    	* posix/tst-mmap-offset.c: Mention BZ #24699.
    	(do_test_bz21270): Rename to do_test_large_offset and use
    	mmap64_maximum_offset to check for maximum expected offset value.
    	* sysdeps/generic/mmap_info.h: New file.
    	* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
    	* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
    	__NR_mmap2 is used.
    
    (cherry picked from commit a008c76b56e4f958cf5a0d6f67d29fade89421b7)
Comment 5 cvs-commit@gcc.gnu.org 2019-07-15 12:26:27 UTC
The release/2.28/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=57922433fa038faa6e37798b9655f85a94978d89

commit 57922433fa038faa6e37798b9655f85a94978d89
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Jun 18 14:09:09 2019 -0300

    posix: Fix large mmap64 offset for mips64n32 (BZ#24699)
    
    The fix for BZ#21270 (commit 158d5fa0e19) added a mask to avoid offset larger
    than 1^44 to be used along __NR_mmap2.  However mips64n32 users __NR_mmap,
    as mips64n64, but still defines off_t as old non-LFS type (other ILP32, such
    x32, defines off_t being equal to off64_t).  This leads to use the same
    mask meant only for __NR_mmap2 call for __NR_mmap, thus limiting the maximum
    offset it can use with mmap64.
    
    This patch fixes by setting the high mask only for __NR_mmap2 usage. The
    posix/tst-mmap-offset.c already tests it and also fails for mips64n32. The
    patch also change the test to check for an arch-specific header that defines
    the maximum supported offset.
    
    Checked on x86_64-linux-gnu, i686-linux-gnu, and I also tests tst-mmap-offset
    on qemu simulated mips64 with kernel 3.2.0 kernel for both mips-linux-gnu and
    mips64-n32-linux-gnu.
    
    	[BZ #24699]
    	* posix/tst-mmap-offset.c: Mention BZ #24699.
    	(do_test_bz21270): Rename to do_test_large_offset and use
    	mmap64_maximum_offset to check for maximum expected offset value.
    	* sysdeps/generic/mmap_info.h: New file.
    	* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
    	* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
    	__NR_mmap2 is used.
    
    (cherry picked from commit a008c76b56e4f958cf5a0d6f67d29fade89421b7)
Comment 6 cvs-commit@gcc.gnu.org 2019-07-15 12:27:28 UTC
The release/2.29/master branch has been updated by Adhemerval Zanella <azanella@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=2d3fefd7ce64ac1015701c96916fffacc073a877

commit 2d3fefd7ce64ac1015701c96916fffacc073a877
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date:   Tue Jun 18 14:09:09 2019 -0300

    posix: Fix large mmap64 offset for mips64n32 (BZ#24699)
    
    The fix for BZ#21270 (commit 158d5fa0e19) added a mask to avoid offset larger
    than 1^44 to be used along __NR_mmap2.  However mips64n32 users __NR_mmap,
    as mips64n64, but still defines off_t as old non-LFS type (other ILP32, such
    x32, defines off_t being equal to off64_t).  This leads to use the same
    mask meant only for __NR_mmap2 call for __NR_mmap, thus limiting the maximum
    offset it can use with mmap64.
    
    This patch fixes by setting the high mask only for __NR_mmap2 usage. The
    posix/tst-mmap-offset.c already tests it and also fails for mips64n32. The
    patch also change the test to check for an arch-specific header that defines
    the maximum supported offset.
    
    Checked on x86_64-linux-gnu, i686-linux-gnu, and I also tests tst-mmap-offset
    on qemu simulated mips64 with kernel 3.2.0 kernel for both mips-linux-gnu and
    mips64-n32-linux-gnu.
    
    	[BZ #24699]
    	* posix/tst-mmap-offset.c: Mention BZ #24699.
    	(do_test_bz21270): Rename to do_test_large_offset and use
    	mmap64_maximum_offset to check for maximum expected offset value.
    	* sysdeps/generic/mmap_info.h: New file.
    	* sysdeps/unix/sysv/linux/mips/mmap_info.h: Likewise.
    	* sysdeps/unix/sysv/linux/mmap64.c (MMAP_OFF_HIGH_MASK): Define iff
    	__NR_mmap2 is used.
    
    (cherry picked from commit a008c76b56e4f958cf5a0d6f67d29fade89421b7)