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

objtool: Detect non-relocated text references

When kernel IBT is enabled, objtool detects all text references in order
to determine which functions can be indirectly branched to.

In text, such references look like one of the following:

mov $0x0,%rax R_X86_64_32S .init.text+0x7e0a0
lea 0x0(%rip),%rax R_X86_64_PC32 autoremove_wake_function-0x4

Either way the function pointer is denoted by a relocation, so objtool
just reads that.

However there are some "lea xxx(%rip)" cases which don't use relocations
because they're referencing code in the same translation unit. Objtool
doesn't have visibility to those.

The only currently known instances of that are a few hand-coded asm text
references which don't actually need ENDBR. So it's not actually a
problem at the moment.

However if we enable -fpie, the compiler would start generating them and
there would definitely be bugs in the IBT sealing.

Detect non-relocated text references and handle them appropriately.

[ Note: I removed the manual static_call_tramp check -- that should
already be handled by the noendbr check. ]

Reported-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Josh Poimboeuf <jpoimboe@kernel.org>

+77 -53
+1
arch/x86/kernel/acpi/wakeup_64.S
··· 87 87 88 88 .align 4 89 89 .Lresume_point: 90 + ANNOTATE_NOENDBR 90 91 /* We don't restore %rax, it must be 0 anyway */ 91 92 movq $saved_context, %rax 92 93 movq saved_context_cr4(%rax), %rbx
+1
arch/x86/kernel/head_64.S
··· 77 77 lretq 78 78 79 79 .Lon_kernel_cs: 80 + ANNOTATE_NOENDBR 80 81 UNWIND_HINT_END_OF_STACK 81 82 82 83 #ifdef CONFIG_AMD_MEM_ENCRYPT
+10 -5
tools/objtool/arch/x86/decode.c
··· 456 456 if (!rex_w) 457 457 break; 458 458 459 - /* skip RIP relative displacement */ 460 - if (is_RIP()) 461 - break; 462 - 463 459 /* skip nontrivial SIB */ 464 460 if (have_SIB()) { 465 461 modrm_rm = sib_base; 466 462 if (sib_index != CFI_SP) 467 463 break; 464 + } 465 + 466 + /* lea disp(%rip), %dst */ 467 + if (is_RIP()) { 468 + insn->type = INSN_LEA_RIP; 469 + break; 468 470 } 469 471 470 472 /* lea disp(%src), %dst */ ··· 739 737 break; 740 738 } 741 739 742 - insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0; 740 + if (ins.immediate.nbytes) 741 + insn->immediate = ins.immediate.value; 742 + else if (ins.displacement.nbytes) 743 + insn->immediate = ins.displacement.value; 743 744 744 745 return 0; 745 746 }
+64 -48
tools/objtool/check.c
··· 4392 4392 return insn->offset == sym->offset + sym->len; 4393 4393 } 4394 4394 4395 + static int __validate_ibt_insn(struct objtool_file *file, struct instruction *insn, 4396 + struct instruction *dest) 4397 + { 4398 + if (dest->type == INSN_ENDBR) { 4399 + mark_endbr_used(dest); 4400 + return 0; 4401 + } 4402 + 4403 + if (insn_func(dest) && insn_func(insn) && 4404 + insn_func(dest)->pfunc == insn_func(insn)->pfunc) { 4405 + /* 4406 + * Anything from->to self is either _THIS_IP_ or 4407 + * IRET-to-self. 4408 + * 4409 + * There is no sane way to annotate _THIS_IP_ since the 4410 + * compiler treats the relocation as a constant and is 4411 + * happy to fold in offsets, skewing any annotation we 4412 + * do, leading to vast amounts of false-positives. 4413 + * 4414 + * There's also compiler generated _THIS_IP_ through 4415 + * KCOV and such which we have no hope of annotating. 4416 + * 4417 + * As such, blanket accept self-references without 4418 + * issue. 4419 + */ 4420 + return 0; 4421 + } 4422 + 4423 + /* 4424 + * Accept anything ANNOTATE_NOENDBR. 4425 + */ 4426 + if (dest->noendbr) 4427 + return 0; 4428 + 4429 + /* 4430 + * Accept if this is the instruction after a symbol 4431 + * that is (no)endbr -- typical code-range usage. 4432 + */ 4433 + if (noendbr_range(file, dest)) 4434 + return 0; 4435 + 4436 + WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset)); 4437 + return 1; 4438 + } 4439 + 4395 4440 static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn) 4396 4441 { 4397 4442 struct instruction *dest; ··· 4449 4404 * direct/indirect branches: 4450 4405 */ 4451 4406 switch (insn->type) { 4407 + 4452 4408 case INSN_CALL: 4453 4409 case INSN_CALL_DYNAMIC: 4454 4410 case INSN_JUMP_CONDITIONAL: ··· 4459 4413 case INSN_RETURN: 4460 4414 case INSN_NOP: 4461 4415 return 0; 4416 + 4417 + case INSN_LEA_RIP: 4418 + if (!insn_reloc(file, insn)) { 4419 + /* local function pointer reference without reloc */ 4420 + 4421 + off = arch_jump_destination(insn); 4422 + 4423 + dest = find_insn(file, insn->sec, off); 4424 + if (!dest) { 4425 + WARN_INSN(insn, "corrupt function pointer reference"); 4426 + return 1; 4427 + } 4428 + 4429 + return __validate_ibt_insn(file, insn, dest); 4430 + } 4431 + break; 4432 + 4462 4433 default: 4463 4434 break; 4464 4435 } ··· 4485 4422 reloc = find_reloc_by_dest_range(file->elf, insn->sec, 4486 4423 reloc_offset(reloc) + 1, 4487 4424 (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) { 4488 - 4489 - /* 4490 - * static_call_update() references the trampoline, which 4491 - * doesn't have (or need) ENDBR. Skip warning in that case. 4492 - */ 4493 - if (reloc->sym->static_call_tramp) 4494 - continue; 4495 4425 4496 4426 off = reloc->sym->offset; 4497 4427 if (reloc_type(reloc) == R_X86_64_PC32 || ··· 4497 4441 if (!dest) 4498 4442 continue; 4499 4443 4500 - if (dest->type == INSN_ENDBR) { 4501 - mark_endbr_used(dest); 4502 - continue; 4503 - } 4504 - 4505 - if (insn_func(dest) && insn_func(insn) && 4506 - insn_func(dest)->pfunc == insn_func(insn)->pfunc) { 4507 - /* 4508 - * Anything from->to self is either _THIS_IP_ or 4509 - * IRET-to-self. 4510 - * 4511 - * There is no sane way to annotate _THIS_IP_ since the 4512 - * compiler treats the relocation as a constant and is 4513 - * happy to fold in offsets, skewing any annotation we 4514 - * do, leading to vast amounts of false-positives. 4515 - * 4516 - * There's also compiler generated _THIS_IP_ through 4517 - * KCOV and such which we have no hope of annotating. 4518 - * 4519 - * As such, blanket accept self-references without 4520 - * issue. 4521 - */ 4522 - continue; 4523 - } 4524 - 4525 - /* 4526 - * Accept anything ANNOTATE_NOENDBR. 4527 - */ 4528 - if (dest->noendbr) 4529 - continue; 4530 - 4531 - /* 4532 - * Accept if this is the instruction after a symbol 4533 - * that is (no)endbr -- typical code-range usage. 4534 - */ 4535 - if (noendbr_range(file, dest)) 4536 - continue; 4537 - 4538 - WARN_INSN(insn, "relocation to !ENDBR: %s", offstr(dest->sec, dest->offset)); 4539 - 4540 - warnings++; 4444 + warnings += __validate_ibt_insn(file, insn, dest); 4541 4445 } 4542 4446 4543 4447 return warnings;
+1
tools/objtool/include/objtool/arch.h
··· 28 28 INSN_CLD, 29 29 INSN_TRAP, 30 30 INSN_ENDBR, 31 + INSN_LEA_RIP, 31 32 INSN_OTHER, 32 33 }; 33 34