From 2cf657fd3f5c277873409c2b375f6a871f5f69ca Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Thu, 22 Jan 2026 11:29:28 +0000 Subject: [PATCH] Patch rust for CVE-2025-12818 --- SPECS/rust/CVE-2025-12818.patch | 528 ++++++++++++++++++++++++++++++++ SPECS/rust/rust.spec | 6 +- 2 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 SPECS/rust/CVE-2025-12818.patch diff --git a/SPECS/rust/CVE-2025-12818.patch b/SPECS/rust/CVE-2025-12818.patch new file mode 100644 index 00000000000..02eaf0effd4 --- /dev/null +++ b/SPECS/rust/CVE-2025-12818.patch @@ -0,0 +1,528 @@ +From 2b2c4a7e48e594e3e530078877da55fbc85801a4 Mon Sep 17 00:00:00 2001 +From: Jacob Champion +Date: Mon, 10 Nov 2025 06:02:34 -0800 +Subject: [PATCH] libpq: Prevent some overflows of int/size_t +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Several functions could overflow their size calculations, when presented +with very large inputs from remote and/or untrusted locations, and then +allocate buffers that were too small to hold the intended contents. + +Switch from int to size_t where appropriate, and check for overflow +conditions when the inputs could have plausibly originated outside of +the libpq trust boundary. (Overflows from within the trust boundary are +still possible, but these will be fixed separately.) A version of +add_size() is ported from the backend to assist with code that performs +more complicated concatenation. + +Reported-by: Aleksey Solovev (Positive Technologies) +Reviewed-by: Noah Misch +Reviewed-by: Álvaro Herrera +Security: CVE-2025-12818 +Backpatch-through: 13 +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/postgres/postgres/commit/600086f471a3bb57ff4953accf1d3f8d2efe0201.patch +--- + .../source/src/interfaces/libpq/fe-connect.c | 17 ++- + .../source/src/interfaces/libpq/fe-exec.c | 101 +++++++++++++++--- + .../source/src/interfaces/libpq/fe-print.c | 61 ++++++++++- + .../src/interfaces/libpq/fe-protocol3.c | 67 ++++++++++-- + .../source/src/interfaces/libpq/libpq-int.h | 11 +- + 5 files changed, 224 insertions(+), 33 deletions(-) + +diff --git a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-connect.c b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-connect.c +index 2f87961..b9e5381 100644 +--- a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-connect.c ++++ b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-connect.c +@@ -18,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1060,7 +1061,7 @@ parse_comma_separated_list(char **startptr, bool *more) + char *p; + char *s = *startptr; + char *e; +- int len; ++ size_t len; + + /* + * Search for the end of the current element; a comma or end-of-string +@@ -5322,7 +5323,21 @@ ldapServiceLookup(const char *purl, PQconninfoOption *options, + /* concatenate values into a single string with newline terminators */ + size = 1; /* for the trailing null */ + for (i = 0; values[i] != NULL; i++) ++ { ++ if (values[i]->bv_len >= INT_MAX || ++ size > (INT_MAX - (values[i]->bv_len + 1))) ++ { ++ libpq_append_error(errorMessage, ++ "connection info string size exceeds the maximum allowed (%d)", ++ INT_MAX); ++ ldap_value_free_len(values); ++ ldap_unbind(ld); ++ return 3; ++ } ++ + size += values[i]->bv_len + 1; ++ } ++ + if ((result = malloc(size)) == NULL) + { + libpq_append_error(errorMessage, "out of memory"); +diff --git a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-exec.c b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-exec.c +index cb55b98..f3a6136 100644 +--- a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-exec.c ++++ b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-exec.c +@@ -511,7 +511,7 @@ PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len) + } + else + { +- attval->value = (char *) pqResultAlloc(res, len + 1, true); ++ attval->value = (char *) pqResultAlloc(res, (size_t) len + 1, true); + if (!attval->value) + goto fail; + attval->len = len; +@@ -603,8 +603,13 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary) + */ + if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD) + { +- size_t alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD; ++ size_t alloc_size; + ++ /* Don't wrap around with overly large requests. */ ++ if (nBytes > SIZE_MAX - PGRESULT_BLOCK_OVERHEAD) ++ return NULL; ++ ++ alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD; + block = (PGresult_data *) malloc(alloc_size); + if (!block) + return NULL; +@@ -1263,7 +1268,7 @@ pqRowProcessor(PGconn *conn, const char **errmsgp) + bool isbinary = (res->attDescs[i].format != 0); + char *val; + +- val = (char *) pqResultAlloc(res, clen + 1, isbinary); ++ val = (char *) pqResultAlloc(res, (size_t) clen + 1, isbinary); + if (val == NULL) + return 0; + +@@ -4203,6 +4208,27 @@ PQescapeString(char *to, const char *from, size_t length) + } + + ++/* ++ * Frontend version of the backend's add_size(), intended to be API-compatible ++ * with the pg_add_*_overflow() helpers. Stores the result into *dst on success. ++ * Returns true instead if the addition overflows. ++ * ++ * TODO: move to common/int.h ++ */ ++static bool ++add_size_overflow(size_t s1, size_t s2, size_t *dst) ++{ ++ size_t result; ++ ++ result = s1 + s2; ++ if (result < s1 || result < s2) ++ return true; ++ ++ *dst = result; ++ return false; ++} ++ ++ + /* + * Escape arbitrary strings. If as_ident is true, we escape the result + * as an identifier; if false, as a literal. The result is returned in +@@ -4215,8 +4241,8 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) + const char *s; + char *result; + char *rp; +- int num_quotes = 0; /* single or double, depending on as_ident */ +- int num_backslashes = 0; ++ size_t num_quotes = 0; /* single or double, depending on as_ident */ ++ size_t num_backslashes = 0; + size_t input_len = strnlen(str, len); + size_t result_size; + char quote_char = as_ident ? '"' : '\''; +@@ -4281,10 +4307,21 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) + } + } + +- /* Allocate output buffer. */ +- result_size = input_len + num_quotes + 3; /* two quotes, plus a NUL */ ++ /* ++ * Allocate output buffer. Protect against overflow, in case the caller ++ * has allocated a large fraction of the available size_t. ++ */ ++ if (add_size_overflow(input_len, num_quotes, &result_size) || ++ add_size_overflow(result_size, 3, &result_size)) /* two quotes plus a NUL */ ++ goto overflow; ++ + if (!as_ident && num_backslashes > 0) +- result_size += num_backslashes + 2; ++ { ++ if (add_size_overflow(result_size, num_backslashes, &result_size) || ++ add_size_overflow(result_size, 2, &result_size)) /* for " E" prefix */ ++ goto overflow; ++ } ++ + result = rp = (char *) malloc(result_size); + if (rp == NULL) + { +@@ -4357,6 +4394,12 @@ PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident) + *rp = '\0'; + + return result; ++ ++overflow: ++ libpq_append_conn_error(conn, ++ "escaped string size exceeds the maximum allowed (%zu)", ++ SIZE_MAX); ++ return NULL; + } + + char * +@@ -4422,16 +4465,25 @@ PQescapeByteaInternal(PGconn *conn, + unsigned char *result; + size_t i; + size_t len; +- size_t bslash_len = (std_strings ? 1 : 2); ++ const size_t bslash_len = (std_strings ? 1 : 2); + + /* +- * empty string has 1 char ('\0') ++ * Calculate the escaped length, watching for overflow as we do with ++ * PQescapeInternal(). The following code relies on a small constant ++ * bslash_len so that small additions and multiplications don't need their ++ * own overflow checks. ++ * ++ * Start with the empty string, which has 1 char ('\0'). + */ + len = 1; + + if (use_hex) + { +- len += bslash_len + 1 + 2 * from_length; ++ /* We prepend "\x" and double each input character. */ ++ if (add_size_overflow(len, bslash_len + 1, &len) || ++ add_size_overflow(len, from_length, &len) || ++ add_size_overflow(len, from_length, &len)) ++ goto overflow; + } + else + { +@@ -4439,13 +4491,25 @@ PQescapeByteaInternal(PGconn *conn, + for (i = from_length; i > 0; i--, vp++) + { + if (*vp < 0x20 || *vp > 0x7e) +- len += bslash_len + 3; ++ { ++ if (add_size_overflow(len, bslash_len + 3, &len)) /* octal "\ooo" */ ++ goto overflow; ++ } + else if (*vp == '\'') +- len += 2; ++ { ++ if (add_size_overflow(len, 2, &len)) /* double each quote */ ++ goto overflow; ++ } + else if (*vp == '\\') +- len += bslash_len + bslash_len; ++ { ++ if (add_size_overflow(len, bslash_len * 2, &len)) /* double each backslash */ ++ goto overflow; ++ } + else +- len++; ++ { ++ if (add_size_overflow(len, 1, &len)) ++ goto overflow; ++ } + } + } + +@@ -4506,6 +4570,13 @@ PQescapeByteaInternal(PGconn *conn, + *rp = '\0'; + + return result; ++ ++overflow: ++ if (conn) ++ libpq_append_conn_error(conn, ++ "escaped bytea size exceeds the maximum allowed (%zu)", ++ SIZE_MAX); ++ return NULL; + } + + unsigned char * +diff --git a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-print.c b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-print.c +index df95f05..d3ffe3a 100644 +--- a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-print.c ++++ b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-print.c +@@ -104,6 +104,16 @@ PQprint(FILE *fout, const PGresult *res, const PQprintOpt *po) + } screen_size; + #endif + ++ /* ++ * Quick sanity check on po->fieldSep, since we make heavy use of int ++ * math throughout. ++ */ ++ if (fs_len < strlen(po->fieldSep)) ++ { ++ fprintf(stderr, libpq_gettext("overlong field separator\n")); ++ goto exit; ++ } ++ + nTups = PQntuples(res); + fieldNames = (const char **) calloc(nFields, sizeof(char *)); + fieldNotNum = (unsigned char *) calloc(nFields, 1); +@@ -391,7 +401,7 @@ do_field(const PQprintOpt *po, const PGresult *res, + { + if (plen > fieldMax[j]) + fieldMax[j] = plen; +- if (!(fields[i * nFields + j] = (char *) malloc(plen + 1))) ++ if (!(fields[i * nFields + j] = (char *) malloc((size_t) plen + 1))) + { + fprintf(stderr, libpq_gettext("out of memory\n")); + return false; +@@ -441,6 +451,27 @@ do_field(const PQprintOpt *po, const PGresult *res, + } + + ++/* ++ * Frontend version of the backend's add_size(), intended to be API-compatible ++ * with the pg_add_*_overflow() helpers. Stores the result into *dst on success. ++ * Returns true instead if the addition overflows. ++ * ++ * TODO: move to common/int.h ++ */ ++static bool ++add_size_overflow(size_t s1, size_t s2, size_t *dst) ++{ ++ size_t result; ++ ++ result = s1 + s2; ++ if (result < s1 || result < s2) ++ return true; ++ ++ *dst = result; ++ return false; ++} ++ ++ + static char * + do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, + const char **fieldNames, unsigned char *fieldNotNum, +@@ -453,15 +484,31 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, + fputs("", fout); + else + { +- int tot = 0; ++ size_t tot = 0; + int n = 0; + char *p = NULL; + ++ /* Calculate the border size, checking for overflow. */ + for (; n < nFields; n++) +- tot += fieldMax[n] + fs_len + (po->standard ? 2 : 0); ++ { ++ /* Field plus separator, plus 2 extra '-' in standard format. */ ++ if (add_size_overflow(tot, fieldMax[n], &tot) || ++ add_size_overflow(tot, fs_len, &tot) || ++ (po->standard && add_size_overflow(tot, 2, &tot))) ++ goto overflow; ++ } + if (po->standard) +- tot += fs_len * 2 + 2; +- border = malloc(tot + 1); ++ { ++ /* An extra separator at the front and back. */ ++ if (add_size_overflow(tot, fs_len, &tot) || ++ add_size_overflow(tot, fs_len, &tot) || ++ add_size_overflow(tot, 2, &tot)) ++ goto overflow; ++ } ++ if (add_size_overflow(tot, 1, &tot)) /* terminator */ ++ goto overflow; ++ ++ border = malloc(tot); + if (!border) + { + fprintf(stderr, libpq_gettext("out of memory\n")); +@@ -524,6 +571,10 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, + else + fprintf(fout, "\n%s\n", border); + return border; ++ ++overflow: ++ fprintf(stderr, libpq_gettext("header size exceeds the maximum allowed\n")); ++ return NULL; + } + + +diff --git a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-protocol3.c b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-protocol3.c +index 3170d48..a51652b 100644 +--- a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-protocol3.c ++++ b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/fe-protocol3.c +@@ -16,6 +16,7 @@ + + #include + #include ++#include + + #ifdef WIN32 + #include "win32.h" +@@ -53,8 +54,8 @@ static int getCopyStart(PGconn *conn, ExecStatusType copytype); + static int getReadyForQuery(PGconn *conn); + static void reportErrorPosition(PQExpBuffer msg, const char *query, + int loc, int encoding); +-static int build_startup_packet(const PGconn *conn, char *packet, +- const PQEnvironmentOption *options); ++static size_t build_startup_packet(const PGconn *conn, char *packet, ++ const PQEnvironmentOption *options); + + + /* +@@ -1220,8 +1221,21 @@ reportErrorPosition(PQExpBuffer msg, const char *query, int loc, int encoding) + * scridx[] respectively. + */ + +- /* we need a safe allocation size... */ ++ /* ++ * We need a safe allocation size. ++ * ++ * The only caller of reportErrorPosition() is pqBuildErrorMessage3(); it ++ * gets its query from either a PQresultErrorField() or a PGcmdQueueEntry, ++ * both of which must have fit into conn->inBuffer/outBuffer. So slen fits ++ * inside an int, but we can't assume that (slen * sizeof(int)) fits ++ * inside a size_t. ++ */ + slen = strlen(wquery) + 1; ++ if (slen > SIZE_MAX / sizeof(int)) ++ { ++ free(wquery); ++ return; ++ } + + qidx = (int *) malloc(slen * sizeof(int)); + if (qidx == NULL) +@@ -2238,15 +2252,43 @@ pqBuildStartupPacket3(PGconn *conn, int *packetlen, + const PQEnvironmentOption *options) + { + char *startpacket; ++ size_t len; ++ ++ len = build_startup_packet(conn, NULL, options); ++ if (len == 0 || len > INT_MAX) ++ return NULL; + +- *packetlen = build_startup_packet(conn, NULL, options); ++ *packetlen = len; + startpacket = (char *) malloc(*packetlen); + if (!startpacket) + return NULL; +- *packetlen = build_startup_packet(conn, startpacket, options); ++ ++ len = build_startup_packet(conn, startpacket, options); ++ Assert(*packetlen == len); ++ + return startpacket; + } + ++/* ++ * Frontend version of the backend's add_size(), intended to be API-compatible ++ * with the pg_add_*_overflow() helpers. Stores the result into *dst on success. ++ * Returns true instead if the addition overflows. ++ * ++ * TODO: move to common/int.h ++ */ ++static bool ++add_size_overflow(size_t s1, size_t s2, size_t *dst) ++{ ++ size_t result; ++ ++ result = s1 + s2; ++ if (result < s1 || result < s2) ++ return true; ++ ++ *dst = result; ++ return false; ++} ++ + /* + * Build a startup packet given a filled-in PGconn structure. + * +@@ -2254,13 +2296,13 @@ pqBuildStartupPacket3(PGconn *conn, int *packetlen, + * To avoid duplicate logic, this routine is called twice: the first time + * (with packet == NULL) just counts the space needed, the second time + * (with packet == allocated space) fills it in. Return value is the number +- * of bytes used. ++ * of bytes used, or zero in the unlikely event of size_t overflow. + */ +-static int ++static size_t + build_startup_packet(const PGconn *conn, char *packet, + const PQEnvironmentOption *options) + { +- int packet_len = 0; ++ size_t packet_len = 0; + const PQEnvironmentOption *next_eo; + const char *val; + +@@ -2279,10 +2321,12 @@ build_startup_packet(const PGconn *conn, char *packet, + do { \ + if (packet) \ + strcpy(packet + packet_len, optname); \ +- packet_len += strlen(optname) + 1; \ ++ if (add_size_overflow(packet_len, strlen(optname) + 1, &packet_len)) \ ++ return 0; \ + if (packet) \ + strcpy(packet + packet_len, optval); \ +- packet_len += strlen(optval) + 1; \ ++ if (add_size_overflow(packet_len, strlen(optval) + 1, &packet_len)) \ ++ return 0; \ + } while(0) + + if (conn->pguser && conn->pguser[0]) +@@ -2317,7 +2361,8 @@ build_startup_packet(const PGconn *conn, char *packet, + /* Add trailing terminator */ + if (packet) + packet[packet_len] = '\0'; +- packet_len++; ++ if (add_size_overflow(packet_len, 1, &packet_len)) ++ return 0; + + return packet_len; + } +diff --git a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/libpq-int.h b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/libpq-int.h +index f36d76b..fbb36de 100644 +--- a/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/libpq-int.h ++++ b/vendor/pq-src-0.3.6+libpq-17.4/source/src/interfaces/libpq/libpq-int.h +@@ -518,7 +518,16 @@ struct pg_conn + pg_prng_state prng_state; /* prng state for load balancing connections */ + + +- /* Buffer for data received from backend and not yet processed */ ++ /* ++ * Buffer for data received from backend and not yet processed. ++ * ++ * NB: We rely on a maximum inBufSize/outBufSize of INT_MAX (and therefore ++ * an INT_MAX upper bound on the size of any and all packet contents) to ++ * avoid overflow; for example in reportErrorPosition(). Changing the type ++ * would require not only an adjustment to the overflow protection in ++ * pqCheck{In,Out}BufferSpace(), but also a careful audit of all libpq ++ * code that uses ints during size calculations. ++ */ + char *inBuffer; /* currently allocated buffer */ + int inBufSize; /* allocated size of buffer */ + int inStart; /* offset to first unconsumed data in buffer */ +-- +2.45.4 + diff --git a/SPECS/rust/rust.spec b/SPECS/rust/rust.spec index 462f1b663de..e42b1f37b76 100644 --- a/SPECS/rust/rust.spec +++ b/SPECS/rust/rust.spec @@ -9,7 +9,7 @@ Summary: Rust Programming Language Name: rust Version: 1.90.0 -Release: 2%{?dist} +Release: 3%{?dist} License: (ASL 2.0 OR MIT) AND BSD AND CC-BY-3.0 Vendor: Microsoft Corporation Distribution: Azure Linux @@ -44,6 +44,7 @@ Source7: https://static.rust-lang.org/dist/%{release_date}/rust-std-%{sta Patch0: CVE-2025-4574.patch Patch1: CVE-2025-53605.patch Patch2: CVE-2024-11738.patch +Patch3: CVE-2025-12818.patch BuildRequires: binutils BuildRequires: cmake # make sure rust relies on curl from CBL-Mariner (instead of using its vendored flavor) @@ -181,6 +182,9 @@ rm %{buildroot}%{_docdir}/docs/html/.lock %{_mandir}/man1/* %changelog +* Thu Jan 22 2026 Azure Linux Security Servicing Account - 1.90.0-3 +- Patch for CVE-2025-12818 + * Mon Jan 19 2026 Kanishk Bansal - 1.90.0-2 - Bump to rebuild with updated glibc