# Re: \[RFC PATCH v2 14/24\] unwind\_user/eh\_frame: Add .eh\_frame\[\_hdr\] validation option

- From: Jens Remus \<hidden\>
- Date: 2026-09-14 14:32:28 
- List: linux-trace-kernel
- Page: https://ratatoskr.run/linux-trace-kernel/2026/09/17574002
- Thread: https://ratatoskr.run/sashiko-reviews/2026/09/17574002/t.md

```text
On 8/21/2026 10:10 PM, sashiko-bot@kernel.org 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)
jremus@de.ibm.com / jremus@linux.ibm.com

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/

```
