On 8/21/2026 10:10 PM, [email protected] wrote:
> Add a debug feature to validate all .eh_frame[_hdr] sections when first
> loading the file rather than on demand.
>> diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
>> @@ -1216,6 +1216,92 @@ int eh_frame_find(unsigned long ip, struct
>> unwind_user_frame *frame)
>> return ret;
>> }
>>
>> +#ifdef CONFIG_EH_FRAME_VALIDATION
>> +
>> +static int eh_frame_validate_section(struct eh_frame_section *sec)
>> +{
> [ ... ]
>> + table_start_ptr = (void __user *)sec->binary_search_table_start;
>> + table_size = sec->binary_search_table_end -
>> sec->binary_search_table_start;
>> +
>> + for (i = 0; i < sec->fde_count; i++) {
>
> [Severity: High]
> Could this unbounded loop cause CPU stalls or trigger soft lockup warnings?
>
> Since fde_count is controlled by the size of the mapped .eh_frame_hdr VMA,
> it can be extremely large for maliciously crafted ELF files, potentially
> stalling the CPU for seconds or minutes.
>
> Because mmap_read_lock is released before this is called, would it be safer
> to add a cond_resched() in this loop?
According to Steven this is not required.
>
>> + struct eh_frame_fde fde;
>> + unsigned long cur;
>> + unsigned long func_addr, fde_addr;
>> + int ret;
>> +
>> + cur = sec->binary_search_table_start + i * entry_size;
>> +
>> + scoped_user_read_access_size(table_start_ptr, table_size,
>> Efault) {
>> + /* Read function start address from table */
>> + ret = read_encoded_pointer(sec, &cur,
>> + sec->binary_search_table_end,
>> + table_enc, &func_addr);
>
> [Severity: High]
> Does this bypass the Spectre v1 mitigations provided by the scoped access
> macro?
>
> The scoped_user_read_access_size() macro shadows the table_start_ptr variable
> to enforce usage of a masked pointer. However, the code ignores that masked
> pointer and instead uses the unmasked cur pointer derived outside the scope:
>
> cur = sec->binary_search_table_start + i * entry_size;
>
> When read_encoded_pointer() uses this unmasked pointer inside
> unsafe_get_user(), the masking mitigations are bypassed, allowing potential
> arbitrary speculative reads.
True. Will resolve as part of resolving the next finding.
>
>> + if (ret) {
>> + dbg_sec_ehfh(cur, "table[%lu]: failed to read
>> function start address\n", i);
>
> [Severity: High]
> Is it safe to call printk wrappers like dbg_sec_ehfh() or dbg_sec() while user
> access (UACCESS) is enabled?
>
> Calling complex functions like printk inside a scoped_user_read_access_size()
> block can lead to deadlocks or kernel panics. It does internal locking, can
> sleep, and may clobber architecture-specific user access state like the SMAP
> AC flag.
Will resolve by introducing and using a safe_read_encoded_pointer()
variant, similar to the safe_read_{fde|fre}() in the .sframe series.
This also gets rid of the scoped user access region and thus resolves
the previous finding as well.
>
>> + return ret;
>> + }
>> + if (i && func_addr <= prev_func_addr) {
>> + dbg_sec(".eh_frame_hdr: table[%lu]: not
>> sorted\n", i);
>> + return -EINVAL;
>> + }
>
Regards,
Jens
--
Jens Remus
Linux on Z Development (D3303)
[email protected] / [email protected]
IBM Deutschland Research & Development GmbH; Vorsitzender des Aufsichtsrats:
Wolfgang Wendt; Geschäftsführung: David Faller; Sitz der Gesellschaft:
Ehningen; Registergericht: Amtsgericht Stuttgart, HRB 243294
IBM Data Privacy Statement: https://www.ibm.com/privacy/