lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Tue, 14 Apr 2020 14:44:57 +0100
From:   Julien Thierry <jthierry@...hat.com>
To:     Alexandre Chartre <alexandre.chartre@...cle.com>, x86@...nel.org
Cc:     linux-kernel@...r.kernel.org, jpoimboe@...hat.com,
        peterz@...radead.org, tglx@...utronix.de
Subject: Re: [PATCH V3 4/9] objtool: Handle return instruction with
 intra-function call

Hi Alexandre,

On 4/14/20 11:36 AM, Alexandre Chartre wrote:
> Intra-function calls are implemented in objtool like unconditional
> jumps. Keep track of intra-functin calls return addresses so that

intra-function*

> objtool can make a return instruction continues the flow at the
> right location.
> 
> Signed-off-by: Alexandre Chartre <alexandre.chartre@...cle.com>
> ---
>   tools/objtool/arch/x86/decode.c |   7 +++
>   tools/objtool/check.c           | 104 ++++++++++++++++++++++++++++++--
>   tools/objtool/check.h           |   3 +
>   3 files changed, 110 insertions(+), 4 deletions(-)
> 
> diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
> index f4d70b8835c4..76b593bb2e4f 100644
> --- a/tools/objtool/arch/x86/decode.c
> +++ b/tools/objtool/arch/x86/decode.c
> @@ -427,6 +427,13 @@ int arch_decode_instruction(struct elf *elf, struct section *sec,
>   	case 0xc2:
>   	case 0xc3:
>   		*type = INSN_RETURN;
> +		/*
> +		 * For the impact on the stack, a ret behaves like
> +		 * a pop of the return address.
> +		 */
> +		op->src.type = OP_SRC_POP;
> +		op->dest.type = OP_DEST_REG;
> +		op->dest.reg = CFI_RA;
>   		break;
>   
>   	case 0xca: /* retf */
> diff --git a/tools/objtool/check.c b/tools/objtool/check.c
> index ad362c5de281..8b1df659cd68 100644
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -26,9 +26,50 @@ struct alternative {
>   	bool skip_orig;
>   };
>   
> +/*
> + * List to keep track of intra-function call return addresses.
> + * The list is a simple static array because we don't expect
> + * to have a lot of nested intra-function calls.
> + */
> +#define RADDR_COUNT_MAX		32
> +#define RADDR_ALTERED		((void *)-1)
> +
> +static struct instruction *raddr_list[RADDR_COUNT_MAX];
> +static int raddr_count;

Could this be part of the insn_state?

> +
>   const char *objname;
>   struct cfi_state initial_func_cfi;
>   
> +static void raddr_clear(void)
> +{
> +	raddr_count = 0;
> +}
> +
> +static bool raddr_push(struct instruction *insn)
> +{
> +	if (raddr_count == RADDR_COUNT_MAX) {
> +		WARN_FUNC("return address list is full",
> +			  insn->sec, insn->offset);
> +		return false;
> +	}
> +
> +	raddr_list[raddr_count++] = insn;
> +	return true;
> +}
> +
> +static bool raddr_pop(struct instruction **insn)
> +{
> +	if (raddr_count == 0)
> +		return false;
> +
> +	*insn = raddr_list[--raddr_count];
> +	return true;
> +}
> +
> +static int validate_branch(struct objtool_file *file, struct symbol *func,
> +			   struct instruction *from,
> +			   struct instruction *first, struct insn_state state);
> +
>   struct instruction *find_insn(struct objtool_file *file,
>   			      struct section *sec, unsigned long offset)
>   {
> @@ -2039,8 +2080,52 @@ static int validate_sibling_call(struct instruction *insn, struct insn_state *st
>   	return validate_call(insn, state);
>   }
>   
> -static int validate_return(struct symbol *func, struct instruction *insn, struct insn_state *state)
> +static int validate_return_address(struct objtool_file *file,
> +				   struct symbol *func,
> +				   struct instruction *insn,
> +				   struct insn_state *state)
>   {
> +	struct instruction *raddr_insn;
> +	int ret;
> +
> +	while (raddr_pop(&raddr_insn)) {

Why is this a loop? Either there is something on the return address 
stack, it gets validated as a branch and the function returns, or 
nothing is on the stack and the function returns.

There doesn't seem to be a point where the loop gets to a second iteration.

> +		/*
> +		 * We are branching somewhere and so processing
> +		 * a return instruction. So update the stack
> +		 * state for this instruction.
> +		 */
> +		update_insn_state(insn, state);
> +
> +		/*
> +		 * If the return address has no instruction then
> +		 * that's the end of the function.
> +		 */
> +		if (!raddr_insn)

I can't think of a valid case where raddr_pop() returns "true" for a 
NULL return address. If you check for that case, it should probably be a 
warning/error rather than silently returning (especially since you'd 
have updated the state).

> +			break;
> +
> +		/*
> +		 * If we are branching to a defined address then
> +		 * just do an unconditional jump there.
> +		 */
> +		ret = validate_branch(file, func, insn,
> +				      raddr_insn, *state);
> +		if (ret) {
> +			if (backtrace)
> +				BT_FUNC("(ret-branch)", insn);
> +			return ret;
> +		}
> +
> +		return 0;
> +	}
> +
> +	return -1;

Returning "-1" is a bit confusing. One might think this is reporting an 
error, but the only caller actually uses it as "I'll just carry on with 
the rest of my checks".

Maybe validate_return() could call validate_return_address() as follows:

	if (radd_pop(&raddr_insn))
		return validate_return_address(file, func, insn, state, raddr_insn);


> +}
> +
> +static int validate_return(struct objtool_file *file, struct symbol *func,
> +			   struct instruction *insn, struct insn_state *state)
> +{
> +	int ret;
> +
>   	if (state->uaccess && !func_uaccess_safe(func)) {
>   		WARN_FUNC("return with UACCESS enabled",
>   			  insn->sec, insn->offset);
> @@ -2059,6 +2144,11 @@ static int validate_return(struct symbol *func, struct instruction *insn, struct
>   		return 1;
>   	}
>   
> +	/* check if we have return address to branch to */
> +	ret = validate_return_address(file, func, insn, state);
> +	if (ret >= 0)
> +		return ret;
> +
>   	if (func && has_modified_stack_frame(state)) {
>   		WARN_FUNC("return with modified stack frame",
>   			  insn->sec, insn->offset);
> @@ -2200,7 +2290,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
>   		switch (insn->type) {
>   
>   		case INSN_RETURN:
> -			return validate_return(func, insn, &state);
> +			return validate_return(file, func, insn, &state);
>   
>   		case INSN_CALL:
>   		case INSN_CALL_DYNAMIC:
> @@ -2223,12 +2313,17 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
>   				/*
>   				 * The call instruction can update the stack
>   				 * state. Then make the intra-function call
> -				 * behaves like and unconditional jump.
> +				 * behaves like and unconditional jump. We
> +				 * track the return address to handle any
> +				 * return instruction.
>   				 */
>   				ret = update_insn_state(insn, &state);
>   				if (ret)
>   					return ret;
>   
> +				if (!raddr_push(next_insn))
> +					return 1;
> +
>   				ret = validate_branch(file, func, insn,
>   						      insn->jump_dest, state);
>   				if (ret) {
> @@ -2383,6 +2478,7 @@ static int validate_unwind_hints(struct objtool_file *file)
>   
>   	for_each_insn(file, insn) {
>   		if (insn->hint && !insn->visited) {
> +			raddr_clear();
>   			ret = validate_branch(file, insn->func,
>   					      NULL, insn, state);
>   			if (ret && backtrace)
> @@ -2522,7 +2618,7 @@ static int validate_section(struct objtool_file *file, struct section *sec)
>   			continue;
>   
>   		state.uaccess = func->uaccess_safe;
> -
> +		raddr_clear();
>   		ret = validate_branch(file, func, NULL, insn, state);
>   		if (ret && backtrace)
>   			BT_FUNC("<=== (func)", insn);
> diff --git a/tools/objtool/check.h b/tools/objtool/check.h
> index 6a80903fc4aa..f7dbecd46bed 100644
> --- a/tools/objtool/check.h
> +++ b/tools/objtool/check.h
> @@ -23,6 +23,7 @@ struct insn_state {
>   	unsigned int uaccess_stack;
>   	int drap_reg, drap_offset;
>   	struct cfi_reg vals[CFI_NUM_REGS];
> +	bool stack_altered;

This isn't used in this patch.

>   };
>   
>   struct instruction {
> @@ -39,6 +40,8 @@ struct instruction {
>   	bool intra_function_call;
>   	bool retpoline_safe;
>   	u8 visited;
> +	u8 raddr_delete;
> +	u8 raddr_alter;

These fields (and the RADDR_ALTERED) don't seem to be used elsewhere in 
this patch. I guess they should be part of patch 5.

>   	struct symbol *call_dest;
>   	struct instruction *jump_dest;
>   	struct instruction *first_jump_src;
> 

Cheers,

-- 
Julien Thierry

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