Fix CVE-2026-43618: integer overflow in compressed-token decoding

Backport upstream commit 901041dddc9a to fix CVE-2026-43618, an integer
overflow in compressed-token decoding in rsync. The patch hardens
the receiver's three compressed-token decoders (zlib, zstd, lz4) in token.c
by replacing inline token decoding with a shared recv_compressed_token_num()
function that includes proper overflow protection. It also adds bounds
checking in receiver.c and simple_recv_token to reject malformed token
values, preventing potential memory leaks to the wire.

Upstream patch:
  https://github.com/RsyncProject/rsync/commit/901041dd.patch

Added a followup commit:
  https://github.com/RsyncProject/rsync/commit/11e3e239

Resolves: RHEL-174939
This commit is contained in:
Michal Ruprich 2026-06-23 11:41:13 +02:00
parent cc69bbdd82
commit 15f6d55f9a
2 changed files with 241 additions and 1 deletions

View File

@ -0,0 +1,232 @@
From 2433971801c8814f2514a97c57dd27b12db1a243 Mon Sep 17 00:00:00 2001
From: Andrew Tridgell <andrew@tridgell.net>
Date: Wed, 29 Apr 2026 11:10:59 +1000
Subject: [PATCH] token: harden compressed-token decoding against integer
overflow
The receiver's three compressed-token decoders --
recv_deflated_token (zlib), recv_zstd_token, and
recv_compressed_token (lz4) -- accumulated rx_token (a 32-bit
signed counter) without overflow checking. A malicious sender
could craft a compressed-token stream that walked rx_token past
INT32_MAX, with careful manipulation leaking process memory
contents to the wire (environment variables, passwords, heap
pointers, library pointers -- significantly weakening ASLR
and facilitating further exploitation).
Cap rx_token at MAX_TOKEN_INDEX = 0x7ffffffe. Fold the
bookkeeping into recv_compressed_token_num() and
recv_compressed_token_run() shared by all three decoders. Reject
negative or out-of-range token values explicitly. Also cap the
simple_recv_token literal-block length at the source: any
wire-supplied length > CHUNK_SIZE is ill-formed (the matching
simple_send_token never writes a chunk larger than CHUNK_SIZE),
so reject before looping on attacker-controlled bytes.
Reach: an authenticated daemon connection with compression
enabled (the default for protocols >= 30 when both peers
advertise it). Disabling compression on the daemon
("refuse options = compress" in rsyncd.conf) is the available
workaround.
Reporter: Omar Elsayed (seks99x).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---
receiver.c | 11 +++++-
token.c | 102 ++++++++++++++++++++++++++++++-----------------------
2 files changed, 67 insertions(+), 46 deletions(-)
diff --git a/receiver.c b/receiver.c
index 6c74468d..f42e764c 100644
--- a/receiver.c
+++ b/receiver.c
@@ -310,7 +310,12 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
}
}
- while ((i = recv_token(f_in, &data)) != 0) {
+ while (1) {
+ data = NULL;
+ i = recv_token(f_in, &data);
+ if (i == 0)
+ break;
+
if (INFO_GTE(PROGRESS, 1))
show_progress(offset, total_size);
@@ -318,6 +323,10 @@ static int receive_data(int f_in, char *fname_r, int fd_r, OFF_T size_r,
maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH | MSK_ACTIVE_RECEIVER);
if (i > 0) {
+ if (!data) {
+ rprintf(FERROR, "Invalid literal token with no data [%s]\n", who_am_i());
+ exit_cleanup(RERR_PROTOCOL);
+ }
if (DEBUG_GTE(DELTASUM, 3)) {
rprintf(FINFO,"data recv %d at %s\n",
i, big_num(offset));
diff --git a/token.c b/token.c
index f5a41c98..7e295326 100644
--- a/token.c
+++ b/token.c
@@ -286,6 +286,10 @@ static int32 simple_recv_token(int f, char **data)
int32 i = read_int(f);
if (i <= 0)
return i;
+ /* A literal run may exceed CHUNK_SIZE: some peers (e.g. the
+ * acrosync library) use a 64k block size. The loop below reads
+ * the run CHUNK_SIZE bytes at a time, so read_buf never writes
+ * past the static CHUNK_SIZE buffer regardless of i. */
residue = i;
}
@@ -488,9 +496,52 @@ static char *cbuf;
static char *dbuf;
/* for decoding runs of tokens */
+#define MAX_TOKEN_INDEX ((int32)0x7ffffffe)
+
static int32 rx_token;
static int32 rx_run;
+static NORETURN void invalid_compressed_token(void)
+{
+ rprintf(FERROR, "invalid token number in compressed stream\n");
+ exit_cleanup(RERR_PROTOCOL);
+}
+
+static int32 recv_compressed_token_num(int f, int32 flag)
+{
+ if (flag & TOKEN_REL) {
+ int32 incr = flag & 0x3f;
+ if (rx_token > MAX_TOKEN_INDEX - incr)
+ invalid_compressed_token();
+ rx_token += incr;
+ flag >>= 6;
+ } else {
+ rx_token = read_int(f);
+ if (rx_token < 0 || rx_token > MAX_TOKEN_INDEX)
+ invalid_compressed_token();
+ }
+
+ if (flag & 1) {
+ rx_run = read_byte(f);
+ rx_run += read_byte(f) << 8;
+ if (rx_run <= 0 || rx_token > MAX_TOKEN_INDEX - rx_run)
+ invalid_compressed_token();
+ recv_state = r_running;
+ }
+
+ return -1 - rx_token;
+}
+
+static int32 recv_compressed_token_run(void)
+{
+ if (rx_run <= 0 || rx_token >= MAX_TOKEN_INDEX)
+ invalid_compressed_token();
+ ++rx_token;
+ if (--rx_run == 0)
+ recv_state = r_idle;
+ return -1 - rx_token;
+}
+
/* Receive a deflated token and inflate it */
static int32 recv_deflated_token(int f, char **data)
{
@@ -581,17 +632,7 @@ static int32 recv_deflated_token(int f, char **data)
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else
- rx_token = read_int(f);
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflating:
rx_strm.next_out = (Bytef *)dbuf;
@@ -611,10 +652,7 @@ static int32 recv_deflated_token(int f, char **data)
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}
@@ -823,17 +861,7 @@ static int32 recv_zstd_token(int f, char **data)
return 0;
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else
- rx_token = read_int(f);
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflated: /* zstd doesn't get into this state */
break;
@@ -864,10 +892,7 @@ static int32 recv_zstd_token(int f, char **data)
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}
@@ -987,17 +1012,7 @@ static int32 recv_compressed_token(int f, char **data)
}
/* here we have a token of some kind */
- if (flag & TOKEN_REL) {
- rx_token += flag & 0x3f;
- flag >>= 6;
- } else
- rx_token = read_int(f);
- if (flag & 1) {
- rx_run = read_byte(f);
- rx_run += read_byte(f) << 8;
- recv_state = r_running;
- }
- return -1 - rx_token;
+ return recv_compressed_token_num(f, flag);
case r_inflating:
avail_out = LZ4_decompress_safe(next_in, dbuf, avail_in, size);
@@ -1013,10 +1028,7 @@ static int32 recv_compressed_token(int f, char **data)
break;
case r_running:
- ++rx_token;
- if (--rx_run == 0)
- recv_state = r_idle;
- return -1 - rx_token;
+ return recv_compressed_token_run();
}
}
}
--
2.52.0

