ZNCString: guard Replace/Split against empty-width arguments

CString::Replace with an empty sReplace underflowed 'p += uReplaceWidth - 1'
to SIZE_MAX and then the per-iteration 'p++' brought p back to the same
byte, so the function looped forever appending sWith to the output and
eventually OOMed.

CString::Split with empty sDelim and bAllowEmpty=false spun in the
prefix-skip loops because strncasecmp(p, "", 0) is unconditionally 0 and
p += 0 never advances.

No in-tree caller currently passes the bad argument, but the public
library API should not be a bear trap for module authors. Same shape as
#1994.
This commit is contained in:
MarkLee131
2026-04-25 10:38:02 +08:00
parent 8566db72dd
commit 514b47cad3
+14 -2
View File
@@ -626,6 +626,14 @@ unsigned int CString::Replace(const CString& sReplace, const CString& sWith,
unsigned int CString::Replace(CString& sStr, const CString& sReplace,
const CString& sWith, const CString& sLeft,
const CString& sRight, bool bRemoveDelims) {
// An empty needle would make strncmp(_, _, 0) match at every position
// and `p += uReplaceWidth - 1` underflow to SIZE_MAX, producing an
// infinite loop that appends sWith until OOM. Guard at the entry so
// the invariant "the loop always advances" holds.
if (sReplace.empty()) {
return 0;
}
unsigned int uRet = 0;
CString sCopy = sStr;
sStr.clear();
@@ -851,7 +859,10 @@ CString::size_type CString::Split(const CString& sDelim, VCString& vsRet,
size_type uRightLen = sRight.length();
const char* p = c_str();
if (!bAllowEmpty) {
// An empty delimiter with bAllowEmpty=false would spin forever in the
// prefix-skip / post-token loops below because `strncasecmp(_, _, 0)`
// returns 0 and `p += 0` never advances.
if (!bAllowEmpty && uDelimLen) {
while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
p += uDelimLen;
}
@@ -890,7 +901,8 @@ CString::size_type CString::Split(const CString& sDelim, VCString& vsRet,
sTmp.clear();
p += uDelimLen;
if (!bAllowEmpty) {
// Same zero-width guard as at the top of the function.
if (!bAllowEmpty && uDelimLen) {
while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
p += uDelimLen;
}