Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/unwind/orc: Detect the end of the stack

The existing UNWIND_HINT_EMPTY annotations happen to be good indicators
of where entry code calls into C code for the first time. So also use
them to mark the end of the stack for the ORC unwinder.

Use that information to set unwind->error if the ORC unwinder doesn't
unwind all the way to the end. This will be needed for enabling
HAVE_RELIABLE_STACKTRACE for the ORC unwinder so we can use it with the
livepatch consistency model.

Thanks to Jiri Slaby for teaching the ORCs about the unwind hints.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/lkml/20180518064713.26440-5-jslaby@suse.cz
Signed-off-by: Ingo Molnar <mingo@kernel.org>

authored by

Josh Poimboeuf and committed by
Ingo Molnar
d31a5802 0c414367

+52 -29
+1
arch/x86/entry/entry_64.S
··· 408 408 409 409 1: 410 410 /* kernel thread */ 411 + UNWIND_HINT_EMPTY 411 412 movq %r12, %rdi 412 413 CALL_NOSPEC %rbx 413 414 /*
+2
arch/x86/include/asm/orc_types.h
··· 88 88 unsigned sp_reg:4; 89 89 unsigned bp_reg:4; 90 90 unsigned type:2; 91 + unsigned end:1; 91 92 } __packed; 92 93 93 94 /* ··· 102 101 s16 sp_offset; 103 102 u8 sp_reg; 104 103 u8 type; 104 + u8 end; 105 105 }; 106 106 #endif /* __ASSEMBLY__ */ 107 107
+10 -6
arch/x86/include/asm/unwind_hints.h
··· 26 26 * the debuginfo as necessary. It will also warn if it sees any 27 27 * inconsistencies. 28 28 */ 29 - .macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL 29 + .macro UNWIND_HINT sp_reg=ORC_REG_SP sp_offset=0 type=ORC_TYPE_CALL end=0 30 30 #ifdef CONFIG_STACK_VALIDATION 31 31 .Lunwind_hint_ip_\@: 32 32 .pushsection .discard.unwind_hints ··· 35 35 .short \sp_offset 36 36 .byte \sp_reg 37 37 .byte \type 38 + .byte \end 39 + .balign 4 38 40 .popsection 39 41 #endif 40 42 .endm 41 43 42 44 .macro UNWIND_HINT_EMPTY 43 - UNWIND_HINT sp_reg=ORC_REG_UNDEFINED 45 + UNWIND_HINT sp_reg=ORC_REG_UNDEFINED end=1 44 46 .endm 45 47 46 48 .macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 iret=0 ··· 88 86 89 87 #else /* !__ASSEMBLY__ */ 90 88 91 - #define UNWIND_HINT(sp_reg, sp_offset, type) \ 89 + #define UNWIND_HINT(sp_reg, sp_offset, type, end) \ 92 90 "987: \n\t" \ 93 91 ".pushsection .discard.unwind_hints\n\t" \ 94 92 /* struct unwind_hint */ \ 95 93 ".long 987b - .\n\t" \ 96 - ".short " __stringify(sp_offset) "\n\t" \ 94 + ".short " __stringify(sp_offset) "\n\t" \ 97 95 ".byte " __stringify(sp_reg) "\n\t" \ 98 96 ".byte " __stringify(type) "\n\t" \ 97 + ".byte " __stringify(end) "\n\t" \ 98 + ".balign 4 \n\t" \ 99 99 ".popsection\n\t" 100 100 101 - #define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE) 101 + #define UNWIND_HINT_SAVE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_SAVE, 0) 102 102 103 - #define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE) 103 + #define UNWIND_HINT_RESTORE UNWIND_HINT(0, 0, UNWIND_HINT_TYPE_RESTORE, 0) 104 104 105 105 #endif /* __ASSEMBLY__ */ 106 106
+31 -21
arch/x86/kernel/unwind_orc.c
··· 198 198 * whitelisted .o files which didn't get objtool generation. 199 199 */ 200 200 orc_a = cur_orc_table + (a - cur_orc_ip_table); 201 - return orc_a->sp_reg == ORC_REG_UNDEFINED ? -1 : 1; 201 + return orc_a->sp_reg == ORC_REG_UNDEFINED && !orc_a->end ? -1 : 1; 202 202 } 203 203 204 204 #ifdef CONFIG_MODULES ··· 352 352 353 353 bool unwind_next_frame(struct unwind_state *state) 354 354 { 355 - unsigned long ip_p, sp, orig_ip, prev_sp = state->sp; 355 + unsigned long ip_p, sp, orig_ip = state->ip, prev_sp = state->sp; 356 356 enum stack_type prev_type = state->stack_info.type; 357 357 struct orc_entry *orc; 358 358 bool indirect = false; ··· 363 363 /* Don't let modules unload while we're reading their ORC data. */ 364 364 preempt_disable(); 365 365 366 - /* Have we reached the end? */ 366 + /* End-of-stack check for user tasks: */ 367 367 if (state->regs && user_mode(state->regs)) 368 - goto done; 368 + goto the_end; 369 369 370 370 /* 371 371 * Find the orc_entry associated with the text address. ··· 374 374 * calls and calls to noreturn functions. 375 375 */ 376 376 orc = orc_find(state->signal ? state->ip : state->ip - 1); 377 - if (!orc || orc->sp_reg == ORC_REG_UNDEFINED) 378 - goto done; 379 - orig_ip = state->ip; 377 + if (!orc) 378 + goto err; 379 + 380 + /* End-of-stack check for kernel threads: */ 381 + if (orc->sp_reg == ORC_REG_UNDEFINED) { 382 + if (!orc->end) 383 + goto err; 384 + 385 + goto the_end; 386 + } 380 387 381 388 /* Find the previous frame's stack: */ 382 389 switch (orc->sp_reg) { ··· 409 402 if (!state->regs || !state->full_regs) { 410 403 orc_warn("missing regs for base reg R10 at ip %pB\n", 411 404 (void *)state->ip); 412 - goto done; 405 + goto err; 413 406 } 414 407 sp = state->regs->r10; 415 408 break; ··· 418 411 if (!state->regs || !state->full_regs) { 419 412 orc_warn("missing regs for base reg R13 at ip %pB\n", 420 413 (void *)state->ip); 421 - goto done; 414 + goto err; 422 415 } 423 416 sp = state->regs->r13; 424 417 break; ··· 427 420 if (!state->regs || !state->full_regs) { 428 421 orc_warn("missing regs for base reg DI at ip %pB\n", 429 422 (void *)state->ip); 430 - goto done; 423 + goto err; 431 424 } 432 425 sp = state->regs->di; 433 426 break; ··· 436 429 if (!state->regs || !state->full_regs) { 437 430 orc_warn("missing regs for base reg DX at ip %pB\n", 438 431 (void *)state->ip); 439 - goto done; 432 + goto err; 440 433 } 441 434 sp = state->regs->dx; 442 435 break; ··· 444 437 default: 445 438 orc_warn("unknown SP base reg %d for ip %pB\n", 446 439 orc->sp_reg, (void *)state->ip); 447 - goto done; 440 + goto err; 448 441 } 449 442 450 443 if (indirect) { 451 444 if (!deref_stack_reg(state, sp, &sp)) 452 - goto done; 445 + goto err; 453 446 } 454 447 455 448 /* Find IP, SP and possibly regs: */ ··· 458 451 ip_p = sp - sizeof(long); 459 452 460 453 if (!deref_stack_reg(state, ip_p, &state->ip)) 461 - goto done; 454 + goto err; 462 455 463 456 state->ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, 464 457 state->ip, (void *)ip_p); ··· 472 465 if (!deref_stack_regs(state, sp, &state->ip, &state->sp)) { 473 466 orc_warn("can't dereference registers at %p for ip %pB\n", 474 467 (void *)sp, (void *)orig_ip); 475 - goto done; 468 + goto err; 476 469 } 477 470 478 471 state->regs = (struct pt_regs *)sp; ··· 484 477 if (!deref_stack_iret_regs(state, sp, &state->ip, &state->sp)) { 485 478 orc_warn("can't dereference iret registers at %p for ip %pB\n", 486 479 (void *)sp, (void *)orig_ip); 487 - goto done; 480 + goto err; 488 481 } 489 482 490 483 state->regs = (void *)sp - IRET_FRAME_OFFSET; ··· 507 500 508 501 case ORC_REG_PREV_SP: 509 502 if (!deref_stack_reg(state, sp + orc->bp_offset, &state->bp)) 510 - goto done; 503 + goto err; 511 504 break; 512 505 513 506 case ORC_REG_BP: 514 507 if (!deref_stack_reg(state, state->bp + orc->bp_offset, &state->bp)) 515 - goto done; 508 + goto err; 516 509 break; 517 510 518 511 default: 519 512 orc_warn("unknown BP base reg %d for ip %pB\n", 520 513 orc->bp_reg, (void *)orig_ip); 521 - goto done; 514 + goto err; 522 515 } 523 516 524 517 /* Prevent a recursive loop due to bad ORC data: */ ··· 527 520 state->sp <= prev_sp) { 528 521 orc_warn("stack going in the wrong direction? ip=%pB\n", 529 522 (void *)orig_ip); 530 - goto done; 523 + goto err; 531 524 } 532 525 533 526 preempt_enable(); 534 527 return true; 535 528 536 - done: 529 + err: 530 + state->error = true; 531 + 532 + the_end: 537 533 preempt_enable(); 538 534 state->stack_info.type = STACK_TYPE_UNKNOWN; 539 535 return false;
+2
tools/objtool/arch/x86/include/asm/orc_types.h
··· 88 88 unsigned sp_reg:4; 89 89 unsigned bp_reg:4; 90 90 unsigned type:2; 91 + unsigned end:1; 91 92 } __packed; 92 93 93 94 /* ··· 102 101 s16 sp_offset; 103 102 u8 sp_reg; 104 103 u8 type; 104 + u8 end; 105 105 }; 106 106 #endif /* __ASSEMBLY__ */ 107 107
+1
tools/objtool/check.c
··· 1156 1156 1157 1157 cfa->offset = hint->sp_offset; 1158 1158 insn->state.type = hint->type; 1159 + insn->state.end = hint->end; 1159 1160 } 1160 1161 1161 1162 return 0;
+1 -1
tools/objtool/check.h
··· 31 31 int stack_size; 32 32 unsigned char type; 33 33 bool bp_scratch; 34 - bool drap; 34 + bool drap, end; 35 35 int drap_reg, drap_offset; 36 36 struct cfi_reg vals[CFI_NUM_REGS]; 37 37 };
+2 -1
tools/objtool/orc_dump.c
··· 203 203 204 204 print_reg(orc[i].bp_reg, orc[i].bp_offset); 205 205 206 - printf(" type:%s\n", orc_type_name(orc[i].type)); 206 + printf(" type:%s end:%d\n", 207 + orc_type_name(orc[i].type), orc[i].end); 207 208 } 208 209 209 210 elf_end(elf);
+2
tools/objtool/orc_gen.c
··· 31 31 struct cfi_reg *cfa = &insn->state.cfa; 32 32 struct cfi_reg *bp = &insn->state.regs[CFI_BP]; 33 33 34 + orc->end = insn->state.end; 35 + 34 36 if (cfa->base == CFI_UNDEFINED) { 35 37 orc->sp_reg = ORC_REG_UNDEFINED; 36 38 continue;