[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:
TD-er
2018-05-13 23:12:02 +02:00
parent 0e8b333f48
commit 0b1f9a81b5
4 changed files with 50 additions and 35 deletions
+17 -17
View File
@@ -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
View File
@@ -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;
+2 -1
View File
@@ -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
View File
@@ -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>");