Page MenuHomeFreeBSD

linker: Harden checks for ELFs
Needs ReviewPublic

Authored by code_fedang.net on Tue, Sep 15, 4:33 PM.
Tags
None
Referenced Files
F175159869: D59710.id188861.diff
Thu, Oct 8, 6:24 PM
F175159543: D59710.id188731.diff
Thu, Oct 8, 6:20 PM
F175139723: D59710.id188731.diff
Thu, Oct 8, 2:28 PM
F175137015: D59710.id188861.diff
Thu, Oct 8, 1:56 PM
F175056046: D59710.diff
Wed, Oct 7, 10:57 PM
Unknown Object (File)
Wed, Oct 7, 3:27 PM
Unknown Object (File)
Tue, Oct 6, 6:58 AM
Unknown Object (File)
Tue, Oct 6, 2:45 AM
Subscribers

Details

Reviewers
bnovkov
jhb
jrtc27
Summary
Added checks that abort the load of ET_DYN modules when the
program segments are overlapping or unsorted.
Also validate that their size is valid and not overflowing.

Event: EuroBSDCon 2026
Test Plan

kyua report -r /root/.kyua/store/results.usr_obj_usr_src_amd64.amd64_tests_sys_kld_checkdir_usr_tests_sys_kld.20260922-135000-264526.db

> Summary

Results read from /root/.kyua/store/results.usr_obj_usr_src_amd64.amd64_tests_sys_kld_checkdir_usr_tests_sys_kld.20260922-135000-264526.db
Test cases: 16 total, 0 skipped, 0 expected failures, 0 broken, 0 failed
Total time: 0.882s

Before the changes it just crashed during testing

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped
Build Status
Buildable 77763
Build 74646: arc lint + arc unit

Event Timeline

sys/kern/link_elf.c
1121–1123

I think we should enforce this assumption (i.e., return EINVAL or something similar) instead of sorting the segments.

Thinking about this from a user's standpoint, a kldload triggering this assertion almost certainly means that something went wrong during linking or that somebody is trying to mess with the kernel.
It would make more sense to have the operation fail and let the user know that something's up instead of silently correcting the error.

sys/kern/link_elf.c
1121–1123

Reading through the ELF specification this is not actually a requirement.
Putting them in order is the de facto standard, but I can see some obscure compiler doing whatever they want.
What would the error message say since the file is technically in-spec?

sys/kern/link_elf.c
1121–1123

An ENOEXEC with something along the lines of "Improperly sorted segments " is fine.

Check that segments are already sorted

code_fedang.net retitled this revision from linker: Prevent UB on malformed ELF to linker: Prevent panic on malformed ELF.Tue, Sep 22, 1:35 PM

Fix other panics found during testing

code_fedang.net edited the test plan for this revision. (Show Details)

Generally LGTM, two nits aside. I'll test this a bit more in the coming days and land it if nothing obvious pops up.

tests/sys/kld/link_elf.c
2

Missing copyright header.

163

Please move this definition below the test case definitions.

Could you please rebase the patch to a recent main? I can't cleanly apply the patch for testing.

code_fedang.net marked an inline comment as done.

Rebase against main. Apparently some of these fixes were pushed already

since the code that caused the panic has already been fixed on main, maybe we should rename this commit to "fix edgecases" or similar?

since the code that caused the panic has already been fixed on main, maybe we should rename this commit to "fix edgecases" or similar?

Yes please, something along the lines of "harden ELF checks" would be great.

tests/sys/kld/link_elf.c
2

Please use the shorter copyright header as noted in style(9).

CCing @jhb and @jrtc27 since they've reviewed a similar patch (D58542) recently.

code_fedang.net retitled this revision from linker: Prevent panic on malformed ELF to linker: Harden checks for ELFs.Tue, Oct 6, 12:54 PM
code_fedang.net edited the summary of this revision. (Show Details)

Alternatively, why not just adjust the existing loop that populates segs to instead take min(p_vaddr) and max(p_vaddr + p_memsz)? It wouldn't be hard to do and would remove the need for this restriction (and without any complex sorting). The only thing you'd miss is handling overlapping segments, but to be honest I don't think that would necessarily be a problem, we'd do exactly what the file asked for and load the segments in that order, but it just might result in the module shooting itself in the foot, which I don't see as any different from the countless other ways a module can corrupt its own internal state and panic your kernel. At the end of the day if you're loading a module into the kernel you're trusting the module to not itself be a panic-fest, all the kernel linker should be doing is avoiding confusing/corrupting things *outside* the module.

Alternatively, why not just adjust the existing loop that populates segs to instead take min(p_vaddr) and max(p_vaddr + p_memsz)? It wouldn't be hard to do and would remove the need for this restriction (and without any complex sorting). The only thing you'd miss is handling overlapping segments, but to be honest I don't think that would necessarily be a problem, we'd do exactly what the file asked for and load the segments in that order, but it just might result in the module shooting itself in the foot, which I don't see as any different from the countless other ways a module can corrupt its own internal state and panic your kernel. At the end of the day if you're loading a module into the kernel you're trusting the module to not itself be a panic-fest, all the kernel linker should be doing is avoiding confusing/corrupting things *outside* the module.

I thought about that at the start, but ended up with the extra check since the subsequent code seems to rely on that.
Overall, I think a sane policy for malformed modules would be: if it has to crash, it should do so outside of the linker.

Alternatively, why not just adjust the existing loop that populates segs to instead take min(p_vaddr) and max(p_vaddr + p_memsz)? It wouldn't be hard to do and would remove the need for this restriction (and without any complex sorting). The only thing you'd miss is handling overlapping segments, but to be honest I don't think that would necessarily be a problem, we'd do exactly what the file asked for and load the segments in that order, but it just might result in the module shooting itself in the foot, which I don't see as any different from the countless other ways a module can corrupt its own internal state and panic your kernel. At the end of the day if you're loading a module into the kernel you're trusting the module to not itself be a panic-fest, all the kernel linker should be doing is avoiding confusing/corrupting things *outside* the module.

I thought about that at the start, but ended up with the extra check since the subsequent code seems to rely on that.
Overall, I think a sane policy for malformed modules would be: if it has to crash, it should do so outside of the linker.

My point is, beyond the lazy way to compute min(p_vaddr) and max(p_vaddr + p_memsz), I don't think the subsequent code does rely on that, so why not just make it less lazy? It's fewer lines of code than verifying the assumptions hold.