Fix MString::read() infinite retry loop, HTTPRequest parser fd leak, Log deadlock, log rotation symlink handling, and JString bounds checks
- MString::read() now gives up after repeated I/O errors instead of looping forever and crashing via realloc() failure (root cause of a recurring barant.com production crash). - HTTPRequest::parse() no longer leaks lineParser/headerParser on a malformed request. - Log gained size-based rotation (config-driven) and a non-static-mutex fix; a separate fix stopped rotation from destroying the logs symlink on its first trigger. - File.cpp: symlink-preserving rotation support. - JString: fixed an array-index out-of-bounds and a setString issue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
694c6b80a4
commit
05374bdb15
7 files changed
+213
-14
No files matched your search
+38
-4
@@ -23,7 +23,26 @@ namespace coreutils {
|
||||
if (!checkParse(*lineParser))
|
||||
return Status::INCOMPLETE;
|
||||
{
|
||||
// line is a view into reader's own buffer (see
|
||||
// LineParser::parse()), not into lineParser itself - safe to
|
||||
// delete lineParser immediately, before the parts.size()
|
||||
// check below can throw. Previously this delete sat AFTER
|
||||
// that check, so a malformed request line (any count other
|
||||
// than exactly 3 space-separated parts - confirmed live via
|
||||
// real scanner/bot traffic hitting this daily) leaked
|
||||
// lineParser and left the member non-NULL. Since state is
|
||||
// never advanced past REQUEST_LINE on this throw, ANY later
|
||||
// reentry into this same case (state itself surviving the
|
||||
// exception, e.g. if this HTTPRequest is ever driven again
|
||||
// before its owning session tears down) would see a
|
||||
// non-NULL lineParser and skip allocating a fresh one,
|
||||
// reusing an already-completed, now-stale parser instance
|
||||
// instead - a real invariant violation that a heap-
|
||||
// corruption crash (munmap_chunk(): invalid pointer) was
|
||||
// traced back to.
|
||||
ZString line = lineParser->getLine();
|
||||
delete lineParser;
|
||||
lineParser = NULL;
|
||||
ZString sp(" ");
|
||||
std::vector<ZString> &parts = line.split(sp);
|
||||
if (parts.size() != 3)
|
||||
@@ -33,8 +52,6 @@ namespace coreutils {
|
||||
path = MString(parts[1]);
|
||||
version = MString(parts[2]);
|
||||
}
|
||||
delete lineParser;
|
||||
lineParser = NULL;
|
||||
{
|
||||
// Split the query string off before anything else sees `path` -
|
||||
// a route table (see the APIServer plan) should never have to
|
||||
@@ -49,16 +66,33 @@ namespace coreutils {
|
||||
return Status::DATA_AVAIL;
|
||||
}
|
||||
|
||||
case HEADERS:
|
||||
case HEADERS: {
|
||||
if (!headerParser)
|
||||
headerParser = new IMFHeader(reader);
|
||||
if (!checkParse(*headerParser))
|
||||
// The actual throw for a malformed header (e.g. a header line
|
||||
// with no ':') happens deep inside checkParse() -> IMFHeader::
|
||||
// parse(), well before headerParser->getHeaders() below ever
|
||||
// runs - same leaked-pointer/stale-reentry hazard as
|
||||
// REQUEST_LINE above, confirmed as the other half of the same
|
||||
// real crash. Cleaning up here before rethrowing keeps
|
||||
// headerParser NULL on every exit path, matching the
|
||||
// REQUEST_LINE fix.
|
||||
bool complete;
|
||||
try {
|
||||
complete = checkParse(*headerParser);
|
||||
} catch (...) {
|
||||
delete headerParser;
|
||||
headerParser = NULL;
|
||||
throw;
|
||||
}
|
||||
if (!complete)
|
||||
return Status::INCOMPLETE;
|
||||
headers = headerParser->getHeaders();
|
||||
delete headerParser;
|
||||
headerParser = NULL;
|
||||
state = BODY;
|
||||
return Status::DATA_AVAIL;
|
||||
}
|
||||
|
||||
case BODY: {
|
||||
MString contentLengthHeader = getHeader("Content-Length");
|
||||
|
||||
Reference in new issue
Block a user