From: Wietse Z Venema Date: Mon, 13 Apr 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260413 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;p=thirdparty%2Fpostfix.git postfix-3.12-20260413 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 23fac43c2..a972260f3 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -30833,12 +30833,12 @@ Apologies for any names omitted. 20260407 - Tech debt: added 'bool' support to internal protocols. + Code health: added 'bool' support to internal protocols. util/attr.h, util/attr_print0.c, util/attr_print64.c, util/attr_print_plain.c, util/attr_scan0.c, util/attr_scan0.ref, util/attr_scan64.c, util/attr_scan64.ref, util/attr_scan_plain.c, util/attr_scan_plain.ref. There is no code that uses this now, - but there are tests to make sure that it is works when neded. + but there are tests to make sure that it is works when needed. 20260408 @@ -30848,7 +30848,7 @@ Apologies for any names omitted. 20260410 - Tech debt: refactored tlsproxy PARAM, INIT and START send + Code health: refactored tlsproxy PARAM, INIT and START send and receive handlers to make the code easier to test, and added some unit tests. This removes tls_proxy_client_misc.c, tls_proxy_client_print.c, tls_proxy_client_scan.c, @@ -30869,6 +30869,44 @@ Apologies for any names omitted. smtpd(8) manpage, missing tls_trust_server_ccerts parameter support in postlink. Files: smtpd/smtpd.c, mantools/postlink. +20260413 + + Viktor Dukhovni scanned Postfix source with Claude Opus + 4.6. It found a minor bug in the proxymap server (requires + an invalid map name configured in main.cf) and in the RFC + 2047 encoder (requires an absurdly long charset name + configured main.cf), two instances where Postfix internal + documentation was not consistent with Postfix implementation + (handling of "*" wildcards in domain names, handling of a + 'truncated list' flag in the DNS client), two bugs in debug + logging (CIDR map, DNS client), one null pointer bug in the + PostgreSQL client (with libpq < 8.0), one resource leak + after fork() failure, ignored errors that may happen during + memory shortage, missing detection of integer overflows + that are unlikely because Postfix memory by design does not + contain large objects or large numbers of objects. The + number of false positives was remarkably low (about three). + Claude also suggested clarification in code comments that + will help to make Postfix maintainable by other people (and + to silence future code scanners). Files: dns/dns.h, + dns/dns_lookup.c, dns/dns_rr.c, dns/dns_rr_test.c, + global/dict_pgsql.c, global/haproxy_srvr.c, global/mime_state.c, + global/pipe_command.c, global/rfc2047_code.c, local/dotforward.c, + proxymap/proxymap.c, smtp/smtp_proto.c, smtpd/smtpd_expand.c, + testing/mock_dns_lookup.c, util/argv.c, util/dict_cdb.c, + util/dict_cidr.c, util/dict.h, util/dict_sockmap.c, + util/midna_domain.c, util/myaddrinfo.c, util/netstring.c, + util/slmdb.c, util/valid_hostname.c, util/valid_hostname.ref, + util/vbuf_print.c, util/vstream.c, util/vstring.c. + + Code health: renamed identifiers to prepare for integration + of proxied tls_server_init() and tls_server_start() calls. + Files: tlsproxy/tlsproxy.c, tlsproxy/tlsproxy.h, + tlsproxy/tlsproxy_state.c. + + Testing: updated postscreen tests for changes in make_attr() + API. File: src/postscreen/postscreen_dnsbl_test.c. + TODO Reorganize PTEST_LIB, PMOCK_LIB, TESTLIB, TESTLIBS, etc. diff --git a/postfix/html/tlsproxy.8.html b/postfix/html/tlsproxy.8.html index 4737654e8..58d68a542 100644 --- a/postfix/html/tlsproxy.8.html +++ b/postfix/html/tlsproxy.8.html @@ -452,5 +452,8 @@ TLSPROXY(8) TLSPROXY(8) 111 8th Avenue New York, NY 10011, USA + Wietse Venema + porcupine.org + TLSPROXY(8) diff --git a/postfix/man/man8/tlsproxy.8 b/postfix/man/man8/tlsproxy.8 index 53010ac56..727140175 100644 --- a/postfix/man/man8/tlsproxy.8 +++ b/postfix/man/man8/tlsproxy.8 @@ -417,3 +417,6 @@ Wietse Venema Google, Inc. 111 8th Avenue New York, NY 10011, USA + +Wietse Venema +porcupine.org diff --git a/postfix/proto/stop.double-cc b/postfix/proto/stop.double-cc index 18329d08d..fb7a0e316 100644 --- a/postfix/proto/stop.double-cc +++ b/postfix/proto/stop.double-cc @@ -364,3 +364,4 @@ application application specific Null name or name name void void defer_ctx To undo a ptest_defer call call the function with a +charset charset diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index 0655242df..60aa8aff8 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -232,3 +232,8 @@ proto proto stop proto stop double cc local local hc local Makefile Dukhovni Files proto postconf proto proto TLS_README html tls tls h tls tls_misc c tls tls_server c + support in postlink Files smtpd smtpd c mantools postlink + to silence future code scanners Files dns dns h + proxymap proxymap c smtp smtp_proto c smtpd smtpd_expand c + Files tlsproxy tlsproxy c tlsproxy tlsproxy h + Files tlsproxy tlsproxy c tlsproxy tlsproxy h diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index 05a0afebb..627642a49 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1976,3 +1976,7 @@ Bool HERMETICITY VPRINT deserializes +PQerrorMessage +deref +openUTS +xff diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index 087e05e99..72635b9c0 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -211,6 +211,7 @@ extern DNS_RR *dns_rr_create(const char *, const char *, unsigned, unsigned, const char *, size_t); extern void dns_rr_free(DNS_RR *); +extern DNS_RR *dns_rr_full_copy(DNS_RR *); extern DNS_RR *dns_rr_copy(DNS_RR *); extern DNS_RR *dns_rr_append(DNS_RR *, DNS_RR *); extern DNS_RR *dns_rr_sort(DNS_RR *, int (*) (DNS_RR *, DNS_RR *)); diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index 11115c3da..b18b7b2b4 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -837,14 +837,15 @@ static int dns_get_rr(DNS_RR **list, const char *orig_name, DNS_REPLY *reply, 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 --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index e5775a276..aea19c040 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -22,6 +22,9 @@ /* void dns_rr_free(list) /* DNS_RR *list; /* +/* DNS_RR *dns_rr_full_copy(record) +/* DNS_RR *record; +/* /* DNS_RR *dns_rr_copy(record) /* DNS_RR *record; /* @@ -99,7 +102,10 @@ /* dns_rr_free() releases the resource used by of zero or more /* resource records. /* -/* dns_rr_copy() makes a copy of a resource record. +/* dns_rr_full_copy() makes a copy of a resource record. +/* +/* dns_rr_copy() makes a sanitized copy of a resource record. +/* This does not copy the DNS_RR_FLAG_TRUNCATED flag. /* /* dns_rr_append() appends an input resource record list to /* an output list. Null arguments are explicitly allowed. @@ -229,6 +235,25 @@ void dns_rr_free(DNS_RR *rr) } } +/* dns_rr_full_copy - copy resource record */ + +DNS_RR *dns_rr_full_copy(DNS_RR *src) +{ + DNS_RR *dst; + + /* + * Note: struct copy, because dns_rr_create() would not copy all fields. + */ + dst = (DNS_RR *) mymalloc(sizeof(*dst)); + *dst = *src; + dst->qname = mystrdup(src->qname); + dst->rname = mystrdup(src->rname); + if (dst->data) + dst->data = mymemdup(src->data, src->data_len); + dst->next = 0; + return (dst); +} + /* dns_rr_copy - copy resource record */ DNS_RR *dns_rr_copy(DNS_RR *src) @@ -240,6 +265,8 @@ DNS_RR *dns_rr_copy(DNS_RR *src) */ dst = (DNS_RR *) mymalloc(sizeof(*dst)); *dst = *src; + /* 202604 Claude: avoid surprising behavior. */ + dst->flags &= ~DNS_RR_FLAG_TRUNCATED; dst->qname = mystrdup(src->qname); dst->rname = mystrdup(src->rname); if (dst->data) diff --git a/postfix/src/dns/dns_rr_test.c b/postfix/src/dns/dns_rr_test.c index 12cf01c58..d6f4b8651 100644 --- a/postfix/src/dns/dns_rr_test.c +++ b/postfix/src/dns/dns_rr_test.c @@ -230,6 +230,33 @@ static int append_to_list_from_list(void) return (eq_dns_rr_free(got, want)); } +static int dns_rr_copy_clears_truncated_flag(void) +{ + DNS_RR *orig; + DNS_RR *copy; + + orig = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + + orig->flags |= DNS_RR_FLAG_TRUNCATED; + copy = dns_rr_copy(orig); + orig->flags &= ~DNS_RR_FLAG_TRUNCATED; + + return (eq_dns_rr_free(copy, orig)); +} + +static int dns_rr_full_copy_copies_truncated_flag(void) +{ + DNS_RR *orig; + DNS_RR *full_copy; + + orig = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); + + orig->flags |= DNS_RR_FLAG_TRUNCATED; + full_copy = dns_rr_full_copy(orig); + + return (eq_dns_rr_free(full_copy, orig)); +} + static int append_propagates_flags(void) { DNS_RR *a = dns_rr_create_noport("qa", "ra", T_MX, C_IN, 3600, 1, "mxa", 4); @@ -471,6 +498,12 @@ static const TEST_CASE test_cases[] = { "append to list from element", append_to_list_from_elem, "append to list from list", append_to_list_from_list, + /* + * Test dns_rr_copy() flag propagation. + */ + "dns_rr_copy clears truncation", dns_rr_copy_clears_truncated_flag, + "dns_rr_full_copy copies truncation", dns_rr_full_copy_copies_truncated_flag, + /* * Test dns_rr_append() flag propagation. */ diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index f5e97321d..6bf2622bc 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -574,8 +574,10 @@ static void plpgsql_connect_single(DICT_PGSQL *dict_pgsql, HOST *host) 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 --git a/postfix/src/global/haproxy_srvr.c b/postfix/src/global/haproxy_srvr.c index 0f09bf03e..e2c495f8b 100644 --- a/postfix/src/global/haproxy_srvr.c +++ b/postfix/src/global/haproxy_srvr.c @@ -61,6 +61,9 @@ /* of bytes parsed, and the non_proxy argument is true or false /* if the haproxy message specifies a non-proxied connection. /* +/* Note: haproxy v2 protocol support requires that the protocol +/* message buffer has "struct proxy_hdr_v2" alignment. +/* /* haproxy_srvr_receive_sa() receives and parses a haproxy protocol /* handshake. This must be called before any I/O is done on /* the specified file descriptor. The result is 0 in case of @@ -583,7 +586,12 @@ int haproxy_srvr_receive_sa(int fd, int *non_proxy, { const char *err; VSTRING *escape_buf; - char read_buf[HAPROXY_HEADER_MAX_LEN + 1]; + + /* 202604 Claude: read_buf should have "struct proxy_hdr_v2" alignment. */ + union { + char b[HAPROXY_HEADER_MAX_LEN + 1]; + struct proxy_hdr_v2 p; + } read_buf; ssize_t read_len; /* @@ -592,7 +600,7 @@ int haproxy_srvr_receive_sa(int fd, int *non_proxy, * therefore we peek, parse the entire input, then read(2) only the * number of bytes parsed. */ - if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { + if ((read_len = recv(fd, read_buf.b, sizeof(read_buf.b) - 1, MSG_PEEK)) <= 0) { msg_warn("haproxy read: EOF"); return (-1); } @@ -600,15 +608,15 @@ int haproxy_srvr_receive_sa(int fd, int *non_proxy, /* * Parse the haproxy handshake, and determine the handshake length. */ - read_buf[read_len] = 0; + read_buf.b[read_len] = 0; - if ((err = haproxy_srvr_parse_sa(read_buf, &read_len, non_proxy, + if ((err = haproxy_srvr_parse_sa(read_buf.b, &read_len, non_proxy, smtp_client_addr, smtp_client_port, smtp_server_addr, smtp_server_port, client_sa, client_sa_len, server_sa, server_sa_len)) != 0) { escape_buf = vstring_alloc(read_len * 2); - escape(escape_buf, read_buf, read_len); + escape(escape_buf, read_buf.b, read_len); msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); vstring_free(escape_buf); return (-1); @@ -617,7 +625,7 @@ int haproxy_srvr_receive_sa(int fd, int *non_proxy, /* * Try to pop the haproxy handshake off the input queue. */ - if (recv(fd, read_buf, read_len, 0) != read_len) { + if (recv(fd, read_buf.b, read_len, 0) != read_len) { msg_warn("haproxy read: %m"); return (-1); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index b3f4491bb..4653dea22 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20260410" +#define MAIL_RELEASE_DATE "20260413" #define MAIL_VERSION_NUMBER "3.12" #ifdef SNAPSHOT diff --git a/postfix/src/global/mime_state.c b/postfix/src/global/mime_state.c index 798e13de7..920e34603 100644 --- a/postfix/src/global/mime_state.c +++ b/postfix/src/global/mime_state.c @@ -714,7 +714,8 @@ static void mime_state_downgrade(MIME_STATE *state, int rec_type, #define QP_ENCODE(buffer, ch) { \ VSTRING_ADDCH(buffer, '='); \ - VSTRING_ADDCH(buffer, hexchars[(ch >> 4) & 0xff]); \ + /* 202406 Claude: 0xff should be 0xf. */ \ + VSTRING_ADDCH(buffer, hexchars[(ch >> 4) & 0xf]); \ VSTRING_ADDCH(buffer, hexchars[ch & 0xf]); \ } diff --git a/postfix/src/global/pipe_command.c b/postfix/src/global/pipe_command.c index 66aec8a67..a7a1d62a5 100644 --- a/postfix/src/global/pipe_command.c +++ b/postfix/src/global/pipe_command.c @@ -460,6 +460,11 @@ int pipe_command(VSTREAM *src, DSN_BUF *why,...) 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 --git a/postfix/src/global/rfc2047_code.c b/postfix/src/global/rfc2047_code.c index 9a9da5eb8..3c8b7b201 100644 --- a/postfix/src/global/rfc2047_code.c +++ b/postfix/src/global/rfc2047_code.c @@ -102,12 +102,13 @@ /* * What ASCII characters are allowed in the 'charset' or 'encoding' tokens - * of an encoded-word. + * of an encoded-word. 202604 Claude: in the macro expansion replace + * several instances of 'ch' with 'c'. */ #define RFC2047_ESPECIALS_STR "()<>@,;:\\\"/[]?.=" #define RFC2047_ALLOWED_TOKEN_CHAR(c) \ - (isascii(c) && !iscntrl(c) && ch != ' ' \ - && !strchr(RFC2047_ESPECIALS_STR, ch)) + (isascii(c) && !iscntrl(c) && c != ' ' \ + && !strchr(RFC2047_ESPECIALS_STR, c)) /* * Common definitions for the base64 and quoted-printable encoders. @@ -274,6 +275,12 @@ char *rfc2047_encode(VSTRING *result, int header_context, 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'", @@ -651,6 +658,13 @@ static const TEST_CASE test_cases[] = { "testme", 0, " ", NO_OUTPUT, "rfc2047_code: warning: rfc2047_encode: " "encoder called with empty charset name\n" }, + {"validates_charset_length", test_rfc2047_encode, + RFC2047_HEADER_CONTEXT_COMMENT, /* charset= */ + "1234567890123456789012345678901234567890", + "testme", 0, " ", NO_OUTPUT, "rfc2047_code: warning: rfc2047_encode: " + "unreasonable charset name: " + "'1234567890123456789012345678901234567890'\n" + }, {"validates_charset", validates_charset}, {"encodes_comment_text", encodes_comment_text}, {"encodes_long_comment_text", test_rfc2047_encode, diff --git a/postfix/src/local/dotforward.c b/postfix/src/local/dotforward.c index 3ce2cfca9..3ba7017e4 100644 --- a/postfix/src/local/dotforward.c +++ b/postfix/src/local/dotforward.c @@ -256,6 +256,19 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) * associated with that alias. The NOTIFY parameter is * propagated to the forwarding addresses, except that any * SUCCESS keyword is removed. + * + * 202604 Claude: a local recipient can attempt to sabotage + * deliveries with malicious targets inside .forward such as + * a /path/to/fifo without listener, or a "|command" that + * sleeps, or by replacing .forward with a fifo. The impact + * of such attempts is limited by design because each local + * recipient is limited to only two concurrent deliveries + * (the defaults are "local_destination_concurrency_limit = + * 2" and "local_destination_recipient_limit = 1"). So they + * will only slow down their own deliveries; 98% of local + * delivery processes will remain available for other + * recipients (the default setting "default_process_limit = + * 100"). */ close_on_exec(fd, CLOSE_ON_EXEC); addr_count = 0; diff --git a/postfix/src/postscreen/postscreen_dnsbl_test.c b/postfix/src/postscreen/postscreen_dnsbl_test.c index 5cbcd4da8..96ec561c4 100644 --- a/postfix/src/postscreen/postscreen_dnsbl_test.c +++ b/postfix/src/postscreen/postscreen_dnsbl_test.c @@ -258,14 +258,14 @@ static void test_single_dnsbl(PTEST_CTX *t, const PTEST_CASE *tp) * event request, so we should send something soon. */ serialized_req = - make_attr(ATTR_FLAG_NONE, + make_attr(attr_vprint, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, tt->req_dnsbl), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, tt->req_addr), SEND_ATTR_INT(MAIL_ATTR_LABEL, request_id), ATTR_TYPE_END); serialized_resp = - make_attr(ATTR_FLAG_NONE, + make_attr(attr_vprint, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, tt->req_dnsbl), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, tt->req_addr), @@ -479,7 +479,7 @@ static void test_multi_dnsbl(PTEST_CTX *t, const PTEST_CASE *tp) * send something soon. */ serialized_req = - make_attr(ATTR_FLAG_NONE, + make_attr(attr_vprint, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, dp[n].req_dnsbl), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, @@ -487,7 +487,7 @@ static void test_multi_dnsbl(PTEST_CTX *t, const PTEST_CASE *tp) SEND_ATTR_INT(MAIL_ATTR_LABEL, request_id), ATTR_TYPE_END); serialized_resp = - make_attr(ATTR_FLAG_NONE, + make_attr(attr_vprint, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_RBL_DOMAIN, dp[n].req_dnsbl), SEND_ATTR_STR(MAIL_ATTR_ACT_CLIENT_ADDR, diff --git a/postfix/src/proxymap/proxymap.c b/postfix/src/proxymap/proxymap.c index c3ef77dab..97053b109 100644 --- a/postfix/src/proxymap/proxymap.c +++ b/postfix/src/proxymap/proxymap.c @@ -477,7 +477,7 @@ static void proxymap_lookup_service(VSTREAM *client_stream) { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; const char *reply_value; int reply_status; @@ -508,11 +508,12 @@ static void proxymap_lookup_service(VSTREAM *client_stream) } /* - * 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 +524,7 @@ static void proxymap_update_service(VSTREAM *client_stream) { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; int dict_status; int reply_status; @@ -567,11 +568,12 @@ static void proxymap_update_service(VSTREAM *client_stream) } /* - * 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 +583,7 @@ static void proxymap_delete_service(VSTREAM *client_stream) { int inst_flags; int request_flags; - DICT *dict; + DICT *dict = 0; int dict_status; int reply_status; @@ -621,11 +623,12 @@ static void proxymap_delete_service(VSTREAM *client_stream) } /* - * 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 --git a/postfix/src/smtp/smtp_proto.c b/postfix/src/smtp/smtp_proto.c index 69a02311e..b5538b4e5 100644 --- a/postfix/src/smtp/smtp_proto.c +++ b/postfix/src/smtp/smtp_proto.c @@ -595,6 +595,7 @@ int smtp_helo(SMTP_STATE *state) msg_warn("bad EHLO SIZE limit \"%s\" from %s", word, session->namaddrport); else + /* We treat size_limit <= 0 as 'unknown'. */ session->size_limit = off_cvt_string(word); } } diff --git a/postfix/src/smtpd/smtpd_expand.c b/postfix/src/smtpd/smtpd_expand.c index 8362bd701..eeabd84dd 100644 --- a/postfix/src/smtpd/smtpd_expand.c +++ b/postfix/src/smtpd/smtpd_expand.c @@ -220,7 +220,8 @@ const char *smtpd_expand_lookup(const char *name, int unused_mode, } else if (STREQN(name, MAIL_ATTR_RECIP, CONST_LEN(MAIL_ATTR_RECIP))) { return (smtpd_expand_addr(state->expand_buf, state->recipient, name, CONST_LEN(MAIL_ATTR_RECIP))); - } if (STREQ(name, MAIL_ATTR_LOCALTIME)) { + /* 202406 Claude: add missing 'else'. No effect on generated code. */ + } else if (STREQ(name, MAIL_ATTR_LOCALTIME)) { if (time(&now) == (time_t) -1) msg_fatal("time lookup failed: %m"); lt = localtime(&now); diff --git a/postfix/src/testing/mock_dns_lookup.c b/postfix/src/testing/mock_dns_lookup.c index a7500b20f..286d3cab7 100644 --- a/postfix/src/testing/mock_dns_lookup.c +++ b/postfix/src/testing/mock_dns_lookup.c @@ -171,16 +171,16 @@ const char *dns_status_to_string(int status) return (str_name_code(status_string, status)); } -/* copy_dns_rrlist - deep copy */ +/* full_copy_dns_rrlist - deep copy */ -static DNS_RR *copy_dns_rrlist(DNS_RR *list) +static DNS_RR *full_copy_dns_rrlist(DNS_RR *list) { DNS_RR *rr; if (list == 0) return (0); - rr = dns_rr_copy(list); - rr->next = copy_dns_rrlist(list->next); + rr = dns_rr_full_copy(list); + rr->next = full_copy_dns_rrlist(list->next); return (rr); } @@ -353,7 +353,7 @@ static void assign_dns_lookup_x(const MOCK_EXPECT *expect, if (pe->retval == DNS_OK) { if (pt->rrlist) - *(pt->rrlist) = copy_dns_rrlist(pe->rrlist); + *(pt->rrlist) = full_copy_dns_rrlist(pe->rrlist); if (pt->fqdn && pe->fqdn) vstring_strcpy(pt->fqdn, STR(pe->fqdn)); } else { @@ -439,7 +439,7 @@ void _expect_dns_lookup_x(const char *file, int line, int calls_expected, pe->herrval = herrval; pe->retval = retval; if (pe->retval == DNS_OK) { - pe->rrlist = copy_dns_rrlist(rrlist); + pe->rrlist = full_copy_dns_rrlist(rrlist); pe->fqdn = VSTRDUP_OR_NULL(fqdn); } else { pe->why = VSTRDUP_OR_NULL(why); diff --git a/postfix/src/tlsproxy/tlsproxy.c b/postfix/src/tlsproxy/tlsproxy.c index cef9a4f23..d9c48001a 100644 --- a/postfix/src/tlsproxy/tlsproxy.c +++ b/postfix/src/tlsproxy/tlsproxy.c @@ -383,6 +383,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* @@ -1465,7 +1468,7 @@ static void tlsp_get_request_event(int event, void *context) state->is_server_role = 0; if (attr_scan(plaintext_stream, ATTR_FLAG_STRICT, RECV_ATTR_FUNC(tls_proxy_client_param_scan, - (void *) &state->tls_params), + (void *) &state->client_params), RECV_ATTR_FUNC(tls_proxy_client_init_scan, (void *) &state->client_init_props), RECV_ATTR_FUNC(tls_proxy_client_start_scan, @@ -1475,7 +1478,7 @@ static void tlsp_get_request_event(int event, void *context) tlsp_state_free(state); return; } - state->appl_state = tlsp_client_init(state->tls_params, + state->appl_state = tlsp_client_init(state->client_params, state->client_init_props); ready = state->appl_state != 0; break; diff --git a/postfix/src/tlsproxy/tlsproxy.h b/postfix/src/tlsproxy/tlsproxy.h index eacbb1fab..b3223ab74 100644 --- a/postfix/src/tlsproxy/tlsproxy.h +++ b/postfix/src/tlsproxy/tlsproxy.h @@ -39,9 +39,10 @@ typedef struct { TLS_APPL_STATE *appl_state; /* libtls state */ TLS_SESS_STATE *tls_context; /* libtls state */ int ssl_last_err; /* TLS I/O state */ - TLS_CLIENT_PARAMS *tls_params; /* globals not part of init_props */ + TLS_SERVER_PARAMS *server_params; /* globals not part of init_props */ TLS_SERVER_INIT_PROPS *server_init_props; TLS_SERVER_START_PROPS *server_start_props; + TLS_CLIENT_PARAMS *client_params; /* globals not part of init_props */ TLS_CLIENT_INIT_PROPS *client_init_props; TLS_CLIENT_START_PROPS *client_start_props; } TLSP_STATE; @@ -66,4 +67,7 @@ extern void tlsp_state_free(TLSP_STATE *); /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ diff --git a/postfix/src/tlsproxy/tlsproxy_state.c b/postfix/src/tlsproxy/tlsproxy_state.c index 4a08d17fe..a82106683 100644 --- a/postfix/src/tlsproxy/tlsproxy_state.c +++ b/postfix/src/tlsproxy/tlsproxy_state.c @@ -66,6 +66,9 @@ /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA +/* +/* Wietse Venema +/* porcupine.org /*--*/ /* @@ -115,9 +118,10 @@ TLSP_STATE *tlsp_state_create(const char *service, state->remote_endpt = 0; state->server_id = 0; state->tls_context = 0; - state->tls_params = 0; + state->server_params = 0; state->server_init_props = 0; state->server_start_props = 0; + state->client_params = 0; state->client_init_props = 0; state->client_start_props = 0; @@ -153,12 +157,14 @@ void tlsp_state_free(TLSP_STATE *state) myfree(state->server_id); if (state->tls_context) tls_free_context(state->tls_context); - if (state->tls_params) - tls_proxy_client_param_free(state->tls_params); + if (state->server_params) + tls_proxy_server_param_free(state->server_params); if (state->server_init_props) tls_proxy_server_init_free(state->server_init_props); if (state->server_start_props) tls_proxy_server_start_free(state->server_start_props); + if (state->client_params) + tls_proxy_client_param_free(state->client_params); if (state->client_init_props) tls_proxy_client_init_free(state->client_init_props); if (state->client_start_props) diff --git a/postfix/src/util/argv.c b/postfix/src/util/argv.c index ea76b5152..a0dc395f3 100644 --- a/postfix/src/util/argv.c +++ b/postfix/src/util/argv.c @@ -262,6 +262,9 @@ static void argv_extend(ARGV *argvp) { ssize_t new_len; + /* 202604 Claude: avoid small non-negative expression 'new_len + 1'. */ + if (argvp->len > SSIZE_MAX / 2) + msg_panic("argv_extend: array length overflow"); new_len = argvp->len * 2; argvp->argv = (char **) myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *)); @@ -409,9 +412,10 @@ void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many) 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 --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index efefb885d..1ca8b7f62 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -51,9 +51,10 @@ typedef struct DICT_OWNER { * When combining tables with different provenance, we initialize to the * highest trust level, and remember the lowest trust level that we find * during aggregation. If we combine tables that are owned by different - * untrusted users, the resulting provenance is "unknown". + * untrusted users, the resulting provenance is "unknown". 202406 Claude: + * add missing 'do'. */ -#define DICT_OWNER_AGGREGATE_INIT(dst) { \ +#define DICT_OWNER_AGGREGATE_INIT(dst) do { \ (dst).status = DICT_OWNER_TRUSTED; \ (dst).uid = 0; \ } while (0) @@ -313,7 +314,7 @@ extern const char *dict_file_lookup(DICT *dict, const char *); * dict_stream(3) */ extern VSTREAM *dict_stream_open(const char *dict_type, const char *mapname, - int open_flags, int dict_flags, struct stat * st, VSTRING **why); + int open_flags, int dict_flags, struct stat *st, VSTRING **why); /* LICENSE /* .ad diff --git a/postfix/src/util/dict_cdb.c b/postfix/src/util/dict_cdb.c index 25b52fbfa..93a1f026c 100644 --- a/postfix/src/util/dict_cdb.c +++ b/postfix/src/util/dict_cdb.c @@ -456,7 +456,8 @@ static DICT *dict_cdbm_open(const char *path, int dict_flags) } #ifndef NO_FTRUNCATE - if (st0.st_size) + /* 202604 Claude: use the post-myflock() fstat result. */ + if (st1.st_size) ftruncate(fd, 0); #endif diff --git a/postfix/src/util/dict_cidr.c b/postfix/src/util/dict_cidr.c index bf59ba758..ca6435d8c 100644 --- a/postfix/src/util/dict_cidr.c +++ b/postfix/src/util/dict_cidr.c @@ -237,11 +237,14 @@ static DICT_CIDR_ENTRY *dict_cidr_parse_rule(DICT *dict, char *p, int lineno, 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 --git a/postfix/src/util/dict_sockmap.c b/postfix/src/util/dict_sockmap.c index 9bd0a9d27..b96b26cff 100644 --- a/postfix/src/util/dict_sockmap.c +++ b/postfix/src/util/dict_sockmap.c @@ -254,7 +254,8 @@ static const char *dict_sockmap_lookup(DICT *dict, const char *key) 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 --git a/postfix/src/util/midna_domain.c b/postfix/src/util/midna_domain.c index 5e9d6f8cb..018b537db 100644 --- a/postfix/src/util/midna_domain.c +++ b/postfix/src/util/midna_domain.c @@ -189,11 +189,13 @@ static void *midna_domain_to_ascii_create(const char *name, void *unused_context */ 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 @@ static void *midna_domain_to_ascii_create(const char *name, void *unused_context * "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 @@ static void *midna_domain_to_utf8_create(const char *name, void *unused_context) */ 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 @@ static void *midna_domain_to_utf8_create(const char *name, void *unused_context) * 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 --git a/postfix/src/util/myaddrinfo.c b/postfix/src/util/myaddrinfo.c index ef4ca7a5d..3ba09c3e3 100644 --- a/postfix/src/util/myaddrinfo.c +++ b/postfix/src/util/myaddrinfo.c @@ -356,15 +356,16 @@ int hostname_to_sockaddr_pf(const char *hostname, int pf, */ if ((hp = gethostbyname(hostname)) == 0) return (h_errno == TRY_AGAIN ? EAI_AGAIN : EAI_NODATA); + /* 202604 Claude: return EAI_NODATA when gethostbyname() returns empty. */ if (hp->h_addrtype != AF_INET + || hp->h_addr_list[0] == 0 || hp->h_length != sizeof(template.sin.sin_addr)) return (EAI_NODATA); /* - * Initialize the result template. + * Initialize the result template. 202604 Claude: always do that. */ - if (template.info.ai_addrlen == 0) - init_ipv4addrinfo(&template, socktype); + init_ipv4addrinfo(&template, socktype); /* * Copy the address information into an addrinfo structure. @@ -474,7 +475,7 @@ int hostname_to_sockaddr_pf(const char *hostname, int pf, /* hostaddr_to_sockaddr - printable address to binary address form */ int hostaddr_to_sockaddr(const char *hostaddr, const char *service, - int socktype, struct addrinfo ** res) + int socktype, struct addrinfo ** res) { #ifdef EMULATE_IPV4_ADDRINFO diff --git a/postfix/src/util/netstring.c b/postfix/src/util/netstring.c index ea68ccd34..fe32ddf56 100644 --- a/postfix/src/util/netstring.c +++ b/postfix/src/util/netstring.c @@ -305,14 +305,17 @@ void netstring_put_multi(VSTREAM *stream,...) 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 --git a/postfix/src/util/slmdb.c b/postfix/src/util/slmdb.c index e1b8fd64c..3f795631e 100644 --- a/postfix/src/util/slmdb.c +++ b/postfix/src/util/slmdb.c @@ -481,8 +481,9 @@ static int slmdb_recover(SLMDB *slmdb, int status) case MDB_MAP_RESIZED: if ((status = mdb_env_set_mapsize(slmdb->env, 0)) == 0) { /* Do not panic. Maps may shrink after bulk update. */ - mdb_env_info(slmdb->env, &info); - slmdb->curr_limit = info.me_mapsize; + /* 202604 Claude: handle impossible mdb_env_info() error. */ + if (mdb_env_info(slmdb->env, &info) == 0) + slmdb->curr_limit = info.me_mapsize; if (slmdb->notify_fn) slmdb->notify_fn(slmdb->cb_context, MDB_MAP_RESIZED, slmdb->curr_limit); diff --git a/postfix/src/util/valid_hostname.c b/postfix/src/util/valid_hostname.c index 457d1f1da..580b4482a 100644 --- a/postfix/src/util/valid_hostname.c +++ b/postfix/src/util/valid_hostname.c @@ -122,7 +122,8 @@ int valid_hostname(const char *name, int flags) if (!ISDIGIT(ch)) non_numeric = 1; } else if ((flags & DO_WILDCARD) && ch == '*') { - if (label_length || label_count || (cp[1] && cp[1] != '.')) { + /* 202604 Claude: leading '*' must be followed by '.'. */ + if (label_length || label_count || cp[1] != '.') { if (gripe) msg_warn("%s: '*' can be the first label only: %.100s", myname, name); return (0); diff --git a/postfix/src/util/valid_hostname.ref b/postfix/src/util/valid_hostname.ref index eccc5585e..d7b0e27ad 100644 --- a/postfix/src/util/valid_hostname.ref +++ b/postfix/src/util/valid_hostname.ref @@ -151,3 +151,4 @@ ./valid_hostname: testing: "foo.bar*" ./valid_hostname: warning: valid_hostname: '*' can be the first label only: foo.bar* ./valid_hostname: testing: "*" +./valid_hostname: warning: valid_hostname: '*' can be the first label only: * diff --git a/postfix/src/util/vbuf_print.c b/postfix/src/util/vbuf_print.c index 5472566cf..6e14cc0e9 100644 --- a/postfix/src/util/vbuf_print.c +++ b/postfix/src/util/vbuf_print.c @@ -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 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) 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 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) 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 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) 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 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap) 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 */ diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 5acfb0f10..136db9de0 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -1224,6 +1224,7 @@ off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence) return (-1); } if (offset > bp->len && (bp->flags & VSTREAM_FLAG_WRITE)) + /* Note: vstring_space() does not return after error. */ vstream_buf_space(bp, offset - bp->len); VSTREAM_BUF_AT_OFFSET(bp, offset); return (offset); @@ -1766,7 +1767,8 @@ void vstream_control(VSTREAM *stream, int name,...) stream->min_data_rate = min_data_rate; break; case VSTREAM_CTL_OWN_VSTRING: - if ((stream->buf.flags |= VSTREAM_FLAG_MEMORY) == 0) + /* 202604 Claude: '|=' should be '&'. */ + if ((stream->buf.flags & VSTREAM_FLAG_MEMORY) == 0) msg_panic("%s: operation on non-VSTRING stream", myname); stream->buf.flags |= VSTREAM_FLAG_OWN_VSTRING; break; diff --git a/postfix/src/util/vstring.c b/postfix/src/util/vstring.c index 301ae5df5..a1de4f18e 100644 --- a/postfix/src/util/vstring.c +++ b/postfix/src/util/vstring.c @@ -517,6 +517,7 @@ VSTRING *vstring_memcpy(VSTRING *vp, const char *src, ssize_t len) VSTRING *vstring_memcat(VSTRING *vp, const char *src, ssize_t len) { + /* VSTRING_SPACE() does not return if the buffer size would wrap around. */ VSTRING_SPACE(vp, len); memcpy(vstring_end(vp), src, len); len += VSTRING_LEN(vp);