git: 64ce9daab9ee - main - libfetch: Limit response line length

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Wed, 30 Sep 2026 16:55:46 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=64ce9daab9ee49b4647f43141a830d7518723142

commit 64ce9daab9ee49b4647f43141a830d7518723142
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-09-30 16:54:25 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2026-09-30 16:54:25 +0000

    libfetch: Limit response line length
    
    When reading an FTP or HTTP response, error out if we read 64 kB before
    hitting a newline.  Otherwise a runaway or malicious server could have
    us spinning for quite a while allocating more and more memory before we
    gave up or crashed.
    
    MFC after:      1 week
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D60007
---
 lib/libfetch/common.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/lib/libfetch/common.c b/lib/libfetch/common.c
index 431fd781ac6a..da0ea6f8aaeb 100644
--- a/lib/libfetch/common.c
+++ b/lib/libfetch/common.c
@@ -57,6 +57,8 @@
 #include "fetch.h"
 #include "common.h"
 
+/* Maximum length of a line, newline included, for fetch_getln() */
+#define FETCH_LINE_MAX		65536
 
 /*** Local data **************************************************************/
 
@@ -1466,9 +1468,15 @@ fetch_getln(conn_t *conn)
 	/* look at the data we already have */
 	if (conn->pos < conn->buflen) {
 		conn->line = conn->buf + conn->pos;
-		while (conn->pos < conn->buflen)
+		while (conn->pos < conn->buflen) {
 			if (conn->buf[conn->pos++] == '\n')
 				goto found;
+			if (conn->buf + conn->pos - conn->line >=
+			    FETCH_LINE_MAX) {
+				errno = EPROTO;
+				goto fail;
+			}
+		}
 		/* reset for the upcoming memmove() */
 		conn->pos = conn->line - conn->buf;
 	}
@@ -1498,9 +1506,14 @@ fetch_getln(conn_t *conn)
 		if (rlen == 0)
 			break;
 		/* look for a newline */
-		while (conn->pos < conn->buflen)
+		while (conn->pos < conn->buflen) {
 			if (conn->buf[conn->pos++] == '\n')
 				goto found;
+			if (conn->pos >= FETCH_LINE_MAX) {
+				errno = EPROTO;
+				goto fail;
+			}
+		}
 		/* do we need a bigger buffer? */
 		if (conn->buflen > conn->bufsize / 2) {
 			tmp = conn->buf;