View File

@ -10,7 +10,7 @@
Summary: A program for synchronizing files over a network
Name: rsync
Version: 3.2.5
Release: 8%{?dist}
Release: 9%{?dist}
URL: https://rsync.samba.org/
Source0: https://download.samba.org/pub/rsync/src/rsync-%{version}%{?prerelease}.tar.gz
@ -73,6 +73,9 @@ Patch13: rsync-3.2.5-fix-cve-2026-29518.patch
# https://github.com/RsyncProject/rsync/commit/3526884f
# https://github.com/RsyncProject/rsync/commit/7192db98
Patch14: rsync-3.2.5-fix-cve-2026-29518-regressions.patch
# https://github.com/RsyncProject/rsync/commit/901041dd
# https://github.com/RsyncProject/rsync/commit/11e3e239
Patch15: rsync-3.2.5-fix-cve-2026-43618.patch
%description
Rsync uses a reliable algorithm to bring remote and host files into
@ -126,6 +129,7 @@ may be used to setup a restricted rsync users via ssh logins.
%patch 12 -p1 -b .cve-2024-12086
%patch 13 -p1 -b .cve-2026-29518
%patch 14 -p1 -b .cve-2026-29518-regressions
%patch 15 -p1 -b .cve-2026-43618
%build
%configure --disable-xxhash --with-rrsync
@ -176,6 +180,10 @@ install -D -m644 %{SOURCE6} $RPM_BUILD_ROOT/%{_unitdir}/rsyncd@.service
%systemd_postun_with_restart rsyncd.service
%changelog
* Tue Jun 23 2026 Michal Ruprich <mruprich@redhat.com> - 3.2.5-9
- Fix integer overflow in compressed-token decoding (CVE-2026-43618)
- Resolves: RHEL-174939
* Tue Jun 23 2026 Michal Ruprich <mruprich@redhat.com> - 3.2.5-8
- Fix TOCTOU symlink race in daemon no-chroot mode (CVE-2026-29518)
- Resolves: RHEL-174953