提交 d7facac1 编写于 作者: P Peter Zijlstra 提交者: Zheng Zengkai

objtool: Handle __sanitize_cov*() tail calls

stable inclusion
from stable-v5.10.133
commit acc0be56b4152046aac56b48a70729925036b187
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I5PTAS
CVE: CVE-2022-29900,CVE-2022-23816,CVE-2022-29901

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=acc0be56b4152046aac56b48a70729925036b187

--------------------------------

commit f56dae88 upstream.

Turns out the compilers also generate tail calls to __sanitize_cov*(),
make sure to also patch those out in noinstr code.

Fixes: 0f1441b4 ("objtool: Fix noinstr vs KCOV")
Signed-off-by: NPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: NMarco Elver <elver@google.com>
Link: https://lore.kernel.org/r/20210624095147.818783799@infradead.orgSigned-off-by: NSasha Levin <sashal@kernel.org>
[bwh: Backported to 5.10:
 - objtool doesn't have any mcount handling
 - Write the NOPs as hex literals since we can't use <asm/nops.h>]
Signed-off-by: NBen Hutchings <ben@decadent.org.uk>
Signed-off-by: NGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: NLin Yujun <linyujun809@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 804d6f28
...@@ -83,6 +83,7 @@ unsigned long arch_jump_destination(struct instruction *insn); ...@@ -83,6 +83,7 @@ unsigned long arch_jump_destination(struct instruction *insn);
unsigned long arch_dest_reloc_offset(int addend); unsigned long arch_dest_reloc_offset(int addend);
const char *arch_nop_insn(int len); const char *arch_nop_insn(int len);
const char *arch_ret_insn(int len);
int arch_decode_hint_reg(u8 sp_reg, int *base); int arch_decode_hint_reg(u8 sp_reg, int *base);
......
...@@ -586,6 +586,26 @@ const char *arch_nop_insn(int len) ...@@ -586,6 +586,26 @@ const char *arch_nop_insn(int len)
return nops[len-1]; return nops[len-1];
} }
#define BYTE_RET 0xC3
const char *arch_ret_insn(int len)
{
static const char ret[5][5] = {
{ BYTE_RET },
{ BYTE_RET, 0x90 },
{ BYTE_RET, 0x66, 0x90 },
{ BYTE_RET, 0x0f, 0x1f, 0x00 },
{ BYTE_RET, 0x0f, 0x1f, 0x40, 0x00 },
};
if (len < 1 || len > 5) {
WARN("invalid RET size: %d\n", len);
return NULL;
}
return ret[len-1];
}
/* asm/alternative.h ? */ /* asm/alternative.h ? */
#define ALTINSTR_FLAG_INV (1 << 15) #define ALTINSTR_FLAG_INV (1 << 15)
......
...@@ -860,6 +860,60 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i ...@@ -860,6 +860,60 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i
return insn->reloc; return insn->reloc;
} }
static void remove_insn_ops(struct instruction *insn)
{
struct stack_op *op, *tmp;
list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
list_del(&op->list);
free(op);
}
}
static void add_call_dest(struct objtool_file *file, struct instruction *insn,
struct symbol *dest, bool sibling)
{
struct reloc *reloc = insn_reloc(file, insn);
insn->call_dest = dest;
if (!dest)
return;
if (insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
/*
* Many compilers cannot disable KCOV with a function attribute
* so they need a little help, NOP out any KCOV calls from noinstr
* text.
*/
if (insn->sec->noinstr &&
!strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
if (reloc) {
reloc->type = R_NONE;
elf_write_reloc(file->elf, reloc);
}
elf_write_insn(file->elf, insn->sec,
insn->offset, insn->len,
sibling ? arch_ret_insn(insn->len)
: arch_nop_insn(insn->len));
insn->type = sibling ? INSN_RETURN : INSN_NOP;
}
/*
* Whatever stack impact regular CALLs have, should be undone
* by the RETURN of the called function.
*
* Annotated intra-function calls retain the stack_ops but
* are converted to JUMP, see read_intra_function_calls().
*/
remove_insn_ops(insn);
}
/* /*
* Find the destination instructions for all jumps. * Find the destination instructions for all jumps.
*/ */
...@@ -898,11 +952,7 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -898,11 +952,7 @@ static int add_jump_destinations(struct objtool_file *file)
continue; continue;
} else if (insn->func) { } else if (insn->func) {
/* internal or external sibling call (with reloc) */ /* internal or external sibling call (with reloc) */
insn->call_dest = reloc->sym; add_call_dest(file, insn, reloc->sym, true);
if (insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
continue; continue;
} else if (reloc->sym->sec->idx) { } else if (reloc->sym->sec->idx) {
dest_sec = reloc->sym->sec; dest_sec = reloc->sym->sec;
...@@ -958,13 +1008,8 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -958,13 +1008,8 @@ static int add_jump_destinations(struct objtool_file *file)
} else if (insn->jump_dest->func->pfunc != insn->func->pfunc && } else if (insn->jump_dest->func->pfunc != insn->func->pfunc &&
insn->jump_dest->offset == insn->jump_dest->func->offset) { insn->jump_dest->offset == insn->jump_dest->func->offset) {
/* internal sibling call (without reloc) */ /* internal sibling call (without reloc) */
insn->call_dest = insn->jump_dest->func; add_call_dest(file, insn, insn->jump_dest->func, true);
if (insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
} }
} }
} }
...@@ -972,16 +1017,6 @@ static int add_jump_destinations(struct objtool_file *file) ...@@ -972,16 +1017,6 @@ static int add_jump_destinations(struct objtool_file *file)
return 0; return 0;
} }
static void remove_insn_ops(struct instruction *insn)
{
struct stack_op *op, *tmp;
list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) {
list_del(&op->list);
free(op);
}
}
static struct symbol *find_call_destination(struct section *sec, unsigned long offset) static struct symbol *find_call_destination(struct section *sec, unsigned long offset)
{ {
struct symbol *call_dest; struct symbol *call_dest;
...@@ -1000,6 +1035,7 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1000,6 +1035,7 @@ static int add_call_destinations(struct objtool_file *file)
{ {
struct instruction *insn; struct instruction *insn;
unsigned long dest_off; unsigned long dest_off;
struct symbol *dest;
struct reloc *reloc; struct reloc *reloc;
for_each_insn(file, insn) { for_each_insn(file, insn) {
...@@ -1009,7 +1045,9 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1009,7 +1045,9 @@ static int add_call_destinations(struct objtool_file *file)
reloc = insn_reloc(file, insn); reloc = insn_reloc(file, insn);
if (!reloc) { if (!reloc) {
dest_off = arch_jump_destination(insn); dest_off = arch_jump_destination(insn);
insn->call_dest = find_call_destination(insn->sec, dest_off); dest = find_call_destination(insn->sec, dest_off);
add_call_dest(file, insn, dest, false);
if (insn->ignore) if (insn->ignore)
continue; continue;
...@@ -1027,9 +1065,8 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1027,9 +1065,8 @@ static int add_call_destinations(struct objtool_file *file)
} else if (reloc->sym->type == STT_SECTION) { } else if (reloc->sym->type == STT_SECTION) {
dest_off = arch_dest_reloc_offset(reloc->addend); dest_off = arch_dest_reloc_offset(reloc->addend);
insn->call_dest = find_call_destination(reloc->sym->sec, dest = find_call_destination(reloc->sym->sec, dest_off);
dest_off); if (!dest) {
if (!insn->call_dest) {
WARN_FUNC("can't find call dest symbol at %s+0x%lx", WARN_FUNC("can't find call dest symbol at %s+0x%lx",
insn->sec, insn->offset, insn->sec, insn->offset,
reloc->sym->sec->name, reloc->sym->sec->name,
...@@ -1037,6 +1074,8 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1037,6 +1074,8 @@ static int add_call_destinations(struct objtool_file *file)
return -1; return -1;
} }
add_call_dest(file, insn, dest, false);
} else if (arch_is_retpoline(reloc->sym)) { } else if (arch_is_retpoline(reloc->sym)) {
/* /*
* Retpoline calls are really dynamic calls in * Retpoline calls are really dynamic calls in
...@@ -1052,39 +1091,7 @@ static int add_call_destinations(struct objtool_file *file) ...@@ -1052,39 +1091,7 @@ static int add_call_destinations(struct objtool_file *file)
continue; continue;
} else } else
insn->call_dest = reloc->sym; add_call_dest(file, insn, reloc->sym, false);
if (insn->call_dest && insn->call_dest->static_call_tramp) {
list_add_tail(&insn->call_node,
&file->static_call_list);
}
/*
* Many compilers cannot disable KCOV with a function attribute
* so they need a little help, NOP out any KCOV calls from noinstr
* text.
*/
if (insn->sec->noinstr &&
!strncmp(insn->call_dest->name, "__sanitizer_cov_", 16)) {
if (reloc) {
reloc->type = R_NONE;
elf_write_reloc(file->elf, reloc);
}
elf_write_insn(file->elf, insn->sec,
insn->offset, insn->len,
arch_nop_insn(insn->len));
insn->type = INSN_NOP;
}
/*
* Whatever stack impact regular CALLs have, should be undone
* by the RETURN of the called function.
*
* Annotated intra-function calls retain the stack_ops but
* are converted to JUMP, see read_intra_function_calls().
*/
remove_insn_ops(insn);
} }
return 0; return 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册