Your message dated Sat, 11 Jul 2026 10:32:49 +0000
with message-id <[email protected]>
and subject line Released in 13.6
has caused the Debian Bug report #1138659,
regarding trixie-pu: package rsync/3.4.1+ds1-5+deb13u4
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1138659: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1138659
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Package: release.debian.org
Control: affects -1 + src:rsync
X-Debbugs-Cc: [email protected]
User: [email protected]
Usertags: pu
Tags: trixie
X-Debbugs-Cc: [email protected]
Severity: normal

[ Reason ]
This update fixes CVE-2026-45232, a minor/no-dsa client-side issue in RSYNC_PROXY handling.

When rsync connects through an HTTP proxy using RSYNC_PROXY, an overlong proxy response line could trigger a one-byte out-of-bounds stack write. The write is a fixed NUL byte, so the practical impact is limited, but the vulnerable code is present in trixie.

[ Impact ]
Clients using RSYNC_PROXY could crash or misbehave when receiving an overlong HTTP proxy response line from a malicious proxy or MITM.

[ Tests ]
The package built successfully in Salsa CI:

https://salsa.debian.org/aquila/rsync/-/pipelines/1099394

The targeted upstream regression test added by this patch also passed:

  PASS    proxy-response-line-too-long

See: https://salsa.debian.org/aquila/rsync/-/jobs/9700932#L3328

[ Risks ]
Low. The patch is small, comes from upstream v3.4.3, and only rejects an invalid overlong HTTP proxy response line.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Import the upstream v3.4.3 fix to reject overlong HTTP proxy response lines.

No upstream version bump and no unrelated fixes are included.

[ Other info ]
This is the matching trixie-pu requested during the review of the corresponding bookworm-pu request.

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1138587#12
diff -Nru rsync-3.4.1+ds1-5+deb13u3/debian/changelog 
rsync-3.4.1+ds1-5+deb13u4/debian/changelog
--- rsync-3.4.1+ds1-5+deb13u3/debian/changelog  2026-05-18 14:33:38.000000000 
-0400
+++ rsync-3.4.1+ds1-5+deb13u4/debian/changelog  2026-06-01 14:03:48.000000000 
-0400
@@ -1,3 +1,12 @@
+rsync (3.4.1+ds1-5+deb13u4) trixie; urgency=medium
+
+  * Non-maintainer upload.
+  * Import upstream patch to reject overlong HTTP proxy response lines,
+    avoiding a one byte out of bounds stack write when using RSYNC_PROXY.
+    (CVE-2026-45232).
+
+ -- Aquila Macedo Costa <[email protected]>  Mon, 01 Jun 2026 14:03:48 -0400
+
 rsync (3.4.1+ds1-5+deb13u3) trixie-security; urgency=high
 
   * Non-maintainer upload by the Security Team.
