Fix memory corruption

This commit is contained in:
Stephan Hadinger 2020-09-23 19:38:24 +02:00
parent c1fd24efc6
commit f3591b8419
15 changed files with 47 additions and 29 deletions

View File

@ -1,9 +1,15 @@
# JSMN lightweight JSON parser for Tasmota
Intro:
Intro: this library uses the JSMN in-place JSON parser.
See https://github.com/zserge/jsmn and https://zserge.com/jsmn/
It is proposed as a replacement for ArduinoJson. It has less features, does only parsing but does it in a very efficient way.
## Benefits
First, the memory impact is very low: 4 bytes per token and no need to add an extra buffer for values.
Second, the code is much smaller than ArduinoJson by 5-7KB.
## How to use
`{"Device":"0x1234","Power":true,"Temperature":25.5}`
@ -24,7 +30,8 @@ If what you need is to parse a JSON Object for values with default values:
#include "JsonParser.h"
char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}";
JsonParserObject root = JsonParser(json_buffer).getRootObject();
JsonParser parser(json_buffer);
JsonParserObject root = parser.getRootObject();
if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; }
uint16_t d = root.getUInt(PSTR("DEVICE"), 0xFFFF); // case insensitive
@ -37,7 +44,8 @@ Alternative pattern, if you want to test the existence of the attribute first:
#include "JsonParser.h"
char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}";
JsonParserObject root = JsonParser(json_buffer).getRootObject();
JsonParser parser(json_buffer);
JsonParserObject root = parser.getRootObject();
if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; }
JsonParserToken val = root[PSTR("DEVICE")];
@ -54,6 +62,12 @@ if (val) {
}
```
WARNING: never use the following form:
```
JsonParserObject root = JsonParser(json_buffer).getRootObject();
```
In this case, the `JsonParser` object is temporary and destroyed at the end of the expression. Setting the JsonParser to a local variable ensures that the lifetime of the object is extended to the end of the scope.
## Types and conversion
JSMN relies on the concept of JSON Tokens `JsonParserToken`. Tokens do not hold any data, but point to token boundaries in JSON string instead. Every jsmn token has a type, which indicates the type of corresponding JSON token. JSMN for Tasmota extends the type from JSMN to ease further parsing.

View File

@ -15,7 +15,7 @@ int main(int argc, char* argv[]) {
// char * json_str = test_complex;
char * json_str = test_simple;
JsonParser parser(64); // size for 64 tokens
// JsonParser parser(64); // size for 64 tokens
int r = parser.parse(json_str);

View File

@ -1473,7 +1473,8 @@ bool JsonTemplate(char* dataBuf)
if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks
JsonParserObject root = JsonParser((char*) dataBuf).getRootObject();
JsonParser parser((char*) dataBuf);
JsonParserObject root = parser.getRootObject();
if (!root) { return false; }
// All parameters are optional allowing for partial changes

View File

@ -27,8 +27,6 @@
#define XDRV_01 1
#include "JsonParser.h"
#ifndef WIFI_SOFT_AP_CHANNEL
#define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI
#endif
@ -3366,7 +3364,8 @@ bool JsonWebColor(const char* dataBuf)
}
}
#else
JsonParserObject root = JsonParser((char*) dataBuf).getRootObject();
JsonParser parser((char*) dataBuf);
JsonParserObject root = parser.getRootObject();
JsonParserArray arr = root[PSTR(D_CMND_WEBCOLOR)].getArray();
if (arr) { // if arr is valid, i.e. json is valid, the key D_CMND_WEBCOLOR was found and the token is an arra
uint32_t i = 0;

View File

@ -25,7 +25,6 @@
#define XDRV_05 5
#include <IRremoteESP8266.h>
#include "JsonParser.h"
enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND };
@ -204,7 +203,8 @@ uint32_t IrRemoteCmndIrSendJson(void)
uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))];
#else
RemoveSpace(XdrvMailbox.data); // TODO is this really needed?
JsonParserObject root = JsonParser((char*) XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) { return IE_INVALID_JSON; }
// IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 }

View File

@ -29,7 +29,6 @@
#include <IRrecv.h>
#include <IRutils.h>
#include <IRac.h>
#include "JsonParser.h"
enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC,
IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL };
@ -288,7 +287,8 @@ uint32_t IrRemoteCmndIrHvacJson(void)
stdAc::state_t state, prev;
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received %s"), XdrvMailbox.data);
JsonParserObject root = JsonParser((char*) XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) { return IE_INVALID_JSON; }
// from: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/CommonAcControl/CommonAcControl.ino

View File

@ -425,7 +425,8 @@ void CmndTimer(void)
#if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0
if (devices_present) {
#endif
JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) {
Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed
error = 1;

View File

@ -532,7 +532,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule)
#else
String buf = event; // copy the string into a new buffer that will be modified
JsonParser parser = JsonParser((char*)buf.c_str());
JsonParser parser((char*)buf.c_str());
JsonParserObject obj = parser.getRootObject();
if (!obj) {
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event too long (%d)"), event.length());
@ -1069,7 +1069,8 @@ bool RulesMqttData(void)
if (event_item.Key.length() == 0) { //If did not specify Key
value = sData;
} else { //If specified Key, need to parse Key/Value from JSON data
JsonParserObject jsonData = JsonParser((char*)sData.c_str()).getRootObject();
JsonParser parser((char*)sData.c_str());
JsonParserObject jsonData = parser.getRootObject();
String key1 = event_item.Key;
String key2;

View File

@ -104,7 +104,8 @@ void CmndRfSend(void)
int repeat = 10;
int pulse = 350;
JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (root) {
// RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350}
char parm_uc[10];

View File

@ -569,7 +569,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) {
if (Webserver->args()) {
response = "[";
JsonParser parser = JsonParser((char*) Webserver->arg((Webserver->args())-1).c_str());
JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str());
JsonParserObject root = parser.getRootObject();
JsonParserToken hue_on = root[PSTR("on")];

View File

@ -19,8 +19,6 @@
#ifdef USE_ZIGBEE
#include "JsonParser.h"
#ifndef ZIGBEE_SAVE_DELAY_SECONDS
#define ZIGBEE_SAVE_DELAY_SECONDS 2 // wait for 2s before saving Zigbee info
#endif

View File

@ -218,7 +218,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) {
if (Webserver->args()) {
response = "[";
JsonParser parser = JsonParser((char*) Webserver->arg((Webserver->args())-1).c_str());
JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str());
JsonParserObject root = parser.getRootObject();
JsonParserToken hue_on = root[PSTR("on")];

View File

@ -21,8 +21,6 @@
#define XDRV_23 23
#include "JsonParser.h"
const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix
#ifdef USE_ZIGBEE_ZNP
D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|"
@ -626,7 +624,8 @@ void CmndZbSend(void) {
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} }
// ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} }
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params
@ -748,7 +747,8 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
// local endpoint is always 1, IEEE addresses are calculated
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// params
@ -1064,8 +1064,8 @@ void CmndZbSave(void) {
// ZbRestore {"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]}
void CmndZbRestore(void) {
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
JsonParser p(XdrvMailbox.data);
JsonParserToken root = p.getRoot();
JsonParser parser(XdrvMailbox.data);
JsonParserToken root = parser.getRoot();
if (!p || !(root.isObject() || root.isArray())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
@ -1219,7 +1219,8 @@ void CmndZbConfig(void) {
// if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
RemoveAllSpaces(XdrvMailbox.data);
if (strlen(XdrvMailbox.data) > 0) {
JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject();
JsonParser parser(XdrvMailbox.data);
JsonParserObject root = parser.getRootObject();
if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
// Channel

View File

@ -1328,7 +1328,8 @@ void ThermostatDebug(uint8_t ctr_output)
#endif // DEBUG_THERMOSTAT
void ThermostatGetLocalSensor(uint8_t ctr_output) {
JsonParserObject root = JsonParser(mqtt_data).getRootObject();
JsonParser parser(mqtt_data);
JsonParserObject root = parser.getRootObject();
if (root) {
JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")];
if (value_token.isNum()) {

View File

@ -226,7 +226,8 @@ void TelegramAnalizeMessage(void) {
Telegram.message[i][5] = "";
String buf = Telegram.message[i][0]; // we need to keep a copy of the buffer
JsonParserObject root = JsonParser((char*)buf.c_str()).getRootObject();
JsonParser parser((char*)buf.c_str());
JsonParserObject root = parser.getRootObject();
if (root) {
Telegram.message[i][0] = root["update_id"].getStr();
Telegram.message[i][1] = root["message"].getObject()["from"].getObject()["id"].getStr();