-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libunwind] use __unw_add_dynamic_eh_frame_section to register dynamic eh_frame section cause segment fault #76957
Comments
ping |
|
That sounds like a bug to me. My understanding is that Linux eh-frame sections should end with a null terminator, and that zero is never a legal start for a CIE (which always has a non-zero length). On top-of-tree, void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
// The eh_frame section start serves as the mh_group
unw_word_t mh_group = eh_frame_start;
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
auto p = (LocalAddressSpace::pint_t)eh_frame_start;
while (true) {
if (CFI_Parser<LocalAddressSpace>::decodeFDE(
LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,
true) == NULL) {
DwarfFDECache<LocalAddressSpace>::add((LocalAddressSpace::pint_t)mh_group,
fdeInfo.pcStart, fdeInfo.pcEnd,
fdeInfo.fdeStart);
p += fdeInfo.fdeLength;
} else if (CFI_Parser<LocalAddressSpace>::parseCIE(
LocalAddressSpace::sThisAddressSpace, p, &cieInfo) == NULL) {
p += cieInfo.cieLength;
} else
return;
}
}
...
pint_t cieLength = (pint_t)addressSpace.get32(p);
p += 4;
pint_t cieContentEnd = p + cieLength;
if (cieLength == 0xffffffff) {
// 0xffffffff means length is really next 8 bytes
cieLength = (pint_t)addressSpace.get64(p);
p += 8;
cieContentEnd = p + cieLength;
}
if (cieLength == 0)
return NULL;
... We'll have to check other users of |
@SihangZhu I think the fix may be as simple as: diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp
index cd610377b63d..54083c4ab21f 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -324,7 +324,7 @@ void __unw_add_dynamic_eh_frame_section(unw_word_t eh_frame_start) {
CFI_Parser<LocalAddressSpace>::CIE_Info cieInfo;
CFI_Parser<LocalAddressSpace>::FDE_Info fdeInfo;
auto p = (LocalAddressSpace::pint_t)eh_frame_start;
- while (true) {
+ while (LocalAddressSpace::sThisAddressSpace.get32(p)) {
if (CFI_Parser<LocalAddressSpace>::decodeFDE(
LocalAddressSpace::sThisAddressSpace, p, &fdeInfo, &cieInfo,
true) == NULL) { Does that change fix your issue? |
Thanks for your reply. In some application scenarios, there may be illegal memory behind the .eh_frame section, and trying to access it in a while loop will still cause segment fault. |
HI @SihangZhu. It is not legal on Linux (as I understand it) to have an empty |
It's worth noting that the null terminator is added by the static linker: ELF relocatable object files will have non-null-terminated |
Maybe the scenario I described before wasn't detailed enough, that's my fault. |
@SihangZhu What I'm saying is that the if (eh_frame_length > 0)
__unw_add_dynamic_eh_frame_section(eh_frame_start); Apologies -- I'm still half thinking in Darwin where the registration logic is different. The above won't help for non-empty I think the only valid solution is to add a null-terminator to the __eh_frame in the patch format. |
Specifically, in the picture I attached at the beginning, the eh_frame section saved in the patch is a legal one. The address in the memory is 0xffff891f9040 to 0xffff891f90a8, and its length is 104. If the address following 0xffff891f90a8 is inaccessible, problems will occur. |
@SihangZhu: Looking at the section above I don't think it is legal. My read is that you have:
There is no null terminator at the end, so the section is not a legal loaded eh-frame section. You need to add a 4-byte null-terminator at the end. |
The original data of eh_frame in the patch area comes from a object file and has not been linked. The difference from the original is that the relocation has been repaired and a terminator has been added at the end. |
Fix this issue [#76957](#76957) Libgcc provides __register_frame to register a dynamic .eh_frame section, while __unw_add_dynamic_eh_frame_section can be used to do the same in libunwind. However, the address after dynamic .eh_frame are padding with 0 value, it will be identified as legal CIE. And __unw_add_dynamic_eh_frame_section will continue to parse subsequent addresses until illegal memory or other sections are accessed. This patch adds length formal parameter for dynamic registration.
Fix this issue [llvm#76957](llvm#76957) Libgcc provides __register_frame to register a dynamic .eh_frame section, while __unw_add_dynamic_eh_frame_section can be used to do the same in libunwind. However, the address after dynamic .eh_frame are padding with 0 value, it will be identified as legal CIE. And __unw_add_dynamic_eh_frame_section will continue to parse subsequent addresses until illegal memory or other sections are accessed. This patch adds length formal parameter for dynamic registration.
In libgcc, we register the eh_frame section of the live patching. We can use the
__register_frame
interface. This libunwind library also provides__register_frame
and__deregister_frame
functions, but they are aliases for__unw_add_dynamic_fde
and__unw_remove_dynamic_fde
and thus can only take a single FDE. I found the__unw_add_dynamic_eh_frame_section
function, but during live patching, the eh_frame section may be followed by all 0. Scenario, the content of all 0 will be regarded as legal CIE.We will fall into an infinite loop or access illegal memory.
the data of patchArea as below
Is it better for this function to have length of eh_frame section as a formal parameter?
The text was updated successfully, but these errors were encountered: