** Description changed:
+ [Impact]
+
+ On Ubuntu 22.04 LTS, the GnuTLS network stream driver in rsyslog can enter a
receive retry loop when a TLS connection is interrupted under poor network
conditions.
+ The affected input thread repeatedly receives EAGAIN, consumes one CPU core,
and delays TLS log delivery.
+
+ The receive retry path does not account for the GnuTLS read/write direction
and may keep retrying even when GnuTLS has no pending data.
+ The upstream fix checks the GnuTLS read/write direction and pending-data
state, and waits for socket readiness when GnuTLS cannot read more data.
+
+ [Test Plan]
+
+ Please use the reproducer scripts in [Other Info].
+
+ The test sets up an anonymous GnuTLS input and two TLS 1.2 sessions,
+ withholding the last byte of one record in each session.
+
+ 1. Install the required packages and configure the test input.
+
+ sudo apt install rsyslog rsyslog-gnutls python3
+ sudo install -m 644 lp2125768.conf /etc/rsyslog.d/99-lp2125768.conf
+ sudo systemctl restart rsyslog
+
+ 2. Start two TLS sessions and observe rsyslogd while session A remains
+ incomplete.
+
+ sudo truncate -s 0 /var/log/lp2125768.log
+ python3 tls-record-proxy.py 16515 12 >proxy-a.log 2>&1 &
+ python3 tls-record-proxy.py 16516 1 >proxy-b.log 2>&1 &
+ sleep 1
+ python3 tls-boundary-client.py 16515 session-a 14 >client-a.log 2>&1 &
+ sleep 1
+ python3 tls-boundary-client.py 16516 session-b 7 >client-b.log 2>&1 &
+ sleep 3
+ sudo grep -c session-b-second /var/log/lp2125768.log # before: 0, after: 1
+ top -b -d 5 -n 2 -p "$(pidof rsyslogd)" | tail -n 3
+ wait
+ sudo grep -c 'session-[ab]-second' /var/log/lp2125768.log # expected: 2
+
+ Before the fix, the first grep prints 0 and rsyslogd consumes
+ approximately one CPU core until session A releases its byte.
+
+ After installing the patched rsyslog and rsyslog-gnutls packages, restart
rsyslog and repeat step 2.
+ The first grep should print 1 while session A is still incomplete and
rsyslogd should remain near idle.
+ The final grep should print 2 both before and after the fix.
+
+
+ [Where problems could occur]
+
+ The change affects the GnuTLS receive and readiness paths used by encrypted
TCP inputs.
+ A regression in these paths could close a fragmented TLS connection too early
or leave buffered input waiting even though it can be processed.
+ The test checks that both TLS sessions complete and that session B is not
blocked while session A is incomplete.
+
+ [Other Info]
+
+ Reproducer files
+
+ lp2125768.conf
+
+ module(load="imtcp")
+
+ input(type="imtcp" port="16514"
+ StreamDriver.Name="gtls"
+ StreamDriver.Mode="1"
+ StreamDriver.AuthMode="anon")
+
+ *.* action(type="omfile" file="/var/log/lp2125768.log")
+
+ tls-record-proxy.py
+
+ #!/usr/bin/env python3
+
+ import socket, sys, threading, time
+
+
+ def relay(source, destination):
+ while data := source.recv(65536):
+ destination.sendall(data)
+
+
+ listen_port = int(sys.argv[1])
+ hold_seconds = int(sys.argv[2])
+
+ with socket.create_server(("127.0.0.1", listen_port)) as listener:
+ client, _ = listener.accept()
+ with client, socket.create_connection(("127.0.0.1", 16514)) as server:
+ threading.Thread(target=relay, args=(server, client),
daemon=True).start()
+ buffered = bytearray()
+ records = []
+
+ while len(records) < 2:
+ buffered.extend(client.recv(65536))
+ while len(buffered) >= 5:
+ size = 5 + int.from_bytes(buffered[3:5], "big")
+ if len(buffered) < size:
+ break
+ record = bytes(buffered[:size])
+ del buffered[:size]
+ if record[0] == 23:
+ records.append(record)
+ else:
+ server.sendall(record)
+
+ payload = b"".join(records)
+ server.sendall(payload[:-1])
+ print("TRIGGER", flush=True)
+ time.sleep(hold_seconds)
+ server.sendall(payload[-1:] + buffered)
+ print("RELEASE", flush=True)
+ relay(client, server)
+
+ tls-boundary-client.py
+
+ #!/usr/bin/env python3
+
+ import socket, ssl, sys, time
+
+
+ port = int(sys.argv[1])
+ label = sys.argv[2]
+ linger_seconds = int(sys.argv[3])
+
+ prefix = f"<134>Sep 5 12:20:00 client {label} ".encode()
+ first = prefix + b"B" * (16383 - len(prefix)) + b"\n"
+ second = f"<134>Sep 5 12:20:01 client {label}-second\n".encode()
+
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+ context.check_hostname = False
+ context.verify_mode = ssl.CERT_NONE
+ context.minimum_version = ssl.TLSVersion.TLSv1_2
+ context.maximum_version = ssl.TLSVersion.TLSv1_2
+ context.set_ciphers("aNULL:@SECLEVEL=0")
+
+ with socket.create_connection(("127.0.0.1", port)) as raw:
+ with context.wrap_socket(raw) as tls:
+ tls.sendall(first)
+ tls.sendall(second)
+ time.sleep(linger_seconds)
+
+
+
+ [Original Description]
+
The rsyslog version 8.2112.0-2ubuntu2.2 in Ubuntu 22.04 has a spin-loop
bug, consuming 100% CPU in the GNU TLS module under certain bad network
conditions.
Apply this fix:
https://github.com/rsyslog/rsyslog/commit/aefcfa4d0f6e213c9fac814c3e6bd53970b7e90e
The change set as-is applies directly to the source.
See an old issue, with fresh comments here:
https://github.com/rsyslog/rsyslog/issues/4927#issuecomment-3338350615
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2125768
Title:
Rsyslog Include patch for 100% CPU spin-loop in version
8.2112.0-2ubuntu2.2
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/rsyslog/+bug/2125768/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs