elf: Support vDSO with more than one PT_LOAD with v_addr starting at 0 (BZ 32583)

The setup_vdso assumes that vDSO will contain only one PT_LOAD segment
and that 0 is the sentinel for the start mapping address.  Although
the kernel avoids adding more than one PT_LOAD to avoid compatibility
issues, there is no impending issue that prevents glibc from supporting
vDSO with multiple PT_LOAD (as some wrapper tools do [1]).

To support multiple PT_LOAD segments, replace the sentinel with a bool
to indicate that the VMA start has already been set.

Testing is really tricky, since the bug report does not indicate which
tool was used to trigger the issue, nor a runtime that provides a vDSO
with multiple PT_LOAD.  I had to modify the qemu user with a custom
script to create 2 PT_LOAD sections, remove checks that prevent the
vDSO object from being created, and remove the load bias adjustment
in load_elf_vdso.  I could not come up with an easy test case to
integrate with glibc.

The Linux kernel provides vDSO with only one PT_LOAD due to
compatibility reasons. For instance

* arch/arm64/kernel/vdso/vdso.lds.S

86 /*
87  * We must supply the ELF program headers explicitly to get just one
88  * PT_LOAD segment, and set the flags explicitly to make segments read-only.
89  /
90 PHDRS
91 {
92         text            PT_LOAD         FLAGS(5) FILEHDR PHDRS; / PF_R|PF_X /
93         dynamic         PT_DYNAMIC      FLAGS(4);               / PF_R /
94         note            PT_NOTE         FLAGS(4);               / PF_R */
95 }

* arch/x86/entry/vdso/vdso-layout.lds.S

95 /*
96  * We must supply the ELF program headers explicitly to get just one
97  * PT_LOAD segment, and set the flags explicitly to make segments read-only.
98  /
99 PHDRS
100 {
101         text            PT_LOAD         FLAGS(5) FILEHDR PHDRS; / PF_R|PF_X /
102         dynamic         PT_DYNAMIC      FLAGS(4);               / PF_R /
103         note            PT_NOTE         FLAGS(4);               / PF_R */
104         eh_frame_hdr    PT_GNU_EH_FRAME;
105 }

Checked on aarch64-linux-gnu.

[1] https://sourceware.org/bugzilla/show_bug.cgi?id=32583#c2

Reviewed-by: Florian Weimer <fweimer@redhat.com>
This commit is contained in:
Adhemerval Zanella
2025-12-09 18:35:13 -03:00
parent 440108ce9e
commit 9021707ca7

View File

@@ -31,6 +31,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
mapped and relocated it normally. */
struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
__RTLD_VDSO, LM_ID_BASE);
bool l_addr_set = false;
if (__glibc_likely (l != NULL))
{
l->l_phdr = ((const void *) GLRO(dl_sysinfo_dso)
@@ -47,8 +48,11 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
}
else if (ph->p_type == PT_LOAD)
{
if (! l->l_addr)
l->l_addr = ph->p_vaddr;
if (!l_addr_set)
{
l->l_addr = ph->p_vaddr;
l_addr_set = true;
}
if (ph->p_vaddr + ph->p_memsz >= l->l_map_end)
l->l_map_end = ph->p_vaddr + ph->p_memsz;
}