1 | /* Unit test for _dl_addr_inside_object. |
2 | Copyright (C) 2016-2024 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <stdio.h> |
20 | #include <stdlib.h> |
21 | #include <link.h> |
22 | #include <elf.h> |
23 | #include <libc-symbols.h> |
24 | |
25 | extern int _dl_addr_inside_object (struct link_map *l, const ElfW(Addr) addr); |
26 | |
27 | static int |
28 | do_test (void) |
29 | { |
30 | int ret, err = 0; |
31 | ElfW(Addr) addr; |
32 | struct link_map map; |
33 | ElfW(Phdr) ; |
34 | map.l_phdr = &header; |
35 | map.l_phnum = 1; |
36 | map.l_addr = 0x0; |
37 | /* Segment spans 0x2000 -> 0x4000. */ |
38 | header.p_vaddr = 0x2000; |
39 | header.p_memsz = 0x2000; |
40 | header.p_type = PT_LOAD; |
41 | /* Address is above the segment e.g. > 0x4000. */ |
42 | addr = 0x5000; |
43 | ret = _dl_addr_inside_object (l: &map, addr); |
44 | switch (ret) |
45 | { |
46 | case 0: |
47 | printf (format: "PASS: Above: Address is detected as outside the segment.\n" ); |
48 | break; |
49 | case 1: |
50 | printf (format: "FAIL: Above: Address is detected as inside the segment.\n" ); |
51 | err++; |
52 | break; |
53 | default: |
54 | printf (format: "FAIL: Above: Invalid return value.\n" ); |
55 | exit (status: 1); |
56 | } |
57 | /* Address is inside the segment e.g. 0x2000 < addr < 0x4000. */ |
58 | addr = 0x3000; |
59 | ret = _dl_addr_inside_object (l: &map, addr); |
60 | switch (ret) |
61 | { |
62 | case 0: |
63 | printf (format: "FAIL: Inside: Address is detected as outside the segment.\n" ); |
64 | err++; |
65 | break; |
66 | case 1: |
67 | printf (format: "PASS: Inside: Address is detected as inside the segment.\n" ); |
68 | break; |
69 | default: |
70 | printf (format: "FAIL: Inside: Invalid return value.\n" ); |
71 | exit (status: 1); |
72 | } |
73 | /* Address is below the segment e.g. < 0x2000. */ |
74 | addr = 0x1000; |
75 | ret = _dl_addr_inside_object (l: &map, addr); |
76 | switch (ret) |
77 | { |
78 | case 0: |
79 | printf (format: "PASS: Below: Address is detected as outside the segment.\n" ); |
80 | break; |
81 | case 1: |
82 | printf (format: "FAIL: Below: Address is detected as inside the segment.\n" ); |
83 | err++; |
84 | break; |
85 | default: |
86 | printf (format: "FAIL: Below: Invalid return value.\n" ); |
87 | exit (status: 1); |
88 | } |
89 | /* Address is in the segment and addr == p_vaddr. */ |
90 | addr = 0x2000; |
91 | ret = _dl_addr_inside_object (l: &map, addr); |
92 | switch (ret) |
93 | { |
94 | case 0: |
95 | printf (format: "FAIL: At p_vaddr: Address is detected as outside the segment.\n" ); |
96 | err++; |
97 | break; |
98 | case 1: |
99 | printf (format: "PASS: At p_vaddr: Address is detected as inside the segment.\n" ); |
100 | break; |
101 | default: |
102 | printf (format: "FAIL: At p_vaddr: Invalid return value.\n" ); |
103 | exit (status: 1); |
104 | } |
105 | /* Address is in the segment and addr == p_vaddr + p_memsz - 1. */ |
106 | addr = 0x2000 + 0x2000 - 0x1; |
107 | ret = _dl_addr_inside_object (l: &map, addr); |
108 | switch (ret) |
109 | { |
110 | case 0: |
111 | printf (format: "FAIL: At p_memsz-1: Address is detected as outside the segment.\n" ); |
112 | err++; |
113 | break; |
114 | case 1: |
115 | printf (format: "PASS: At p_memsz-1: Address is detected as inside the segment.\n" ); |
116 | break; |
117 | default: |
118 | printf (format: "FAIL: At p_memsz-1: Invalid return value.\n" ); |
119 | exit (status: 1); |
120 | } |
121 | /* Address is outside the segment and addr == p_vaddr + p_memsz. */ |
122 | addr = 0x2000 + 0x2000; |
123 | ret = _dl_addr_inside_object (l: &map, addr); |
124 | switch (ret) |
125 | { |
126 | case 0: |
127 | printf (format: "PASS: At p_memsz: Address is detected as outside the segment.\n" ); |
128 | break; |
129 | case 1: |
130 | printf (format: "FAIL: At p_memsz: Address is detected as inside the segment.\n" ); |
131 | err++; |
132 | break; |
133 | default: |
134 | printf (format: "FAIL: At p_memsz: Invalid return value.\n" ); |
135 | exit (status: 1); |
136 | } |
137 | /* Address is outside the segment and p_vaddr at maximum address. */ |
138 | addr = 0x0 - 0x2; |
139 | header.p_vaddr = 0x0 - 0x1; |
140 | header.p_memsz = 0x1; |
141 | ret = _dl_addr_inside_object (l: &map, addr); |
142 | switch (ret) |
143 | { |
144 | case 0: |
145 | printf (format: "PASS: At max: Address is detected as outside the segment.\n" ); |
146 | break; |
147 | case 1: |
148 | printf (format: "FAIL: At max: Address is detected as inside the segment.\n" ); |
149 | err++; |
150 | break; |
151 | default: |
152 | printf (format: "FAIL: At max: Invalid return value.\n" ); |
153 | exit (status: 1); |
154 | } |
155 | /* Address is outside the segment and p_vaddr at minimum address. */ |
156 | addr = 0x1; |
157 | header.p_vaddr = 0x0; |
158 | header.p_memsz = 0x1; |
159 | ret = _dl_addr_inside_object (l: &map, addr); |
160 | switch (ret) |
161 | { |
162 | case 0: |
163 | printf (format: "PASS: At min: Address is detected as outside the segment.\n" ); |
164 | break; |
165 | case 1: |
166 | printf (format: "FAIL: At min: Address is detected as inside the segment.\n" ); |
167 | err++; |
168 | break; |
169 | default: |
170 | printf (format: "FAIL: At min: Invalid return value.\n" ); |
171 | exit (status: 1); |
172 | } |
173 | /* Address is always inside the segment with p_memsz at max. */ |
174 | addr = 0x0; |
175 | header.p_vaddr = 0x0; |
176 | header.p_memsz = 0x0 - 0x1; |
177 | ret = _dl_addr_inside_object (l: &map, addr); |
178 | switch (ret) |
179 | { |
180 | case 0: |
181 | printf (format: "FAIL: At maxmem: Address is detected as outside the segment.\n" ); |
182 | err++; |
183 | break; |
184 | case 1: |
185 | printf (format: "PASS: At maxmem: Address is detected as inside the segment.\n" ); |
186 | break; |
187 | default: |
188 | printf (format: "FAIL: At maxmem: Invalid return value.\n" ); |
189 | exit (status: 1); |
190 | } |
191 | /* Attempt to wrap addr into the segment. |
192 | Pick a load address in the middle of the address space. |
193 | Place the test address at 0x0 so it wraps to the middle again. */ |
194 | map.l_addr = 0x0 - 0x1; |
195 | map.l_addr = map.l_addr / 2; |
196 | addr = 0; |
197 | /* Setup a segment covering 1/2 the address space. */ |
198 | header.p_vaddr = 0x0; |
199 | header.p_memsz = 0x0 - 0x1 - map.l_addr; |
200 | /* No matter where you place addr everything is shifted modulo l_addr |
201 | and even with this underflow you're always 1 byte away from being |
202 | in the range. */ |
203 | ret = _dl_addr_inside_object (l: &map, addr); |
204 | switch (ret) |
205 | { |
206 | case 0: |
207 | printf (format: "PASS: Underflow: Address is detected as outside the segment.\n" ); |
208 | break; |
209 | case 1: |
210 | printf (format: "FAIL: Underflow: Address is detected as inside the segment.\n" ); |
211 | err++; |
212 | break; |
213 | default: |
214 | printf (format: "FAIL: Underflow: Invalid return value.\n" ); |
215 | exit (status: 1); |
216 | } |
217 | |
218 | return err; |
219 | } |
220 | |
221 | #include <support/test-driver.c> |
222 | |