Print this page
10468 __ctype_mask[EOF] has been working by accident
10469 GCC's -faggressive-loop-optimizations is too aggressive
10470 array over-read in has_saved_fp()
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: John Levon <john.levon@joyent.com>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libsaveargs/amd64/saveargs.c
+++ new/usr/src/lib/libsaveargs/amd64/saveargs.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
↓ open down ↓ |
15 lines elided |
↑ open up ↑ |
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 +/*
27 + * Copyright 2019 Joyent, Inc.
28 + */
26 29
27 30 /*
28 31 * The Sun Studio and GCC (patched for opensolaris/illumos) compilers
29 32 * implement a argument saving scheme on amd64 via the -Wu,save-args or
30 33 * options. When the option is specified, INTEGER type function arguments
31 34 * passed via registers will be saved on the stack immediately after %rbp, and
32 35 * will not be modified through out the life of the routine.
33 36 *
34 37 * +--------+
35 38 * %rbp --> | %rbp |
36 39 * +--------+
37 40 * -0x8(%rbp) | %rdi |
38 41 * +--------+
39 42 * -0x10(%rbp) | %rsi |
40 43 * +--------+
41 44 * -0x18(%rbp) | %rdx |
42 45 * +--------+
43 46 * -0x20(%rbp) | %rcx |
44 47 * +--------+
45 48 * -0x28(%rbp) | %r8 |
46 49 * +--------+
47 50 * -0x30(%rbp) | %r9 |
48 51 * +--------+
49 52 *
50 53 *
51 54 * For example, for the following function,
52 55 *
53 56 * void
54 57 * foo(int a1, int a2, int a3, int a4, int a5, int a6, int a7)
55 58 * {
56 59 * ...
57 60 * }
58 61 *
59 62 * Disassembled code will look something like the following:
60 63 *
61 64 * pushq %rbp
62 65 * movq %rsp, %rbp
63 66 * subq $imm8, %rsp **
64 67 * movq %rdi, -0x8(%rbp)
65 68 * movq %rsi, -0x10(%rbp)
66 69 * movq %rdx, -0x18(%rbp)
67 70 * movq %rcx, -0x20(%rbp)
68 71 * movq %r8, -0x28(%rbp)
69 72 * movq %r9, -0x30(%rbp)
70 73 * ...
71 74 * or
72 75 * pushq %rbp
73 76 * movq %rsp, %rbp
74 77 * subq $imm8, %rsp **
75 78 * movq %r9, -0x30(%rbp)
76 79 * movq %r8, -0x28(%rbp)
77 80 * movq %rcx, -0x20(%rbp)
78 81 * movq %rdx, -0x18(%rbp)
79 82 * movq %rsi, -0x10(%rbp)
80 83 * movq %rdi, -0x8(%rbp)
81 84 * ...
82 85 * or
83 86 * pushq %rbp
84 87 * movq %rsp, %rbp
85 88 * pushq %rdi
86 89 * pushq %rsi
87 90 * pushq %rdx
88 91 * pushq %rcx
89 92 * pushq %r8
90 93 * pushq %r9
91 94 *
92 95 * **: The space being reserved is in addition to what the current
93 96 * function prolog already reserves.
94 97 *
95 98 * We loop through the first SAVEARGS_INSN_SEQ_LEN bytes of the function
96 99 * looking for each argument saving instruction we would expect to see.
97 100 *
98 101 * If there are odd number of arguments to a function, additional space is
99 102 * reserved on the stack to maintain 16-byte alignment. For example,
100 103 *
101 104 * argc == 0: no argument saving.
102 105 * argc == 3: save 3, but space for 4 is reserved
103 106 * argc == 7: save 6.
104 107 */
105 108
106 109 #include <sys/sysmacros.h>
107 110 #include <sys/types.h>
108 111 #include <libdisasm.h>
109 112 #include <string.h>
110 113
111 114 #include <saveargs.h>
112 115
113 116 /*
114 117 * Size of the instruction sequence arrays. It should correspond to
115 118 * the maximum number of arguments passed via registers.
116 119 */
117 120 #define INSTR_ARRAY_SIZE 6
118 121
119 122 #define INSTR1(ins, off) (ins[(off)])
120 123 #define INSTR2(ins, off) (ins[(off)] + (ins[(off) + 1] << 8))
121 124 #define INSTR3(ins, off) \
122 125 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16))
123 126 #define INSTR4(ins, off) \
124 127 (ins[(off)] + (ins[(off) + 1] << 8) + (ins[(off + 2)] << 16) + \
125 128 (ins[(off) + 3] << 24))
126 129
127 130 /*
128 131 * Sun Studio 10 patch implementation saves %rdi first;
129 132 * GCC 3.4.3 Sun branch implementation saves them in reverse order.
130 133 */
131 134 static const uint32_t save_instr[INSTR_ARRAY_SIZE] = {
132 135 0xf87d8948, /* movq %rdi, -0x8(%rbp) */
133 136 0xf0758948, /* movq %rsi, -0x10(%rbp) */
134 137 0xe8558948, /* movq %rdx, -0x18(%rbp) */
135 138 0xe04d8948, /* movq %rcx, -0x20(%rbp) */
136 139 0xd845894c, /* movq %r8, -0x28(%rbp) */
137 140 0xd04d894c /* movq %r9, -0x30(%rbp) */
138 141 };
139 142
140 143 static const uint16_t save_instr_push[] = {
141 144 0x57, /* pushq %rdi */
142 145 0x56, /* pushq %rsi */
143 146 0x52, /* pushq %rdx */
144 147 0x51, /* pushq %rcx */
145 148 0x5041, /* pushq %r8 */
146 149 0x5141 /* pushq %r9 */
147 150 };
148 151
149 152 /*
150 153 * If the return type of a function is a structure greater than 16 bytes in
151 154 * size, %rdi will contain the address to which it should be stored, and
152 155 * arguments will begin at %rsi. Studio will push all of the normal argument
153 156 * registers anyway, GCC will start pushing at %rsi, so we need a separate
154 157 * pattern.
155 158 */
156 159 static const uint32_t save_instr_sr[INSTR_ARRAY_SIZE-1] = {
157 160 0xf8758948, /* movq %rsi,-0x8(%rbp) */
158 161 0xf0558948, /* movq %rdx,-0x10(%rbp) */
159 162 0xe84d8948, /* movq %rcx,-0x18(%rbp) */
160 163 0xe045894c, /* movq %r8,-0x20(%rbp) */
161 164 0xd84d894c /* movq %r9,-0x28(%rbp) */
162 165 };
163 166
164 167 static const uint8_t save_fp_pushes[] = {
165 168 0x55, /* pushq %rbp */
166 169 0xcc /* int $0x3 */
167 170 };
168 171 #define NUM_FP_PUSHES (sizeof (save_fp_pushes) / sizeof (save_fp_pushes[0]))
169 172
170 173 static const uint32_t save_fp_movs[] = {
171 174 0x00e58948, /* movq %rsp,%rbp, encoding 1 */
172 175 0x00ec8b48, /* movq %rsp,%rbp, encoding 2 */
173 176 };
174 177 #define NUM_FP_MOVS (sizeof (save_fp_movs) / sizeof (save_fp_movs[0]))
175 178
176 179 typedef struct {
177 180 uint8_t *data;
178 181 size_t size;
179 182 } text_t;
180 183
181 184 static int
182 185 do_read(void *data, uint64_t addr, void *buf, size_t len)
183 186 {
184 187 text_t *t = data;
185 188
186 189 if (addr >= t->size)
187 190 return (-1);
188 191
189 192 len = MIN(len, t->size - addr);
190 193
191 194 (void) memcpy(buf, (char *)t->data + addr, len);
192 195
193 196 return (len);
194 197 }
195 198
196 199 /* ARGSUSED */
197 200 int
198 201 do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
199 202 size_t *symlen)
200 203 {
201 204 /* We don't actually need lookup info */
202 205 return (-1);
203 206 }
204 207
205 208 static int
206 209 instr_size(dis_handle_t *dhp, uint8_t *ins, unsigned int i, size_t size)
207 210 {
208 211 text_t t;
209 212
↓ open down ↓ |
174 lines elided |
↑ open up ↑ |
210 213 t.data = ins;
211 214 t.size = size;
212 215
213 216 dis_set_data(dhp, &t);
214 217 return (dis_instrlen(dhp, i));
215 218 }
216 219
217 220 static boolean_t
218 221 has_saved_fp(dis_handle_t *dhp, uint8_t *ins, int size)
219 222 {
220 - int i, j;
223 + int i, j;
221 224 uint32_t n;
222 225 boolean_t found_push = B_FALSE;
223 226 ssize_t sz = 0;
224 227
225 228 for (i = 0; i < size; i += sz) {
226 229 if ((sz = instr_size(dhp, ins, i, size)) < 1)
227 230 return (B_FALSE);
228 231
229 232 if (found_push == B_FALSE) {
230 233 if (sz != 1)
231 234 continue;
232 235
233 236 n = INSTR1(ins, i);
234 - for (j = 0; j <= NUM_FP_PUSHES; j++)
237 + for (j = 0; j < NUM_FP_PUSHES; j++)
235 238 if (save_fp_pushes[j] == n) {
236 239 found_push = B_TRUE;
237 240 break;
238 241 }
239 242 } else {
240 243 if (sz != 3)
241 244 continue;
242 245 n = INSTR3(ins, i);
243 - for (j = 0; j <= NUM_FP_MOVS; j++)
246 + for (j = 0; j < NUM_FP_MOVS; j++)
244 247 if (save_fp_movs[j] == n)
245 248 return (B_TRUE);
246 249 }
247 250 }
248 251
249 252 return (B_FALSE);
250 253 }
251 254
252 255 int
253 256 saveargs_has_args(uint8_t *ins, size_t size, uint_t argc, int start_index)
254 257 {
255 258 int i, j;
256 259 uint32_t n;
257 260 uint8_t found = 0;
258 261 ssize_t sz = 0;
259 262 dis_handle_t *dhp = NULL;
260 263 int ret = SAVEARGS_NO_ARGS;
261 264
262 265 argc = MIN((start_index + argc), INSTR_ARRAY_SIZE);
263 266
264 267 if ((dhp = dis_handle_create(DIS_X86_SIZE64, NULL, do_lookup,
265 268 do_read)) == NULL)
266 269 return (SAVEARGS_NO_ARGS);
267 270
268 271 if (!has_saved_fp(dhp, ins, size)) {
269 272 dis_handle_destroy(dhp);
270 273 return (SAVEARGS_NO_ARGS);
271 274 }
272 275
273 276 /*
274 277 * For each possible style of argument saving, walk the insn stream as
275 278 * we've been given it, and set bit N in 'found' if we find the
276 279 * instruction saving the Nth argument.
277 280 */
278 281
279 282 /*
280 283 * Compare against regular implementation
281 284 */
282 285 found = 0;
283 286 for (i = 0; i < size; i += sz) {
284 287 sz = instr_size(dhp, ins, i, size);
285 288
286 289 if (sz < 1)
287 290 break;
288 291 else if (sz != 4)
289 292 continue;
290 293
291 294 n = INSTR4(ins, i);
292 295
293 296 for (j = 0; j < argc; j++) {
294 297 if (n == save_instr[j]) {
295 298 found |= (1 << j);
296 299
297 300 if (found == ((1 << argc) - 1)) {
298 301 ret = start_index ?
299 302 SAVEARGS_STRUCT_ARGS :
300 303 SAVEARGS_TRAD_ARGS;
301 304 goto done;
302 305 }
303 306
304 307 break;
305 308 }
306 309 }
307 310 }
308 311
309 312 /*
310 313 * Compare against GCC push-based implementation
311 314 */
312 315 found = 0;
313 316 for (i = 0; i < size; i += sz) {
314 317 if ((sz = instr_size(dhp, ins, i, size)) < 1)
315 318 break;
316 319
317 320 for (j = start_index; j < argc; j++) {
318 321 if (sz == 2) /* Two byte */
319 322 n = INSTR2(ins, i);
320 323 else if (sz == 1)
321 324 n = INSTR1(ins, i);
322 325 else
323 326 continue;
324 327
325 328 if (n == save_instr_push[j]) {
326 329 found |= (1 << (j - start_index));
327 330
328 331 if (found ==
329 332 ((1 << (argc - start_index)) - 1)) {
330 333 ret = SAVEARGS_TRAD_ARGS;
331 334 goto done;
332 335 }
333 336
334 337 break;
335 338 }
336 339 }
337 340 }
338 341
339 342 /*
340 343 * Look for a GCC-style returned structure.
341 344 */
342 345 found = 0;
343 346 if (start_index != 0) {
344 347 for (i = 0; i < size; i += sz) {
345 348 sz = instr_size(dhp, ins, i, size);
346 349
347 350 if (sz < 1)
348 351 break;
349 352 else if (sz != 4)
350 353 continue;
351 354
352 355 n = INSTR4(ins, i);
353 356
354 357 /* argc is inclusive of start_index, allow for that */
355 358 for (j = 0; j < (argc - start_index); j++) {
356 359 if (n == save_instr_sr[j]) {
357 360 found |= (1 << j);
358 361
359 362 if (found ==
360 363 ((1 << (argc - start_index)) - 1)) {
361 364 ret = SAVEARGS_TRAD_ARGS;
362 365 goto done;
363 366 }
364 367
365 368 break;
366 369 }
367 370 }
368 371 }
369 372 }
370 373
371 374 done:
372 375 dis_handle_destroy(dhp);
373 376 return (ret);
374 377 }
↓ open down ↓ |
121 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX