git: e17d7ab869bb - main - xdr_string: don't leak strings with xdr_free

From: Brooks Davis <brooks_at_FreeBSD.org>
Date: Fri, 23 Jan 2026 11:07:28 UTC
The branch main has been updated by brooks:

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

commit e17d7ab869bbfe3fa5a7da4b74d9f4fa51a0d69f
Author:     Brooks Davis <brooks@FreeBSD.org>
AuthorDate: 2026-01-23 10:35:55 +0000
Commit:     Brooks Davis <brooks@FreeBSD.org>
CommitDate: 2026-01-23 10:35:55 +0000

    xdr_string: don't leak strings with xdr_free
    
    Historically (and in a small amount of older software such as OpenAFS),
    developers would attempt to free XDR strings with
    
            xdr_free((xdrproc_t)xdr_string, &string)
    
    This resulted in xdr_free calling xdr_string with only two intentional
    arguments and whatever was left in the third argument register.  If the
    register held a sufficently small number, xdr_string would return FALSE
    and not free the string (no one checks the return values).
    
    Software should instead free strings with:
    
            xdr_free((xdrproc_t)xdr_wrapstring, &string)
    
    Because buggy software exists in the wild, act as though xdr_wrapstring
    was used in the XDR_FREE case and plug these leaks.
    
    Reviewed by:    kib
    MFC after:      3 days
    Effort:         CHERI upstreaming
    Sponsored by:   Innovate UK
    Differential Revision:  https://reviews.freebsd.org/D54825
---
 lib/libc/xdr/xdr.c | 7 +++++++
 sys/xdr/xdr.c      | 7 +++++++
 2 files changed, 14 insertions(+)

diff --git a/lib/libc/xdr/xdr.c b/lib/libc/xdr/xdr.c
index 59a843405abf..47aafea4bc30 100644
--- a/lib/libc/xdr/xdr.c
+++ b/lib/libc/xdr/xdr.c
@@ -696,6 +696,13 @@ xdr_string(XDR *xdrs, char **cpp, u_int maxsize)
 		if (sp == NULL) {
 			return(TRUE);	/* already free */
 		}
+		/*
+		 * XXX: buggy software may call this without a third
+		 * argument via xdr_free().  Ignore maxsize since it may
+		 * be invalid.  Otherwise, if it's very small, we might
+		 * fail to free the string.
+		 */
+		maxsize = RPC_MAXDATASIZE;
 		/* FALLTHROUGH */
 	case XDR_ENCODE:
 		size = strlen(sp);
diff --git a/sys/xdr/xdr.c b/sys/xdr/xdr.c
index 81d238ebf19f..f983a474abdd 100644
--- a/sys/xdr/xdr.c
+++ b/sys/xdr/xdr.c
@@ -620,6 +620,13 @@ xdr_string(XDR *xdrs, char **cpp, u_int maxsize)
 		if (sp == NULL) {
 			return(TRUE);	/* already free */
 		}
+		/*
+		 * XXX: buggy software may call this without a third
+		 * argument via xdr_free().  Ignore maxsize since it may
+		 * be invalid.  Otherwise, if it's very small, we might
+		 * fail to free the string.
+		 */
+		maxsize = RPC_MAXDATASIZE;
 		/* FALLTHROUGH */
 	case XDR_ENCODE:
 		size = strlen(sp);