1 | /* BZ #18877, BZ #21270, and BZ #24699 mmap offset test. |
2 | |
3 | Copyright (C) 2015-2022 Free Software Foundation, Inc. |
4 | This file is part of the GNU C Library. |
5 | |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2.1 of the License, or (at your option) any later version. |
10 | |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, see |
18 | <https://www.gnu.org/licenses/>. */ |
19 | |
20 | #include <stdint.h> |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <unistd.h> |
25 | #include <errno.h> |
26 | #include <sys/mman.h> |
27 | #include <mmap_info.h> |
28 | |
29 | #include <support/check.h> |
30 | |
31 | static int fd; |
32 | static long int page_shift; |
33 | static char fname[] = "/tmp/tst-mmap-offset-XXXXXX" ; |
34 | |
35 | static void |
36 | do_prepare (int argc, char **argv) |
37 | { |
38 | fd = mkstemp64 (template: fname); |
39 | if (fd < 0) |
40 | FAIL_EXIT1 ("mkstemp failed" ); |
41 | |
42 | if (unlink (name: fname)) |
43 | FAIL_EXIT1 ("unlink failed" ); |
44 | |
45 | long sz = sysconf(_SC_PAGESIZE); |
46 | if (sz == -1) |
47 | sz = 4096L; |
48 | page_shift = ffs (sz) - 1; |
49 | } |
50 | |
51 | #define PREPARE do_prepare |
52 | |
53 | |
54 | /* Check if negative offsets are handled correctly by mmap. */ |
55 | static int |
56 | do_test_bz18877 (void) |
57 | { |
58 | const int prot = PROT_READ | PROT_WRITE; |
59 | const int flags = MAP_SHARED; |
60 | const unsigned long length = 0x10000; |
61 | const unsigned long offset = 0xace00000; |
62 | const unsigned long size = offset + length; |
63 | void *addr; |
64 | |
65 | if (ftruncate64 (fd: fd, length: size)) |
66 | FAIL_RET ("ftruncate64 failed" ); |
67 | |
68 | addr = mmap (NULL, len: length, prot: prot, flags: flags, fd: fd, offset: offset); |
69 | if (MAP_FAILED == addr) |
70 | FAIL_RET ("mmap failed" ); |
71 | |
72 | /* This memcpy is likely to SIGBUS if mmap has messed up with offset. */ |
73 | memcpy (addr, fname, sizeof (fname)); |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | /* Check if invalid offset are handled correctly by mmap. */ |
79 | static int |
80 | do_test_large_offset (void) |
81 | { |
82 | /* For architectures with sizeof (off_t) < sizeof (off64_t) mmap is |
83 | implemented with __SYS_mmap2 syscall and the offset is represented in |
84 | multiples of page size. For offset larger than |
85 | '1 << (page_shift + 8 * sizeof (off_t))' (that is, 1<<44 on system with |
86 | page size of 4096 bytes) the system call silently truncates the offset. |
87 | For this case glibc mmap implementation returns EINVAL. */ |
88 | const int prot = PROT_READ | PROT_WRITE; |
89 | const int flags = MAP_SHARED; |
90 | const int64_t offset = 1ULL << (page_shift + 8 * sizeof (uint32_t)); |
91 | const size_t length = 4096; |
92 | |
93 | void *addr = mmap64 (NULL, len: length, prot: prot, flags: flags, fd: fd, offset: offset); |
94 | if (mmap64_maximum_offset (page_shift) < UINT64_MAX) |
95 | { |
96 | if ((addr != MAP_FAILED) && (errno != EINVAL)) |
97 | FAIL_RET ("mmap succeed" ); |
98 | } |
99 | else |
100 | { |
101 | if (addr == MAP_FAILED) |
102 | FAIL_RET ("mmap failed" ); |
103 | } |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | int |
109 | do_test (void) |
110 | { |
111 | int ret = 0; |
112 | |
113 | ret += do_test_bz18877 (); |
114 | ret += do_test_large_offset (); |
115 | |
116 | return ret; |
117 | } |
118 | |
119 | #include <support/test-driver.c> |
120 | |