jsoncpp Reader::parse线程不安全

问题分析

线上服务异常终止,通过分析core dump信息,对应用层的数据进行分析,排除了应用层的问题,然后怀疑是json库存在问题

部分core日志信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
... ...
saveBackTrace|19|CoreTime:2019-06-04 20:05:03 ThreadId:140319607437056 SignalId:6
saveBackTrace|24|backtrace rank:37 addr2line -f -C -e 应用名 地址
saveBackTrace|32|37: program_name(_Z13saveBackTracei+0x436) [0x579926]
saveBackTrace|32|36: /lib64/libc.so.6(+0x32510) [0x7f9f10cfb510]
saveBackTrace|32|35: /lib64/libc.so.6(gsignal+0x35) [0x7f9f10cfb495]
saveBackTrace|32|34: /lib64/libc.so.6(abort+0x175) [0x7f9f10cfcc75]
saveBackTrace|32|33: /usr/lib64/libstdc++.so.6(_ZN9__gnu_cxx27__verbose_terminate_handlerEv+0x41) [0x7f9f115b59a1]
saveBackTrace|32|32: /usr/lib64/libstdc++.so.6(+0xbcbe6) [0x7f9f115b3be6]
saveBackTrace|32|31: /usr/lib64/libstdc++.so.6(+0xbcc13) [0x7f9f115b3c13]
saveBackTrace|32|30: /usr/lib64/libstdc++.so.6(+0xbcd32) [0x7f9f115b3d32]
saveBackTrace|32|29: program_name(_ZN4Json17throwRuntimeErrorERKSs+0x3a) [0x6cc8ba]
saveBackTrace|32|28: program_name(_ZN4Json6Reader9readValueEv+0x2a4) [0x6c3404]
saveBackTrace|32|27: program_name(_ZN4Json6Reader9readArrayERNS0_5TokenE+0xb2) [0x6c3532]
saveBackTrace|32|26: program_name(_ZN4Json6Reader9readValueEv+0x15b) [0x6c32bb]
saveBackTrace|32|25: program_name(_ZN4Json6Reader10readObjectERNS0_5TokenE+0x18a) [0x6c2caa]
saveBackTrace|32|24: program_name(_ZN4Json6Reader9readValueEv+0x14b) [0x6c32ab]
saveBackTrace|32|23: program_name(_ZN4Json6Reader9readArrayERNS0_5TokenE+0xb2) [0x6c3532]
saveBackTrace|32|22: program_name(_ZN4Json6Reader9readValueEv+0x15b) [0x6c32bb]
saveBackTrace|32|21: program_name(_ZN4Json6Reader10readObjectERNS0_5TokenE+0x18a) [0x6c2caa]
saveBackTrace|32|20: program_name(_ZN4Json6Reader9readValueEv+0x14b) [0x6c32ab]
saveBackTrace|32|19: program_name(_ZN4Json6Reader5parseEPKcS2_RNS_5ValueEb+0x291) [0x6c3ae1]
... ...

可以看到在调用Json::Reader::parse后经过Json::Reader::readValue等调用,最后再调用Json::Reader::readValue时调用Json::throwRuntimeError抛出异常

github jsoncpp下载jsoncpp源码

查看函数Reader::readValue()

1
2
3
4
5
6
7
bool Reader::readValue() {
if (stackDepth_g >= stackLimit_g) throwRuntimeError("Exceeded stackLimit in readValue().");
++stackDepth_g;
... ...
--stackDepth_g;
return successful;
}

可以看到只有一个地方会调用Json::throwRuntimeError抛出异常,而stackDepth_g是个坑爹的静态全局变量,线程不安全,而出问题的服务是多线程的

1
2
static int const stackLimit_g = 1000;
static int stackDepth_g = 0; // see readValue()

解决

1
2
3
4
5
6
7
8
//stackDepth_是OurReader的成员变量
bool OurReader::readValue() {
if (stackDepth_ >= features_.stackLimit_) throwRuntimeError("Exceeded stackLimit in readValue().");
++stackDepth_;
... ...
--stackDepth_;
return successful;
}

分析源码,找到一个线程安全的Json解析类Json::OurReader,但该类在reader.h未声明,通过查看代码可以使用以下方法使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
bool Json::FromString(Json::Value &value, const string &data)
{
if(data.empty())
{
return true;
}
static Json::CharReaderBuilder oBuilder;
std::auto_ptr<Json::CharReader> pReader(oBuilder.newCharReader());
if(NULL == pReader.get())
{
return false;
}
const char* begin = data.c_str();
const char* end = begin + data.length();
string strError;
bool bRet = pReader->parse(begin, end, &value, &strError);
return bRet;
}