diff -Nru rsync-3.4.1+ds1-5+deb13u3/debian/patches/CVE-2026-45232.patch 
rsync-3.4.1+ds1-5+deb13u4/debian/patches/CVE-2026-45232.patch
--- rsync-3.4.1+ds1-5+deb13u3/debian/patches/CVE-2026-45232.patch       
1969-12-31 19:00:00.000000000 -0500
+++ rsync-3.4.1+ds1-5+deb13u4/debian/patches/CVE-2026-45232.patch       
2026-06-01 14:03:48.000000000 -0400
@@ -0,0 +1,236 @@
+From a5fc5ebe7a8ef1aa72f6e344599f97fd4427ecba Mon Sep 17 00:00:00 2001
+From: Andrew Tridgell <[email protected]>
+Date: Wed, 13 May 2026 20:35:35 +1000
+Subject: [PATCH] socket: reject over-long proxy response line
+
+fixes a one byte stack overflow when using RSYNC_PROXY with a
+malicious proxy.
+
+Reach: only when RSYNC_PROXY is set and a malicious or MITM'd
+proxy returns the pathological response.  The byte written is
+always '\0' and the attacker doesn't choose the offset, so impact
+is corruption of one adjacent stack byte and possible later
+misbehaviour or crash -- no information disclosure beyond the
+existing rprintf of buffer contents.
+
+Reported by Aisle Research via Michal Ruprich
+
+(cherry picked from commit a5fc5ebe7a8ef1aa72f6e344599f97fd4427ecba)
+---
+ socket.c                                    |  30 ++++---
+ testsuite/proxy-response-line-too-long.test | 128 ++++++++++++++++++++++++++++
+ 2 files changed, 145 insertions(+), 13 deletions(-)
+ create mode 100755 testsuite/proxy-response-line-too-long.test
+
+diff --git a/socket.c b/socket.c
+index c2075ad..6a8f6f4 100644
+--- a/socket.c
++++ b/socket.c
+@@ -47,21 +47,23 @@ static struct sigaction sigact;
+ 
+ static int sock_exec(const char *prog);
+ 
++#define PROXY_BUF_SIZE 1024
++
+ /* Establish a proxy connection on an open socket to a web proxy by using the
+  * CONNECT method.  If proxy_user and proxy_pass are not NULL, they are used 
to
+  * authenticate to the proxy using the "Basic" proxy-authorization protocol. 
*/
+ static int establish_proxy_connection(int fd, char *host, int port, char 
*proxy_user, char *proxy_pass)
+ {
+-      char *cp, buffer[1024];
+-      char *authhdr, authbuf[1024];
++      char *cp, buffer[PROXY_BUF_SIZE + 1];
++      char *authhdr, authbuf[PROXY_BUF_SIZE + 1];
+       int len;
+ 
+       if (proxy_user && proxy_pass) {
+-              stringjoin(buffer, sizeof buffer,
++              stringjoin(buffer, PROXY_BUF_SIZE,
+                        proxy_user, ":", proxy_pass, NULL);
+               len = strlen(buffer);
+ 
+-              if ((len*8 + 5) / 6 >= (int)sizeof authbuf - 3) {
++              if ((len*8 + 5) / 6 >= PROXY_BUF_SIZE - 3) {
+                       rprintf(FERROR,
+                               "authentication information is too long\n");
+                       return -1;
+@@ -74,14 +76,14 @@ static int establish_proxy_connection(int fd, char *host, 
int port, char *proxy_
+               authhdr = "";
+       }
+ 
+-      len = snprintf(buffer, sizeof buffer, "CONNECT %s:%d 
HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
+-      assert(len > 0 && len < (int)sizeof buffer);
++      len = snprintf(buffer, PROXY_BUF_SIZE, "CONNECT %s:%d 
HTTP/1.0%s%s\r\n\r\n", host, port, authhdr, authbuf);
++      assert(len > 0 && len < PROXY_BUF_SIZE);
+       if (write(fd, buffer, len) != len) {
+               rsyserr(FERROR, errno, "failed to write to proxy");
+               return -1;
+       }
+ 
+-      for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
++      for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE - 1]; cp++) {
+               if (read(fd, cp, 1) != 1) {
+                       rsyserr(FERROR, errno, "failed to read from proxy");
+                       return -1;
+@@ -90,11 +92,13 @@ static int establish_proxy_connection(int fd, char *host, 
int port, char *proxy_
+                       break;
+       }
+ 
+-      if (*cp != '\n')
+-              cp++;
+-      *cp-- = '\0';
+-      if (*cp == '\r')
+-              *cp = '\0';
++      if (cp == &buffer[PROXY_BUF_SIZE - 1]) {
++              rprintf(FERROR, "proxy response line too long\n");
++              return -1;
++      }
++      *cp = '\0';
++      if (cp > buffer && cp[-1] == '\r')
++              cp[-1] = '\0';
+       if (strncmp(buffer, "HTTP/", 5) != 0) {
+               rprintf(FERROR, "bad response from proxy -- %s\n",
+                       buffer);
+@@ -110,7 +114,7 @@ static int establish_proxy_connection(int fd, char *host, 
int port, char *proxy_
+       }
+       /* throw away the rest of the HTTP header */
+       while (1) {
+-              for (cp = buffer; cp < &buffer[sizeof buffer - 1]; cp++) {
++              for (cp = buffer; cp < &buffer[PROXY_BUF_SIZE]; cp++) {
+                       if (read(fd, cp, 1) != 1) {
+                               rsyserr(FERROR, errno,
+                                       "failed to read from proxy");
+diff --git a/testsuite/proxy-response-line-too-long.test 
b/testsuite/proxy-response-line-too-long.test
+new file mode 100755
+index 0000000..7f55c43
+--- /dev/null
++++ b/testsuite/proxy-response-line-too-long.test
+@@ -0,0 +1,128 @@
++#!/bin/sh
++
++# Copyright (C) 2026 by Andrew Tridgell
++
++# This program is distributable under the terms of the GNU GPL (see
++# COPYING).
++
++# Regression test for the off-by-one stack OOB write in
++# establish_proxy_connection() in socket.c when a malicious or
++# man-in-the-middle HTTP proxy returns a first response line of
++# 1023+ bytes without a '\n' terminator.
++#
++# Pre-fix: the read loop walked buffer[0..sizeof-2] one byte at a
++# time, then post-loop logic did "if (*cp != '\n') cp++; *cp-- =
++# '\0';".  If no newline arrived before the loop filled the buffer,
++# cp was left at &buffer[sizeof-1] (never written by the loop),
++# *cp held stale stack bytes, and cp++ pushed cp one past the array.
++# The null-termination then wrote one byte out of bounds on the
++# stack.  AddressSanitizer reports stack-buffer-overflow at the
++# null-termination site.
++#
++# Post-fix: the bound-exhaustion case is detected by position and
++# rejected with an "proxy response line too long" message, so no
++# OOB write occurs and rsync exits with a non-signal status.
++
++. "$suitedir/rsync.fns"
++
++command -v python3 >/dev/null 2>&1 || test_skipped "python3 not available"
++
++workdir="$scratchdir/workdir"
++mkdir -p "$workdir"
++cd "$workdir"
++
++port_file="$workdir/port"
++proxy_log="$workdir/proxy.log"
++
++# A minimal TCP listener: binds to an ephemeral port on 127.0.0.1,
++# writes the chosen port to $port_file *before* accept() so the test
++# can synchronise without a sleep, accepts one connection, reads
++# until end-of-headers or 64 KiB, sends exactly 1023 bytes of 'X'
++# with no '\n', then closes.
++python3 - "$port_file" >"$proxy_log" 2>&1 <<'PYEOF' &
++import socket, sys, os
++port_file = sys.argv[1]
++s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
++s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
++s.bind(("127.0.0.1", 0))
++port = s.getsockname()[1]
++tmp = port_file + ".tmp"
++with open(tmp, "w") as fp:
++    fp.write("%d\n" % port)
++os.rename(tmp, port_file)   # atomic visibility to the shell side
++s.listen(1)
++conn, _ = s.accept()
++conn.settimeout(5)
++try:
++    data = b""
++    while b"\r\n\r\n" not in data and len(data) < 65536:
++        chunk = conn.recv(8192)
++        if not chunk:
++            break
++        data += chunk
++except socket.timeout:
++    pass
++conn.sendall(b"X" * 1023)    # exactly the buffer-1 trigger size
++try:
++    conn.shutdown(socket.SHUT_RDWR)
++except OSError:
++    pass
++conn.close()
++s.close()
++PYEOF
++proxy_pid=$!
++
++# Wait up to ~10s for the listener to publish its port.
++i=0
++while [ ! -s "$port_file" ] && [ $i -lt 10 ]; do
++    sleep 1
++    i=$((i + 1))
++done
++
++if [ ! -s "$port_file" ]; then
++    kill "$proxy_pid" 2>/dev/null
++    cat "$proxy_log" >&2 2>/dev/null
++    test_fail "proxy listener never published a port"
++fi
++
++port=`cat "$port_file"`
++case "$port" in
++    *[!0-9]*|"") kill "$proxy_pid" 2>/dev/null; test_fail "bogus port from 
listener: '$port'" ;;
++esac
++
++# Run rsync through the malicious proxy.  Any rsync:// URL works:
++# the proxy intercepts the CONNECT and never forwards anywhere.
++rsync_err="$workdir/rsync.err"
++
++# rsync MUST exit non-zero here (the proxy is misbehaving).
++# Use `|| status=$?` so we capture the real exit code under `sh -e`;
++# `if ! cmd; then status=$?` would only ever see 0 because the `!`
++# is the last command before `$?`.
++status=0
++RSYNC_PROXY="127.0.0.1:$port" \
++    $RSYNC rsync://example.invalid:873/whatever/ "$workdir/out/" \
++    >/dev/null 2>"$rsync_err" || status=$?
++
++# Reap the listener.
++wait "$proxy_pid" 2>/dev/null || true
++
++# 1. rsync must not have crashed (SIGSEGV/SIGABRT report >= 128).
++if [ "$status" -ge 128 ]; then
++    cat "$rsync_err" >&2
++    test_fail "rsync killed by signal (status=$status) -- possible stack OOB 
regression"
++fi
++
++# 2. rsync must have actually exited non-zero (i.e. saw the bad proxy).
++if [ "$status" -eq 0 ]; then
++    cat "$rsync_err" >&2
++    test_fail "rsync returned success despite malformed proxy response"
++fi
++
++# 3. The new error message must appear.
++if ! grep -q "proxy response line too long" "$rsync_err"; then
++    cat "$rsync_err" >&2
++    test_fail "expected 'proxy response line too long' in rsync stderr"
++fi
++
++echo "OK: over-long proxy response line rejected cleanly without crashing"
++exit 0
diff -Nru rsync-3.4.1+ds1-5+deb13u3/debian/patches/series 
rsync-3.4.1+ds1-5+deb13u4/debian/patches/series
--- rsync-3.4.1+ds1-5+deb13u3/debian/patches/series     2026-05-18 
14:33:38.000000000 -0400
+++ rsync-3.4.1+ds1-5+deb13u4/debian/patches/series     2026-06-01 
14:03:48.000000000 -0400
@@ -38,3 +38,4 @@
 2026-05-20/0034-defence-in-depth-bound-wire-supplied-counts-and-leng.patch
 2026-05-20/0035-defence-in-depth-guard-cumulative-snprintf-against-l.patch
 2026-05-20/0036-defence-in-depth-receiver-block-index-bounds-read_de.patch
+CVE-2026-45232.patch

--- End Message ---
--- Begin Message ---
Version: 13.6

This update was released as part of 13.6.

--- End Message ---

Reply via email to