提交 6cea94bb 编写于 作者: C Chen Zhongjin 提交者: openeuler-sync-bot

x86/unwind: Fix orc entry for paravirt {save,restore}_fl

hulk inclusion
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I6DK3O
CVE: NA

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

When CONFIG_PARAVIRT_XXL is enabled, the code of {save,restore}_fl
is defined as:

ff 14 25 00 00 00 00    callq  *0x0

which will be patched to call the xen paravirt function, or native
implementation, in 'paravirt_patch_64.c':

pushfq; popq %rax // for native_save_fl
pushq %rdi; popfq // for native_restore_fl

The orc metadata is generated with insn 'callq', so it can become
inconsistent with the real insn 'push;pop'.
This makes stacktrace on the 'pop' insn fail and incorrect stacktrace
result can be returned.

To prevent reliable stacktrace broken, check the insns when unwind
pt_regs stack frame:

When there are 'push;pop' combination and both insns don't change orc
entry, it means the stack state is inconsistent with orc on pop.
Add one slot to sp_offset for on original orc entry to get the correct
orc entry.
Signed-off-by: NChen Zhongjin <chenzhongjin@huawei.com>
Reviewed-by: NXu Kuohai <xukuohai@huawei.com>
(cherry picked from commit cd5fe777)
上级 36771615
...@@ -146,6 +146,34 @@ static struct orc_entry orc_fp_entry = { ...@@ -146,6 +146,34 @@ static struct orc_entry orc_fp_entry = {
.end = 0, .end = 0,
}; };
#ifdef CONFIG_PARAVIRT_XXL
static bool check_paravirt(struct unwind_state *state, struct orc_entry *orc)
{
u8 *ip = (u8 *)state->ip;
/*
* In paravirt_patch.c, patched paravirt opcode should be:
* pushfq; popq %rax // 0x9c 0x58
* pushq %rdi; popfq // 0x57 0x9d
*
* Error unwinding only happens when:
* 1. In irq or preempt context.
* 2. Current insn is popq, and it doesn't change orc.
* 3. Last insn doesn't change orc, checking it first to
* promise ip - 1 is valid.
* 4. Last byte fits pushf.
*/
if (state->regs && orc->type == UNWIND_HINT_TYPE_CALL &&
(ip[0] == 0x58 || ip[0] == 0x9d) &&
orc == orc_find((unsigned long)(ip + 1)) &&
orc == orc_find((unsigned long)(ip - 1)) &&
(ip[-1] == 0x9c || ip[-1] == 0x57))
return true;
return false;
}
#endif
static struct orc_entry *orc_find(unsigned long ip) static struct orc_entry *orc_find(unsigned long ip)
{ {
static struct orc_entry *orc; static struct orc_entry *orc;
...@@ -425,6 +453,9 @@ bool unwind_next_frame(struct unwind_state *state) ...@@ -425,6 +453,9 @@ bool unwind_next_frame(struct unwind_state *state)
enum stack_type prev_type = state->stack_info.type; enum stack_type prev_type = state->stack_info.type;
struct orc_entry *orc; struct orc_entry *orc;
bool indirect = false; bool indirect = false;
#ifdef CONFIG_PARAVIRT_XXL
struct orc_entry para_orc;
#endif
if (unwind_done(state)) if (unwind_done(state))
return false; return false;
...@@ -457,6 +488,18 @@ bool unwind_next_frame(struct unwind_state *state) ...@@ -457,6 +488,18 @@ bool unwind_next_frame(struct unwind_state *state)
state->error = true; state->error = true;
} }
#ifdef CONFIG_PARAVIRT_XXL
/*
* When hitting paravirt POP insn, the orc entry should add
* one slot for PUSH insn.
*/
if (!state->error && check_paravirt(state, orc)) {
para_orc = *orc;
para_orc.sp_offset += sizeof(long);
orc = &para_orc;
}
#endif
/* End-of-stack check for kernel threads: */ /* End-of-stack check for kernel threads: */
if (orc->sp_reg == ORC_REG_UNDEFINED) { if (orc->sp_reg == ORC_REG_UNDEFINED) {
if (!orc->end) if (!orc->end)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册