that allow this, therefore it should not trigger a panic
in Postfix. Files: mymalloc.c, mymalloc_test.c.
+20260623
+
+ Bug (defect introduced: before Postfix alpha, date 19970424):
+ the DNS client could read up to two bytes past the end of
+ an MX record, before discovering that the record was too
+ short. This behavior was later copied with SRV records,
+ potentially over-reading up to six bytes. Problem reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ dns_lookup.c.
+
+ Non-bug: tighten a bound on the size of a TXT record fragment.
+ Suggested by Qualys, assisted by Claude Mythos Preview. File:
+ dns_lookup.c.
+
+ Non-bug: timer leak in a short-lived test program. Reported
+ by Qualys, assisted by Claude Mythos Preview. File:
+ scache_single.c.
+
+ Future proofing: destroy objects after logging. Suggested
+ by Qualys, assisted by Claude Mythos Preview. File:
+ mail_addr_map.c.
+
+ Bug (defect introduced: Postfix 1.0, date: 20001028): double
+ ldap_msgfree(resloop) call when special_result_attribute
+ is configured, and an attacker controls LDAP server or can
+ play attacker-in-the-middle. Reported by Qualys, assisted
+ by Claude Mythos Preview. File: dict_ldap.c.
+
+ Bug (defect introduced: before Postfix alpha, date: 19971106):
+ 'int' over-shift, in the queue file record-length parser.
+ Postfix programs do not generate such records, but an
+ attacker could cause postdrop to reject input or panic().
+ Reported by Qualys, assisted by Claude Mythos Preview. File:
+ record.c.
+
+ Contract violation (defect introduced: Postfix 1.1, date:
+ 20020518): rec_streamlf_get() did not null-terminate all
+ results. No existing code was affected by this. Found by
+ Qualys, assisted by Claude Mythos Preview. File: rec_streamlf.c.
+
+20260624:
+
+ Bug (defect introduced: Postfix < alpha, date: < 1998):
+ missing recursion guard while processing :include: files
+ that directly :include: other files in local(8) aliases or
+ .forward files. This could result in exhausting stack space
+ (segfault) or file handles (fatal error). Reported by Qualys,
+ assisted by Claude Mythos Preview. File: local/include.c.
+
+ Safety: added a global nesting guard. File: local/recipient.c.
+
TODO
Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc.
vstring_strcat(multi_line_buf, STR(line_buf));
}
if (vstream_feof(fp))
+ /* 202606 Qualys+Mythos: log the saved end marker. */
msg_warn("%s, line %d: missing \"%s\" end marker",
VSTREAM_PATH(fp), lineno, STR(saved_end_marker));
member_name = STR(saved_member_name);
int frag_len;
int ch;
+ /* At this point, (pos + fixed.length <= reply->end) */
+
#define MIN2(a, b) ((unsigned)(a) < (unsigned)(b) ? (a) : (b))
*list = 0;
data_len = strlen(temp) + 1;
break;
case T_SRV:
+ if (fixed->length < 3 * NS_INT16SZ)
+ return (DNS_RETRY);
GETSHORT(pref, pos);
GETSHORT(weight, pos);
GETSHORT(port, pos);
data_len = strlen(temp) + 1;
break;
case T_MX:
+ if (fixed->length < NS_INT16SZ)
+ return (DNS_RETRY);
GETSHORT(pref, pos);
if (dn_expand(reply->buf, reply->end, pos, temp, sizeof(temp)) < 0)
return (DNS_RETRY);
src < pos + fixed->length; /* */ ) {
frag_len = *src++;
/* 202604 Claude: move debug logging after the frag_len check. */
- if (frag_len > reply->end - src
+ /* 202606 Qualys+Mythos: tighter bound for fragment size. */
+ if (frag_len > pos + fixed->length - src
|| frag_len >= ((unsigned char *) ltemp + sizeof(ltemp)) - dst) {
msg_warn("extract_answer: bad TXT string length: %d", frag_len);
return (DNS_RETRY);
break;
}
- if (resloop != 0)
+ if (resloop != 0) {
ldap_msgfree(resloop);
+ resloop = 0;
+ }
if (dict_ldap->dict.error != 0)
break;
*/
argv = mail_addr_crunch_opt(string, propagate ? extension : 0,
MA_FORM_EXTERNAL, out_form);
- if (buffer)
- vstring_free(buffer);
- if (ext_address)
- vstring_free(ext_address);
if (msg_verbose)
for (i = 0; i < argv->argc; i++)
msg_info("%s: %s -> %d: %s", myname, address, i, argv->argv[i]);
argv = argv_free(argv);
path->error = DICT_ERR_RETRY;
}
+
+ /* 202606 Qualys+Mythos: future proofing: destroy objects last. */
+ if (buffer)
+ vstring_free(buffer);
+ if (ext_address)
+ vstring_free(ext_address);
}
/*
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20260623"
+#define MAIL_RELEASE_DATE "20260624"
#define MAIL_VERSION_NUMBER "3.12"
#ifdef SNAPSHOT
*/
VSTRING_RESET(buf);
while (n-- > 0) {
- if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF)
+ if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
+ VSTRING_TERMINATE(buf);
return (VSTRING_LEN(buf) > 0 ? REC_TYPE_CONT : REC_TYPE_EOF);
+ }
if (ch == '\n') {
VSTRING_TERMINATE(buf);
return (REC_TYPE_NORM);
const char *myname = "rec_get";
int type;
ssize_t len;
- int len_byte;
+ ssize_t len_byte;
unsigned shift;
/*
* limit.
*/
for (len = 0, shift = 0; /* void */ ; shift += 7) {
- if (shift >= (int) (NBBY * sizeof(int))) {
+ if (shift +7 >= (int) (NBBY * sizeof(int))) {
msg_warn("%s: too many length bits, record type %d",
VSTREAM_PATH(stream), type);
return (REC_TYPE_ERROR);
{
SCACHE_SINGLE *sp = (SCACHE_SINGLE *) scache;
+ if (SCACHE_SINGLE_ENDP_BUSY(sp))
+ scache_single_free_endp(sp);
+ if (SCACHE_SINGLE_DEST_BUSY(sp))
+ scache_single_free_dest(sp);
+
vstring_free(sp->endp.endp_label);
vstring_free(sp->endp.endp_prop);
if (sp->endp.fd >= 0)
if (msg_verbose)
MSG_LOG_STATE(myname, state);
+ /* 202606 Qualys+Mythos: add missing nesting limit. */
+ if (state.level > 100) {
+ msg_warn(":include: nesting limit exceeded for %s", path);
+ dsb_simple(state.msg_attr.why, "5.4.6",
+ ":include: nesting limit exceeded");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+
/*
* DUPLICATE ELIMINATION
*
if (msg_verbose)
MSG_LOG_STATE(myname, state);
+ /*
+ * Global recursion safety check for loops that are not already broken
+ * locally, such as :include: files that directly :include: another
+ * file).
+ */
+ if (state.level > 100) {
+ msg_warn("recipient nesting limit exceeded for %s",
+ state.msg_attr.rcpt.address);
+ dsb_simple(state.msg_attr.why, "5.4.6",
+ "recipient nesting limit exceeded");
+ return (bounce_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
+ }
+
/*
* Duplicate filter.
*/
#include <ptest.h>
/*
- * Scafffolding.
+ * Scaffolding.
*/
static bool memset_was_called;