]>
Commit | Line | Data |
---|---|---|
321d628a FG |
1 | From 338c7d8678b82c46668ce3b73f7339f71ab69cc8 Mon Sep 17 00:00:00 2001 |
2 | From: Josh Poimboeuf <jpoimboe@redhat.com> | |
3 | Date: Tue, 11 Jul 2017 10:33:43 -0500 | |
7c7389df | 4 | Subject: [PATCH 034/232] objtool, x86: Add facility for asm code to provide |
321d628a FG |
5 | unwind hints |
6 | MIME-Version: 1.0 | |
7 | Content-Type: text/plain; charset=UTF-8 | |
8 | Content-Transfer-Encoding: 8bit | |
9 | ||
10 | CVE-2017-5754 | |
11 | ||
12 | Some asm (and inline asm) code does special things to the stack which | |
13 | objtool can't understand. (Nor can GCC or GNU assembler, for that | |
14 | matter.) In such cases we need a facility for the code to provide | |
15 | annotations, so the unwinder can unwind through it. | |
16 | ||
17 | This provides such a facility, in the form of unwind hints. They're | |
18 | similar to the GNU assembler .cfi* directives, but they give more | |
19 | information, and are needed in far fewer places, because objtool can | |
20 | fill in the blanks by following branches and adjusting the stack pointer | |
21 | for pushes and pops. | |
22 | ||
23 | Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com> | |
24 | Cc: Andy Lutomirski <luto@kernel.org> | |
25 | Cc: Borislav Petkov <bp@alien8.de> | |
26 | Cc: Brian Gerst <brgerst@gmail.com> | |
27 | Cc: Denys Vlasenko <dvlasenk@redhat.com> | |
28 | Cc: H. Peter Anvin <hpa@zytor.com> | |
29 | Cc: Jiri Slaby <jslaby@suse.cz> | |
30 | Cc: Linus Torvalds <torvalds@linux-foundation.org> | |
31 | Cc: Mike Galbraith <efault@gmx.de> | |
32 | Cc: Peter Zijlstra <peterz@infradead.org> | |
33 | Cc: Thomas Gleixner <tglx@linutronix.de> | |
34 | Cc: live-patching@vger.kernel.org | |
35 | Link: http://lkml.kernel.org/r/0f5f3c9104fca559ff4088bece1d14ae3bca52d5.1499786555.git.jpoimboe@redhat.com | |
36 | Signed-off-by: Ingo Molnar <mingo@kernel.org> | |
37 | (cherry picked from commit 39358a033b2e4432052265c1fa0f36f572d8cfb5) | |
38 | Signed-off-by: Andy Whitcroft <apw@canonical.com> | |
39 | Signed-off-by: Kleber Sacilotto de Souza <kleber.souza@canonical.com> | |
40 | (cherry picked from commit a1fed2e10e84d48643a09861c2d127968621813e) | |
41 | Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com> | |
42 | --- | |
43 | tools/objtool/Makefile | 3 + | |
44 | arch/x86/include/asm/orc_types.h | 107 ++++++++++++++++++++ | |
45 | arch/x86/include/asm/unwind_hints.h | 103 +++++++++++++++++++ | |
46 | tools/objtool/check.h | 4 +- | |
47 | tools/objtool/orc_types.h | 22 +++++ | |
48 | tools/objtool/check.c | 191 +++++++++++++++++++++++++++++++++--- | |
49 | 6 files changed, 417 insertions(+), 13 deletions(-) | |
50 | create mode 100644 arch/x86/include/asm/orc_types.h | |
51 | create mode 100644 arch/x86/include/asm/unwind_hints.h | |
52 | ||
53 | diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile | |
54 | index 0e2765e243c0..3a6425fefc43 100644 | |
55 | --- a/tools/objtool/Makefile | |
56 | +++ b/tools/objtool/Makefile | |
57 | @@ -52,6 +52,9 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) | |
58 | diff -I'^#include' arch/x86/insn/inat.h ../../arch/x86/include/asm/inat.h >/dev/null && \ | |
59 | diff -I'^#include' arch/x86/insn/inat_types.h ../../arch/x86/include/asm/inat_types.h >/dev/null) \ | |
60 | || echo "warning: objtool: x86 instruction decoder differs from kernel" >&2 )) || true | |
61 | + @(test -d ../../kernel -a -d ../../tools -a -d ../objtool && (( \ | |
62 | + diff ../../arch/x86/include/asm/orc_types.h orc_types.h >/dev/null) \ | |
63 | + || echo "warning: objtool: orc_types.h differs from kernel" >&2 )) || true | |
64 | $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ | |
65 | ||
66 | ||
67 | diff --git a/arch/x86/include/asm/orc_types.h b/arch/x86/include/asm/orc_types.h | |
68 | new file mode 100644 | |
69 | index 000000000000..7dc777a6cb40 | |
70 | --- /dev/null | |
71 | +++ b/arch/x86/include/asm/orc_types.h | |
72 | @@ -0,0 +1,107 @@ | |
73 | +/* | |
74 | + * Copyright (C) 2017 Josh Poimboeuf <jpoimboe@redhat.com> | |
75 | + * | |
76 | + * This program is free software; you can redistribute it and/or | |
77 | + * modify it under the terms of the GNU General Public License | |
78 | + * as published by the Free Software Foundation; either version 2 | |
79 | + * of the License, or (at your option) any later version. | |
80 | + * | |
81 | + * This program is distributed in the hope that it will be useful, | |
82 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
83 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
84 | + * GNU General Public License for more details. | |
85 | + * | |
86 | + * You should have received a copy of the GNU General Public License | |
87 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
88 | + */ | |
89 | + | |
90 | +#ifndef _ORC_TYPES_H | |
91 | +#define _ORC_TYPES_H | |
92 | + | |
93 | +#include <linux/types.h> | |
94 | +#include <linux/compiler.h> | |
95 | + | |
96 | +/* | |
97 | + * The ORC_REG_* registers are base registers which are used to find other | |
98 | + * registers on the stack. | |
99 | + * | |
100 | + * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the | |
101 | + * address of the previous frame: the caller's SP before it called the current | |
102 | + * function. | |
103 | + * | |
104 | + * ORC_REG_UNDEFINED means the corresponding register's value didn't change in | |
105 | + * the current frame. | |
106 | + * | |
107 | + * The most commonly used base registers are SP and BP -- which the previous SP | |
108 | + * is usually based on -- and PREV_SP and UNDEFINED -- which the previous BP is | |
109 | + * usually based on. | |
110 | + * | |
111 | + * The rest of the base registers are needed for special cases like entry code | |
112 | + * and GCC realigned stacks. | |
113 | + */ | |
114 | +#define ORC_REG_UNDEFINED 0 | |
115 | +#define ORC_REG_PREV_SP 1 | |
116 | +#define ORC_REG_DX 2 | |
117 | +#define ORC_REG_DI 3 | |
118 | +#define ORC_REG_BP 4 | |
119 | +#define ORC_REG_SP 5 | |
120 | +#define ORC_REG_R10 6 | |
121 | +#define ORC_REG_R13 7 | |
122 | +#define ORC_REG_BP_INDIRECT 8 | |
123 | +#define ORC_REG_SP_INDIRECT 9 | |
124 | +#define ORC_REG_MAX 15 | |
125 | + | |
126 | +/* | |
127 | + * ORC_TYPE_CALL: Indicates that sp_reg+sp_offset resolves to PREV_SP (the | |
128 | + * caller's SP right before it made the call). Used for all callable | |
129 | + * functions, i.e. all C code and all callable asm functions. | |
130 | + * | |
131 | + * ORC_TYPE_REGS: Used in entry code to indicate that sp_reg+sp_offset points | |
132 | + * to a fully populated pt_regs from a syscall, interrupt, or exception. | |
133 | + * | |
134 | + * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset | |
135 | + * points to the iret return frame. | |
136 | + * | |
137 | + * The UNWIND_HINT macros are used only for the unwind_hint struct. They | |
138 | + * aren't used in struct orc_entry due to size and complexity constraints. | |
139 | + * Objtool converts them to real types when it converts the hints to orc | |
140 | + * entries. | |
141 | + */ | |
142 | +#define ORC_TYPE_CALL 0 | |
143 | +#define ORC_TYPE_REGS 1 | |
144 | +#define ORC_TYPE_REGS_IRET 2 | |
145 | +#define UNWIND_HINT_TYPE_SAVE 3 | |
146 | +#define UNWIND_HINT_TYPE_RESTORE 4 | |
147 | + | |
148 | +#ifndef __ASSEMBLY__ | |
149 | +/* | |
150 | + * This struct is more or less a vastly simplified version of the DWARF Call | |
151 | + * Frame Information standard. It contains only the necessary parts of DWARF | |
152 | + * CFI, simplified for ease of access by the in-kernel unwinder. It tells the | |
153 | + * unwinder how to find the previous SP and BP (and sometimes entry regs) on | |
154 | + * the stack for a given code address. Each instance of the struct corresponds | |
155 | + * to one or more code locations. | |
156 | + */ | |
157 | +struct orc_entry { | |
158 | + s16 sp_offset; | |
159 | + s16 bp_offset; | |
160 | + unsigned sp_reg:4; | |
161 | + unsigned bp_reg:4; | |
162 | + unsigned type:2; | |
163 | +}; | |
164 | + | |
165 | +/* | |
166 | + * This struct is used by asm and inline asm code to manually annotate the | |
167 | + * location of registers on the stack for the ORC unwinder. | |
168 | + * | |
169 | + * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. | |
170 | + */ | |
171 | +struct unwind_hint { | |
172 | + u32 ip; | |
173 | + s16 sp_offset; | |
174 | + u8 sp_reg; | |
175 | + u8 type; | |
176 | +}; | |
177 | +#endif /* __ASSEMBLY__ */ | |
178 | + | |
179 | +#endif /* _ORC_TYPES_H */ | |
180 | diff --git a/arch/x86/include/asm/unwind_hints.h b/arch/x86/include/asm/unwind_hints.h | |
181 | new file mode 100644 | |
182 | index 000000000000..5e02b11c9b86 | |
183 | --- /dev/null | |
184 | +++ b/arch/x86/include/asm/unwind_hints.h | |
185 | @@ -0,0 +1,103 @@ | |
186 | +#ifndef _ASM_X86_UNWIND_HINTS_H | |
187 | +#define _ASM_X86_UNWIND_HINTS_H | |
188 | + | |
189 | +#include "orc_types.h" | |
190 | + | |
191 | +#ifdef __ASSEMBLY__ | |
192 | + | |
193 | +/* | |
194 | + * In asm, there are two kinds of code: normal C-type callable functions and | |
195 | + * the rest. The normal callable functions can be called by other code, and | |
196 | + * don't do anything unusual with the stack. Such normal callable functions | |
197 | + * are annotated with the ENTRY/ENDPROC macros. Most asm code falls in this | |
198 | + * category. In this case, no special debugging annotations are needed because | |
199 | + * objtool can automatically generate the ORC data for the ORC unwinder to read | |
200 | + * at runtime. | |
201 | + * | |
202 | + * Anything which doesn't fall into the above category, such as syscall and | |
203 | + * interrupt handlers, tends to not be called directly by other functions, and | |
204 | + * often does unusual non-C-function-type things with the stack pointer. Such | |
205 | + * code needs to be annotated such that objtool can understand it. The | |
206 | + * following CFI hint macros are for this type of code. | |
207 | + * | |
208 | + * These macros provide hints to objtool about the state of the stack at each | |
209 | + * instruction. Objtool starts from the hints and follows the code flow, | |
210 | + * making automatic CFI adjustments when it sees pushes and pops, filling out | |
211 | + * the debuginfo as necessary. It will also warn if it sees any | |
212 | + * inconsistencies. | |
213 | + */ | |
214 | +.macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL | |
215 | +#ifdef CONFIG_STACK_VALIDATION | |
216 | +.Lunwind_hint_ip_\@: | |
217 | + .pushsection .discard.unwind_hints | |
218 | + /* struct unwind_hint */ | |
219 | + .long .Lunwind_hint_ip_\@ - . | |
220 | + .short \sp_offset | |
221 | + .byte \sp_reg | |
222 | + .byte \type | |
223 | + .popsection | |
224 | +#endif | |
225 | +.endm | |
226 | + | |
227 | +.macro UNWIND_HINT_EMPTY | |
228 | + UNWIND_HINT sp_reg=ORC_REG_UNDEFINED | |
229 | +.endm | |
230 | + | |
231 | +.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0 | |
232 | + .if \base == %rsp && \indirect | |
233 | + .set sp_reg, ORC_REG_SP_INDIRECT | |
234 | + .elseif \base == %rsp | |
235 | + .set sp_reg, ORC_REG_SP | |
236 | + .elseif \base == %rbp | |
237 | + .set sp_reg, ORC_REG_BP | |
238 | + .elseif \base == %rdi | |
239 | + .set sp_reg, ORC_REG_DI | |
240 | + .elseif \base == %rdx | |
241 | + .set sp_reg, ORC_REG_DX | |
242 | + .elseif \base == %r10 | |
243 | + .set sp_reg, ORC_REG_R10 | |
244 | + .else | |
245 | + .error "UNWIND_HINT_REGS: bad base register" | |
246 | + .endif | |
247 | + | |
248 | + .set sp_offset, \offset | |
249 | + | |
250 | + .if \iret | |
251 | + .set type, ORC_TYPE_REGS_IRET | |
252 | + .elseif \extra == 0 | |
253 | + .set type, ORC_TYPE_REGS_IRET | |
254 | + .set sp_offset, \offset + (16*8) | |
255 | + .else | |
256 | + .set type, ORC_TYPE_REGS | |
257 | + .endif | |
258 | + | |
259 | + UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type | |
260 | +.endm | |
261 | + | |
262 | +.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 | |
263 | + UNWIND_HINT_REGS base=\base offset=\offset iret=1 | |
264 | +.endm | |
265 | + | |
266 | +.macro UNWIND_HINT_FUNC sp_offset=8 | |
267 | + UNWIND_HINT sp_offset=\sp_offset | |
268 | +.endm | |
269 | + | |
270 | +#else /* !__ASSEMBLY__ */ | |
271 | + | |
272 | +#define UNWIND_HINT(sp_reg, sp_offset, type) \ | |
273 | + "987: \n\t" \ | |
274 | + ".pushsection .discard.unwind_hints\n\t" \ | |
275 | + /* struct unwind_hint */ \ | |
276 | + ".long 987b - .\n\t" \ | |
277 | + ".short " __stringify(sp_offset) "\n\t" \ | |
278 | + ".byte " __stringify(sp_reg) "\n\t" \ | |
279 | + ".byte " __stringify(type) "\n\t" \ | |
280 | + ".popsection\n\t" | |
281 | + | |
282 | +#define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE) | |
283 | + | |
284 | +#define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE) | |
285 | + | |
286 | +#endif /* __ASSEMBLY__ */ | |
287 | + | |
288 | +#endif /* _ASM_X86_UNWIND_HINTS_H */ | |
289 | diff --git a/tools/objtool/check.h b/tools/objtool/check.h | |
290 | index 046874bbe226..ac3d4b13f17b 100644 | |
291 | --- a/tools/objtool/check.h | |
292 | +++ b/tools/objtool/check.h | |
293 | @@ -43,7 +43,7 @@ struct instruction { | |
294 | unsigned int len; | |
295 | unsigned char type; | |
296 | unsigned long immediate; | |
297 | - bool alt_group, visited, dead_end, ignore; | |
298 | + bool alt_group, visited, dead_end, ignore, hint, save, restore; | |
299 | struct symbol *call_dest; | |
300 | struct instruction *jump_dest; | |
301 | struct list_head alts; | |
302 | @@ -58,7 +58,7 @@ struct objtool_file { | |
303 | struct list_head insn_list; | |
304 | DECLARE_HASHTABLE(insn_hash, 16); | |
305 | struct section *rodata, *whitelist; | |
306 | - bool ignore_unreachables, c_file; | |
307 | + bool ignore_unreachables, c_file, hints; | |
308 | }; | |
309 | ||
310 | int check(const char *objname, bool nofp, bool orc); | |
311 | diff --git a/tools/objtool/orc_types.h b/tools/objtool/orc_types.h | |
312 | index fc5cf6cffd9a..9c9dc579bd7d 100644 | |
313 | --- a/tools/objtool/orc_types.h | |
314 | +++ b/tools/objtool/orc_types.h | |
315 | @@ -61,11 +61,19 @@ | |
316 | * | |
317 | * ORC_TYPE_REGS_IRET: Used in entry code to indicate that sp_reg+sp_offset | |
318 | * points to the iret return frame. | |
319 | + * | |
320 | + * The UNWIND_HINT macros are used only for the unwind_hint struct. They | |
321 | + * aren't used in struct orc_entry due to size and complexity constraints. | |
322 | + * Objtool converts them to real types when it converts the hints to orc | |
323 | + * entries. | |
324 | */ | |
325 | #define ORC_TYPE_CALL 0 | |
326 | #define ORC_TYPE_REGS 1 | |
327 | #define ORC_TYPE_REGS_IRET 2 | |
328 | +#define UNWIND_HINT_TYPE_SAVE 3 | |
329 | +#define UNWIND_HINT_TYPE_RESTORE 4 | |
330 | ||
331 | +#ifndef __ASSEMBLY__ | |
332 | /* | |
333 | * This struct is more or less a vastly simplified version of the DWARF Call | |
334 | * Frame Information standard. It contains only the necessary parts of DWARF | |
335 | @@ -82,4 +90,18 @@ struct orc_entry { | |
336 | unsigned type:2; | |
337 | } __packed; | |
338 | ||
339 | +/* | |
340 | + * This struct is used by asm and inline asm code to manually annotate the | |
341 | + * location of registers on the stack for the ORC unwinder. | |
342 | + * | |
343 | + * Type can be either ORC_TYPE_* or UNWIND_HINT_TYPE_*. | |
344 | + */ | |
345 | +struct unwind_hint { | |
346 | + u32 ip; | |
347 | + s16 sp_offset; | |
348 | + u8 sp_reg; | |
349 | + u8 type; | |
350 | +}; | |
351 | +#endif /* __ASSEMBLY__ */ | |
352 | + | |
353 | #endif /* _ORC_TYPES_H */ | |
354 | diff --git a/tools/objtool/check.c b/tools/objtool/check.c | |
355 | index cb57c526ba17..368275de5f23 100644 | |
356 | --- a/tools/objtool/check.c | |
357 | +++ b/tools/objtool/check.c | |
358 | @@ -100,7 +100,6 @@ static bool gcov_enabled(struct objtool_file *file) | |
359 | static bool ignore_func(struct objtool_file *file, struct symbol *func) | |
360 | { | |
361 | struct rela *rela; | |
362 | - struct instruction *insn; | |
363 | ||
364 | /* check for STACK_FRAME_NON_STANDARD */ | |
365 | if (file->whitelist && file->whitelist->rela) | |
366 | @@ -113,11 +112,6 @@ static bool ignore_func(struct objtool_file *file, struct symbol *func) | |
367 | return true; | |
368 | } | |
369 | ||
370 | - /* check if it has a context switching instruction */ | |
371 | - func_for_each_insn(file, func, insn) | |
372 | - if (insn->type == INSN_CONTEXT_SWITCH) | |
373 | - return true; | |
374 | - | |
375 | return false; | |
376 | } | |
377 | ||
378 | @@ -879,6 +873,99 @@ static int add_switch_table_alts(struct objtool_file *file) | |
379 | return 0; | |
380 | } | |
381 | ||
382 | +static int read_unwind_hints(struct objtool_file *file) | |
383 | +{ | |
384 | + struct section *sec, *relasec; | |
385 | + struct rela *rela; | |
386 | + struct unwind_hint *hint; | |
387 | + struct instruction *insn; | |
388 | + struct cfi_reg *cfa; | |
389 | + int i; | |
390 | + | |
391 | + sec = find_section_by_name(file->elf, ".discard.unwind_hints"); | |
392 | + if (!sec) | |
393 | + return 0; | |
394 | + | |
395 | + relasec = sec->rela; | |
396 | + if (!relasec) { | |
397 | + WARN("missing .rela.discard.unwind_hints section"); | |
398 | + return -1; | |
399 | + } | |
400 | + | |
401 | + if (sec->len % sizeof(struct unwind_hint)) { | |
402 | + WARN("struct unwind_hint size mismatch"); | |
403 | + return -1; | |
404 | + } | |
405 | + | |
406 | + file->hints = true; | |
407 | + | |
408 | + for (i = 0; i < sec->len / sizeof(struct unwind_hint); i++) { | |
409 | + hint = (struct unwind_hint *)sec->data->d_buf + i; | |
410 | + | |
411 | + rela = find_rela_by_dest(sec, i * sizeof(*hint)); | |
412 | + if (!rela) { | |
413 | + WARN("can't find rela for unwind_hints[%d]", i); | |
414 | + return -1; | |
415 | + } | |
416 | + | |
417 | + insn = find_insn(file, rela->sym->sec, rela->addend); | |
418 | + if (!insn) { | |
419 | + WARN("can't find insn for unwind_hints[%d]", i); | |
420 | + return -1; | |
421 | + } | |
422 | + | |
423 | + cfa = &insn->state.cfa; | |
424 | + | |
425 | + if (hint->type == UNWIND_HINT_TYPE_SAVE) { | |
426 | + insn->save = true; | |
427 | + continue; | |
428 | + | |
429 | + } else if (hint->type == UNWIND_HINT_TYPE_RESTORE) { | |
430 | + insn->restore = true; | |
431 | + insn->hint = true; | |
432 | + continue; | |
433 | + } | |
434 | + | |
435 | + insn->hint = true; | |
436 | + | |
437 | + switch (hint->sp_reg) { | |
438 | + case ORC_REG_UNDEFINED: | |
439 | + cfa->base = CFI_UNDEFINED; | |
440 | + break; | |
441 | + case ORC_REG_SP: | |
442 | + cfa->base = CFI_SP; | |
443 | + break; | |
444 | + case ORC_REG_BP: | |
445 | + cfa->base = CFI_BP; | |
446 | + break; | |
447 | + case ORC_REG_SP_INDIRECT: | |
448 | + cfa->base = CFI_SP_INDIRECT; | |
449 | + break; | |
450 | + case ORC_REG_R10: | |
451 | + cfa->base = CFI_R10; | |
452 | + break; | |
453 | + case ORC_REG_R13: | |
454 | + cfa->base = CFI_R13; | |
455 | + break; | |
456 | + case ORC_REG_DI: | |
457 | + cfa->base = CFI_DI; | |
458 | + break; | |
459 | + case ORC_REG_DX: | |
460 | + cfa->base = CFI_DX; | |
461 | + break; | |
462 | + default: | |
463 | + WARN_FUNC("unsupported unwind_hint sp base reg %d", | |
464 | + insn->sec, insn->offset, hint->sp_reg); | |
465 | + return -1; | |
466 | + } | |
467 | + | |
468 | + cfa->offset = hint->sp_offset; | |
469 | + insn->state.type = hint->type; | |
470 | + } | |
471 | + | |
472 | + return 0; | |
473 | +} | |
474 | + | |
475 | static int decode_sections(struct objtool_file *file) | |
476 | { | |
477 | int ret; | |
478 | @@ -909,6 +996,10 @@ static int decode_sections(struct objtool_file *file) | |
479 | if (ret) | |
480 | return ret; | |
481 | ||
482 | + ret = read_unwind_hints(file); | |
483 | + if (ret) | |
484 | + return ret; | |
485 | + | |
486 | return 0; | |
487 | } | |
488 | ||
489 | @@ -1382,7 +1473,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
490 | struct insn_state state) | |
491 | { | |
492 | struct alternative *alt; | |
493 | - struct instruction *insn; | |
494 | + struct instruction *insn, *next_insn; | |
495 | struct section *sec; | |
496 | struct symbol *func = NULL; | |
497 | int ret; | |
498 | @@ -1397,6 +1488,8 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
499 | } | |
500 | ||
501 | while (1) { | |
502 | + next_insn = next_insn_same_sec(file, insn); | |
503 | + | |
504 | if (file->c_file && insn->func) { | |
505 | if (func && func != insn->func) { | |
506 | WARN("%s() falls through to next function %s()", | |
507 | @@ -1414,13 +1507,54 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
508 | } | |
509 | ||
510 | if (insn->visited) { | |
511 | - if (!!insn_state_match(insn, &state)) | |
512 | + if (!insn->hint && !insn_state_match(insn, &state)) | |
513 | return 1; | |
514 | ||
515 | return 0; | |
516 | } | |
517 | ||
518 | - insn->state = state; | |
519 | + if (insn->hint) { | |
520 | + if (insn->restore) { | |
521 | + struct instruction *save_insn, *i; | |
522 | + | |
523 | + i = insn; | |
524 | + save_insn = NULL; | |
525 | + func_for_each_insn_continue_reverse(file, func, i) { | |
526 | + if (i->save) { | |
527 | + save_insn = i; | |
528 | + break; | |
529 | + } | |
530 | + } | |
531 | + | |
532 | + if (!save_insn) { | |
533 | + WARN_FUNC("no corresponding CFI save for CFI restore", | |
534 | + sec, insn->offset); | |
535 | + return 1; | |
536 | + } | |
537 | + | |
538 | + if (!save_insn->visited) { | |
539 | + /* | |
540 | + * Oops, no state to copy yet. | |
541 | + * Hopefully we can reach this | |
542 | + * instruction from another branch | |
543 | + * after the save insn has been | |
544 | + * visited. | |
545 | + */ | |
546 | + if (insn == first) | |
547 | + return 0; | |
548 | + | |
549 | + WARN_FUNC("objtool isn't smart enough to handle this CFI save/restore combo", | |
550 | + sec, insn->offset); | |
551 | + return 1; | |
552 | + } | |
553 | + | |
554 | + insn->state = save_insn->state; | |
555 | + } | |
556 | + | |
557 | + state = insn->state; | |
558 | + | |
559 | + } else | |
560 | + insn->state = state; | |
561 | ||
562 | insn->visited = true; | |
563 | ||
564 | @@ -1497,6 +1631,14 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
565 | ||
566 | return 0; | |
567 | ||
568 | + case INSN_CONTEXT_SWITCH: | |
569 | + if (func && (!next_insn || !next_insn->hint)) { | |
570 | + WARN_FUNC("unsupported instruction in callable function", | |
571 | + sec, insn->offset); | |
572 | + return 1; | |
573 | + } | |
574 | + return 0; | |
575 | + | |
576 | case INSN_STACK: | |
577 | if (update_insn_state(insn, &state)) | |
578 | return -1; | |
579 | @@ -1510,7 +1652,7 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
580 | if (insn->dead_end) | |
581 | return 0; | |
582 | ||
583 | - insn = next_insn_same_sec(file, insn); | |
584 | + insn = next_insn; | |
585 | if (!insn) { | |
586 | WARN("%s: unexpected end of section", sec->name); | |
587 | return 1; | |
588 | @@ -1520,6 +1662,27 @@ static int validate_branch(struct objtool_file *file, struct instruction *first, | |
589 | return 0; | |
590 | } | |
591 | ||
592 | +static int validate_unwind_hints(struct objtool_file *file) | |
593 | +{ | |
594 | + struct instruction *insn; | |
595 | + int ret, warnings = 0; | |
596 | + struct insn_state state; | |
597 | + | |
598 | + if (!file->hints) | |
599 | + return 0; | |
600 | + | |
601 | + clear_insn_state(&state); | |
602 | + | |
603 | + for_each_insn(file, insn) { | |
604 | + if (insn->hint && !insn->visited) { | |
605 | + ret = validate_branch(file, insn, state); | |
606 | + warnings += ret; | |
607 | + } | |
608 | + } | |
609 | + | |
610 | + return warnings; | |
611 | +} | |
612 | + | |
613 | static bool is_kasan_insn(struct instruction *insn) | |
614 | { | |
615 | return (insn->type == INSN_CALL && | |
616 | @@ -1665,8 +1828,9 @@ int check(const char *_objname, bool _nofp, bool orc) | |
617 | hash_init(file.insn_hash); | |
618 | file.whitelist = find_section_by_name(file.elf, ".discard.func_stack_frame_non_standard"); | |
619 | file.rodata = find_section_by_name(file.elf, ".rodata"); | |
620 | - file.ignore_unreachables = false; | |
621 | file.c_file = find_section_by_name(file.elf, ".comment"); | |
622 | + file.ignore_unreachables = false; | |
623 | + file.hints = false; | |
624 | ||
625 | arch_initial_func_cfi_state(&initial_func_cfi); | |
626 | ||
627 | @@ -1683,6 +1847,11 @@ int check(const char *_objname, bool _nofp, bool orc) | |
628 | goto out; | |
629 | warnings += ret; | |
630 | ||
631 | + ret = validate_unwind_hints(&file); | |
632 | + if (ret < 0) | |
633 | + goto out; | |
634 | + warnings += ret; | |
635 | + | |
636 | if (!warnings) { | |
637 | ret = validate_reachable_instructions(&file); | |
638 | if (ret < 0) | |
639 | -- | |
640 | 2.14.2 | |
641 |