https://sourceware.org/bugzilla/show_bug.cgi?id=30975
Bug ID: 30975
Summary: elfutils incorrectly reports core files with
non-contiguous segments
Product: elfutils
Version: unspecified
Status: UNCONFIRMED
Severity: normal
Priority: P2
Component: general
Assignee: unassigned at sourceware dot org
Reporter: pablogsal at gmail dot com
CC: elfutils-devel at sourceware dot org
Target Milestone: ---
I am trying to use elfutils to report a core file. Unfortunately elfutils
doesn't properly process all segments that appear in the core file which not
only causes it to incorrectly report them when using the relevant APIs but also
fails to resolve symbols or unwind where addressed are located in the
incorrectly missing modules.
After debugging the problem I found that the culprit is this code in
dwfl_segment_report_module.c:
if (r_debug_info != NULL)
for (const struct r_debug_info_module *module = r_debug_info->module;
module != NULL; module = module->next)
if (module_start <= module->l_ld && module->l_ld < module_end)
{
/* L_LD read from link map must be right while DYN_VADDR is unsafe.
Therefore subtract DYN_VADDR and add L_LD to get a possibly
corrective displacement for all addresses computed so far. */
GElf_Addr fixup = module->l_ld - dyn_vaddr;
if ((fixup & (dwfl->segment_align - 1)) == 0
&& module_start + fixup <= module->l_ld
&& module->l_ld < module_end + fixup)
{
module_start += fixup;
module_end += fixup;
dyn_vaddr += fixup;
bias += fixup;
if (module->name[0] != '\0')
{
name = basename (module->name);
name_is_final = true;
}
break;
}
}
if (r_debug_info != NULL)
{
bool skip_this_module = false;
for (struct r_debug_info_module *module = r_debug_info->module;
module != NULL; module = module->next)
if ((module_end > module->start && module_start < module->end)
|| dyn_vaddr == module->l_ld)
{
if (module->elf != NULL
&& invalid_elf (module->elf, module->disk_file_has_build_id,
build_id, build_id_len))
{
elf_end (module->elf);
close (module->fd);
module->elf = NULL;
module->fd = -1;
}
if (module->elf != NULL)
{
/* Ignore this found module if it would conflict in address
space with any already existing module of DWFL. */
skip_this_module = true;
}
}
if (skip_this_module)
{
free (build_id);
return finish ();
}
}
This code removes all modules that "collide" with the given segments being
analyzed in this function. The problem is that the value of module_end is
incorrectly calculated for shared objects that are not contiguous in memory,
which causes this code to believe that all modules between the **start of the
first segment** and the **end of the last segment** are invalid, and their
module->elf pointer is closed.
For instance, here is an example in Red Hat Enterprise Linux Server release
7.9:
# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.9 (Maipo)
uname -a
Linux dev-10-98-113-219 3.10.0-1160.99.1.el7.x86_64 #1 SMP Thu Aug 10 10:46:21
EDT 2023 x86_64 x86_64 x86_64 GNU/Linux
# lsb_release -a
LSB Version::core-4.1-amd64:core-4.1-noarch
Distributor ID: RedHatEnterpriseServer
Description:Red Hat Enterprise Linux Server release 7.9 (Maipo)
Release:7.9
Codename: Maipo
# cat /proc/self/maps
0040-0040b000 r-xp fd:01 12760634
/usr/bin/cat
0060b000-0060c000 r--p b000 fd:01 12760634
/usr/bin/cat
0060c000-0060d000 rw-p c000 fd:01 12760634
/usr/bin/cat
0176b000-0178c000 rw-p 00:00 0 [heap]
7f192309b000-7f19295de000 r--p fd:01 4300838
/usr/lib/locale/locale-archive
7f19295de000-7f19297a2000 r-xp fd:01 8195
/usr/lib64/libc-2.17.so
7f19297a2000-7f19299a1000 ---p 001c4000 fd:01 8195
/usr/lib64/libc-2.17.so
7f19299a1000-7f19299a5000 r--p 001c3000 fd:01 8195
/usr/lib64/libc-2.17.so
7f19299a5000-7f19299a7000 rw-p 001c7000 fd:01 8195
/usr/lib64/libc-2.17.so
7f19299a7000-7f19299ac000 rw-p 00:00 0
7f19299ac000-7f19299ce000 r-xp fd:01 8188
/usr/lib64/ld-2.17.so
7f1929bc2000-7f1929bc5000 rw-p 0