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

Reply via email to