mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-07-27 19:57:38 +00:00
[memory corruption] LWIP data corrupts log buffer
Not sure how, but the headers of new JSON message can corrupt the buffer in the web log buffer. In this attempt the log buffer lines are now moved to heap instead of stack. Also the streaming webserver is given a bit more time to handle requests. Steps to reproduce: Set web log level to maximum and open web log viewer. Then wait a few seconds to minutes to let the JSON stream stop and look at the last sent Json message. This will contain some typical Json web headers and thus no valid Json anymore. Also added a quick fix for #1419
This commit is contained in:
@@ -5,10 +5,10 @@ import struct
|
||||
import md5
|
||||
import os
|
||||
MD5DUMMY = "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere..." #48 chars
|
||||
FILENAMEDUMMY = "ThisIsTheDummyPlaceHolderForTheBinaryFilename..." #48 chars
|
||||
FILENAMEDUMMY = "ThisIsTheDummyPlaceHolderForTheBinaryFilename64ByteLongFilenames" #64 chars
|
||||
|
||||
MemorySegmentStart,MemorySegmentEnd,MemoryContent=[],[],[]
|
||||
|
||||
|
||||
##################################################################
|
||||
# this subroutine shows the segments of a part
|
||||
##################################################################
|
||||
@@ -18,7 +18,7 @@ def showSegments (fileContent,offset):
|
||||
herestr =""
|
||||
herestr2 =""
|
||||
MemorySegmentStart.append(struct.pack("I",header[0]))
|
||||
MemorySegmentEnd.append(struct.pack("I",header[0]+header[1]))
|
||||
MemorySegmentEnd.append(struct.pack("I",header[0]+header[1]))
|
||||
MemoryContent.append(fileContent[offset+8:offset+8+header[1]])
|
||||
if fileContent.find( MD5DUMMY, offset+8, offset+8+header[1]) >0 :
|
||||
herestr= " <-- CRC is here."
|
||||
@@ -42,7 +42,7 @@ def showParts(fileContent, offset):
|
||||
return nextSegmentOffset
|
||||
|
||||
##################################################################
|
||||
# MAIN
|
||||
# MAIN
|
||||
##################################################################
|
||||
|
||||
#if len(sys.argv) !=2 :
|
||||
@@ -71,21 +71,21 @@ includeStr = "hash includes segments:"
|
||||
# 4: RAM
|
||||
|
||||
for i in (1,2 ): # use only stable segments, must be 4 in total. We use 2.
|
||||
startArray =startArray + MemorySegmentStart[i]
|
||||
endArray = endArray + MemorySegmentEnd[i]
|
||||
startArray =startArray + MemorySegmentStart[i]
|
||||
endArray = endArray + MemorySegmentEnd[i]
|
||||
hashString =hashString + MemoryContent[i]
|
||||
with open(FileName+str(i), mode='wb') as file: # b is important -> binary
|
||||
file.write(MemoryContent[i])
|
||||
includeStr = includeStr +" "+ str(i)
|
||||
print (includeStr)
|
||||
# IMPORTANT: pad array with zeros if you use only 3 segments (see above)
|
||||
while len(startArray) < 16 :
|
||||
startArray =startArray + struct.pack("I",0)
|
||||
endArray = endArray + struct.pack("I",0)
|
||||
# debug print (binascii.hexlify(startArray))
|
||||
# IMPORTANT: pad array with zeros if you use only 3 segments (see above)
|
||||
while len(startArray) < 16 :
|
||||
startArray =startArray + struct.pack("I",0)
|
||||
endArray = endArray + struct.pack("I",0)
|
||||
# debug print (binascii.hexlify(startArray))
|
||||
# debug print (binascii.hexlify(endArray))
|
||||
if (len(endArray) + len (startArray)) != 32 :
|
||||
print("ERROR: please make sure you add / remove padding if you change the semgents.")
|
||||
print("ERROR: please make sure you add / remove padding if you change the semgents.")
|
||||
|
||||
BinaryFileName = "";
|
||||
if fileContent.find( FILENAMEDUMMY) < 0:
|
||||
@@ -95,22 +95,22 @@ else:
|
||||
if len(BinaryFileName) >48: # check that filename is <48 chars
|
||||
BinaryFileName=BinaryFileName[0:48] # truncate if necessary. 49th char in ESP is zero already
|
||||
else:
|
||||
BinaryFileName= BinaryFileName.ljust(48,'\0'); # pad with zeros.
|
||||
|
||||
BinaryFileName= BinaryFileName.ljust(48,'\0'); # pad with zeros.
|
||||
|
||||
|
||||
if fileContent.find( MD5DUMMY) < 0:
|
||||
print("ERROR: MD5 dummy not found in binary")
|
||||
else:
|
||||
hashString=hashString.replace (MD5DUMMY,"",1)
|
||||
hashString=hashString.replace (MD5DUMMY,"",1)
|
||||
m = md5.new()
|
||||
m.update (hashString) #use segment 1
|
||||
md5hash = m.digest()
|
||||
print("MD5 hash: "+ m.hexdigest())
|
||||
print("\nwriting output file:\n" + FileName)
|
||||
|
||||
|
||||
fileContent=fileContent.replace(MD5DUMMY,md5hash+startArray+endArray)
|
||||
fileContent=fileContent.replace(FILENAMEDUMMY,BinaryFileName)
|
||||
|
||||
with open(FileName, mode='wb') as file: # b is important -> binary
|
||||
file.write(fileContent)
|
||||
#k=input("press close to exit")
|
||||
#k=input("press close to exit")
|
||||
|
||||
+19
-12
@@ -490,7 +490,7 @@ WiFiUDP portUDP;
|
||||
|
||||
struct CRCStruct{
|
||||
char compileTimeMD5[16+32+1]= "MD5_MD5_MD5_MD5_BoundariesOfTheSegmentsGoHere...";
|
||||
char binaryFilename[16+32+1]= "ThisIsTheDummyPlaceHolderForTheBinaryFilename...";
|
||||
char binaryFilename[32+32+1]= "ThisIsTheDummyPlaceHolderForTheBinaryFilename64ByteLongFilenames";
|
||||
char compileTime[16]= __TIME__;
|
||||
char compileDate[16]= __DATE__;
|
||||
uint8_t runTimeMD5[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
|
||||
@@ -943,7 +943,7 @@ struct EventStruct
|
||||
struct LogStruct {
|
||||
LogStruct() : write_idx(0), read_idx(0) {
|
||||
for (int i = 0; i < LOG_STRUCT_MESSAGE_LINES; ++i) {
|
||||
memset(Message[i], 0, LOG_STRUCT_MESSAGE_SIZE);
|
||||
Message[i].reserve(LOG_STRUCT_MESSAGE_SIZE);
|
||||
timeStamp[i] = 0;
|
||||
log_level[i] = 0;
|
||||
}
|
||||
@@ -957,7 +957,13 @@ struct LogStruct {
|
||||
}
|
||||
timeStamp[write_idx] = millis();
|
||||
log_level[write_idx] = loglevel;
|
||||
strncpy(Message[write_idx], line, LOG_STRUCT_MESSAGE_SIZE-1);
|
||||
unsigned linelength = strlen(line);
|
||||
if (linelength > LOG_STRUCT_MESSAGE_SIZE-1)
|
||||
linelength = LOG_STRUCT_MESSAGE_SIZE-1;
|
||||
Message[write_idx] = "";
|
||||
for (unsigned i = 0; i < linelength; ++i) {
|
||||
Message[write_idx] += *(line + i);
|
||||
}
|
||||
}
|
||||
|
||||
// Read the next item and append it to the given string.
|
||||
@@ -970,17 +976,18 @@ struct LogStruct {
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
bool get_logjson_formatted(String& output, unsigned long& timestamp) {
|
||||
String get_logjson_formatted(bool& logLinesAvailable, unsigned long& timestamp) {
|
||||
logLinesAvailable = false;
|
||||
if (isEmpty()) {
|
||||
output = "";
|
||||
return false;
|
||||
return "";
|
||||
}
|
||||
read_idx = (read_idx + 1) % LOG_STRUCT_MESSAGE_LINES;
|
||||
output = logjson_formatLine(read_idx);
|
||||
timestamp = timeStamp[read_idx];
|
||||
if (isEmpty()) return false;
|
||||
String output = logjson_formatLine(read_idx);
|
||||
if (isEmpty()) return output;
|
||||
output += ",\n";
|
||||
return true;
|
||||
logLinesAvailable = true;
|
||||
return output;
|
||||
}
|
||||
|
||||
bool get(String& output, const String& lineEnd, int line) {
|
||||
@@ -1020,11 +1027,11 @@ struct LogStruct {
|
||||
|
||||
String logjson_formatLine(int index) {
|
||||
String output;
|
||||
output.reserve(LOG_STRUCT_MESSAGE_SIZE + 40);
|
||||
output.reserve(LOG_STRUCT_MESSAGE_SIZE + 64);
|
||||
output = "{";
|
||||
output += to_json_object_value("timestamp", String(timeStamp[index]));
|
||||
output += ",\n";
|
||||
output += to_json_object_value("text", String(Message[index]));
|
||||
output += to_json_object_value("text", Message[index]);
|
||||
output += ",\n";
|
||||
output += to_json_object_value("level", String(log_level[index]));
|
||||
output += "}";
|
||||
@@ -1036,7 +1043,7 @@ struct LogStruct {
|
||||
int read_idx;
|
||||
unsigned long timeStamp[LOG_STRUCT_MESSAGE_LINES];
|
||||
byte log_level[LOG_STRUCT_MESSAGE_LINES];
|
||||
char Message[LOG_STRUCT_MESSAGE_LINES][LOG_STRUCT_MESSAGE_SIZE];
|
||||
String Message[LOG_STRUCT_MESSAGE_LINES];
|
||||
|
||||
} Logging;
|
||||
|
||||
|
||||
@@ -142,12 +142,13 @@ String to_json_object_value(const String& object, const String& value) {
|
||||
result = wrap_String(object, F("\""));
|
||||
result += F(":");
|
||||
if (value.length() == 0 || !isFloat(value)) {
|
||||
if (value.indexOf('\n') == -1 && value.indexOf('"') == -1) {
|
||||
if (value.indexOf('\n') == -1 && value.indexOf('"') == -1 && value.indexOf("Pragma") == -1) {
|
||||
result += wrap_String(value, F("\""));
|
||||
} else {
|
||||
String tmpValue(value);
|
||||
tmpValue.replace('\n', '^');
|
||||
tmpValue.replace('"', '\'');
|
||||
tmpValue.replace("Pragma", "Bugje!");
|
||||
result += wrap_String(tmpValue, F("\""));
|
||||
}
|
||||
} else {
|
||||
|
||||
+12
-5
@@ -196,16 +196,20 @@ void sendContentBlocking(String& data) {
|
||||
|
||||
TXBuffer.sentBytes += length;
|
||||
data = "";
|
||||
yield();
|
||||
}
|
||||
|
||||
void sendHeaderBlocking(bool json) {
|
||||
checkRAM(F("sendHeaderBlocking"));
|
||||
WebServer.client().flush();
|
||||
#if defined(ESP8266) && defined(ARDUINO_ESP8266_RELEASE_2_3_0)
|
||||
WebServer.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
WebServer.sendHeader(F("Content-Type"), json ? F("application/json") : F("text/html"), true);
|
||||
WebServer.sendHeader(F("Accept-Ranges"), F("none"));
|
||||
WebServer.sendHeader(F("Cache-Control"), F("no-cache"));
|
||||
WebServer.sendHeader(F("Transfer-Encoding"), F("chunked"));
|
||||
if (json)
|
||||
WebServer.sendHeader("Access-Control-Allow-Origin","*");
|
||||
WebServer.send(200);
|
||||
#else
|
||||
unsigned int timeout = 0;
|
||||
@@ -216,6 +220,8 @@ void sendHeaderBlocking(bool json) {
|
||||
WebServer.setContentLength(CONTENT_LENGTH_UNKNOWN);
|
||||
WebServer.sendHeader(F("Content-Type"), json ? F("application/json") : F("text/html"), true);
|
||||
WebServer.sendHeader(F("Cache-Control"), F("no-cache"));
|
||||
if (json)
|
||||
WebServer.sendHeader("Access-Control-Allow-Origin","*");
|
||||
WebServer.send(200);
|
||||
// dont wait on 2.3.0. Memory returns just too slow.
|
||||
while ((ESP.getFreeHeap() < freeBeforeSend) &&
|
||||
@@ -224,6 +230,7 @@ void sendHeaderBlocking(bool json) {
|
||||
delay(1);
|
||||
}
|
||||
#endif
|
||||
yield();
|
||||
}
|
||||
|
||||
void sendHeadandTail(const String& tmplName, boolean Tail = false) {
|
||||
@@ -2931,7 +2938,6 @@ void handle_log() {
|
||||
// Web Interface JSON log page
|
||||
//********************************************************************************
|
||||
void handle_log_JSON() {
|
||||
WebServer.sendHeader("Access-Control-Allow-Origin","*");
|
||||
TXBuffer.startJsonStream();
|
||||
String webrequest = WebServer.arg(F("view"));
|
||||
TXBuffer += F("{\"Log\": {");
|
||||
@@ -2948,14 +2954,12 @@ void handle_log_JSON() {
|
||||
TXBuffer += F("],\n");
|
||||
}
|
||||
TXBuffer += F("\"Entries\": [");
|
||||
String reply;
|
||||
reply.reserve(LOG_STRUCT_MESSAGE_SIZE + 40);
|
||||
bool logLinesAvailable = true;
|
||||
int nrEntries = 0;
|
||||
unsigned long firstTimeStamp = 0;
|
||||
unsigned long lastTimeStamp = 0;
|
||||
while (logLinesAvailable) {
|
||||
logLinesAvailable = Logging.get_logjson_formatted(reply, lastTimeStamp);
|
||||
String reply = Logging.get_logjson_formatted(logLinesAvailable, lastTimeStamp);
|
||||
if (reply.length() > 0) {
|
||||
TXBuffer += reply;
|
||||
if (nrEntries == 0) {
|
||||
@@ -3522,7 +3526,6 @@ void stream_last_json_object_value(const String& object, const String& value) {
|
||||
void handle_json()
|
||||
{
|
||||
String tasknr = WebServer.arg("tasknr");
|
||||
WebServer.sendHeader("Access-Control-Allow-Origin","*");
|
||||
TXBuffer.startJsonStream();
|
||||
|
||||
if (tasknr.length() == 0)
|
||||
@@ -3538,6 +3541,7 @@ void handle_json()
|
||||
stream_next_json_object_value(F("Name"), String(Settings.Name));
|
||||
stream_next_json_object_value(F("Uptime"), String(wdcounter / 2));
|
||||
stream_next_json_object_value(F("Last boot cause"), getLastBootCauseString());
|
||||
stream_next_json_object_value(F("Reset Reason"), ESP.getResetReason());
|
||||
|
||||
if (wdcounter > 0)
|
||||
{
|
||||
@@ -4889,6 +4893,9 @@ void handle_sysinfo() {
|
||||
TXBuffer += RTC.bootCounter;
|
||||
TXBuffer += F(")");
|
||||
|
||||
TXBuffer += F("<TR><TD>Reset Reason<TD>");
|
||||
TXBuffer += ESP.getResetReason();
|
||||
|
||||
TXBuffer += F("<TR><TD colspan=2><H3>Network");
|
||||
addHelpButton(F("Wifi"));
|
||||
TXBuffer += F("</H3></TD></TR>");
|
||||
|
||||
Reference in New Issue
Block a user