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/


Reply via email to