在 OceanBase 开源代码中,有这样一段代码,它会导致在系统退出时发生 coredump:
oceanbase::sql::ObSQLSessionInfo &session() { static oceanbase::sql::ObSQLSessionInfo SESSION; return SESSION; } ObArenaAllocator &session_alloc() { static ObArenaAllocator SESSION_ALLOC; return SESSION_ALLOC; } int ObTableApiProcessorBase::init_session() { int ret = OB_SUCCESS; static const uint32_t sess_version = 0; static const uint32_t sess_id = 1; static const uint64_t proxy_sess_id = 1; if (OB_FAIL(session().test_init(sess_version, sess_id, proxy_sess_id, &session_alloc()))) { LOG_WARN("init session failed", K(ret)); } // more ... return ret; }
利用 ASAN 诊断发现,静态对象 SESSION 析构时会引用一个 SESSION_ALLOC,而 SESSION_ALLOC 也是一个静态对象,当 SESSION_ALLOC 先于 SESSION 析构时,SESSION 析构时就会访问到非法内存(因为 SESSION_ALLOC 已经析构)。
C 语言中,对于 static 变量,析构规则是:先构造者后析构 可以用下面的程序来验证:
[xiaochu.yh ~] $cat test_destroy.cpp // Copyright 1999-2021 Alibaba Inc. All Rights Reserved. // Author: // xiaochu.yh@alipay.com // // test static variable destory order #include <iostream> using namespace std; class A { public: A() { cout << "construct A" << endl; } ~A() { cout << "deconstruct A" << endl; } void init() {} }; class B { public: B() { cout << "construct B" << endl; } ~B() { cout << "deconstruct B" << endl; } void init(A &a) { a.init(); } }; A &getA() { static A a; return a; } B &getB() { static B b; return b; } void func() { getB().init(getA()); } int main(int argc, const char *argv[]) { func(); return 0; } [xiaochu.yh ~] $g++ test_destroy.cpp -o test_destroy [xiaochu.yh ~] $./test_destroy construct A construct B deconstruct B deconstruct A
既然先构造者后析构 ,那么我们可以在 SESSION 构造之前,主动调用一次 session_alloc,使得 SESSION_ALLOC 先构造即可。
代码如下:
oceanbase::sql::ObSQLSessionInfo &session() { static oceanbase::sql::ObSQLSessionInfo SESSION; return SESSION; } ObArenaAllocator &session_alloc() { static ObArenaAllocator SESSION_ALLOC; return SESSION_ALLOC; } int ObTableApiProcessorBase::init_session() { int ret = OB_SUCCESS; static const uint32_t sess_version = 0; static const uint32_t sess_id = 1; static const uint64_t proxy_sess_id = 1; // ensure allocator is constructed before session to // avoid coredump at observer exit ObArenaAllocator &dummy_allocator = session_alloc(); UNUSED(dummy_allocator); if (OB_FAIL(session().test_init(sess_version, sess_id, proxy_sess_id, &session_alloc()))) { LOG_WARN("init session failed", K(ret)); } // more ... return ret; }