git: 13bcc9a88260 - main - libc: Fix stack buffer overflow in cgetnext()

From: Faraz Vahedi <kfv_at_FreeBSD.org>
Date: Thu, 29 Oct 2026 12:10:48 UTC
The branch main has been updated by kfv:

URL: https://cgit.FreeBSD.org/src/commit/?id=13bcc9a882603da216c7c38c086fcb501bd9d0ec

commit 13bcc9a882603da216c7c38c086fcb501bd9d0ec
Author:     Faraz Vahedi <kfv@FreeBSD.org>
AuthorDate: 2026-07-05 15:03:31 +0000
Commit:     Faraz Vahedi <kfv@FreeBSD.org>
CommitDate: 2026-10-29 12:09:45 +0000

    libc: Fix stack buffer overflow in cgetnext()
    
    `cgetnext()` copies a capability record's name field into a fixed
    1024 byte on-stack buffer (`nbuf[BSIZE]`) one character at a time,
    but never checks that the write pointer stays within the buffer.
    Therefore, a record name that is longer than `BSIZE` (either on a
    single line, or accumulated across several `\`-continued lines as
    the pointer is not reset amidst continuation) writes past the end
    of `nbuf` and smashes the stack.
    
    The name field is read directly from the capability file, so any
    consumer of `cgetnext()`/`cgetfirst()` that processes an untrusted
    `termcap(5)`- or `printcap(5)`-style database (e.g. `cap_mkdb(1)`,
    `lpr(1)`, or any third-party caller) can be exposed and hence made
    to corrupt the stack.
    
    Reviewed by:    fuz, dteske
    Approved by:    fuz (mentor), dteske (mentor)
    MFC after:      1 week
    Pull Request:   https://github.com/freebsd/freebsd-src/pull/2309
---
 lib/libc/gen/getcap.3 | 20 ++++++++++++++++++--
 lib/libc/gen/getcap.c |  5 +++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/lib/libc/gen/getcap.3 b/lib/libc/gen/getcap.3
index 00c7edd8026f..9edf947877fc 100644
--- a/lib/libc/gen/getcap.3
+++ b/lib/libc/gen/getcap.3
@@ -28,7 +28,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd March 22, 2002
+.Dd July 23, 2026
 .Dt GETCAP 3
 .Os
 .Sh NAME
@@ -258,7 +258,10 @@ expansion, \-1 is returned if a system error occurred, and \-2
 is returned if a potential reference loop is detected (see
 .Ic tc=
 comments below).
-Upon completion of database (0 return) the database is closed.
+Upon completion of database (0 return) or occurrence of an error (negative
+return), the database is closed and a subsequent call to
+.Fn cgetnext
+starts again from the first record.
 .Pp
 The
 .Fn cgetclose
@@ -550,6 +553,19 @@ as follows:
 .It Bq Er ENOMEM
 No memory to allocate.
 .El
+.Pp
+The
+.Fn cgetfirst
+and
+.Fn cgetnext
+functions
+may fail and set
+.Va errno
+as follows:
+.Bl -tag -width Er
+.It Bq Er ENAMETOOLONG
+The name field of a capability record is too long.
+.El
 .Sh SEE ALSO
 .Xr cap_mkdb 1 ,
 .Xr malloc 3
diff --git a/lib/libc/gen/getcap.c b/lib/libc/gen/getcap.c
index cfb81ed0d065..1ee83c8ca28b 100644
--- a/lib/libc/gen/getcap.c
+++ b/lib/libc/gen/getcap.c
@@ -709,6 +709,11 @@ cgetnext(char **bp, char **db_array)
 		np = nbuf;
 		for (;;) {
 			for (cp = line; *cp != '\0'; cp++) {
+				if (np >= nbuf + sizeof(nbuf) - 1) {
+					(void)cgetclose();
+					errno = ENAMETOOLONG;
+					return (-1);
+				}
 				if (*cp == ':') {
 					*np++ = ':';
 					done = 1;