OceanBase 源码中 static 变量析构顺序导致的 coredump

问题背景

在 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;
}
上一篇:OceanBase 联合山东移动斩获殊荣:入选2021年信息技术应用创新安全优秀解决方案


下一篇:记一次电脑蓝屏Driver_overRan_stack_buffer处理