-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
[lldb] Fix nullptr dereference on running x86 binary with x86-disabled llvm #82603
Conversation
…d llvm If `LLVM_TARGETS_TO_BUILD` does not contain `X86` and we try to run an x86 binary in lldb, we get a `nullptr` dereference in `LLVMDisasmInstruction(...)`. We try to call `getDisAsm()` method on a `LLVMDisasmContext *DC` which is null. The pointer is passed from `x86AssemblyInspectionEngine::instruction_length(...)` and is originally `m_disasm_context` member of `x86AssemblyInspectionEngine`. This should be filled by `LLVMCreateDisasm(...)` in the class constructor, but not having X86 target enabled in llvm makes `TargetRegistry::lookupTarget(...)` call return `nullptr`, which results in `m_disasm_context` initialized with `nullptr` as well. This patch adds if statements against `m_disasm_context` in `x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(...)` and `x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(...)` so subsequent calls to `x86AssemblyInspectionEngine::instruction_length(...)` do not cause a null pointer dereference.
Please suggest ideas how this could be tested. It looks like that UnwindAssembly unittests is the right place for it - but UnwindAssemblyx86Tests cmake target is not built without X86 in |
@llvm/pr-subscribers-lldb Author: Daniil Kovalev (kovdan01) ChangesIf This patch adds if statements against Full diff: https://github.com/llvm/llvm-project/pull/82603.diff 1 Files Affected:
diff --git a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
index 2032c5a68d054c..6bfaa54135a959 100644
--- a/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
+++ b/lldb/source/Plugins/UnwindAssembly/x86/x86AssemblyInspectionEngine.cpp
@@ -909,6 +909,9 @@ bool x86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(
if (!m_register_map_initialized)
return false;
+ if (m_disasm_context == nullptr)
+ return false;
+
addr_t current_func_text_offset = 0;
int current_sp_bytes_offset_from_fa = 0;
bool is_aligned = false;
@@ -1570,6 +1573,9 @@ bool x86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(
if (!m_register_map_initialized)
return false;
+ if (m_disasm_context == nullptr)
+ return false;
+
while (offset < size) {
int regno;
int insn_len;
|
Can this code be hit when using an x86 core file? Then you could write a shell test thatis It won't get run on the build bots, they enable all targets, but it's nice to have anyway. Someone downstream might appreciate it. |
I'm a little curious the use case that led to this failure. We have a build of llvm/lldb without the X86 target, and we're using that lldb to debug an i386/x86_64 target (gdb connection or corefile)? Because we can't use instruction emulation based unwinding, if we don't have eh_frame for every function, backtraces will go poorly. Once we're off of the currently-executing stack frame, we can follow the frame pointer / pc spilled to the stack (assuming the code wasn't compiled omit-frame-pointer), but we won't be able to find any other spilled registers for printing variable contents in non-volatile / caller spilled. (and getting off of frame 0 without eh_frame is going to fail if we're in the prologue/epilogue on a thread) |
tbh most of lldb would work without the llvm target compiled in, but the disassembler and the Intel assembly scanning unwind plan creator both need it, and the latter is more important. |
Well, it would change source-level next and step too if we didn't have the disassembler. When we're doing a source-level step/next, we look at the stream of instructions and when we have a block of non-branching instructions, we put a breakpoint after the block and continue, instead of instruction stepping. We use the disassembler to detect instructions that can branch. source level step and next would still work, but they would be a bit slower. |
Also this is plugin code, it's probably more appropriate to disable the whole plugin if the necessary backend isn't there? |
tbh I have no problems with the patch, but I think it's fixing something that I think should be reconsidered altogether, I'm interested to hear more about what the use case looks like that led to this being a problem. |
@jasonmolenda The use case is very simple, describing it "as is". I was working on AArch64-specific stuff in lldb in downstream and revealed an x86-related issue while reading the code (see #82364). When working on the latter issue, I tried to run a random x86-64 executable inside lldb and got this error. It occurs literally on the simplest use case:
Yes, it's that simple. |
Thanks for suggestion! I'll try that and notify here if such approach succeeded. |
@DavidSpickett The issue is present when loading an x86 core files via |
The spirit of the test is fine but I think we can avoid adding another core file. As this plugin seems related to backtrace perhaps the test should at least run Now we need a corefile, the closest ones are in |
I had similar thoughts, but I'm not sure if placing the test in I would be glad to here other people's thoughts on that - I'm personally not sure which testing approach is less evil here. |
@jasonmolenda Just in case you've missed - I've provided the use case description leading to the issue (as you requested) above #82603 (comment). Would be glad if you let me know if it gives you enough info & if this particular PR is OK or the issue should be fixed in another way. |
I will be away for a while so @jasonmolenda please merge it if it looks fine to you. |
@jasonmolenda Would be glad to see your feedback - see answers to your previous comments above |
@jasonmolenda A kind reminder regarding the PR - see answers to your previous comments above |
Hi sorry @kovdan01 I missed this one in the emails. You're using an lldb which was built without the
In Testx86AssemblyInspectionEngine.cpp we initialize llvm state in
and run the unwind engine on those bytes. Could we add a |
Thanks @jasonmolenda for your feedback and suggestion! See f96989d - I've deleted the test with corefile and added the test you've mentioned. Basically, I've just left the most simple test from "normal" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, please land it. Thanks for the pings and rewriting the test case!
If
LLVM_TARGETS_TO_BUILD
does not containX86
and we try to run an x86 binary in lldb, we get anullptr
dereference inLLVMDisasmInstruction(...)
. We try to callgetDisAsm()
method on aLLVMDisasmContext *DC
which is null. The pointer is passed fromx86AssemblyInspectionEngine::instruction_length(...)
and is originallym_disasm_context
member ofx86AssemblyInspectionEngine
. This should be filled byLLVMCreateDisasm(...)
in the class constructor, but not having X86 target enabled in llvm makesTargetRegistry::lookupTarget(...)
call returnnullptr
, which results inm_disasm_context
initialized withnullptr
as well.This patch adds if statements against
m_disasm_context
inx86AssemblyInspectionEngine::GetNonCallSiteUnwindPlanFromAssembly(...)
andx86AssemblyInspectionEngine::FindFirstNonPrologueInstruction(...)
so subsequent calls tox86AssemblyInspectionEngine::instruction_length(...)
do not cause a null pointer dereference.