Prereq: "3.11.1" diff -ur --new-file /var/tmp/postfix-3.11.1/src/global/mail_version.h ./src/global/mail_version.h --- /var/tmp/postfix-3.11.1/src/global/mail_version.h 2026-03-10 19:08:28.000000000 -0400 +++ ./src/global/mail_version.h 2026-05-01 14:59:09.000000000 -0400 @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20260310" -#define MAIL_VERSION_NUMBER "3.11.1" +#define MAIL_RELEASE_DATE "20260501" +#define MAIL_VERSION_NUMBER "3.11.2" #ifdef SNAPSHOT #define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -ur --new-file /var/tmp/postfix-3.11.1/HISTORY ./HISTORY --- /var/tmp/postfix-3.11.1/HISTORY 2026-03-10 19:17:51.000000000 -0400 +++ ./HISTORY 2026-05-01 19:45:50.000000000 -0400 @@ -30609,7 +30609,55 @@ Bugfix (defect introduced: Postfix 3.0): buffer over-read when Postfix is configured with an enhanced status code not followed by other text. For example, "5.7.2" without text - after the three-number code, in an access(5) table, header - or body checks, or with "$rbl_code $rbl_text" in rbl_reply_maps - or default_rbl_reply. Problem reported by Kamil Frankowicz. - File: global/dsn_split.c. + after the three-number code in a remote server response, + in a local access(5) table, header or body checks, or with + "$rbl_code $rbl_text" in rbl_reply_maps or default_rbl_reply. + Problem reported by Kamil Frankowicz. File: global/dsn_split.c. + +20260316 + + Bugfix (defect introduced: 20260309) a change, to set the + service_name default value to "amnesiac", violated a test + that parameter names in postconf output must match 1:1 with + parameter names in the postlink script. File: + postconf/postconf_builtin.c. + +20260426 + + Portability: support for recent FreeBSD, NetBSD, and OpenBSD + versions. Brad Smith. Files: makedefs, util/sys_defs.h. + +20260501 + + Bugfix (defect introduced: Postfix 3.11): the proxymap + daemon dereferenced an uninitialized pointer after a request + protocol error. The proxymap server is not exposed to users. + Found by Claude Opus 4.6. File: proxymap/proxymap.c. + + Bugfix (defect introduced: Postfix 3.10): The RFC 2047 + encoder for the sender "full name" could loop when a very + long full_name_encoding_charset value was configured in + main.cf. Found by Claude Opus 4.6. File: global/rfc2047_code.c. + + Bugfix (defect introduced: Postfix 2.2, date 20041207): + When truncating a database file, the CDB client looked at + the file size from before requesting an exclusive lock on + a database file, instead of the file size after the exclusive + lock was granted. Found by Claude Opus 4.6. File: + util/dict_cdb.c. + + Bugfix (defect introduced: Postfix alpha, date 19980309): + file descriptor leak after fork() failure. Found by Claude + Opus 4.6. File: global/pipe_command.c. + + Mistakes in debug logging. Found by Claude Opus 4.6. Files: + dns/dns_lookup.c, util/dict_cidr.c, tls/tls_prng_file.c. + + Unchecked null pointer results after an out-of-memory + condition in a library dependency. Found by Claude Opus + 4.6. Files: util/dict_pcre.c, util/midna_domain.c, + global/dict_pgsql.c. + + Missing or incomplete guards for ssize_t or int overflow, + found by Claude Opus 4.6. Files: util/argv.c, util/netstring.c, + util/vbuf_print.c. diff -ur --new-file /var/tmp/postfix-3.11.1/makedefs ./makedefs --- /var/tmp/postfix-3.11.1/makedefs 2026-03-05 10:59:44.000000000 -0500 +++ ./makedefs 2026-05-01 14:55:47.000000000 -0400 @@ -353,6 +353,24 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC} -shared"} ;; + FreeBSD.15*) SYSTYPE=FREEBSD15 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; + FreeBSD.16*) SYSTYPE=FREEBSD16 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; DragonFly.*) SYSTYPE=DRAGONFLY ;; OpenBSD.2*) SYSTYPE=OPENBSD2 @@ -388,9 +406,18 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC} -shared"} ;; + OpenBSD.8*) SYSTYPE=OPENBSD8 + : ${CC=cc} + : ${SHLIB_SUFFIX=.so.1.0} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC} -shared"} + ;; ekkoBSD.1*) SYSTYPE=EKKOBSD1 ;; - NetBSD.1*) SYSTYPE=NETBSD1 + NetBSD.1.*) SYSTYPE=NETBSD1 ;; NetBSD.2*) SYSTYPE=NETBSD2 ;; @@ -440,6 +467,22 @@ : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} : ${PLUGIN_LD="${CC-gcc} -shared"} ;; + NetBSD.11*) SYSTYPE=NETBSD11 + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC-gcc} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC-gcc} -shared"} + ;; + NetBSD.12*) SYSTYPE=NETBSD12 + : ${SHLIB_SUFFIX=.so} + : ${SHLIB_CFLAGS=-fPIC} + : ${SHLIB_LD="${CC-gcc} -shared"' -Wl,-soname,${LIB}'} + : ${SHLIB_RPATH='-Wl,-rpath,${SHLIB_DIR}'} + : ${SHLIB_ENV="LD_LIBRARY_PATH=`pwd`/lib"} + : ${PLUGIN_LD="${CC-gcc} -shared"} + ;; BSD/OS.2*) SYSTYPE=BSDI2 ;; BSD/OS.3*) SYSTYPE=BSDI3 diff -ur --new-file /var/tmp/postfix-3.11.1/src/dns/dns_lookup.c ./src/dns/dns_lookup.c --- /var/tmp/postfix-3.11.1/src/dns/dns_lookup.c 2024-10-10 18:15:18.000000000 -0400 +++ ./src/dns/dns_lookup.c 2026-05-01 14:53:40.000000000 -0400 @@ -834,14 +834,15 @@ for (src = pos, dst = (unsigned char *) ltemp; src < pos + fixed->length; /* */ ) { frag_len = *src++; - if (msg_verbose) - msg_info("frag_len=%d text=\"%.*s\"", - (int) frag_len, (int) frag_len, (char *) src); + /* 202604 Claude: move debug logging after the frag_len check. */ if (frag_len > reply->end - src || frag_len >= ((unsigned char *) ltemp + sizeof(ltemp)) - dst) { msg_warn("extract_answer: bad TXT string length: %d", frag_len); return (DNS_RETRY); } + if (msg_verbose) + msg_info("frag_len=%d text=\"%.*s\"", + (int) frag_len, (int) frag_len, (char *) src); while (frag_len-- > 0) { ch = *src++; *dst++ = (ISPRINT(ch) ? ch : ' '); diff -ur --new-file /var/tmp/postfix-3.11.1/src/global/dict_pgsql.c ./src/global/dict_pgsql.c --- /var/tmp/postfix-3.11.1/src/global/dict_pgsql.c 2025-06-24 17:24:27.000000000 -0400 +++ ./src/global/dict_pgsql.c 2026-05-01 14:53:40.000000000 -0400 @@ -574,8 +574,10 @@ dict_pgsql->password); } if (host->db == NULL || PQstatus(host->db) != CONNECTION_OK) { + /* 202604 Claude: don't call PQerrorMessage(NULL). */ msg_warn("connect to pgsql server %s: %s", - host->hostname, PQerrorMessage(host->db)); + host->hostname, host->db ? PQerrorMessage(host->db) : + "PQconnectdb or PQsetdbLogin failed"); plpgsql_down_host(host, dict_pgsql->retry_interval); return; } diff -ur --new-file /var/tmp/postfix-3.11.1/src/global/pipe_command.c ./src/global/pipe_command.c --- /var/tmp/postfix-3.11.1/src/global/pipe_command.c 2014-12-25 11:47:18.000000000 -0500 +++ ./src/global/pipe_command.c 2026-05-01 14:53:40.000000000 -0400 @@ -460,6 +460,11 @@ msg_warn("fork: %m"); dsb_unix(why, "4.3.0", sys_exits_detail(EX_OSERR)->text, "Delivery failed: %m"); + /* 202604 Claude: close pipes for the child and parent paths. */ + close(cmd_in_pipe[0]); + close(cmd_in_pipe[1]); + close(cmd_out_pipe[0]); + close(cmd_out_pipe[1]); return (PIPE_STAT_DEFER); /* diff -ur --new-file /var/tmp/postfix-3.11.1/src/global/rfc2047_code.c ./src/global/rfc2047_code.c --- /var/tmp/postfix-3.11.1/src/global/rfc2047_code.c 2025-12-22 17:01:57.000000000 -0500 +++ ./src/global/rfc2047_code.c 2026-05-01 14:53:40.000000000 -0400 @@ -274,6 +274,12 @@ msg_warn("%s: encoder called with empty charset name", myname); return (0); } + /* 202604 Claude: avoid 'space_left' underflow. */ + if (strlen(charset) > ENC_WORD_MAX_LEN / 2) { + msg_warn("%s: unreasonable charset name: '%.100s'", + myname, charset); + return (0); + } for (cp = (const unsigned char *) charset; (ch = *cp) != 0; cp++) { if (!RFC2047_ALLOWED_TOKEN_CHAR(ch)) { msg_warn("%s: invalid character: 0x%x in charset name: '%s'", diff -ur --new-file /var/tmp/postfix-3.11.1/src/postconf/postconf_builtin.c ./src/postconf/postconf_builtin.c --- /var/tmp/postfix-3.11.1/src/postconf/postconf_builtin.c 2025-11-22 18:53:26.000000000 -0500 +++ ./src/postconf/postconf_builtin.c 2026-05-01 15:26:30.000000000 -0400 @@ -185,7 +185,6 @@ * effects, then those side effects must happen only once. */ static CONFIG_STR_TABLE pcf_adhoc_procname = {VAR_PROCNAME}; -static CONFIG_STR_TABLE pcf_adhoc_servname = {VAR_SERVNAME}; static CONFIG_INT_TABLE pcf_adhoc_pid = {VAR_PID}; #define STR(x) vstring_str(x) @@ -466,10 +465,6 @@ PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_procname.name, PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, (void *) &pcf_adhoc_procname, pcf_conv_str_parameter); - pcf_adhoc_servname.defval = mystrdup(""); - PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_servname.name, - PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, - (void *) &pcf_adhoc_servname, pcf_conv_str_parameter); pcf_adhoc_pid.defval = pid; PCF_PARAM_TABLE_ENTER(pcf_param_table, pcf_adhoc_pid.name, PCF_PARAM_FLAG_BUILTIN | PCF_PARAM_FLAG_READONLY, diff -ur --new-file /var/tmp/postfix-3.11.1/src/proxymap/proxymap.c ./src/proxymap/proxymap.c --- /var/tmp/postfix-3.11.1/src/proxymap/proxymap.c 2026-01-11 10:34:30.000000000 -0500 +++ ./src/proxymap/proxymap.c 2026-05-01 14:53:40.000000000 -0400 @@ -422,7 +422,7 @@ { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; int request_func; const char *reply_key; const char *reply_value; @@ -461,11 +461,12 @@ } /* - * Respond to the client. + * Respond to the client. 202604 Claude: don't dereference uninitialized + * dict. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), - SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags), + SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict ? dict->flags : 0), SEND_ATTR_STR(MAIL_ATTR_KEY, reply_key), SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value), ATTR_TYPE_END); @@ -477,7 +478,7 @@ { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; const char *reply_value; int reply_status; @@ -508,11 +509,12 @@ } /* - * Respond to the client. + * Respond to the client. 202604 Claude: don't dereference uninitialized + * dict. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), - SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags), + SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict ? dict->flags : 0), SEND_ATTR_STR(MAIL_ATTR_VALUE, reply_value), ATTR_TYPE_END); } @@ -523,7 +525,7 @@ { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; int dict_status; int reply_status; @@ -567,11 +569,12 @@ } /* - * Respond to the client. + * Respond to the client. 202604 Claude: don't dereference uninitialized + * dict. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), - SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags), + SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict ? dict->flags : 0), ATTR_TYPE_END); } @@ -581,7 +584,7 @@ { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; int dict_status; int reply_status; @@ -621,11 +624,12 @@ } /* - * Respond to the client. + * Respond to the client. 202604 Claude: don't dereference uninitialized + * dict. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, reply_status), - SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict->flags), + SEND_ATTR_INT(MAIL_ATTR_FLAGS, dict ? dict->flags : 0), ATTR_TYPE_END); } diff -ur --new-file /var/tmp/postfix-3.11.1/src/tls/tls_prng_file.c ./src/tls/tls_prng_file.c --- /var/tmp/postfix-3.11.1/src/tls/tls_prng_file.c 2014-12-06 20:35:33.000000000 -0500 +++ ./src/tls/tls_prng_file.c 2026-05-01 14:53:40.000000000 -0400 @@ -132,7 +132,8 @@ RAND_seed(buffer, count); } if (msg_verbose) - msg_info("read %ld bytes from entropy file %s: %m", + /* 202604 Claude: remove '%m' from non-error logging. */ + msg_info("read %ld bytes from entropy file %s", (long) (len - to_read), fh->name); return (len - to_read); } diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/argv.c ./src/util/argv.c --- /var/tmp/postfix-3.11.1/src/util/argv.c 2025-08-24 14:55:43.000000000 -0400 +++ ./src/util/argv.c 2026-05-01 14:53:40.000000000 -0400 @@ -210,6 +210,9 @@ argvp = (ARGV *) mymalloc(sizeof(*argvp)); argvp->len = 0; sane_len = (len < 2 ? 2 : len); + /* 202604 Claude: avoid overflowing sane_len + 1 */ + if (sane_len > SSIZE_MAX - 1) + msg_panic("argv_alloc: array length overflow"); argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *)); argvp->len = sane_len; argvp->argc = 0; @@ -268,6 +271,9 @@ { ssize_t new_len; + /* 202604 Claude: avoid overflowing (new_len + 1) * sizeof(char *). */ + if (argvp->len > SSIZE_MAX / (2 * sizeof(char *)) - 1) + msg_panic("argv_extend: array length overflow"); new_len = argvp->len * 2; argvp->argv = (char **) myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *)); @@ -411,9 +417,10 @@ ssize_t pos; /* - * Sanity check. + * Sanity check. 202604 Claude: avoid expression 'first + how_many'. */ - if (first < 0 || how_many < 0 || first + how_many > argvp->argc) + if (first < 0 || how_many < 0 || first > argvp->argc + || how_many > argvp->argc - first) msg_panic("argv_delete bad range: (start=%ld count=%ld)", (long) first, (long) how_many); diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/dict_cdb.c ./src/util/dict_cdb.c --- /var/tmp/postfix-3.11.1/src/util/dict_cdb.c 2025-06-24 17:24:27.000000000 -0400 +++ ./src/util/dict_cdb.c 2026-05-01 14:53:40.000000000 -0400 @@ -456,7 +456,8 @@ } #ifndef NO_FTRUNCATE - if (st0.st_size) + /* 202604 Claude: use the post-myflock() fstat result. */ + if (st1.st_size) ftruncate(fd, 0); #endif diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/dict_cidr.c ./src/util/dict_cidr.c --- /var/tmp/postfix-3.11.1/src/util/dict_cidr.c 2025-06-24 17:24:27.000000000 -0400 +++ ./src/util/dict_cidr.c 2026-05-01 14:53:40.000000000 -0400 @@ -237,11 +237,14 @@ rule->lineno = lineno; if (msg_verbose) { - if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, - hostaddr.buf, sizeof(hostaddr.buf)) == 0) - msg_fatal("inet_ntop: %m"); - msg_info("dict_cidr_open: add %s/%d %s", - hostaddr.buf, cidr_info.mask_shift, rule->value); + /* 202604 Claude: ENDIF has no address pattern. */ + if (cidr_info.op != CIDR_MATCH_OP_ENDIF) { + if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes, + hostaddr.buf, sizeof(hostaddr.buf)) == 0) + msg_fatal("inet_ntop: %m"); + msg_info("dict_cidr_open: add %s/%d %s", + hostaddr.buf, cidr_info.mask_shift, rule->value); + } } return (rule); } diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/dict_pcre.c ./src/util/dict_pcre.c --- /var/tmp/postfix-3.11.1/src/util/dict_pcre.c 2025-06-24 17:24:27.000000000 -0400 +++ ./src/util/dict_pcre.c 2026-05-01 14:53:40.000000000 -0400 @@ -728,6 +728,9 @@ } engine->match_data = pcre2_match_data_create_from_pattern( engine->pattern, (void *) 0); + /* 202604 Claude: handle error result. */ + if (engine->match_data == 0) + msg_fatal("out of memory in pcre2_match_data_create_from_pattern()"); #endif return (1); } diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/dict_sockmap.c ./src/util/dict_sockmap.c --- /var/tmp/postfix-3.11.1/src/util/dict_sockmap.c 2025-06-24 17:24:27.000000000 -0400 +++ ./src/util/dict_sockmap.c 2026-05-01 14:53:40.000000000 -0400 @@ -254,7 +254,8 @@ reply_payload = split_at(STR(dp->rdwr_buf), ' '); if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_OK) == 0) { dict->error = 0; - return (reply_payload); + /* 202604 Claude: don't return NULL with dict->error==0. */ + return (reply_payload ? reply_payload : ""); } else if (strcmp(STR(dp->rdwr_buf), DICT_SOCKMAP_PROT_NOTFOUND) == 0) { dict->error = 0; return (0); diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/midna_domain.c ./src/util/midna_domain.c --- /var/tmp/postfix-3.11.1/src/util/midna_domain.c 2024-11-21 12:49:05.000000000 -0500 +++ ./src/util/midna_domain.c 2026-05-01 14:53:40.000000000 -0400 @@ -189,11 +189,13 @@ */ idna = uidna_openUTS46(midna_domain_transitional ? UIDNA_DEFAULT : UIDNA_NONTRANSITIONAL_TO_ASCII, &error); - anl = uidna_nameToASCII_UTF8(idna, - name, strlen(name), - buf, sizeof(buf) - 1, - &info, - &error); + /* 202604 Claude: avoid null deref after uidna_openUTS46() failure. */ + if (idna && U_SUCCESS(error)) + anl = uidna_nameToASCII_UTF8(idna, + name, strlen(name), + buf, sizeof(buf) - 1, + &info, + &error); uidna_close(idna); /* @@ -203,7 +205,7 @@ * "fake" A-labels, as required by UTS 46 section 4.1, but we rely on * valid_hostname() on the output side just to be sure. */ - if (U_SUCCESS(error) && info.errors == 0 && anl > 0) { + if (idna && U_SUCCESS(error) && info.errors == 0 && anl > 0) { buf[anl] = 0; /* XXX */ if (!valid_hostname(buf, DONT_GRIPE)) { msg_warn("%s: Problem translating domain \"%.100s\" to ASCII form: %s", @@ -243,11 +245,13 @@ */ idna = uidna_openUTS46(midna_domain_transitional ? UIDNA_DEFAULT : UIDNA_NONTRANSITIONAL_TO_UNICODE, &error); - anl = uidna_nameToUnicodeUTF8(idna, - name, strlen(name), - buf, sizeof(buf) - 1, - &info, - &error); + /* 202604 Claude: avoid null deref after uidna_openUTS46() failure. */ + if (idna && U_SUCCESS(error)) + anl = uidna_nameToUnicodeUTF8(idna, + name, strlen(name), + buf, sizeof(buf) - 1, + &info, + &error); uidna_close(idna); /* @@ -256,7 +260,7 @@ * other invalid forms that are not covered in UTS 46, section 4.1). We * rely on midna_domain_to_ascii() to validate the output. */ - if (U_SUCCESS(error) && info.errors == 0 && anl > 0) { + if (idna && U_SUCCESS(error) && info.errors == 0 && anl > 0) { buf[anl] = 0; /* XXX */ if (midna_domain_to_ascii(buf) == 0) return (0); diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/netstring.c ./src/util/netstring.c --- /var/tmp/postfix-3.11.1/src/util/netstring.c 2025-01-07 17:30:28.000000000 -0500 +++ ./src/util/netstring.c 2026-05-01 14:53:40.000000000 -0400 @@ -305,14 +305,17 @@ VA_COPY(ap2, ap); /* - * Figure out the total result size. + * Figure out the total result size. 202604 Claude: move the wrap-around + * guard inside the loop. */ - for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len) + for (total = 0; (data = va_arg(ap, char *)) != 0; /* see below */ ) { if ((data_len = va_arg(ap, ssize_t)) < 0) msg_panic("%s: bad data length %ld", myname, (long) data_len); + if (data_len > SSIZE_T_MAX - total) + msg_panic("%s: total length overflow", myname); + total += data_len; + } va_end(ap); - if (total < 0) - msg_panic("%s: bad total length %ld", myname, (long) total); if (msg_verbose > 1) msg_info("%s: write total length %ld", myname, (long) total); diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/sys_defs.h ./src/util/sys_defs.h --- /var/tmp/postfix-3.11.1/src/util/sys_defs.h 2026-02-25 19:29:13.000000000 -0500 +++ ./src/util/sys_defs.h 2026-05-01 14:55:47.000000000 -0400 @@ -31,14 +31,15 @@ || defined(FREEBSD5) || defined(FREEBSD6) || defined(FREEBSD7) \ || defined(FREEBSD8) || defined(FREEBSD9) || defined(FREEBSD10) \ || defined(FREEBSD11) || defined(FREEBSD12) || defined(FREEBSD13) \ - || defined(FREEBSD14) \ + || defined(FREEBSD14) || defined(FREEBSD15) || defined(FREEBSD16) \ || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ || defined(OPENBSD2) || defined(OPENBSD3) || defined(OPENBSD4) \ || defined(OPENBSD5) || defined(OPENBSD6) || defined(OPENBSD7) \ + || defined(OPENBSD8) \ || defined(NETBSD1) || defined(NETBSD2) || defined(NETBSD3) \ || defined(NETBSD4) || defined(NETBSD5) || defined(NETBSD6) \ || defined(NETBSD7) | defined(NETBSD8) || defined(NETBSD9) \ - || defined(NETBSD10) \ + || defined(NETBSD10) || defined(NETBSD11) || defined(NETBSD12) \ || defined(EKKOBSD1) || defined(DRAGONFLY) #define SUPPORTED #include diff -ur --new-file /var/tmp/postfix-3.11.1/src/util/vbuf_print.c ./src/util/vbuf_print.c --- /var/tmp/postfix-3.11.1/src/util/vbuf_print.c 2025-07-29 18:14:02.000000000 -0400 +++ ./src/util/vbuf_print.c 2026-05-01 14:53:40.000000000 -0400 @@ -109,12 +109,16 @@ /* * Helper macros... Note that there is no need to check the result from - * VSTRING_SPACE() because that always succeeds or never returns. + * VSTRING_SPACE() because that always succeeds or never returns. 202406 + * Claude: avoid integer overflow in field width computations. */ #ifndef NO_SNPRINTF -#define VBUF_SNPRINTF(bp, sz, fmt, arg) do { \ +#define VBUF_SNPRINTF(bp, width_or_prec, type_space, fmt, arg) do { \ ssize_t _ret; \ - if (VBUF_SPACE((bp), (sz)) != 0) \ + if ((width_or_prec) > INT_MAX - (type_space)) \ + msg_panic("vbuf_print: field width (%d + %lu) > INT_MAX", \ + (width_or_prec), (unsigned long) (type_space)); \ + if (VBUF_SPACE((bp), (width_or_prec) + (type_space)) != 0) \ return (bp); \ _ret = snprintf((char *) (bp)->ptr, (bp)->cnt, (fmt), (arg)); \ if (_ret < 0) \ @@ -139,7 +143,7 @@ } while (0) #define VSTRING_ADDNUM(vp, n) do { \ - VBUF_SNPRINTF(&(vp)->vbuf, INT_SPACE, "%d", n); \ + VBUF_SNPRINTF(&(vp)->vbuf, 0, INT_SPACE, "%d", n); \ } while (0) #define VBUF_STRCAT(bp, s) do { \ @@ -274,7 +278,7 @@ msg_panic("%s: %%j%c is not supported", myname, *cp); s = va_arg(ap, char *); if (prec >= 0 || (width > 0 && width > strlen(s))) { - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), s); } else { VBUF_STRCAT(bp, s); @@ -295,16 +299,16 @@ msg_panic("%s: '%s%c' has both 'j' and 'l' modifiers", myname, vstring_str(fmt), *cp); if (intmax_flag) - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + IMX_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), IMX_SPACE, vstring_str(fmt), va_arg(ap, intmax_t)); else if (long_flag == 2) - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + LL_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), LL_SPACE, vstring_str(fmt), va_arg(ap, long long)); else if (long_flag == 1) - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), va_arg(ap, long)); else if (long_flag == 0) - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + INT_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), INT_SPACE, vstring_str(fmt), va_arg(ap, int)); else msg_panic("%s: bad long_flag: %u", myname, long_flag); @@ -313,7 +317,7 @@ case 'f': case 'g': /* C99 *printf ignore the 'l' modifier. */ - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + DBL_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), DBL_SPACE, vstring_str(fmt), va_arg(ap, double)); break; case 'm': @@ -325,7 +329,7 @@ msg_panic("%s: %%l%c is not supported", myname, *cp); if (intmax_flag) msg_panic("%s: %%j%c is not supported", myname, *cp); - VBUF_SNPRINTF(bp, (width > prec ? width : prec) + PTR_SPACE, + VBUF_SNPRINTF(bp, (width > prec ? width : prec), PTR_SPACE, vstring_str(fmt), va_arg(ap, char *)); break; default: /* anything else is bad */