crash in QJsonDocument::fromJson
-
Here is the code:
static bool getObjFromJson(const QString& json, QJsonObject& jsonObj) {
QJsonParseError jsonParseError;
QJsonDocument jsonDoc =
QJsonDocument::fromJson(json.toUtf8(), &jsonParseError);if (jsonParseError.error != QJsonParseError::NoError) {
return false;
}jsonObj = jsonDoc.object();
return true;
}sometimes, I get crash like this:
02 00dbcb48 53cfe2a2 35f79300 00dbce88 00dbcbac ucrtbase!_errno+0x1c
03 00dbcb5c 53cf14c0 35525408 00dbcb7c 0000000a Qt5Core!qstrtoll+0x12 [D:\code\hoyo-qt\qtbase\src\corelib\text\qlocale_tools.cpp @ 442]
04 00dbcb7c 53cedfd8 35525401 0000000a 00dbcbf3 Qt5Core!QLocaleData::bytearrayToLongLong+0x20 [D:\code\hoyo-qt\qtbase\src\corelib\text\qlocale.cpp @ 4201]
05 (Inline) -------- -------- -------- -------- Qt5Core!toIntegral_helper+0x18 [D:\code\hoyo-qt\qtbase\src\corelib\text\qbytearray.cpp @ 3819]
06 (Inline) -------- -------- -------- -------- Qt5Core!toIntegral_helper+0x18 [D:\code\hoyo-qt\qtbase\src\corelib\text\qbytearray.cpp @ 3840]
07 00dbcbac 53e6aa82 00dbcbf3 0000000a 9e1e0cb4 Qt5Core!QByteArray::toLongLong+0x48 [D:\code\hoyo-qt\qtbase\src\corelib\text\qbytearray.cpp @ 3871]
08 00dbcc00 53e6b57c 9e1e0c80 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseNumber+0x142 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 755]
09 00dbcc34 53e6acc8 9e1e0cec 36014cbf 35f792f3 Qt5Core!QJsonPrivate::Parser::parseValue+0x2ac [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 680]
0a (Inline) -------- -------- -------- -------- Qt5Core!QJsonPrivate::Parser::parseMember+0x34 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 534]
0b 00dbcc58 53e6b521 9e1e0c38 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseObject+0xc8 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 489]
0c 00dbcc8c 53e6acc8 9e1e0c04 36014cbf 35f789a1 Qt5Core!QJsonPrivate::Parser::parseValue+0x251 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 667]
0d (Inline) -------- -------- -------- -------- Qt5Core!QJsonPrivate::Parser::parseMember+0x34 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 534]
0e 00dbccb0 53e6b521 9e1e0c50 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseObject+0xc8 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 489]
0f 00dbcce4 53e6acc8 9e1e0dbc 36014cbf 35f7886c Qt5Core!QJsonPrivate::Parser::parseValue+0x251 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 667]
10 (Inline) -------- -------- -------- -------- Qt5Core!QJsonPrivate::Parser::parseMember+0x34 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 534]
11 00dbcd08 53e6b521 9e1e0d88 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseObject+0xc8 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 489]
12 00dbcd3c 53e6acc8 9e1e0dd4 36014cbf 35f778d8 Qt5Core!QJsonPrivate::Parser::parseValue+0x251 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 667]
13 (Inline) -------- -------- -------- -------- Qt5Core!QJsonPrivate::Parser::parseMember+0x34 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 534]
14 00dbcd60 53e6b521 9e1e0d20 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseObject+0xc8 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 489]
15 00dbcd94 53e6a846 9e1e0d0c 00dbce88 35f62056 Qt5Core!QJsonPrivate::Parser::parseValue+0x251 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 667]
16 00dbcdb8 53e6b4dd 9e1e0d58 00dbce88 00000000 Qt5Core!QJsonPrivate::Parser::parseArray+0xf6 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 567]
17 00dbcdec 53e6acc8 9e1e0ea4 00dbce88 00dbce9c Qt5Core!QJsonPrivate::Parser::parseValue+0x20d [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 659]
18 (Inline) -------- -------- -------- -------- Qt5Core!QJsonPrivate::Parser::parseMember+0x34 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 534]
19 00dbce10 53e6a615 9e1e0ed8 01136248 00dbcef0 Qt5Core!QJsonPrivate::Parser::parseObject+0xc8 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 489]
1a 00dbce6c 53e611e1 00dbcea0 00dbcee4 9e1e0e74 Qt5Core!QJsonPrivate::Parser::parse+0x1b5 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsonparser.cpp @ 345]
1b 00dbcec0 783b1419 00dbcef0 00dbceec 00dbcee4 Qt5Core!QJsonDocument::fromJson+0x71 [D:\code\hoyo-qt\qtbase\src\corelib\serialization\qjsondocument.cpp @ 517]It happens on Windows.
Can anyone know why? -
Welcome to the forum.
You appear to have a common C++ programming mistake: holding/returning a reference to temporary object.
jsonObj
refers to the temporary QJsonObject returned byjsonDoc.object()
. QJsonObject is an implicitly shared class, and shares the data with the document it has been created from. Bothjsondoc
and the temporary object are destroyed at function exit. Using the reference afterward is a bad idea. -
Welcome to the forum.
You appear to have a common C++ programming mistake: holding/returning a reference to temporary object.
jsonObj
refers to the temporary QJsonObject returned byjsonDoc.object()
. QJsonObject is an implicitly shared class, and shares the data with the document it has been created from. Bothjsondoc
and the temporary object are destroyed at function exit. Using the reference afterward is a bad idea.@ChrisW67
Hi ChrisW67,Thanks for your welcome and your input! I appreciate you taking the time to look at my code.
I understand your concern about returning a reference to a temporary object, and in general C++ programming, you are absolutely right, it's usually a bad practice and can lead to dangling references.
However, in this specific case with Qt's QJsonObject, I believe the situation is different due to its implicit sharing (copy-on-write) mechanism. jsonDoc.object() does return a temporary QJsonObject, but assigning it to jsonObj (which is a reference parameter) doesn't actually mean jsonObj is directly referencing a temporary object that will be immediately destroyed and become invalid.
Because QJsonObject is implicitly shared, the assignment jsonObj = jsonDoc.object(); results in jsonObj sharing the data of the JSON object with jsonDoc. It's not a deep copy in the traditional sense, but rather a shallow copy and a reference count increment. The underlying JSON data will be valid as long as either jsonDoc or jsonObj (or any other QJsonObject sharing the same data) is alive. Since jsonObj is passed by reference and exists in the calling scope outside of this function, the data it refers to will remain valid even after getObjFromJson returns and jsonDoc is destroyed.
Therefore, I believe in this specific scenario, the code is safe and doesn't suffer from the dangling reference issue you mentioned, precisely because of QJsonObject's implicit sharing.
Regarding the crash I'm experiencing, I suspect it might be caused by something else, perhaps issues within the JSON data itself causing problems during parsing within Qt's internal functions like QLocaleData::bytearrayToLongLong or QJsonPrivate::Parser::parseNumber. The crash stack seems to point towards number parsing issues within Qt's JSON parsing logic.
Could you (or anyone else) perhaps shed more light on whether my understanding of QJsonObject's implicit sharing in this context is correct? And if so, any ideas on what else might be causing the crash based on the stack trace?
Thanks again for your help!
Best regards,
JayceGao
-
@ChrisW67
Hi ChrisW67,Thanks for your welcome and your input! I appreciate you taking the time to look at my code.
I understand your concern about returning a reference to a temporary object, and in general C++ programming, you are absolutely right, it's usually a bad practice and can lead to dangling references.
However, in this specific case with Qt's QJsonObject, I believe the situation is different due to its implicit sharing (copy-on-write) mechanism. jsonDoc.object() does return a temporary QJsonObject, but assigning it to jsonObj (which is a reference parameter) doesn't actually mean jsonObj is directly referencing a temporary object that will be immediately destroyed and become invalid.
Because QJsonObject is implicitly shared, the assignment jsonObj = jsonDoc.object(); results in jsonObj sharing the data of the JSON object with jsonDoc. It's not a deep copy in the traditional sense, but rather a shallow copy and a reference count increment. The underlying JSON data will be valid as long as either jsonDoc or jsonObj (or any other QJsonObject sharing the same data) is alive. Since jsonObj is passed by reference and exists in the calling scope outside of this function, the data it refers to will remain valid even after getObjFromJson returns and jsonDoc is destroyed.
Therefore, I believe in this specific scenario, the code is safe and doesn't suffer from the dangling reference issue you mentioned, precisely because of QJsonObject's implicit sharing.
Regarding the crash I'm experiencing, I suspect it might be caused by something else, perhaps issues within the JSON data itself causing problems during parsing within Qt's internal functions like QLocaleData::bytearrayToLongLong or QJsonPrivate::Parser::parseNumber. The crash stack seems to point towards number parsing issues within Qt's JSON parsing logic.
Could you (or anyone else) perhaps shed more light on whether my understanding of QJsonObject's implicit sharing in this context is correct? And if so, any ideas on what else might be causing the crash based on the stack trace?
Thanks again for your help!
Best regards,
JayceGao
@JayceGao said in crash in QJsonDocument::fromJson:
Therefore, I believe in this specific scenario, the code is safe and doesn't suffer from the dangling reference issue you mentioned, precisely because of QJsonObject's implicit sharing.
You are wrong. References and implicit sharing are two different things. Fix your code.
Assigning something to a reference does nothing with the object itself and therefore no refcount can be increased or decreased. -
@JayceGao said in crash in QJsonDocument::fromJson:
Therefore, I believe in this specific scenario, the code is safe and doesn't suffer from the dangling reference issue you mentioned, precisely because of QJsonObject's implicit sharing.
You are wrong. References and implicit sharing are two different things. Fix your code.
Assigning something to a reference does nothing with the object itself and therefore no refcount can be increased or decreased.Hi again,
Thanks for the continued discussion. To provide more context, here's how I typically use this function in my code:
QString json = ""; // some json text
QJsonObject json_obj;
bool parsed = getObjFromJson(json, json_obj);// other code using json_obj ...
C++
This pattern is used extensively throughout my project, and in the vast majority of cases, it works without any issues. The crash I reported is an occasional occurrence, which makes me wonder if this common usage pattern itself could be problematic, even if it seems to work most of the time.Could you please advise if this typical usage scenario might still have potential issues, despite the implicit sharing of QJsonObject? Or is it more likely that the occasional crashes are indeed related to the content of the json string being parsed, or perhaps some other factor unrelated to the function's structure and temporary object handling?
Thank you again for your insights.
Best regards,
JayceGao
-
Hi again,
Thanks for the continued discussion. To provide more context, here's how I typically use this function in my code:
QString json = ""; // some json text
QJsonObject json_obj;
bool parsed = getObjFromJson(json, json_obj);// other code using json_obj ...
C++
This pattern is used extensively throughout my project, and in the vast majority of cases, it works without any issues. The crash I reported is an occasional occurrence, which makes me wonder if this common usage pattern itself could be problematic, even if it seems to work most of the time.Could you please advise if this typical usage scenario might still have potential issues, despite the implicit sharing of QJsonObject? Or is it more likely that the occasional crashes are indeed related to the content of the json string being parsed, or perhaps some other factor unrelated to the function's structure and temporary object handling?
Thank you again for your insights.
Best regards,
JayceGao
@JayceGao
Your code as shown is fine, so long as you only use theQJsonObject json_obj;
and its correspondingQJsonDocument jsonDoc
within the scope it is declared, e.g. if this is in a function until the end of that function. What is wrong in your original is that you have a reference parameterQJsonObject& jsonObj
to your function, and that gets assigned to the result ofjsonDoc.object()
, which itself is an object in theQJsonDocument jsonDoc
. Nothing copies that object. So on exit from the function it references an object which has been destroyed.It is possible that it "works" from time to time depending on what has happened to the memory holding the object since the function was exited. The behaviour is of course "undefined" ("UB"): as soon as the memory gets clobbered crashes are likely, if the memory happens not to have been overwritten/used elsewhere it will appear to work. That might be why you see an "intermittent" problem.
Btw, it is unlikely that Qt JSON code would "crash" on encountering malformed or problematic JSON text to parse. That would be a bug in Qt code. Rather you should find that the parse() (
fromJson()
) call returns failure and sets aQJsonParseError
you can examine, if that happened. -
@JayceGao
Your code as shown is fine, so long as you only use theQJsonObject json_obj;
and its correspondingQJsonDocument jsonDoc
within the scope it is declared, e.g. if this is in a function until the end of that function. What is wrong in your original is that you have a reference parameterQJsonObject& jsonObj
to your function, and that gets assigned to the result ofjsonDoc.object()
, which itself is an object in theQJsonDocument jsonDoc
. Nothing copies that object. So on exit from the function it references an object which has been destroyed.It is possible that it "works" from time to time depending on what has happened to the memory holding the object since the function was exited. The behaviour is of course "undefined" ("UB"): as soon as the memory gets clobbered crashes are likely, if the memory happens not to have been overwritten/used elsewhere it will appear to work. That might be why you see an "intermittent" problem.
Btw, it is unlikely that Qt JSON code would "crash" on encountering malformed or problematic JSON text to parse. That would be a bug in Qt code. Rather you should find that the parse() (
fromJson()
) call returns failure and sets aQJsonParseError
you can examine, if that happened. -
Hi,
One thing you could do is use the swap function.
Other possibilities, return the QJsonObject and pass the Boolean as output parameter (like for example the QString conversion functions).
Use std::optional for the return value so you don't need an output parameter.
-
Since QJsonObject() has a function isEmpty() I don't see why you can't simply return a QJsonObject
-
Since QJsonObject() has a function isEmpty() I don't see why you can't simply return a QJsonObject
@Christian-Ehrlicher one possible reason that comes to mind: empty does not imply failure to load the json data and it might be a valid case.
-
Return the
QJsonObject
and pass a pointer to a boolean to set a result, if necessary at all.static QJsonObject getObjFromJson(const QString &json, bool *ok = nullptr) { QJsonParseError jsonParseError; const QJsonDocument jsonDoc = QJsonDocument::fromJson(json.toUtf8(), &jsonParseError); const bool result = jsonParseError.error == QJsonParseError::NoError; if (ok) *ok = result; return jsonDoc.object(); }