Skip to content

Commit 6c74a8a

Browse files
committed
miltertest: fix stack overflow when milter replaces body > BUFRSZ bytes
mt_milter_read() read the protocol-specified payload length directly into a caller-supplied fixed-size stack buffer with no bounds check. When a milter sends SMFIR_REPLBODY with a body larger than BUFRSZ (1024 bytes) the read() overflows the buffer. Split header parsing into a static mt_milter_read_hdr() helper, and add a bounds check in mt_milter_read() so oversized payloads fail cleanly instead of silently corrupting the stack. Fix mt_eom() to call mt_milter_read_hdr() directly and dynamically allocate a heap buffer (dynbuf) when the payload exceeds BUFRSZ. The stack buffer is used for normal-sized responses; dynbuf is freed after each iteration. This allows SMFIR_REPLBODY of any size to be received and stored correctly. Fixes #66.
1 parent f9ad07a commit 6c74a8a

1 file changed

Lines changed: 95 additions & 17 deletions

File tree

miltertest/miltertest.c

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -403,23 +403,21 @@ mt_eom_request(struct mt_context *ctx, char cmd, size_t len, char *data)
403403
}
404404

405405
/*
406-
** MT_MILTER_READ -- read from a connected filter
406+
** MT_MILTER_READ_HDR -- read the milter protocol header (cmd byte + payload length)
407407
**
408408
** Parameters:
409-
** fd -- descriptor to which to write
409+
** fd -- descriptor to read from
410410
** cmd -- milter command received (returned)
411-
** buf -- where to write data
412-
** buflen -- bytes available at "buf" (updated)
413-
**
411+
** paylen -- payload byte count, not including cmd byte (returned)
412+
**
414413
** Return value:
415414
** TRUE iff successful.
416415
*/
417416

418-
_Bool
419-
mt_milter_read(int fd, char *cmd, const char *buf, size_t *len)
417+
static _Bool
418+
mt_milter_read_hdr(int fd, char *cmd, size_t *paylen)
420419
{
421420
int i;
422-
int expl;
423421
size_t rlen;
424422
fd_set fds;
425423
struct timeval timeout;
@@ -461,18 +459,54 @@ mt_milter_read(int fd, char *cmd, const char *buf, size_t *len)
461459
*cmd = data[MILTER_LEN_BYTES];
462460
data[MILTER_LEN_BYTES] = '\0';
463461
(void) memcpy(&i, data, MILTER_LEN_BYTES);
464-
expl = ntohl(i) - 1;
462+
*paylen = (size_t) (ntohl(i) - 1);
463+
464+
return TRUE;
465+
}
466+
467+
/*
468+
** MT_MILTER_READ -- read from a connected filter
469+
**
470+
** Parameters:
471+
** fd -- descriptor to which to write
472+
** cmd -- milter command received (returned)
473+
** buf -- where to write data
474+
** len -- bytes available at "buf" (updated with bytes written)
475+
**
476+
** Return value:
477+
** TRUE iff successful.
478+
*/
479+
480+
_Bool
481+
mt_milter_read(int fd, char *cmd, const char *buf, size_t *len)
482+
{
483+
size_t paylen;
484+
size_t rlen;
485+
486+
assert(fd >= 0);
487+
488+
if (!mt_milter_read_hdr(fd, cmd, &paylen))
489+
return FALSE;
465490

466491
rlen = 0;
467492

468-
if (expl > 0)
493+
if (paylen > 0)
469494
{
470-
rlen = read(fd, (void *) buf, expl);
471-
if (rlen != expl)
495+
if (paylen > *len)
496+
{
497+
fprintf(stderr,
498+
"%s: mt_milter_read(%d): response size %zu exceeds buffer %zu\n",
499+
progname, fd, paylen, *len);
500+
501+
return FALSE;
502+
}
503+
504+
rlen = read(fd, (void *) buf, paylen);
505+
if (rlen != paylen)
472506
{
473507
fprintf(stderr,
474508
"%s: read(%d): returned %ld, expected %ld\n",
475-
progname, fd, (long) rlen, (long) expl);
509+
progname, fd, (long) rlen, (long) paylen);
476510

477511
return FALSE;
478512
}
@@ -486,7 +520,7 @@ mt_milter_read(int fd, char *cmd, const char *buf, size_t *len)
486520

487521
*len = rlen;
488522

489-
return (expl == rlen);
523+
return TRUE;
490524
}
491525

492526
/*
@@ -3072,6 +3106,9 @@ mt_eom(lua_State *l)
30723106
{
30733107
char rcmd;
30743108
size_t buflen;
3109+
size_t paylen;
3110+
char *dynbuf;
3111+
char *rbuf;
30753112
struct mt_context *ctx;
30763113
char buf[BUFRSZ];
30773114

@@ -3100,28 +3137,69 @@ mt_eom(lua_State *l)
31003137

31013138
for (;;)
31023139
{
3103-
buflen = sizeof buf;
3140+
dynbuf = NULL;
3141+
rbuf = buf;
3142+
buflen = 0;
31043143

3105-
if (!mt_milter_read(ctx->ctx_fd, &rcmd, buf, &buflen))
3144+
if (!mt_milter_read_hdr(ctx->ctx_fd, &rcmd, &paylen))
31063145
{
31073146
lua_pushstring(l, "mt.milter_read() failed");
31083147
return 1;
31093148
}
31103149

3150+
if (paylen > 0)
3151+
{
3152+
if (paylen > sizeof buf)
3153+
{
3154+
dynbuf = (char *) malloc(paylen);
3155+
if (dynbuf == NULL)
3156+
{
3157+
lua_pushstring(l, "mt.eom(): malloc() failed");
3158+
return 1;
3159+
}
3160+
rbuf = dynbuf;
3161+
}
3162+
3163+
buflen = read(ctx->ctx_fd, rbuf, paylen);
3164+
if (buflen != paylen)
3165+
{
3166+
fprintf(stderr,
3167+
"%s: read(%d): returned %ld, expected %ld\n",
3168+
progname, ctx->ctx_fd,
3169+
(long) buflen, (long) paylen);
3170+
free(dynbuf);
3171+
lua_pushstring(l, "mt.milter_read() failed");
3172+
return 1;
3173+
}
3174+
}
3175+
3176+
if (verbose > 1)
3177+
{
3178+
fprintf(stdout,
3179+
"%s: mt_milter_read(%d): cmd %c, len %ld\n",
3180+
progname, ctx->ctx_fd, rcmd, (long) buflen);
3181+
}
3182+
31113183
if (rcmd == SMFIR_CONTINUE ||
31123184
rcmd == SMFIR_ACCEPT ||
31133185
rcmd == SMFIR_REJECT ||
31143186
rcmd == SMFIR_TEMPFAIL ||
31153187
rcmd == SMFIR_DISCARD)
3188+
{
3189+
free(dynbuf);
31163190
break;
3191+
}
31173192

31183193
if (!mt_eom_request(ctx, rcmd, buflen,
3119-
buflen == 0 ? NULL : buf))
3194+
buflen == 0 ? NULL : rbuf))
31203195
{
3196+
free(dynbuf);
31213197
lua_pushstring(l, "mt.eom_request() failed");
31223198
return 1;
31233199
}
31243200

3201+
free(dynbuf);
3202+
31253203
if (rcmd == SMFIR_REPLYCODE)
31263204
break;
31273205
}

0 commit comments

Comments
 (0)