一段应急的C++对象检查代码

一个JNI项目,偶发离奇崩溃,怀疑是野指针类的问题。开address sanitizer本地难复现,测试版开as太卡,大部分测试机还是6、7的系统。

还好大部分都对象都派生自引用计数的基类,于是按fail-fast的思路憋出了以下代码,在加减引用怀疑区域手动插桩检查。

附上代码,无关部分做了简化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <type_traits>
#include <stdio.h>
#include <assert.h>

#define LOG(...) do { fprintf(stderr, __VA_ARGS__); fflush(stderr); } while (false)

class Object
{
public:
Object()
: _falgs(0xcafecafe)
{
}
virtual ~Object()
{
_falgs = 0xdeaddead;
}
// is not strict but help avoid most of error
bool isValid() const
{
return static_cast<const void *>(this) && (_falgs == 0xcafecafe);
}

private:
int _falgs;
};

class ObjectCheck
{
public:
template <typename T>
static bool Check(T obj)
{
return doCheck<T>(obj);
}

template <typename T>
static bool CheckNullable(T obj)
{
return obj == nullptr || doCheck<T>(obj);
}

private:
template <typename T>
static bool doCheck(typename std::enable_if<std::is_base_of<Object, typename std::remove_pointer<T>::type>::value && !std::is_void<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("dynamic_cast && isValid %p %p ", dynamic_cast<T>(reinterpret_cast<Object*>(obj)), obj);
return (obj == dynamic_cast<T>(reinterpret_cast<Object*>(obj)) && obj->isValid());
}

template <typename T>
static bool doCheck(typename std::enable_if<!std::is_base_of<Object, typename std::remove_pointer<T>::type>::value && std::is_polymorphic<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("dynamic_cast %p %p ", dynamic_cast<T>(obj), obj);
return (obj == dynamic_cast<T>(obj));
}

template <typename T>
static bool doCheck(typename std::enable_if<std::is_void<typename std::remove_pointer<T>::type>::value, T>::type obj)
{
LOG("static_cast void* ");
return (obj == static_cast<void *>(obj));
}
};

class Dummy
{
public:
virtual ~Dummy() = default;
int dummy;
};
class SubObject : public Object
{
public:
int dummy;
};

int main()
{
void *t1 = nullptr;
Object *t2 = nullptr;
Dummy *t3 = nullptr;
SubObject *t4 = nullptr;
Object *t5 = new Object();
Dummy *t6 = new Dummy();
SubObject *t7 = new SubObject();
SubObject *t8 = reinterpret_cast<SubObject *>(t6);
SubObject *t9 = reinterpret_cast<SubObject *>(t5);
SubObject *t10 = new SubObject();
delete t10;

LOG("result %d \n", ObjectCheck::CheckNullable(t1));
LOG("result %d \n", ObjectCheck::CheckNullable(t2));
LOG("result %d \n", ObjectCheck::CheckNullable(t3));
LOG("result %d \n", ObjectCheck::CheckNullable(t4));
LOG("result %d \n", ObjectCheck::Check(t5));
LOG("result %d \n", ObjectCheck::Check(t6));
LOG("result %d \n", ObjectCheck::Check(t7));
LOG("result %d \n", ObjectCheck::Check(t8));
LOG("result %d \n", ObjectCheck::Check(t9));
LOG("result %d \n", ObjectCheck::Check(t10));
}

输出

1
2
3
4
5
6
7
8
9
10
result 1 
result 1
result 1
result 1
dynamic_cast && isValid 0x7fca1cc02b40 0x7fca1cc02b40 result 1
dynamic_cast 0x7fca1cc02b50 0x7fca1cc02b50 result 1
dynamic_cast && isValid 0x7fca1cc02b60 0x7fca1cc02b60 result 1
dynamic_cast && isValid 0x0 0x7fca1cc02b50 result 0
dynamic_cast && isValid 0x0 0x7fca1cc02b40 result 0
[1] 30935 segmentation fault ./a.out