Re: [RFC PATCH v1 17/25] unwind_user/eh_frame: Ignore DW_CFA_GNU_args_size

[email protected]
Newsgroups org.kernel.vger.linux-trace-kernel,org.kernel.vger.linux-s390
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

Pre-existing issues:
- [High] An integer overflow when evaluating `cur + expr_len` bypasses bounds checks, allowing an attacker to move the instruction pointer backward and trap the kernel in an infinite loop.
- [High] The CFI interpreter loop does not impose a maximum execution limit, allowing an attacker to stall the CPU by providing a maliciously crafted, highly padded sequence of CFI instructions.
--

--- Patch [17]: [RFC PATCH v1 17/25] unwind_user/eh_frame: Ignore DW_CFA_GNU_args_size ---
commit e1c2b0ab16134f39bfe2a1cb074739516466f777
Author: Jens Remus <[email protected]>

unwind_user/eh_frame: Ignore DW_CFA_GNU_args_size

This commit updates the unwind mechanism to safely ignore the
DW_CFA_GNU_args_size DWARF instruction. This instruction is used by GCC
to track exception handling stack sizes, but is not needed for kernel
stack tracing.

> diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
> index ac288cec8021f..64176242b7d8f 100644
> --- a/kernel/unwind/eh_frame.c
> +++ b/kernel/unwind/eh_frame.c
[ ... ]
> @@ -1012,6 +1012,17 @@ static __always_inline int __do_cfi_insn(struct eh_frame_section *sec,
>  			break;
>  		}
>  
> +		case DW_CFA_GNU_args_size: {
> +			unsigned long args_size;
> +
> +			ret = read_uleb128(&cur, end, &args_size);
> +			if (ret)
> +				return ret;
> +
> +			/* Ignore DW_CFA_GNU_args_size */
> +			break;
> +		}
> +
>  		case DW_CFA_remember_state:
>  			if (ctx->stack_depth >= EH_FRAME_MAX_STATE_STACK)
>  				return -EINVAL;

[Severity: High]
This is a pre-existing issue, but does this code allow an integer overflow
when evaluating cur + expr_len?

kernel/unwind/eh_frame.c:__do_cfi_insn() {
    ...
		case DW_CFA_val_expression: {
			unsigned long reg, expr_len;

			ret = read_uleb128(&cur, end, &reg);
			if (ret)
				return ret;
			ret = read_uleb128(&cur, end, &expr_len);
			if (ret)
				return ret;

			if (cur + expr_len > end)
				return -EINVAL;
    ...
}

If a maliciously crafted .eh_frame section provides an extremely large
expr_len value, cur + expr_len can wrap around to a value less than end.
This bypasses the bounds check, and when the instruction pointer cur is
updated via cur += expr_len, it moves backwards.

Since the unwinder operates on user-supplied call frame information, can
this trap the kernel in an uninterruptible infinite loop and cause a local
denial of service?


[Severity: High]
This is a pre-existing issue, but is it possible for this loop to run without
an execution limit?

kernel/unwind/eh_frame.c:__do_cfi_program() {
    ...
		cur = fde->cfi_insn_start;
		while (cur < fde->cfi_insn_end && ip <= target_ip) {
			ret = __do_cfi_insn(sec, fde, &cur, fde->cfi_insn_end, &ip, target_ip, ctx);
			if (ret)
				return ret;
		}
    ...
}

If a local attacker maps a maximally sized frame description entry filled
with DW_CFA_nop instructions, the kernel will sequentially process billions
of instructions without yielding or aborting.

Since this can run in non-preemptible or non-maskable interrupt contexts
during stack unwinding, can this stall the processor and trigger a
watchdog panic?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=17
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.