runtime: cleanup zero-size alloc handling

Why
Guard obvious zero-size allocation edges and remove a couple of
stale or misleading notes in the imtcp/tcpsrv path so future
reviews see the intended behavior directly in code.

Impact
Empty key and lookup-table files now fail before zero-size
allocations; OpenSSL accepted sessions use per-session callback
context.

Before/After
Before, empty files could flow into malloc(0)-style paths and the
OpenSSL accept path stored listener-owned callback context.
After, those runtime paths fail early and accepted OpenSSL sessions
carry their own callback context.

Technical Overview
Reject empty key files in the libgcry and OpenSSL runtime helpers
before allocating buffers from sb.st_size.
Reject empty lookup-table files before allocating and parsing the
JSON payload.
Update accepted OpenSSL sessions to store pNew-owned callback state
in SSL ex-data instead of the listener object's pointers.
Remove a stale TODO in the imtcp octet-count parser because the
zero-count case already falls into the framing-error path.
Add a short comment that maxReads==0 is intentional and documented
behavior that disables starvation protection.

With the help of AI-Agents: Codex
This commit is contained in:
Rainer Gerhards 2026-04-12 17:38:02 +02:00
parent 23af807717
commit d0d84a824d
6 changed files with 17 additions and 4 deletions

View File

@ -56,6 +56,10 @@ int cryGetKeyFromFile(const char *const fn, char **const key, unsigned *const ke
errno = EMSGSIZE;
goto done;
}
if (sb.st_size == 0) {
errno = EINVAL;
goto done;
}
if ((*key = malloc(sb.st_size)) == NULL) goto done;
if (read(fd, *key, sb.st_size) != sb.st_size) goto done;
*keylen = sb.st_size;

View File

@ -77,6 +77,10 @@ int osslGetKeyFromFile(const char* const fn, char** const key, unsigned* const k
errno = EMSGSIZE;
goto done;
}
if (sb.st_size == 0) {
errno = EINVAL;
goto done;
}
if ((*key = malloc(sb.st_size)) == NULL) goto done;
if (read(fd, *key, sb.st_size) != sb.st_size) goto done;
*keylen = sb.st_size;

View File

@ -1051,6 +1051,11 @@ static rsRetVal ATTR_NONNULL()
ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
}
if (sb.st_size == 0) {
LogError(0, RS_RET_JSON_PARSE_ERR, "lookup table file '%s' is empty", filename);
ABORT_FINALIZE(RS_RET_JSON_PARSE_ERR);
}
CHKmalloc(iobuf = malloc(sb.st_size));
tokener = json_tokener_new();

View File

@ -1070,9 +1070,9 @@ static rsRetVal AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew, char *const connInfo)
/* Store nsd_ossl_t* reference in SSL obj
* Index allocation: 0=pTcp, 1=permitExpiredCerts, 2=imdtls instance, 3=revocationCheck
*/
SSL_set_ex_data(pNew->pNetOssl->ssl, 0, pThis->pTcp);
SSL_set_ex_data(pNew->pNetOssl->ssl, 1, &pThis->permitExpiredCerts);
SSL_set_ex_data(pNew->pNetOssl->ssl, 3, &pThis->DrvrTlsRevocationCheck);
SSL_set_ex_data(pNew->pNetOssl->ssl, 0, pNew->pTcp);
SSL_set_ex_data(pNew->pNetOssl->ssl, 1, &pNew->permitExpiredCerts);
SSL_set_ex_data(pNew->pNetOssl->ssl, 3, &pNew->DrvrTlsRevocationCheck);
/* We now do the handshake */
CHKiRet(osslHandshakeCheck(pNew));

View File

@ -737,7 +737,6 @@ static rsRetVal ATTR_NONNULL(1) processDataRcvd(tcps_sess_t *pThis,
cnf_params->pszInputName, peerName, peerIP, peerPort, c);
}
if (pThis->iOctetsRemain < 1) {
/* TODO: handle the case where the octet count is 0! */
LogError(0, NO_ERRCODE,
"imtcp %s: Framing Error in received TCP message from "
"peer: (hostname) %s, (ip) %s, (port) %s: invalid octet count %d.",

View File

@ -1008,6 +1008,7 @@ static rsRetVal ATTR_NONNULL(1)
while (state == RS_READING || state == RS_STARVATION) {
switch (state) {
case RS_READING:
/* maxReads==0 is intentional and documented: it disables starvation protection. */
while (state == RS_READING && (maxReads == 0 || read_calls < maxReads)) {
iRet = pThis->pRcvData(pSess, buf, sizeof(buf), &iRcvd, &oserr, &pioDescr->ioDirection);