tcp: log pre-truncated oversize frames

Why:

TCP inputs can detect oversized frames before the core submit path sees a raw message larger than maxMessageSize. That left oversizemsg.errorfile unwritten for truncated TCP input.

Impact:

Configured oversize error logs now receive a JSON record for imtcp and imptcp frames that are truncated before core submit.

Before/After:

Before, pre-truncated TCP frames only emitted the normal warning; after, they also honor oversizemsg.errorfile.

Technical Overview:

Add a per-session flag that marks the current TCP frame as oversized when imtcp or imptcp detects the condition before submit. When the truncated message object is created, write the configured oversize JSON record before handing it to the ratelimit/submit path. Reset the flag when the message is submitted or a new frame starts. Add an imptcp regression test for oversizemsg.input.mode=truncate with oversizemsg.errorfile.

Closes: https://github.com/rsyslog/rsyslog/issues/5228

With the help of AI-Agents: Codex
This commit is contained in:
Rainer Gerhards 2026-05-18 11:55:58 +02:00
parent e722703739
commit d0bd66241f
5 changed files with 52 additions and 0 deletions

View File

@ -295,6 +295,7 @@ struct ptcpsess_s {
sbool bSuppOctetFram; /**< copy from listener, to speed up access */
sbool bSPFramingFix;
enum { eAtStrtFram, eInOctetCnt, eInMsg, eInMsgTruncation } inputState; /* our current state */
sbool bFrameOversize; /* current frame exceeded maxMessageSize before submit */
int iOctetsRemain; /* Number of Octets remaining in message */
TCPFRAMINGMODE eFraming;
uchar *pMsg; /* message (fragment) received */
@ -971,6 +972,9 @@ static rsRetVal doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t
MsgSetRcvFrom(pMsg, pThis->peerName);
CHKiRet(MsgSetRcvFromIP(pMsg, pThis->peerIP));
MsgSetRuleset(pMsg, pSrv->pRuleset);
if (pThis->bFrameOversize) {
writeOversizeMessageLog(pMsg);
}
localRet = ratelimitAddMsg(pSrv->ratelimiter, pMultiSub, pMsg);
if (localRet == RS_RET_OK) {
STATSCOUNTER_INC(pThis->pLstn->ctrSubmit, pThis->pLstn->mutCtrSubmit);
@ -987,6 +991,7 @@ finalize_it:
/* reset status variables */
pThis->bAtStrtOfFram = 1;
pThis->iMsg = 0;
pThis->bFrameOversize = 0;
RETiRet;
}
@ -1070,6 +1075,7 @@ static rsRetVal ATTR_NONNULL(1, 2) processDataRcvd(ptcpsess_t *const __restrict_
if (pThis->inputState == eAtStrtFram) {
if (pThis->bSuppOctetFram && isdigit((int)c)) {
pThis->inputState = eInOctetCnt;
pThis->bFrameOversize = 0;
pThis->iOctetsRemain = 0;
pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
} else if (pThis->bSPFramingFix && c == ' ') {
@ -1081,6 +1087,7 @@ static rsRetVal ATTR_NONNULL(1, 2) processDataRcvd(ptcpsess_t *const __restrict_
FINALIZE;
} else {
pThis->inputState = eInMsg;
pThis->bFrameOversize = 0;
pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
}
}
@ -1116,6 +1123,7 @@ static rsRetVal ATTR_NONNULL(1, 2) processDataRcvd(ptcpsess_t *const __restrict_
propPeerName, propPeerIP, pThis->iOctetsRemain);
pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
} else if (pThis->iOctetsRemain > iMaxLine) {
pThis->bFrameOversize = 1;
/* while we can not do anything against it, we can at least log an indication
* that something went wrong) -- rgerhards, 2008-03-14
*/
@ -1159,6 +1167,7 @@ static rsRetVal ATTR_NONNULL(1, 2) processDataRcvd(ptcpsess_t *const __restrict_
"imptcp %s: message received is at least %d byte larger than "
"max msg size; message will be split starting at: \"%.*s\"\n",
pThis->pLstn->pSrv->pszInputName, i, (i < 32) ? i : 32, *buff);
pThis->bFrameOversize = 1;
doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
iMsg = 0;
++(*pnMsgs);

View File

@ -357,6 +357,9 @@ static rsRetVal defaultDoSubmitMessage(tcps_sess_t *pThis,
CHKiRet(MsgSetRcvFromIP(pMsg, pThis->fromHostIP));
CHKiRet(MsgSetRcvFromPort(pMsg, pThis->fromHostPort));
MsgSetRuleset(pMsg, cnf_params->pRuleset);
if (pThis->bFrameOversize) {
writeOversizeMessageLog(pMsg);
}
if (pThis->pLstnInfo->ratelimiter->pShared != NULL && pThis->pLstnInfo->ratelimiter->pShared->per_source_enabled) {
const char *per_source_key = NULL;
@ -456,6 +459,7 @@ static rsRetVal defaultDoSubmitMessage(tcps_sess_t *pThis,
finalize_it:
/* reset status variables */
pThis->iMsg = 0;
pThis->bFrameOversize = 0;
RETiRet;
}
@ -630,6 +634,7 @@ static rsRetVal ATTR_NONNULL(1) processDataRcvd(tcps_sess_t *pThis,
if (pThis->inputState == eAtStrtFram) {
if (c >= '0' && c <= '9' && pThis->bSuppOctetFram) {
pThis->inputState = eInOctetCnt;
pThis->bFrameOversize = 0;
pThis->iOctetsRemain = 0;
pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
} else if (c == ' ' && pThis->bSPFramingFix) {
@ -641,6 +646,7 @@ static rsRetVal ATTR_NONNULL(1) processDataRcvd(tcps_sess_t *pThis,
FINALIZE;
} else {
pThis->inputState = eInMsg;
pThis->bFrameOversize = 0;
pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
}
}
@ -686,6 +692,7 @@ static rsRetVal ATTR_NONNULL(1) processDataRcvd(tcps_sess_t *pThis,
/* emergency, we now need to flush, no matter if we are at end of message or not... */
DBGPRINTF("error: message received is larger than max msg size, we %s it - c=%x\n",
pThis->pSrv->discardTruncatedMsg == 1 ? "truncate" : "split", c);
pThis->bFrameOversize = 1;
defaultDoSubmitMessage(pThis, stTime, ttGenTime, pMultiSub);
++(*pnMsgs);
if (pThis->pSrv->discardTruncatedMsg == 1) {
@ -754,6 +761,7 @@ static rsRetVal ATTR_NONNULL(1) processDataRcvd(tcps_sess_t *pThis,
cnf_params->pszInputName, peerName, peerIP, peerPort, pThis->iOctetsRemain);
pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
} else if (pThis->iOctetsRemain > pThis->iMaxLine) {
pThis->bFrameOversize = 1;
/* while we can not do anything against it, we can at least log an indication
* that something went wrong) -- rgerhards, 2008-03-14
*/

View File

@ -46,6 +46,7 @@ struct tcps_sess_s {
eInMsgTruncating,
eInMsgCheckMultiLine
} inputState; /* our current state */
sbool bFrameOversize; /* current frame exceeded maxMessageSize before submit */
int iOctetsRemain; /* Number of Octets remaining in message */
TCPFRAMINGMODE eFraming;
uchar *pMsg; /* message (fragment) received */

View File

@ -1193,6 +1193,7 @@ TESTS_IMPTCP = \
imptcp_addtlframedelim.sh \
imptcp_conndrop.sh \
imptcp_no_octet_counted.sh \
imptcp-oversize-errorfile-truncate.sh \
imptcp_multi_line.sh \
imptcp_spframingfix.sh \
imptcp_maxsessions.sh \

View File

@ -0,0 +1,33 @@
#!/bin/bash
# Regression test for oversizemsg.errorfile with imptcp truncation.
# imptcp can detect and truncate an oversized TCP frame before core
# submission sees rawmsg > maxMessageSize. The test passes only if that
# pre-submit oversize detection still writes the configured JSON errorfile.
. ${srcdir:=.}/diag.sh init
generate_conf
add_conf '
$MaxMessageSize 128
global(processInternalMessages="on"
oversizemsg.input.mode="truncate"
oversizemsg.errorfile="'$RSYSLOG2_OUT_LOG'")
module(load="../plugins/imptcp/.libs/imptcp")
input(type="imptcp" port="0" listenPortFileName="'$RSYSLOG_DYNNAME'.tcpflood_port")
action(type="omfile" file="'$RSYSLOG_OUT_LOG'")
'
startup
msg="<120> 2011-03-01T11:22:12Z host tag: this is a way too long message that has to be truncated test1 test2 test3 test4 test5 abcdefghijklmnopqrstuvwxyz"
printf '%s %s' "${#msg}" "$msg" > $RSYSLOG_DYNNAME.inputfile
tcpflood -I $RSYSLOG_DYNNAME.inputfile
shutdown_when_empty
wait_shutdown
grep '"rawmsg":.*way too long message.*"input": "imptcp"' "$RSYSLOG2_OUT_LOG" > /dev/null
if [ $? -ne 0 ]; then
echo
echo "FAIL: expected oversize JSON record not found. $RSYSLOG2_OUT_LOG is:"
cat "$RSYSLOG2_OUT_LOG"
error_exit 1
fi
exit_test