https://bugs.documentfoundation.org/show_bug.cgi?id=103690
--- Comment #8 from [email protected] ---
My internet is too slow to download the gigantic LO repo so I loaded up LO in a
debugger and disassembled Application::GetSolarMutex(), the function in which
the sefault (NULL pointer) dereference happened:
(lldb) disas
libvcllo.dylib`Application::GetSolarMutex:
-> 0x102a823c0 <+0>: pushq %rbp
0x102a823c1 <+1>: movq %rsp, %rbp
0x102a823c4 <+4>: callq 0x102a85050 ; ImplGetSVData()
0x102a823c9 <+9>: movq 0x8(%rax), %rdi
0x102a823cd <+13>: movq (%rdi), %rax ; THIS IS THE OFFENDING
LINE
0x102a823d0 <+16>: popq %rbp
0x102a823d1 <+17>: jmpq *0xa8(%rax)
0x102a823d7 <+23>: nopw (%rax,%rax)
So, we expect to see a NULL pointer in %rdi, because it's trying to dereference
that and placing it into %rax. Let's check the register state.
Thread 0 crashed with X86 Thread State (64-bit):
rax: 0x000000010b3989d0 rbx: 0x000060800005ec00 rcx: 0x0000608000115c30
rdx: 0x0000600000248640
rdi: 0x0000000000000000 rsi: 0x000000010b2a7d70 rbp: 0x00007fff5751a980
rsp: 0x00007fff5751a980
r8: 0x0000000000000000 r9: 0x0000000000000000 r10: 0x001faa01001faa80
r11: 0x000000010b1f2f50
r12: 0x0000000000000000 r13: 0x0000608000071380 r14: 0x0000600000248640
r15: 0x0000000000000000
rip: 0x000000010b1203cd rfl: 0x0000000000010202 cr2: 0x0000000000000000
Indeed. %rdi is 0. Normally in such a short function I'd assume %rdi is the
first argument, as detailed in the calling convention
(https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI).
However, we can see that %rdi is actually *(%rax + 8). And %rax is the return
value of the call to ImplGetSVData(). We know from the thread state that %rax
contains 0x000000010b3989d0, which isn't very useful right now since we don't
have a core dump. How is ImplGetSVData() implemented?
EDIT: Got the sources.
comphelper::SolarMutex& Application::GetSolarMutex()
{
ImplSVData* pSVData = ImplGetSVData();
return *(pSVData->mpDefInst->GetYieldMutex());
}
So mpDefInst is somehow NULL. Why?
(lldb) disas -n ImplGetSVData
libvcllo.dylib`ImplGetSVData:
0x102a85050 <+0>: pushq %rbp
0x102a85051 <+1>: movq %rsp, %rbp
0x102a85054 <+4>: pushq %r14
0x102a85056 <+6>: pushq %rbx
0x102a85057 <+7>: movq 0x27596a(%rip), %rbx ; (anonymous
namespace)::rtl_Instance<ImplSVData, rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance, osl::Guard<osl::Mutex>,
osl::GetGlobalMutex, int, int>::m_pInstance
0x102a8505e <+14>: testq %rbx, %rbx
0x102a85061 <+17>: jne 0x102a850f0 ; <+160>
0x102a85067 <+23>: callq 0x102b93dfa ; symbol stub for:
osl_getGlobalMutex
0x102a8506c <+28>: movq %rax, %r14
0x102a8506f <+31>: movq (%r14), %rdi
0x102a85072 <+34>: callq 0x102b93d58 ; symbol stub for:
osl_acquireMutex
0x102a85077 <+39>: movq 0x27594a(%rip), %rbx ; (anonymous
namespace)::rtl_Instance<ImplSVData, rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance, osl::Guard<osl::Mutex>,
osl::GetGlobalMutex, int, int>::m_pInstance
0x102a8507e <+46>: testq %rbx, %rbx
0x102a85081 <+49>: jne 0x102a850e8 ; <+152>
0x102a85083 <+51>: movb 0x275ca7(%rip), %al ; guard variable for
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance
0x102a85089 <+57>: testb %al, %al
0x102a8508b <+59>: jne 0x102a850da ; <+138>
0x102a8508d <+61>: leaq 0x27593c(%rip), %rbx ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance
0x102a85094 <+68>: movl $0x360, %esi ; imm = 0x360
0x102a85099 <+73>: movq %rbx, %rdi
0x102a8509c <+76>: callq 0x102b948f8 ; symbol stub for:
__bzero
0x102a850a1 <+81>: movw $0x100, 0x275bee(%rip) ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance + 708,
imm = 0x100
0x102a850aa <+90>: movl $0xffffffff, 0x275c0c(%rip) ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance + 748,
imm = 0xFFFFFFFF
0x102a850b4 <+100>: movw $0x4000, 0x275c07(%rip) ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance + 752,
imm = 0x4000
0x102a850bd <+109>: leaq 0xeec(%rip), %rdi ;
ImplSVData::~ImplSVData()
0x102a850c4 <+116>: leaq -0x2ce0cb(%rip), %rdx
0x102a850cb <+123>: movq %rbx, %rsi
0x102a850ce <+126>: callq 0x102b948fe ; symbol stub for:
__cxa_atexit
0x102a850d3 <+131>: movb $0x1, 0x275c56(%rip) ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance + 863
0x102a850da <+138>: leaq 0x2758ef(%rip), %rbx ;
rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance::operator()()::instance
0x102a850e1 <+145>: movq %rbx, 0x2758e0(%rip) ; (anonymous
namespace)::rtl_Instance<ImplSVData, rtl::Static<ImplSVData, (anonymous
namespace)::private_aImplSVData>::StaticInstance, osl::Guard<osl::Mutex>,
osl::GetGlobalMutex, int, int>::m_pInstance
0x102a850e8 <+152>: movq (%r14), %rdi
0x102a850eb <+155>: callq 0x102b93e66 ; symbol stub for:
osl_releaseMutex
0x102a850f0 <+160>: movq %rbx, %rax
0x102a850f3 <+163>: popq %rbx
0x102a850f4 <+164>: popq %r14
0x102a850f6 <+166>: popq %rbp
0x102a850f7 <+167>: retq
0x102a850f8 <+168>: movq %rax, %rdi
0x102a850fb <+171>: callq 0x1027bc6f0 ;
__clang_call_terminate
Well, that's annoying... The code?
ImplSVData* ImplGetSVData() {
return &private_aImplSVData::get();
}
Heavily inlined. And private_aImplSVData is?
namespace
{
struct private_aImplSVData :
public rtl::Static<ImplSVData, private_aImplSVData> {};
}
What the...? I have no idea what this is. A class inheriting from a class that
takes two template parameters, including itself (Curiously Recurring Template
Pattern?). Sure. The strange thing is that private_aImplSVData is mentioned
only on the 3 lines I've already printed out. It's empty. I suppose ImplSVData
is the true meat here, then. It's defined as:
struct ImplSVData
{
SalData* mpSalData = nullptr;
SalInstance* mpDefInst = nullptr; // Default
SalInstance
Application* mpApp = nullptr; // pApp
VclPtr<WorkWindow> mpDefaultWin; // Default-Window
bool mbDeInit = false; // Is VCL
deinitializing
// Tons of stuff more.
}
Right, so the second pointer plainly never gets initialized.
Anyway, back to this rtl::Static thingy, which I assume is some sort of fancy
wrapper to make a singleton out of everything, the Java programmers' dream.
It's a doozy, we find it in core/include/rtl/instance.hxx:
/** Helper base class for a late-initialized (default-constructed)
static variable, implementing the double-checked locking pattern correctly.
@derive
Derive from this class (common practice), e.g.
<pre>
struct MyStatic : public rtl::Static<MyType, MyStatic> {};
...
MyType & rStatic = MyStatic::get();
...
</pre>
@tparam T
variable's type
@tparam Unique
Implementation trick to make the inner static holder unique,
using the outer class
(the one that derives from this base class)
*/
#if HAVE_THREADSAFE_STATICS
template<typename T, typename Unique>
class Static {
public:
/** Gets the static. Mutual exclusion is implied by a functional
-fthreadsafe-statics
@return
static variable
*/
static T & get() {
static T instance;
return instance;
}
};
#else
template<typename T, typename Unique>
class Static {
public:
/** Gets the static. Mutual exclusion is performed using the
osl global mutex.
@return
static variable
*/
static T & get() {
return *rtl_Instance<
T, StaticInstance,
::osl::MutexGuard, ::osl::GetGlobalMutex >::create(
StaticInstance(), ::osl::GetGlobalMutex() );
}
private:
struct StaticInstance {
T * operator () () {
static T instance;
return &instance;
}
};
};
#endif
One very interesting thing to note here is that the disassembly of
ImplGetSVData I posted earlier contained calls to ::osl::GetGlobalMutex. Which
means that for some reason on macOS Sierra, something thinks we don't
HAVE_THREADSAFE_STATICS. I'm pretty sure we do on macOS Sierra, with any recent
GCC or Clang for sure, and I bet this one was built with xcode (thus Clang).
This (http://stackoverflow.com/a/35080639) SO answer implies that
-f(no-)threadsafe-statics flag is GCC specific. Indeed I can't find it in my
clang(1) man page. So that might be another "bug", basically assuming that the
compiler is GCC.
I won't even paste the code of rtl_Instance here (it's in the same file), but
suffice to say that a lot of text is spent on indicating how/why it can be
optimized on which compilers. Just avoiding this shenanigans would thus be a
good optimization, it seems.
Anyhow. It's pretty clear that our ImplSVData is just not being initialized,
and it's being called for the first time by GetSolarMutex, which obviously
didn't expect an uninitialized ImplSVData back.
The LO codebase is not familiar to me, so I'll leave it to someone with
experience to figure out why ImplSVData is not being initialized.
Thanks
--
You are receiving this mail because:
You are the assignee for the bug._______________________________________________
Libreoffice-bugs mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/libreoffice-bugs