On 8/21/2026 10:18 PM, [email protected] wrote:
> This commit adds logic to unregister .eh_frame or .eh_frame_hdr sections on
> demand when corruption is detected during lookup. This prevents repeated
> attempts to parse bad unwind sections by removing them immediately upon an
> error return.
>> diff --git a/kernel/unwind/eh_frame.c b/kernel/unwind/eh_frame.c
>> @@ -1202,6 +1202,15 @@ int eh_frame_find(unsigned long ip, struct
>> unwind_user_frame *frame)
>> if (!ret)
>> ret = __find_frame_row(sec, &fde, ip, frame);
>>
>> + /*
>> + * Unregister .eh_frame[_hdr] in case of an error,
>> + * e.g. EINVAL (corrupted) or EFAULT (inaccessible).
>> + * Keep if ENOENT (not found) or EOPNOTSUPP (unsupported CFI).
>> + */
>> + if (ret && (ret != -ENOENT && ret != -EOPNOTSUPP))
>> + if (eh_frame_remove_section(sec->eh_frame_hdr_start))
>> + dbg("eh_frame_remove_section() failed\n");
>> +
>
> [Severity: High]
> Does this introduce a race condition with memory unmapping and mapping?
>
> If a thread is delayed after retrieving sec in eh_frame_find(), another
> thread could unmap the library and map a new library at the exact same
> virtual address, registering it with the same eh_frame_hdr_start. When the
> first thread resumes and fails to parse against the new memory, passing the
> virtual address sec->eh_frame_hdr_start to eh_frame_remove_section() seems
> like it would unintentionally remove the newly loaded, valid library's
> section.
I don't think that is possible. eh_frame_find() and likewise
eh_frame_remove_section() both guard using eh_frame_srcu. So while one
thread has a reference to a section that cannot be removed and replaced.
I am missing something?
>
> [Severity: High]
> Is it safe to assume -EINVAL always means corruption?
>
> Looking at __do_cfi_program(), it also returns -EINVAL when kernel limits
> are hit, such as exceeding EH_FRAME_CFI_INSN_LIMIT or
> EH_FRAME_MAX_CODE_ALIGN.
>
> If a valid library simply has a complex function that hits these limits,
> wouldn't this code globally and permanently disable unwinding for it by
> removing its section?
Following are the used conventions:
- EINVAL: bad .eh_frame; section should be removed
- EOPNOTSUPP: unsupported .eh_frame
- ENOENT: no .eh_frame for IP
One can argue whether a CIE/FDE with too many CFI instructions should
be considered bad or unsupported. For now I decided for bad, to
prevent future unwind attempts in case of crafted .eh_frame.
> [Severity: High]
> This is a pre-existing issue, but looking at eh_frame_remove_section()
> which is called here, is there a memory leak if mas_erase() drops the lock?
>
> eh_frame_remove_section() holds mtree_lock and iterates via mas_for_each().
> It calls __eh_frame_remove_section(), which calls mas_erase().
>
> If mas_erase() needs to allocate memory via mas_nomem(mas, GFP_KERNEL), it
> can drop the tree lock. If a concurrent thread inserts a new section
> covering the same range during this unlocked window, mas_erase() retries,
> erases the new section, and returns it.
>
> __eh_frame_remove_section() then sees the returned entry doesn't match sec,
> returns -EINVAL, and skips call_srcu(), potentially leaking the new section.
Help! I fail to understand how the (internal) maple tree locking and
its advanced API are then supposed to be used correctly to iterate over
the items in the tree and remove some while doing so. Is that why other
users use an external lock (MT_FLAGS_LOCK_EXTERN, MTREE_INIT_EXT())?
See MM_MT_FLAGS.
> [Severity: Critical]
> This is a pre-existing issue, but looking at __eh_frame_remove_section()
> called by eh_frame_remove_section(), can a memory allocation failure lead
> to a use-after-free?
>
> If mas_nomem() fails to allocate memory during mas_erase(), it sets an
> error state and aborts without removing the node, but still returns the
> original sec:
>
> __eh_frame_remove_section() {
> ...
> if (mas_erase(mas) != sec) {
> return -EINVAL;
> }
> call_srcu(&eh_frame_srcu, &sec->rcu, eh_frame_free_srcu);
> ...
> }
>
> Because __eh_frame_remove_section() only checks if the return value does
> not equal sec, it misses the error state completely. It then calls
> call_srcu() to free the section while the node remains active in the maple
> tree. Could subsequent calls to eh_frame_find() load and access this freed
> memory?
This is resolved in the next version of "[RFC PATCH v2 08/24] unwind_user/
eh_frame: Store .eh_frame_hdr section data in per-mm maple tree". See my
respective reply.
>
>> return ret;
>> }
>>
>
Thanks and 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/