From fec84e347768080e4370e5aeb05886bfe19ae54b Mon Sep 17 00:00:00 2001
From: Michael Meffie <mmeffie@sinenomine.net>
Date: Fri, 10 Mar 2023 17:51:17 -0500
Subject: [PATCH 1/9] xdr: Avoid xdr_string maxsize check when freeing

The maxsize argument in xdr_string() is garbage when called by
xdr_free(), since xdr_free() only passes the XDR handle and the xdr
string to be freed. Sometimes the size check fails and xdr_string()
returns early, without freeing the string and without setting the object
pointer to NULL.

Usually this just results in leaking the string's memory. But since
commit 9ae5b599c7 (bos: Let xdr allocate rpc output strings), many
callers in bos.c rely on xdr_free(xdr_string) to set the given string
to NULL; if this doesn't happen, subsequent calls to BOZO_ RPCs can
corrupt memory, often causing the 'bos' process to segfault.

We only need the maxsize check when encoding or decoding, so avoid
accessing the maxsize agument when the op mode is XDR_FREE.

In general, xdr_free() can only safely be used on xdr 2-argument xdr
functions, so must be avoided when freeing xdr opaque, byte, and union
types.

This change makes it safe to use xdr_free() to free xdr strings, but in
the future, we should provide a typesafe and less fragile function for
freeing xdr strings returned from RPCs.  Currently, xdr_free(xdr_string)
is only called by the bos client and the tests.

Reviewed-on: https://gerrit.openafs.org/15343
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit bbb1e8adfed6804ac6fbae0a073dc6927096e16a)

Change-Id: I1f190d28acab5fa1621919f283571fcacb495ce4
Reviewed-on: https://gerrit.openafs.org/15939
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/rx/xdr.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/rx/xdr.c b/src/rx/xdr.c
index bfc1b7f6fe..88f7772c99 100644
--- a/src/rx/xdr.c
+++ b/src/rx/xdr.c
@@ -530,7 +530,7 @@ xdr_string(XDR * xdrs, char **cpp, u_int maxsize)
     if (!xdr_u_int(xdrs, &size)) {
 	return (FALSE);
     }
-    if (size > maxsize) {
+    if (xdrs->x_op != XDR_FREE && size > maxsize) {
 	return (FALSE);
     }
     nodesize = size + 1;
-- 
2.45.2


From 40440c3eb628ff1772588bdc99d7496292097bbd Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Thu, 13 Jun 2024 15:28:38 -0500
Subject: [PATCH 2/9] OPENAFS-SA-2024-003: xdr: Avoid prealloc'd string OUT
 args

CVE-2024-10397

Currently, several callers call RPCs with string OUT arguments, and
provide preallocated memory for those arguments. This can easily allow a
response from the server to overrun the allocated buffer, stomping over
stack or heap memory.

We could simply make our preallocated buffers larger than the maximum
size that the RPC allows, but relying on that is error prone, and
there's no way for XDR to check if a string buffer is large enough.

Instead, to make sure we don't overrun a given preallocated buffer,
avoid giving a preallocated buffer to such RPCs, and let XDR allocate
the memory for us.

Specifically, this commit changes several callers to
RXAFS_GetVolumeStatus(), and one caller of BOZO_GetInstanceParm(), to
avoid passing in a preallocated string buffer.

All other callers of RPCs with string OUT args already let XDR allocate
the buffers for them.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15918
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit 00a1b266af51a828a022c23e7bb006a39740eaad)

Change-Id: Ib174d008eaf1fd10d42702bcdb607e45b26acf58
Reviewed-on: https://gerrit.openafs.org/15940
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/WINNT/afsd/cm_ioctl.c           | 26 +++++++++++++++++-----
 src/WINNT/afsd/cm_vnodeops.c        | 18 +++++++--------
 src/WINNT/afsd/cm_volume.c          | 19 ++++++++--------
 src/WINNT/afsrdr/user/RDRFunction.c | 34 ++++++++++++++---------------
 src/afs/afs_pioctl.c                | 20 ++++++++---------
 src/libadmin/bos/afs_bosAdmin.c     | 16 +++++++++++---
 6 files changed, 76 insertions(+), 57 deletions(-)

diff --git a/src/WINNT/afsd/cm_ioctl.c b/src/WINNT/afsd/cm_ioctl.c
index fd7c6adc8a..7ca2846c3c 100644
--- a/src/WINNT/afsd/cm_ioctl.c
+++ b/src/WINNT/afsd/cm_ioctl.c
@@ -762,9 +762,6 @@ cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     cm_conn_t *connp;
     AFSFetchVolumeStatus volStat;
     char *cp;
-    char *Name;
-    char *OfflineMsg;
-    char *MOTD;
     struct rx_connection * rxconnp;
 
 #ifdef AFS_FREELANCE_CLIENT
@@ -783,19 +780,23 @@ cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
     {
         cm_fid_t    vfid;
         cm_scache_t *vscp;
+	char *Name = NULL;
+	char *OfflineMsg = NULL;
+	char *MOTD = NULL;
 
         cm_SetFid(&vfid, scp->fid.cell, scp->fid.volume, 1, 1);
         code = cm_GetSCache(&vfid, NULL, &vscp, userp, reqp);
         if (code)
             return code;
 
-        Name = volName;
-        OfflineMsg = offLineMsg;
-        MOTD = motd;
         do {
             code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
             if (code) continue;
 
+	    xdr_free((xdrproc_t) xdr_string, &Name);
+	    xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+	    xdr_free((xdrproc_t) xdr_string, &MOTD);
+
             rxconnp = cm_GetRxConn(connp);
             code = RXAFS_GetVolumeStatus(rxconnp, vfid.volume,
                                          &volStat, &Name, &OfflineMsg, &MOTD);
@@ -805,6 +806,19 @@ cm_IoctlGetVolumeStatus(struct cm_ioctl *ioctlp, struct cm_user *userp, cm_scach
         code = cm_MapRPCError(code, reqp);
 
         cm_ReleaseSCache(vscp);
+
+	strncpy(volName, Name, sizeof(volName));
+	volName[sizeof(volName) - 1] = '\0';
+
+	strncpy(offLineMsg, OfflineMsg, sizeof(offLineMsg));
+	offLineMsg[sizeof(offLineMsg) - 1] = '\0';
+
+	strncpy(motd, MOTD, sizeof(motd));
+	motd[sizeof(motd) - 1] = '\0';
+
+	xdr_free((xdrproc_t) xdr_string, &Name);
+	xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+	xdr_free((xdrproc_t) xdr_string, &MOTD);
     }
 
     if (code)
diff --git a/src/WINNT/afsd/cm_vnodeops.c b/src/WINNT/afsd/cm_vnodeops.c
index 074a970450..226bf7af00 100644
--- a/src/WINNT/afsd/cm_vnodeops.c
+++ b/src/WINNT/afsd/cm_vnodeops.c
@@ -2743,12 +2743,6 @@ cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_re
     AFSFetchVolumeStatus volStat;
     cm_volume_t *volp = NULL;
     afs_uint32   volType;
-    char *Name;
-    char *OfflineMsg;
-    char *MOTD;
-    char volName[32]="(unknown)";
-    char offLineMsg[256]="server temporarily inaccessible";
-    char motd[256]="server temporarily inaccessible";
     osi_hyper_t freespace;
     cm_fid_t    vfid;
     cm_scache_t *vscp;
@@ -2778,11 +2772,11 @@ cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_re
                           CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS);
         lock_ReleaseWrite(&vscp->rw);
         if (code == 0) {
-            Name = volName;
-            OfflineMsg = offLineMsg;
-            MOTD = motd;
-
             do {
+		char *Name = NULL;
+		char *OfflineMsg = NULL;
+		char *MOTD = NULL;
+
                 code = cm_ConnFromFID(&vfid, userp, reqp, &connp);
                 if (code) continue;
 
@@ -2791,6 +2785,10 @@ cm_IsSpaceAvailable(cm_fid_t * fidp, osi_hyper_t *sizep, cm_user_t *userp, cm_re
                                              &volStat, &Name, &OfflineMsg, &MOTD);
                 rx_PutConnection(rxconnp);
 
+		xdr_free((xdrproc_t) xdr_string, &Name);
+		xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+		xdr_free((xdrproc_t) xdr_string, &MOTD);
+
             } while (cm_Analyze(connp, userp, reqp, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
             code = cm_MapRPCError(code, reqp);
         }
diff --git a/src/WINNT/afsd/cm_volume.c b/src/WINNT/afsd/cm_volume.c
index c85107ba76..de2d80be41 100644
--- a/src/WINNT/afsd/cm_volume.c
+++ b/src/WINNT/afsd/cm_volume.c
@@ -1311,24 +1311,14 @@ cm_CheckOfflineVolumeState(cm_volume_t *volp, cm_vol_state_t *statep, afs_uint32
     cm_conn_t *connp;
     long code;
     AFSFetchVolumeStatus volStat;
-    char *Name;
-    char *OfflineMsg;
-    char *MOTD;
     cm_req_t req;
     struct rx_connection * rxconnp;
-    char volName[32];
     afs_uint32 volType;
-    char offLineMsg[256];
-    char motd[256];
     long alldown, alldeleted;
     cm_serverRef_t *serversp;
     cm_fid_t vfid;
     cm_scache_t *vscp = NULL;
 
-    Name = volName;
-    OfflineMsg = offLineMsg;
-    MOTD = motd;
-
     volType = cm_VolumeType(volp, volID);
 
     if (statep->ID != 0 && (!volID || volID == statep->ID)) {
@@ -1374,6 +1364,10 @@ cm_CheckOfflineVolumeState(cm_volume_t *volp, cm_vol_state_t *statep, afs_uint32
                 code = cm_GetSCache(&vfid, NULL, &vscp, cm_rootUserp, &req);
                 if (code = 0) {
 		    do {
+			char *Name = NULL;
+			char *OfflineMsg = NULL;
+			char *MOTD = NULL;
+
 			code = cm_ConnFromVolume(volp, statep->ID, cm_rootUserp, &req, &connp);
 			if (code)
 			   continue;
@@ -1382,6 +1376,11 @@ cm_CheckOfflineVolumeState(cm_volume_t *volp, cm_vol_state_t *statep, afs_uint32
 			code = RXAFS_GetVolumeStatus(rxconnp, statep->ID,
 						     &volStat, &Name, &OfflineMsg, &MOTD);
 			rx_PutConnection(rxconnp);
+
+			xdr_free((xdrproc_t) xdr_string, &Name);
+			xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+			xdr_free((xdrproc_t) xdr_string, &MOTD);
+
 		    } while (cm_Analyze(connp, cm_rootUserp, &req, &vfid, NULL, 0, NULL, NULL, NULL, NULL, code));
 		    code = cm_MapRPCError(code, &req);
 
diff --git a/src/WINNT/afsrdr/user/RDRFunction.c b/src/WINNT/afsrdr/user/RDRFunction.c
index cffb9b58c6..db767cbb8a 100644
--- a/src/WINNT/afsrdr/user/RDRFunction.c
+++ b/src/WINNT/afsrdr/user/RDRFunction.c
@@ -5820,14 +5820,8 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
     DWORD       status;
     FILETIME ft = {0x832cf000, 0x01abfcc4}; /* October 1, 1982 00:00:00 +0600 */
 
-    char volName[32]="(unknown)";
-    char offLineMsg[256]="server temporarily inaccessible";
-    char motd[256]="server temporarily inaccessible";
     cm_conn_t *connp;
     AFSFetchVolumeStatus volStat;
-    char *Name;
-    char *OfflineMsg;
-    char *MOTD;
     struct rx_connection * rxconnp;
     int scp_locked = 0;
 
@@ -5928,13 +5922,14 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
         
         if (code == -1)
         {
-	    Name = volName;
-	    OfflineMsg = offLineMsg;
-	    MOTD = motd;
 	    lock_ReleaseWrite(&scp->rw);
 	    scp_locked = 0;
 
 	    do {
+		char *Name = NULL;
+		char *OfflineMsg = NULL;
+		char *MOTD = NULL;
+
 		code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
 		if (code) continue;
 
@@ -5943,6 +5938,10 @@ RDR_GetVolumeInfo( IN cm_user_t     *userp,
 					     &volStat, &Name, &OfflineMsg, &MOTD);
 		rx_PutConnection(rxconnp);
 
+		xdr_free((xdrproc_t) xdr_string, &Name);
+		xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+		xdr_free((xdrproc_t) xdr_string, &MOTD);
+
 	    } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
 	    code = cm_MapRPCError(code, &req);
 
@@ -6056,14 +6055,8 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
     cm_req_t    req;
     DWORD       status;
 
-    char volName[32]="(unknown)";
-    char offLineMsg[256]="server temporarily inaccessible";
-    char motd[256]="server temporarily inaccessible";
     cm_conn_t *connp;
     AFSFetchVolumeStatus volStat;
-    char *Name;
-    char *OfflineMsg;
-    char *MOTD;
     struct rx_connection * rxconnp;
     int scp_locked = 0;
 
@@ -6141,13 +6134,14 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
         
         if (code == -1)
         {
-	    Name = volName;
-	    OfflineMsg = offLineMsg;
-	    MOTD = motd;
 	    lock_ReleaseWrite(&scp->rw);
 	    scp_locked = 0;
 
 	    do {
+		char *Name = NULL;
+		char *OfflineMsg = NULL;
+		char *MOTD = NULL;
+
 		code = cm_ConnFromFID(&scp->fid, userp, &req, &connp);
 		if (code) continue;
 
@@ -6156,6 +6150,10 @@ RDR_GetVolumeSizeInfo( IN cm_user_t     *userp,
 					     &volStat, &Name, &OfflineMsg, &MOTD);
 		rx_PutConnection(rxconnp);
 
+		xdr_free((xdrproc_t) xdr_string, &Name);
+		xdr_free((xdrproc_t) xdr_string, &OfflineMsg);
+		xdr_free((xdrproc_t) xdr_string, &MOTD);
+
 	    } while (cm_Analyze(connp, userp, &req, &scp->fid, NULL, 0, NULL, NULL, NULL, NULL, code));
 	    code = cm_MapRPCError(code, &req);
 
diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c
index 95202f020c..baa3182ffb 100644
--- a/src/afs/afs_pioctl.c
+++ b/src/afs/afs_pioctl.c
@@ -1994,32 +1994,31 @@ DECL_PIOCTL(PSetTokens)
  */
 DECL_PIOCTL(PGetVolumeStatus)
 {
-    char volName[32];
-    char *offLineMsg = afs_osi_Alloc(256);
-    char *motd = afs_osi_Alloc(256);
+    char *volName = NULL;
+    char *offLineMsg = NULL;
+    char *motd = NULL;
     struct afs_conn *tc;
     afs_int32 code = 0;
     struct AFSFetchVolumeStatus volstat;
-    char *Name;
     struct rx_connection *rxconn;
     XSTATS_DECLS;
 
-    osi_Assert(offLineMsg != NULL);
-    osi_Assert(motd != NULL);
     AFS_STATCNT(PGetVolumeStatus);
     if (!avc) {
 	code = EINVAL;
 	goto out;
     }
-    Name = volName;
     do {
 	tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
 	if (tc) {
 	    XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_GETVOLUMESTATUS);
 	    RX_AFS_GUNLOCK();
+	    xdr_free((xdrproc_t) xdr_string, &volName);
+	    xdr_free((xdrproc_t) xdr_string, &offLineMsg);
+	    xdr_free((xdrproc_t) xdr_string, &motd);
 	    code =
 		RXAFS_GetVolumeStatus(rxconn, avc->f.fid.Fid.Volume, &volstat,
-				      &Name, &offLineMsg, &motd);
+				      &volName, &offLineMsg, &motd);
 	    RX_AFS_GLOCK();
 	    XSTATS_END_TIME;
 	} else
@@ -2040,8 +2039,9 @@ DECL_PIOCTL(PGetVolumeStatus)
     if (afs_pd_putString(aout, motd) != 0)
 	return E2BIG;
   out:
-    afs_osi_Free(offLineMsg, 256);
-    afs_osi_Free(motd, 256);
+    xdr_free((xdrproc_t) xdr_string, &volName);
+    xdr_free((xdrproc_t) xdr_string, &offLineMsg);
+    xdr_free((xdrproc_t) xdr_string, &motd);
     return code;
 }
 
diff --git a/src/libadmin/bos/afs_bosAdmin.c b/src/libadmin/bos/afs_bosAdmin.c
index 76436c0496..8ec0129770 100644
--- a/src/libadmin/bos/afs_bosAdmin.c
+++ b/src/libadmin/bos/afs_bosAdmin.c
@@ -1289,6 +1289,8 @@ bos_ProcessNotifierGet(const void *serverHandle, const char *processName,
     int rc = 0;
     afs_status_t tst = 0;
     bos_server_p b_handle = (bos_server_p) serverHandle;
+    char *tnotif = NULL;
+    size_t len;
 
     if (!isValidServerHandle(b_handle, &tst)) {
 	goto fail_bos_ProcessNotifierGet;
@@ -1305,14 +1307,22 @@ bos_ProcessNotifierGet(const void *serverHandle, const char *processName,
     }
 
     tst = BOZO_GetInstanceParm(b_handle->server, (char *)processName,
-			       999, &notifier);
+			       999, &tnotif);
+    if (tst != 0) {
+	goto fail_bos_ProcessNotifierGet;
+    }
 
-    if (tst == 0) {
-	rc = 1;
+    len = strlcpy(notifier, tnotif, BOS_MAX_NAME_LEN);
+    if (len >= BOS_MAX_NAME_LEN) {
+	tst = ADMRPCTOOBIG;
+	goto fail_bos_ProcessNotifierGet;
     }
 
+    rc = 1;
+
   fail_bos_ProcessNotifierGet:
 
+    xdr_free((xdrproc_t)xdr_string, &tnotif);
     if (st != NULL) {
 	*st = tst;
     }
-- 
2.45.2


From c18640c6b98b10cd6f78c63195ff822689cb5348 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Thu, 13 Jun 2024 15:30:50 -0500
Subject: [PATCH 3/9] OPENAFS-SA-2024-003: xdr: Set _len for prealloc'd
 opaque/array OUT args

CVE-2024-10397

Currently, a few RPCs with arrays or opaque OUT arguments are called
with preallocated memory for the arg, but also provide a _len of 0 (or
an uninitialized _len). This makes it impossible for the xdr routine to
tell whether we have allocated enough space to actually hold the
response from the server.

To help this situation, either specify an appropriate _len for the
preallocated value (cm_IoctlGetACL, fsprobe_LWP), or don't provide a
preallocated buffer at all and let xdr allocate a buffer for us
(PGetAcl).

Note that this commit doesn't change xdr to actually check the value of
the given _len; but now a future commit can do so without breaking
callers.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15919
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit b2b1110ddd9e19670dbc6a3217dc2a74af432f82)

Change-Id: Ibdee49b79da1476c4e606bcad5fb3d08eb259ad7
Reviewed-on: https://gerrit.openafs.org/15941
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/WINNT/afsd/cm_ioctl.c | 2 +-
 src/afs/afs_pioctl.c      | 8 ++++----
 src/fsprobe/fsprobe.c     | 1 +
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/src/WINNT/afsd/cm_ioctl.c b/src/WINNT/afsd/cm_ioctl.c
index 7ca2846c3c..87e4ac5f89 100644
--- a/src/WINNT/afsd/cm_ioctl.c
+++ b/src/WINNT/afsd/cm_ioctl.c
@@ -433,7 +433,7 @@ cm_IoctlGetACL(cm_ioctl_t *ioctlp, cm_user_t *userp, cm_scache_t *scp, cm_req_t
         afid.Unique = scp->fid.unique;
         do {
             acl.AFSOpaque_val = ioctlp->outDatap;
-            acl.AFSOpaque_len = 0;
+	    acl.AFSOpaque_len = SMB_IOCTL_MAXDATA - (ioctlp->outDatap - ioctlp->outAllocp);
             code = cm_ConnFromFID(&scp->fid, userp, reqp, &connp);
             if (code)
                 continue;
diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c
index baa3182ffb..5337986c4e 100644
--- a/src/afs/afs_pioctl.c
+++ b/src/afs/afs_pioctl.c
@@ -1590,11 +1590,10 @@ DECL_PIOCTL(PGetAcl)
 	    return ERANGE;
 	Fid.Vnode |= (ain->remaining << 30);
     }
-    acl.AFSOpaque_val = aout->ptr;
+    memset(&acl, 0, sizeof(acl));
     do {
 	tconn = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
 	if (tconn) {
-	    acl.AFSOpaque_val[0] = '\0';
 	    XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHACL);
 	    RX_AFS_GUNLOCK();
 	    code = RXAFS_FetchACL(rxconn, &Fid, &acl, &OutStatus, &tsync);
@@ -1608,7 +1607,7 @@ DECL_PIOCTL(PGetAcl)
 
     if (code == 0) {
 	if (acl.AFSOpaque_len == 0) {
-	    afs_pd_skip(aout, 1); /* leave the NULL */
+	    code = afs_pd_putBytes(aout, "\0", 1); /* leave a single NUL byte */
 
 	} else if (memchr(acl.AFSOpaque_val, '\0', acl.AFSOpaque_len) == NULL) {
 	    /* Do not return an unterminated ACL string. */
@@ -1619,9 +1618,10 @@ DECL_PIOCTL(PGetAcl)
 	    code = EINVAL;
 
 	} else {
-	    afs_pd_skip(aout, acl.AFSOpaque_len); /* Length of the ACL */
+	    code = afs_pd_putBytes(aout, acl.AFSOpaque_val, acl.AFSOpaque_len);
 	}
     }
+    xdr_free((xdrproc_t) xdr_AFSOpaque, &acl);
     return code;
 }
 
diff --git a/src/fsprobe/fsprobe.c b/src/fsprobe/fsprobe.c
index f29a56fdd9..18a03cad1e 100644
--- a/src/fsprobe/fsprobe.c
+++ b/src/fsprobe/fsprobe.c
@@ -223,6 +223,7 @@ fsprobe_LWP(void *unused)
     struct ProbeViceStatistics *curr_stats;	/*Current stats region */
     int *curr_probeOK;		/*Current probeOK field */
     ViceStatistics64 stats64;      /*Current stats region */
+    stats64.ViceStatistics64_len = STATS64_VERSION;
     stats64.ViceStatistics64_val = malloc(STATS64_VERSION *
 					  sizeof(afs_uint64));
     while (1) {			/*Service loop */
-- 
2.45.2


From d253a52d3b59bd691eae8863ea2f06d99ad18550 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Sun, 4 Oct 2020 23:04:06 -0500
Subject: [PATCH 4/9] OPENAFS-SA-2024-003: xdr: Prevent XDR_DECODE buffer
 overruns

CVE-2024-10397

When making an RPC call from a client, output arguments that use
arrays (or array-like objects like strings and opaques) can be
allocated by XDR, like so:

{
    struct idlist ids;

    ids.idlist_val = NULL;
    ids.idlist_len = 0;
    code = PR_NameToID(rxconn, names, &ids);
    /* data inside ids.idlist_val[...] */
    xdr_free((xdrproc_t) xdr_idlist, &ids);
}

With this approach, during XDR_DECODE, xdr_array() reads in the number
of array elements from the peer, then allocates enough memory to hold
that many elements, and then reads in the array elements.

Alternatively, the caller can provide preallocated memory, like so:

{
    struct idlist ids;
    afs_int32 ids_buf[30];

    ids.idlist_val = ids_buf;
    ids.idlist_len = 30;
    code = PR_NameToID(rxconn, names, &ids);
    /* data inside ids.idlist_val[...] */
}

With this approach, during XDR_DECODE, xdr_array() reads in the number
of array elements from the peer, and then reads in the array elements
into the supplied buffer. However, in this case, xdr_array() never
checks that the number of array elements will actually fit into the
supplied buffer; the _len field provided by the caller is just ignored.
In this example, if the ptserver responds with 50 elements for the 'ids'
output argument, xdr_array() will write 50 afs_int32's into
'ids.idlist_val', going beyond the end of the 30 elements that are
actually allocated.

It's also possible, and in fact very easy, to use xdr-allocated
buffers and then reuse them as a preallocated buffer, possibly
accidentally. For example:

{
    struct idlist ids;

    ids.idlist_val = NULL;
    ids.idlist_len = 0;
    while (some_condition) {
        code = PR_NameToID(rxconn, names, &ids);
    }
}

In this case, the first call to PR_NameToID can cause the buffer for
'ids' to be allocated by XDR, which will then be reused by the
subsequent calls to PR_NameToId. Note that this can happen even if the
first PR_NameToID call fails; the call can be aborted after the output
array is allocated.

Retrying an RPC in this way is effectively what all ubik_Call*
codepaths do (including all ubik_* wrappers, e.g. ubik_PR_NameToID).
Or some callers retry effectively the same RPC when falling back to
earlier versions (e.g. VL_ListAttributesN2 -> VL_ListAttributesN).

To prevent this for arrays and opaques, change xdr_array (and
xdr_bytes) to check if the _len field for preallocated buffers is
large enough, and return failure if it's not.

Also perform the same check for the ka_CBS and ka_BBS structures. These
are mostly the same as opaques, but they have custom serialization
functions in src/kauth/kaaux.c. ka_BBS also has two lengths: the actual
length of bytes, and a 'max' length. ka_CBS isn't used for any RPC
output arguments, but fix it for consistency.

For strings, the situation is complicated by the fact that callers
cannot pass in how much space was allocated for the string, since
callers only provide a char**. So for strings, just refuse to use a
preallocated buffer at all, and return failure if one is provided.

Note that for some callers using preallocated arrays or strings, the
described buffer overruns are not possible, since the preallocated
buffers are larger than the max length specified in the relevant
RPC-L. For example, afs_DoBulkStat() allocates AFSCBMAX entries for
the output args for RXAFS_InlineBulkStatus, which is the max length
specified in the RPC-L, so a buffer overrun is impossible. But since
it is so easy to allow a buffer overrun, enforce the length checks for
everyone.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15920
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit 13413eceed80d106cbed5ffb91c4dfbc8cccf55c)

Change-Id: I1010d2fa309d4a441ebaf285168c2e7e887753b9
Reviewed-on: https://gerrit.openafs.org/15942
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/kauth/kaaux.c   | 33 +++++++++++++++++++++++++++++++--
 src/rx/xdr.c        | 36 +++++++++++++++++++++++++++++++-----
 src/rx/xdr_array.c  | 24 +++++++++++++++++++++---
 src/rx/xdr_arrayn.c | 24 +++++++++++++++++++++---
 4 files changed, 104 insertions(+), 13 deletions(-)

diff --git a/src/kauth/kaaux.c b/src/kauth/kaaux.c
index eea1402a24..e951af52a2 100644
--- a/src/kauth/kaaux.c
+++ b/src/kauth/kaaux.c
@@ -34,7 +34,17 @@ xdr_ka_CBS(XDR * x, struct ka_CBS *abbs)
 	xdr_afs_int32(x, &len);
 	if (len < 0 || len > MAXBS)
 	    return FALSE;
-	if (!abbs->SeqBody)
+	if (abbs->SeqBody != NULL) {
+	    if (len > abbs->SeqLen) {
+		/*
+		 * We've been given a preallocated buffer to decode into, but
+		 * we're decoding 'len' bytes, which is larger than the
+		 * provided buffer (only abbs->SeqLen bytes large). This won't
+		 * work.
+		 */
+		return FALSE;
+	    }
+	} else
 	    abbs->SeqBody = malloc(len);
 	abbs->SeqLen = len;
 	xdr_opaque(x, abbs->SeqBody, len);
@@ -61,7 +71,26 @@ xdr_ka_BBS(XDR * x, struct ka_BBS *abbs)
 	if (!xdr_afs_int32(x, &maxLen) || !xdr_afs_int32(x, &len) || (len < 0)
 	    || (len > MAXBS) || (len > maxLen))
 	    return FALSE;
-	if (!abbs->SeqBody)
+	if (abbs->SeqBody != NULL) {
+	    if (len > abbs->MaxSeqLen) {
+		/*
+		 * We've been given a preallocated buffer to decode into, but
+		 * we're decoding 'len' bytes, which is larger than the
+		 * provided buffer (only abbs->MaxSeqLen bytes large). This
+		 * won't work.
+		 */
+		return FALSE;
+	    }
+	    if (maxLen > abbs->MaxSeqLen) {
+		/*
+		 * Our preallocated buffer only has space for MaxSeqLen bytes.
+		 * Don't let the peer change 'abbs' so that it looks like we
+		 * have space for more bytes than that; that could cause us to
+		 * access memory beyond what we've actually allocated.
+		 */
+		return FALSE;
+	    }
+	} else
 	    abbs->SeqBody = malloc(maxLen);
 	abbs->MaxSeqLen = maxLen;
 	abbs->SeqLen = len;
diff --git a/src/rx/xdr.c b/src/rx/xdr.c
index 88f7772c99..ef31d455ac 100644
--- a/src/rx/xdr.c
+++ b/src/rx/xdr.c
@@ -404,10 +404,28 @@ xdr_bytes(XDR * xdrs, char **cpp, u_int * sizep,
     /*
      * first deal with the length since xdr bytes are counted
      */
-    if (!xdr_u_int(xdrs, sizep)) {
-	return (FALSE);
+
+    if (xdrs->x_op == XDR_DECODE && sp != NULL) {
+	/*
+	 * We've been given a preallocated array to decode into. Before we
+	 * modify *sizep, check that we have enough space to fit the elements
+	 * that follow.
+	 */
+	if (!xdr_u_int(xdrs, &nodesize)) {
+	   return FALSE;
+	}
+	if (nodesize > *sizep) {
+	   return FALSE;
+	}
+	*sizep = nodesize;
+
+    } else {
+	if (!xdr_u_int(xdrs, sizep)) {
+	    return (FALSE);
+	}
+	nodesize = *sizep;
     }
-    nodesize = *sizep;
+
     if ((nodesize > maxsize) && (xdrs->x_op != XDR_FREE)) {
 	return (FALSE);
     }
@@ -541,8 +559,16 @@ xdr_string(XDR * xdrs, char **cpp, u_int maxsize)
     switch (xdrs->x_op) {
 
     case XDR_DECODE:
-	if (sp == NULL)
-	    *cpp = sp = (char *)osi_alloc(nodesize);
+	if (sp != NULL) {
+	    /*
+	     * Refuse to use preallocated strings, since we cannot verify
+	     * whether enough memory has been allocated to handle the decoded
+	     * string.
+	     */
+	    return FALSE;
+	}
+
+	*cpp = sp = (char *)osi_alloc(nodesize);
 	if (sp == NULL) {
 	    return (FALSE);
 	}
diff --git a/src/rx/xdr_array.c b/src/rx/xdr_array.c
index 1785019922..bfbd429db8 100644
--- a/src/rx/xdr_array.c
+++ b/src/rx/xdr_array.c
@@ -93,10 +93,28 @@ xdr_array(XDR * xdrs, caddr_t * addrp, u_int * sizep, u_int maxsize,
 	maxsize = i;
 
     /* like strings, arrays are really counted arrays */
-    if (!xdr_u_int(xdrs, sizep)) {
-	return (FALSE);
+
+    if (xdrs->x_op == XDR_DECODE && target != NULL) {
+	/*
+	 * We've been given a preallocated array to decode into. Before we
+	 * modify *sizep, check that we have enough space to fit the elements
+	 * that follow.
+	 */
+	if (!xdr_u_int(xdrs, &c)) {
+	    return FALSE;
+	}
+	if (c > *sizep) {
+	    return FALSE;
+	}
+	*sizep = c;
+
+    } else {
+	if (!xdr_u_int(xdrs, sizep)) {
+	    return (FALSE);
+	}
+	c = *sizep;
     }
-    c = *sizep;
+
     if ((c > maxsize) && (xdrs->x_op != XDR_FREE)) {
 	return (FALSE);
     }
diff --git a/src/rx/xdr_arrayn.c b/src/rx/xdr_arrayn.c
index d6cb1f7d12..80d1dfa054 100644
--- a/src/rx/xdr_arrayn.c
+++ b/src/rx/xdr_arrayn.c
@@ -93,10 +93,28 @@ xdr_arrayN(XDR * xdrs, caddr_t * addrp, u_int * sizep, u_int maxsize,
 	maxsize = i;
 
     /* like strings, arrays are really counted arrays */
-    if (!xdr_u_int(xdrs, sizep)) {
-	return (FALSE);
+
+    if (xdrs->x_op == XDR_DECODE && target != NULL) {
+	/*
+	 * We've been given a preallocated array to decode into. Before we
+	 * modify *sizep, check that we have enough space to fit the elements
+	 * that follow.
+	 */
+	if (!xdr_u_int(xdrs, &c)) {
+	    return FALSE;
+	}
+	if (c > *sizep) {
+	    return FALSE;
+	}
+	*sizep = c;
+
+    } else {
+	if (!xdr_u_int(xdrs, sizep)) {
+	    return (FALSE);
+	}
+	c = *sizep;
     }
-    c = *sizep;
+
     if ((c > maxsize) && (xdrs->x_op != XDR_FREE)) {
 	return (FALSE);
     }
-- 
2.45.2


From 0ff2cd9e0f5656e8327c5fe47935998de3669678 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Thu, 15 Oct 2020 23:18:53 -0500
Subject: [PATCH 5/9] OPENAFS-SA-2024-003: Check sanity on lengths of RPC
 returned arrays

CVE-2024-10397

Various RPCs return a variable-length array in an OUT argument, but
are only supposed to return specific sizes. A few instances of this
include the following (but this is not an exhaustive list):

- AFSVolListOneVolume should only return a single volintInfo.

- PR_NameToID should return the same number of IDs as names given.

- VL_GetAddrsU should return the same number of addresses as the
  'nentries' OUT argument.

Some callers of these RPCs just assume that the server has not
violated these rules. If the server responds with a nonsensical array
size, this could cause us to read beyond the end of the array, or
cause a NULL dereference or other errors.

For example, some callers of VL_GetAddrsU will iterate over 'nentries'
addresses, even if the 'blkaddrs' OUT argument contains fewer entries.
Or with AFSVolListOneVolume, some callers assume that at least 1
volintInfo has been returned; if 0 have been returned, we can try to
access a NULL array.

To avoid all of this, add various sanity checks on the relevant
returned lengths of these RPCs. For most cases, if the lengths are not
sane, return an internal error from the appropriate subsystem (or
RXGEN_CC_UNMARSHAL if there isn't one). For VL_GetAddrsU, if
'nentries' is too long, just set it to the length of the returned
array.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15921
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit c732715e4ee78ed1e2414c813ae5a4b3574107a0)

Change-Id: I2cfc0723f4c3a2692238fa1e59145aceee17e0d6
Reviewed-on: https://gerrit.openafs.org/15943
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/WINNT/afsd/cm_getaddrs.c           |  2 ++
 src/WINNT/afsd/cm_vnodeops.c           |  7 +++++
 src/afs/afs_volume.c                   |  4 +++
 src/bucoord/ubik_db_if.c               |  6 ++++
 src/butc/dump.c                        | 18 ++++++++---
 src/libadmin/adminutil/afs_utilAdmin.c | 12 ++++++--
 src/libadmin/pts/afs_ptsAdmin.c        |  4 +++
 src/libadmin/vos/afs_vosAdmin.c        |  8 +++++
 src/libadmin/vos/vosutils.c            |  6 ++++
 src/libadmin/vos/vsprocs.c             | 22 +++++++++++---
 src/libafscp/afscp_server.c            |  7 +++++
 src/ptserver/ptuser.c                  |  6 ++++
 src/venus/cacheout.c                   |  4 +++
 src/viced/host.c                       |  4 +++
 src/vlserver/vlclient.c                | 21 +++++++++++++
 src/volser/vsprocs.c                   | 42 +++++++++++++++++---------
 src/volser/vsutils.c                   |  3 ++
 17 files changed, 151 insertions(+), 25 deletions(-)

diff --git a/src/WINNT/afsd/cm_getaddrs.c b/src/WINNT/afsd/cm_getaddrs.c
index 29e9d1a6cc..48d42489be 100644
--- a/src/WINNT/afsd/cm_getaddrs.c
+++ b/src/WINNT/afsd/cm_getaddrs.c
@@ -260,6 +260,8 @@ cm_GetAddrsU(cm_cell_t *cellp, cm_user_t *userp, cm_req_t *reqp,
 	if (code)
 	    return CM_ERROR_RETRY;
 
+	nentries = min(nentries, addrs.bulkaddrs_len);
+
 	if (nentries == 0) {
 	    xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
 	    code = CM_ERROR_INVAL;
diff --git a/src/WINNT/afsd/cm_vnodeops.c b/src/WINNT/afsd/cm_vnodeops.c
index 226bf7af00..64a3dbe65d 100644
--- a/src/WINNT/afsd/cm_vnodeops.c
+++ b/src/WINNT/afsd/cm_vnodeops.c
@@ -2519,6 +2519,13 @@ cm_TryBulkStatRPC(cm_scache_t *dscp, cm_bulkStat_t *bbp, cm_user_t *userp, cm_re
 	    }
             rx_PutConnection(rxconnp);
 
+	    if (code == 0) {
+		if (statStruct.AFSBulkStats_len != filesThisCall ||
+		    callbackStruct.AFSCBs_len != filesThisCall) {
+		    code = RXGEN_CC_UNMARSHAL;
+		}
+	    }
+
             /*
              * If InlineBulk RPC was called and it succeeded,
              * then pull out the return code from the status info
diff --git a/src/afs/afs_volume.c b/src/afs/afs_volume.c
index d3d4d00ff1..0600ccbfaf 100644
--- a/src/afs/afs_volume.c
+++ b/src/afs/afs_volume.c
@@ -1225,6 +1225,10 @@ LockAndInstallUVolumeEntry(struct volume *av, struct uvldbentry *ve, int acell,
 		    return;
 		}
 
+		if (addrs.bulkaddrs_len < nentries) {
+		    nentries = addrs.bulkaddrs_len;
+		}
+
 		addrp = addrs.bulkaddrs_val;
 		for (k = 0; k < nentries; k++) {
 		    addrp[k] = htonl(addrp[k]);
diff --git a/src/bucoord/ubik_db_if.c b/src/bucoord/ubik_db_if.c
index be58e3bbb7..9a05459410 100644
--- a/src/bucoord/ubik_db_if.c
+++ b/src/bucoord/ubik_db_if.c
@@ -120,6 +120,12 @@ afs_int32 bcdb_listDumps (afs_int32 sflags, afs_int32 groupId,
 	free(dumpsList.budb_dumpsList_val);
     if (flagsList.budb_dumpsList_val)
 	free(flagsList.budb_dumpsList_val);
+
+    if (code == 0 &&
+	dumpsPtr->budb_dumpsList_len != flagsPtr->budb_dumpsList_len) {
+	code = BUDB_INTERNALERROR;
+    }
+
     return (code);
 }
 
diff --git a/src/butc/dump.c b/src/butc/dump.c
index 8139d572ce..365544ec7b 100644
--- a/src/butc/dump.c
+++ b/src/butc/dump.c
@@ -173,6 +173,18 @@ Bind(afs_uint32 server)
     return (curr_fromconn);
 }
 
+static int
+ListOneVolume(struct rx_connection *aconn, afs_int32 apart, afs_uint32 avolid,
+	      volEntries *entries)
+{
+    afs_int32 code;
+    code = AFSVolListOneVolume(aconn, apart, avolid, entries);
+    if (code == 0 && entries->volEntries_len != 1) {
+	code = VOLSERFAILEDOP;
+    }
+    return code;
+}
+
 /* notes
  * 1) save the chunksize or otherwise ensure tape space remaining is
  *	check frequently enough
@@ -217,8 +229,7 @@ dumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
     /* Determine when the volume was last cloned and updated */
     volumeInfo.volEntries_val = (volintInfo *) 0;
     volumeInfo.volEntries_len = 0;
-    rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
-			     &volumeInfo);
+    rc = ListOneVolume(fromconn, curDump->partition, curDump->vid, &volumeInfo);
     if (rc)
 	ERROR_EXIT(rc);
     updatedate = volumeInfo.volEntries_val[0].updateDate;
@@ -543,8 +554,7 @@ xbsaDumpVolume(struct tc_dumpDesc * curDump, struct dumpRock * dparamsPtr)
     /* Determine when the volume was last cloned and updated */
     volumeInfo.volEntries_val = (volintInfo *) 0;
     volumeInfo.volEntries_len = 0;
-    rc = AFSVolListOneVolume(fromconn, curDump->partition, curDump->vid,
-			     &volumeInfo);
+    rc = ListOneVolume(fromconn, curDump->partition, curDump->vid, &volumeInfo);
     if (rc)
 	ERROR_EXIT(rc);
     updatedate = volumeInfo.volEntries_val[0].updateDate;
diff --git a/src/libadmin/adminutil/afs_utilAdmin.c b/src/libadmin/adminutil/afs_utilAdmin.c
index 20da3320b7..3afb5cc9be 100644
--- a/src/libadmin/adminutil/afs_utilAdmin.c
+++ b/src/libadmin/adminutil/afs_utilAdmin.c
@@ -2322,6 +2322,9 @@ util_CMClientConfig(struct rx_connection *conn, afs_ClientConfig_p config,
     afs_uint32 allocbytes;
     struct cacheConfig tconfig;
 
+    tconfig.cacheConfig_val = NULL;
+    tconfig.cacheConfig_len = 0;
+
     if (conn == NULL) {
 	tst = ADMRXCONNNULL;
 	goto fail_util_CMClientConfig;
@@ -2333,8 +2336,6 @@ util_CMClientConfig(struct rx_connection *conn, afs_ClientConfig_p config,
     }
 
     config->clientVersion = AFS_CLIENT_RETRIEVAL_VERSION;
-    tconfig.cacheConfig_val = NULL;
-    tconfig.cacheConfig_len = 0;
     tst =
 	RXAFSCB_GetCacheConfig(conn, config->clientVersion,
 			       &config->serverVersion, &allocbytes, &tconfig);
@@ -2343,12 +2344,17 @@ util_CMClientConfig(struct rx_connection *conn, afs_ClientConfig_p config,
 	goto fail_util_CMClientConfig;
     }
 
+    if (tconfig.cacheConfig_len != sizeof(cm_initparams_v1)/sizeof(afs_uint32)) {
+	tst = RXGEN_CC_UNMARSHAL;
+	goto fail_util_CMClientConfig;
+    }
+
     UnmarshallCMClientConfig(config->serverVersion, tconfig.cacheConfig_val,
 			     &config->c);
     rc = 1;
-    free(tconfig.cacheConfig_val);
 
   fail_util_CMClientConfig:
+    free(tconfig.cacheConfig_val);
 
     if (st != NULL) {
 	*st = tst;
diff --git a/src/libadmin/pts/afs_ptsAdmin.c b/src/libadmin/pts/afs_ptsAdmin.c
index 80f7d4ccff..97c44ce09d 100644
--- a/src/libadmin/pts/afs_ptsAdmin.c
+++ b/src/libadmin/pts/afs_ptsAdmin.c
@@ -125,6 +125,10 @@ TranslatePTSNames(const afs_cell_handle_p cellHandle, namelist * names,
 	goto fail_TranslatePTSNames;
     }
 
+    if (ids->idlist_len != names->namelist_len) {
+	tst = ADMPTSFAILEDNAMETRANSLATE;
+	goto fail_TranslatePTSNames;
+    }
 
     /*
      * Check to see if the lookup failed
diff --git a/src/libadmin/vos/afs_vosAdmin.c b/src/libadmin/vos/afs_vosAdmin.c
index c93875d5d7..8231ca0028 100644
--- a/src/libadmin/vos/afs_vosAdmin.c
+++ b/src/libadmin/vos/afs_vosAdmin.c
@@ -1217,6 +1217,10 @@ GetServerRPC(void *rpc_specific, int slot, int *last_item,
 		goto fail_GetServerRPC;
 	    }
 
+	    if (addr_multi.bulkaddrs_len < total_multi) {
+		total_multi = addr_multi.bulkaddrs_len;
+	    }
+
 	    /*
 	     * Remove any bogus IP addresses which the user may have
 	     * been unable to remove.
@@ -1368,6 +1372,10 @@ vos_FileServerGetBegin(const void *cellHandle, vos_MessageCallBack_t callBack,
 	goto fail_vos_FileServerGetBegin;
     }
 
+    if (serv->addresses.bulkaddrs_len < serv->total_addresses) {
+	serv->total_addresses = serv->addresses.bulkaddrs_len;
+    }
+
     /*
      * Remove any bogus IP addresses which the user may have
      * been unable to remove.
diff --git a/src/libadmin/vos/vosutils.c b/src/libadmin/vos/vosutils.c
index 1b99d2b551..eb788b20ff 100644
--- a/src/libadmin/vos/vosutils.c
+++ b/src/libadmin/vos/vosutils.c
@@ -324,6 +324,9 @@ VLDB_ListAttributesN2(afs_cell_handle_p cellHandle,
 		  nextindexp);
     if (!tst) {
 	rc = 1;
+	if (blkentriesp->nbulkentries_len < *nentriesp) {
+	    *nentriesp = blkentriesp->nbulkentries_len;
+	}
     }
 
     if (st != NULL) {
@@ -365,6 +368,9 @@ VLDB_IsSameAddrs(afs_cell_handle_p cellHandle, afs_int32 serv1,
 	*equal = 0;
 	goto fail_VLDB_IsSameAddrs;
     }
+    if (addrs.bulkaddrs_len < nentries) {
+	nentries = addrs.bulkaddrs_len;
+    }
 
     addrp = addrs.bulkaddrs_val;
     for (i = 0; i < nentries; i++, addrp++) {
diff --git a/src/libadmin/vos/vsprocs.c b/src/libadmin/vos/vsprocs.c
index 081825fe9b..ab39cd2b36 100644
--- a/src/libadmin/vos/vsprocs.c
+++ b/src/libadmin/vos/vsprocs.c
@@ -600,7 +600,7 @@ UV_MoveVolume(afs_cell_handle_p cellHandle, afs_uint32 afromvol,
      */
     volumeInfo.volEntries_val = (volintInfo *) 0;	/*this hints the stub to allocate space */
     volumeInfo.volEntries_len = 0;
-    tst = AFSVolListOneVolume(fromconn, afrompart, afromvol, &volumeInfo);
+    tst = ListOneVolume(fromconn, afrompart, afromvol, &volumeInfo);
     if (tst) {
 	goto fail_UV_MoveVolume;
     }
@@ -1162,6 +1162,18 @@ UV_BackupVolume(afs_cell_handle_p cellHandle, afs_int32 aserver,
     return rc;
 }
 
+static int
+ListOneVolume(struct rx_connection *aconn, afs_int32 apart, afs_uint32 avolid,
+	      volEntries *entries)
+{
+    afs_int32 code;
+    code = AFSVolListOneVolume(aconn, apart, avolid, entries);
+    if (code == 0 && entries->volEntries_len != 1) {
+	code = VOLSERFAILEDOP;
+    }
+    return code;
+}
+
 static int
 DelVol(struct rx_connection *conn, afs_uint32 vid, afs_int32 part,
        afs_int32 flags)
@@ -1314,7 +1326,7 @@ VolumeExists(afs_cell_handle_p cellHandle, afs_int32 server,
     if (conn) {
 	volumeInfo.volEntries_val = (volintInfo *) 0;
 	volumeInfo.volEntries_len = 0;
-	tst = AFSVolListOneVolume(conn, partition, volumeid, &volumeInfo);
+	tst = ListOneVolume(conn, partition, volumeid, &volumeInfo);
 	if (volumeInfo.volEntries_val)
 	    free(volumeInfo.volEntries_val);
 	if (tst == VOLSERILLEGAL_PARTITION) {
@@ -2773,7 +2785,9 @@ UV_XListOneVolume(struct rx_connection *server, afs_int32 a_partID,
     volumeXInfo.volXEntries_len = 0;
 
     tst = AFSVolXListOneVolume(server, a_partID, a_volID, &volumeXInfo);
-
+    if (tst == 0 && volumeXInfo.volXEntries_len != 1) {
+	tst = VOLSERFAILEDOP;
+    }
     if (tst) {
 	goto fail_UV_XListOneVolume;
     } else {
@@ -2833,7 +2847,7 @@ int UV_ListOneVolume(struct rx_connection *server, afs_int32 a_partID,
     volumeInfo.volEntries_val = (volintInfo *) 0;
     volumeInfo.volEntries_len = 0;
 
-    tst = AFSVolListOneVolume(server, a_partID, a_volID, &volumeInfo);
+    tst = ListOneVolume(server, a_partID, a_volID, &volumeInfo);
 
     if (tst) {
 	goto fail_UV_ListOneVolume;
diff --git a/src/libafscp/afscp_server.c b/src/libafscp/afscp_server.c
index 726d2e5a3b..af1fb4bb43 100644
--- a/src/libafscp/afscp_server.c
+++ b/src/libafscp/afscp_server.c
@@ -305,6 +305,9 @@ afscp_ServerById(struct afscp_cell *thecell, afsUUID * u)
     if (code != 0) {
 	return NULL;
     }
+    if (addrs.bulkaddrs_len < nentries) {
+	nentries = addrs.bulkaddrs_len;
+    }
     if (nentries > AFS_MAXHOSTS) {
 	nentries = AFS_MAXHOSTS;
 	/* XXX I don't want to do *that* much dynamic allocation */
@@ -405,6 +408,10 @@ afscp_ServerByAddr(struct afscp_cell *thecell, afs_uint32 addr)
 	afsUUID_to_string(&uuid, s, 511);
 	afs_dprintf(("GetServerByAddr 0x%x -> uuid %s\n", addr, s));
 
+	if (addrs.bulkaddrs_len < nentries) {
+	    nentries = addrs.bulkaddrs_len;
+	}
+
 	if (nentries > AFS_MAXHOSTS) {
 	    nentries = AFS_MAXHOSTS;
 	    /* XXX I don't want to do *that* much dynamic allocation */
diff --git a/src/ptserver/ptuser.c b/src/ptserver/ptuser.c
index 7df92916a1..1eb7c5c500 100644
--- a/src/ptserver/ptuser.c
+++ b/src/ptserver/ptuser.c
@@ -521,6 +521,9 @@ pr_NameToId(namelist *names, idlist *ids)
 	stolower(names->namelist_val[i]);
     }
     code = ubik_PR_NameToID(pruclient, 0, names, ids);
+    if (code == 0 && ids->idlist_len != names->namelist_len) {
+	code = PRINTERNAL;
+    }
     return code;
 }
 
@@ -566,6 +569,9 @@ string_PR_IDToName(struct ubik_client *client, afs_int32 flags,
     code = ubik_PR_IDToName(client, flags, ids, names);
     if (code)
 	return code;
+    if (names->namelist_len != ids->idlist_len) {
+	return PRINTERNAL;
+    }
     for (i = 0; i < names->namelist_len; i++) {
 	code = check_length(names->namelist_val[i]);
 	if (code)
diff --git a/src/venus/cacheout.c b/src/venus/cacheout.c
index 656bceb57b..810bea3b91 100644
--- a/src/venus/cacheout.c
+++ b/src/venus/cacheout.c
@@ -71,6 +71,10 @@ ListServers(void)
 	return 1;
     }
 
+    if (addrs.bulkaddrs_len < server_count) {
+	server_count = addrs.bulkaddrs_len;
+    }
+
     for (i = 0; i < server_count; ++i) {
 	ip = addrs.bulkaddrs_val[i];
 
diff --git a/src/viced/host.c b/src/viced/host.c
index f2ce790ba3..1adc7aa199 100644
--- a/src/viced/host.c
+++ b/src/viced/host.c
@@ -30,6 +30,7 @@
 #include <afs/ihandle.h>
 #include <afs/acl.h>
 #include <afs/ptclient.h>
+#include <afs/pterror.h>
 #include <afs/ptuser.h>
 #include <afs/prs_fs.h>
 #include <afs/auth.h>
@@ -403,6 +404,9 @@ hpr_NameToId(namelist *names, idlist *ids)
     for (i = 0; i < names->namelist_len; i++)
         stolower(names->namelist_val[i]);
     code = ubik_PR_NameToID(uclient, 0, names, ids);
+    if (code == 0 && ids->idlist_len != names->namelist_len) {
+	code = PRINTERNAL;
+    }
     return code;
 }
 
diff --git a/src/vlserver/vlclient.c b/src/vlserver/vlclient.c
index 20c14a5c1f..8426e7f680 100644
--- a/src/vlserver/vlclient.c
+++ b/src/vlserver/vlclient.c
@@ -561,6 +561,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 		    printf("VL_ListAttributes returned code = %d\n", code);
 		    continue;
 		}
+		if (entries.bulkentries_len < nentries) {
+		    nentries = entries.bulkentries_len;
+		}
 		entry = (struct vldbentry *)entries.bulkentries_val;
 		for (i = 0; i < nentries; i++, entry++)
 		    display_entry(entry, 0);
@@ -596,6 +599,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 			       code);
 			break;
 		    }
+		    if (entries.nbulkentries_len < nentries) {
+			nentries = entries.nbulkentries_len;
+		    }
 
 		    t += nentries;
 		    entry = (struct nvldbentry *)entries.nbulkentries_val;
@@ -764,6 +770,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 		    printf("VL_GetAddrs returned code = %d\n", code);
 		    continue;
 		}
+		if (addrs.bulkaddrs_len < nentries) {
+		    nentries = addrs.bulkaddrs_len;
+		}
 		addrp = addrs.bulkaddrs_val;
 		for (i = 0; i < nentries; i++, addrp++) {
 		    if ((*addrp & 0xff000000) == 0xff000000)
@@ -789,6 +798,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 		    printf("VL_GetAddrs returned code = %d\n", code);
 		    continue;
 		}
+		if (addrs.bulkaddrs_len < nentries) {
+		    nentries = addrs.bulkaddrs_len;
+		}
 		addrp = addrs.bulkaddrs_val;
 		for (i = 0; i < nentries; i++, addrp++) {
 		    if ((*addrp & 0xff000000) == 0xff000000) {
@@ -814,6 +826,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 			    printf("VL_GetAddrsU returned code = %d\n", code);
 			    continue;
 			}
+			if (mhaddrs.bulkaddrs_len < mhnentries) {
+			    mhnentries = mhaddrs.bulkaddrs_len;
+			}
 			printf
 			    ("   [%d]: uuid[%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x]\n   addrunique=%d, ip address(es):\n",
 			     attrs.index, uuid.time_low, uuid.time_mid,
@@ -864,6 +879,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 		    printf("VL_GetAddrs returned code = %d\n", code);
 		    continue;
 		}
+		if (addrs1.bulkaddrs_len < nentries1) {
+		    nentries1 = addrs1.bulkaddrs_len;
+		}
 		addrp1 = addrs1.bulkaddrs_val;
 		for (i = 0; i < nentries1; i++, addrp1++) {
 		    if ((*addrp1 & 0xff000000) != 0xff000000) {
@@ -887,6 +905,9 @@ handleit(struct cmd_syndesc *as, void *arock)
 			    printf("VL_GetAddrsU returned code = %d\n", code);
 			    break;
 			}
+			if (addrs2.bulkaddrs_len < nentries2) {
+			    nentries2 = addrs2.bulkaddrs_len;
+			}
 
 			addrp2 = addrs2.bulkaddrs_val;
 			for (j = 0; j < nentries2; j++) {
diff --git a/src/volser/vsprocs.c b/src/volser/vsprocs.c
index b24ec77f74..e20749fb81 100644
--- a/src/volser/vsprocs.c
+++ b/src/volser/vsprocs.c
@@ -2865,6 +2865,18 @@ UV_BackupVolume(afs_uint32 aserver, afs_int32 apart, afs_uint32 avolid)
     return error;
 }
 
+static int
+ListOneVolume(struct rx_connection *aconn, afs_int32 apart, afs_uint32 avolid,
+	      volEntries *entries)
+{
+    afs_int32 code;
+    code = AFSVolListOneVolume(aconn, apart, avolid, entries);
+    if (code == 0 && entries->volEntries_len != 1) {
+	code = VOLSERFAILEDOP;
+    }
+    return code;
+}
+
 /* Make a new clone of volume <avolid> on <aserver> and <apart>
  * using volume ID <acloneid>, or a new ID allocated from the VLDB.
  * The new volume is named by <aname>, or by appending ".clone" to
@@ -2892,7 +2904,7 @@ UV_CloneVolume(afs_uint32 aserver, afs_int32 apart, afs_uint32 avolid,
     if (!aname) {
 	volumeInfo.volEntries_val = (volintInfo *) 0;
 	volumeInfo.volEntries_len = 0;
-	code = AFSVolListOneVolume(aconn, apart, avolid, &volumeInfo);
+	code = ListOneVolume(aconn, apart, avolid, &volumeInfo);
 	if (code) {
 	    fprintf(stderr, "Could not get info for volume %lu\n",
 		    (unsigned long)avolid);
@@ -3620,9 +3632,8 @@ UV_ReleaseVolume(afs_uint32 afromvol, afs_uint32 afromserver,
 		}
 		volumeInfo.volEntries_val = NULL;
 		volumeInfo.volEntries_len = 0;
-		code = AFSVolListOneVolume(conn, entry.serverPartition[vldbindex],
-		                           entry.volumeId[ROVOL],
-		                           &volumeInfo);
+		code = ListOneVolume(conn, entry.serverPartition[vldbindex],
+				     entry.volumeId[ROVOL], &volumeInfo);
 		if (code) {
 		    fprintf(STDERR, "Could not fetch information about RO vol %lu from server %s\n",
 		                    (unsigned long)entry.volumeId[ROVOL],
@@ -3650,8 +3661,7 @@ UV_ReleaseVolume(afs_uint32 afromvol, afs_uint32 afromserver,
 	    volEntries volumeInfo;
 	    volumeInfo.volEntries_val = NULL;
 	    volumeInfo.volEntries_len = 0;
-	    code = AFSVolListOneVolume(fromconn, afrompart, afromvol,
-	                               &volumeInfo);
+	    code = ListOneVolume(fromconn, afrompart, afromvol, &volumeInfo);
 	    if (code) {
 		fprintf(STDERR, "Could not fetch information about RW vol %lu from server %s\n",
 		                (unsigned long)afromvol,
@@ -3993,6 +4003,10 @@ UV_ReleaseVolume(afs_uint32 afromvol, afs_uint32 afromserver,
 	    nservers = 1;
 	}
 
+	if (code == 0 && results.manyResults_len != tr.manyDests_len) {
+	    code = VOLSERFAILEDOP;
+	}
+
 	if (code) {
 	    PrintError("Release failed: ", code);
 	} else {
@@ -5503,7 +5517,7 @@ UV_ListOneVolume(afs_uint32 aserver, afs_int32 apart, afs_uint32 volid,
     volumeInfo.volEntries_len = 0;
 
     aconn = UV_Bind(aserver, AFSCONF_VOLUMEPORT);
-    code = AFSVolListOneVolume(aconn, apart, volid, &volumeInfo);
+    code = ListOneVolume(aconn, apart, volid, &volumeInfo);
     if (code) {
 	fprintf(STDERR,
 		"Could not fetch the information about volume %lu from the server\n",
@@ -5570,6 +5584,9 @@ UV_XListOneVolume(afs_uint32 a_serverID, afs_int32 a_partID, afs_uint32 a_volID,
      */
     rxConnP = UV_Bind(a_serverID, AFSCONF_VOLUMEPORT);
     code = AFSVolXListOneVolume(rxConnP, a_partID, a_volID, &volumeXInfo);
+    if (code == 0 && volumeXInfo.volXEntries_len != 1) {
+	code = VOLSERFAILEDOP;
+    }
     if (code)
 	fprintf(STDERR,
 		"[UV_XListOneVolume] Couldn't fetch the volume information\n");
@@ -6191,9 +6208,7 @@ UV_SyncVolume(afs_uint32 aserver, afs_int32 apart, char *avolname, int flags)
 	/* If a volume ID were given, search for it on each partition */
 	if ((volumeid = atol(avolname))) {
 	    for (j = 0; j < pcnt; j++) {
-		code =
-		    AFSVolListOneVolume(aconn, PartList.partId[j], volumeid,
-					&volumeInfo);
+		code = ListOneVolume(aconn, PartList.partId[j], volumeid, &volumeInfo);
 		if (code) {
 		    if (code != ENODEV) {
 			fprintf(STDERR, "Could not query server\n");
@@ -6231,9 +6246,8 @@ UV_SyncVolume(afs_uint32 aserver, afs_int32 apart, char *avolname, int flags)
 	    for (k = 0; k < pcnt; k++) {	/* For each partition */
 		volumeInfo.volEntries_val = (volintInfo *) 0;
 		volumeInfo.volEntries_len = 0;
-		code =
-		    AFSVolListOneVolume(aconn, PartList.partId[k],
-					vldbentry.volumeId[j], &volumeInfo);
+		code = ListOneVolume(aconn, PartList.partId[k],
+				     vldbentry.volumeId[j], &volumeInfo);
 		if (code) {
 		    if (code != ENODEV) {
 			fprintf(STDERR, "Could not query server\n");
@@ -6485,7 +6499,7 @@ VolumeExists(afs_uint32 server, afs_int32 partition, afs_uint32 volumeid)
     if (conn) {
 	volumeInfo.volEntries_val = (volintInfo *) 0;
 	volumeInfo.volEntries_len = 0;
-	code = AFSVolListOneVolume(conn, partition, volumeid, &volumeInfo);
+	code = ListOneVolume(conn, partition, volumeid, &volumeInfo);
 	if (volumeInfo.volEntries_val)
 	    free(volumeInfo.volEntries_val);
 	if (code == VOLSERILLEGAL_PARTITION)
diff --git a/src/volser/vsutils.c b/src/volser/vsutils.c
index e5d0beb7ec..fca2283e7a 100644
--- a/src/volser/vsutils.c
+++ b/src/volser/vsutils.c
@@ -377,6 +377,9 @@ VLDB_IsSameAddrs(afs_uint32 serv1, afs_uint32 serv2, afs_int32 *errorp)
     }
 
     code = 0;
+    if (addrs.bulkaddrs_len < nentries) {
+	nentries = addrs.bulkaddrs_len;
+    }
     if (nentries > GETADDRUCACHESIZE)
 	nentries = GETADDRUCACHESIZE;	/* safety check; should not happen */
     if (++cacheip_index >= GETADDRUCACHESIZE)
-- 
2.45.2


From a82212ab20f0635a40c52648a52a1e9eaccc4937 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Thu, 15 Oct 2020 20:30:14 -0500
Subject: [PATCH 6/9] OPENAFS-SA-2024-003: xdr: Ensure correct string length in
 xdr_string

CVE-2024-10397

Currently, if a caller calls an RPC with a string output argument,
like so:

{
    char *str = NULL;
    code = RXAFS_SomeCall(&str);
    /* do something with 'str' */
    xdr_free((xdrproc_t) xdr_string, &str);
}

Normally, xdr_free causes xdr_string to call osi_free, specifying the
same size that we allocated for the string. However, since we only
have a char*, the amount of space allocated for the string is not
recorded separately, and so xdr_string calculates the size of the
buffer to free by using strlen().

This works for well-formed strings, but if we fail to decode the
payload of the string, or if our peer gave us a string with a NUL byte
in the middle of it, then strlen() may be significantly less than the
actual allocated size. And so in this case, the size given to osi_free
will be wrong.

The size given to osi_free is ignored in userspace, and for KERNEL on
many platforms like Linux and DARWIN. However, it is notably not
ignored for KERNEL on Solaris and some other less supported platforms
(HPUX, Irix, NetBSD). At least on Solaris, an incorrect size given to
osi_free can cause a system panic or possibly memory corruption.

To avoid this, change xdr_string during XDR_DECODE to make sure that
strlen() of the string always reflects the allocated size. If we fail
to decode the string's payload, replace the payload with non-NUL bytes
(fill it with 'z', an arbitrary choice). And if we do successfully
decode the payload, check if the strlen() is wrong (that is, if the
payload contains NUL '\0' bytes), and fail if so, also filling the
payload with 'z'. This is only strictly needed in KERNEL on certain
platforms, but do it everywhere so our behavior is consistent.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15922
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit 7d0675e6c6a2f3200a3884fbe46b3ef8ef9ffd24)

Change-Id: Ieb8827474a7458ce80176b14ce87f3402aed7a86
Reviewed-on: https://gerrit.openafs.org/15944
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/rx/xdr.c | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/rx/xdr.c b/src/rx/xdr.c
index ef31d455ac..6826a28715 100644
--- a/src/rx/xdr.c
+++ b/src/rx/xdr.c
@@ -573,7 +573,26 @@ xdr_string(XDR * xdrs, char **cpp, u_int maxsize)
 	    return (FALSE);
 	}
 	sp[size] = 0;
-	AFS_FALLTHROUGH;
+
+	/* Get the actual string. */
+	if (!xdr_opaque(xdrs, sp, size)) {
+	    /* Make sure strlen(sp) == size, so we can calculate the correct
+	     * size for osi_free when freeing the string. */
+	    memset(sp, 'z', size);
+	    return FALSE;
+	}
+
+	/*
+	 * If the string contains a '\0' character, the string is invalid.
+	 * Don't allow this, because this makes it impossible for us to pass
+	 * the correct size to osi_free later on, when freeing the string.
+	 */
+	if (strlen(sp) != size) {
+	    /* Make sure strlen(sp) == size. */
+	    memset(sp, 'z', size);
+	    return FALSE;
+	}
+	return TRUE;
 
     case XDR_ENCODE:
 	return (xdr_opaque(xdrs, sp, size));
-- 
2.45.2


From 25ad3931d5c03ead625a96e6b626febeb3e20453 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Fri, 16 Oct 2020 10:52:03 -0500
Subject: [PATCH 7/9] OPENAFS-SA-2024-003: Run xdr_free for retried RPCs

CVE-2024-10397

A few areas of code retry the same RPC, like so:

    do {
        code = VL_SomeRPC(rxconn, &array_out);
    } while (some_condition);
    xdr_free((xdrproc_t) xdr_foo, &array_out);

Or try a different version/variant of an RPC (e.g.
VLDB_ListAttributesN2 -> VLDB_ListAttributes).

If the first RPC call causes the output array to be allocated with
length N, then the subsequent RPC calls may fail if the server
responds with an array larger than N.

Furthermore, if the subsequent call responds with an array smaller
than N, then when we xdr_free the array, our length will be smaller
than the actual number of allocated elements. That results in two
potential issues:

- We'll fail to free the elements at the end of the array. This is
  only a problem if each element in the array also uses
  dynamically-allocated memory (e.g. each element contains a string or
  another array). Fortunately, there are only a few such structures in
  any of our RPC-L definitions: SysNameList and CredInfos. And neither
  of those are used in such a retry loop, so this isn't a problem.

- We'll give the wrong length to osi_free when freeing the array
  itself. This only matters for KERNEL, and only on some platforms
  (such as Solaris), since the length given to osi_free is ignored
  everywhere else.

To avoid these possible issues, change the relevant retry loops to
free our xdr-allocated arrays on every iteration of the loop, like
this:

    do {
        xdr_free((xdrproc_t) xdr_foo, &array_out);
        code = VL_SomeRPC(rxconn, &array_out);
    } while (some_condition);
    xdr_free((xdrproc_t) xdr_foo, &array_out);

Or like this:

    do {
        code = VL_SomeRPC(rxconn, &array_out);
        xdr_free((xdrproc_t) xdr_foo, &array_out);
    } while (some_condition);

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15923
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit 1f5e1ef9e35f6b5e8693c91199c976d5e030c0d0)

Change-Id: I77ce3a904d502784cbf356e113972dfab838256e
Reviewed-on: https://gerrit.openafs.org/15945
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/WINNT/afsd/cm_getaddrs.c | 1 +
 src/afs/afs_pioctl.c         | 1 +
 src/afs/afs_volume.c         | 1 +
 src/ptserver/pts.c           | 1 +
 src/ptserver/testpt.c        | 1 +
 src/volser/vos.c             | 1 +
 6 files changed, 6 insertions(+)

diff --git a/src/WINNT/afsd/cm_getaddrs.c b/src/WINNT/afsd/cm_getaddrs.c
index 48d42489be..a165271e2a 100644
--- a/src/WINNT/afsd/cm_getaddrs.c
+++ b/src/WINNT/afsd/cm_getaddrs.c
@@ -234,6 +234,7 @@ cm_GetAddrsU(cm_cell_t *cellp, cm_user_t *userp, cm_req_t *reqp,
 	    code = cm_ConnByMServers(cellp->vlServersp, 0, userp, reqp, &connp);
 	    if (code)
 		continue;
+	    xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
 	    rxconnp = cm_GetRxConn(connp);
 	    code = VL_GetAddrsU(rxconnp, &attrs, &uuid, &unique, &nentries,
 				 &addrs);
diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c
index 5337986c4e..faafdd3d2d 100644
--- a/src/afs/afs_pioctl.c
+++ b/src/afs/afs_pioctl.c
@@ -1596,6 +1596,7 @@ DECL_PIOCTL(PGetAcl)
 	if (tconn) {
 	    XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHACL);
 	    RX_AFS_GUNLOCK();
+	    xdr_free((xdrproc_t) xdr_AFSOpaque, &acl);
 	    code = RXAFS_FetchACL(rxconn, &Fid, &acl, &OutStatus, &tsync);
 	    RX_AFS_GLOCK();
 	    XSTATS_END_TIME;
diff --git a/src/afs/afs_volume.c b/src/afs/afs_volume.c
index 0600ccbfaf..42f1ba9d91 100644
--- a/src/afs/afs_volume.c
+++ b/src/afs/afs_volume.c
@@ -1205,6 +1205,7 @@ LockAndInstallUVolumeEntry(struct volume *av, struct uvldbentry *ve, int acell,
 					 0, &rxconn);
 		    if (tconn) {
 			RX_AFS_GUNLOCK();
+			xdr_free((xdrproc_t) xdr_bulkaddrs, &addrs);
 			code =
 			    VL_GetAddrsU(rxconn, &attrs, &uuid, &unique,
 					 &nentries, &addrs);
diff --git a/src/ptserver/pts.c b/src/ptserver/pts.c
index c97307bdf1..05508a70df 100644
--- a/src/ptserver/pts.c
+++ b/src/ptserver/pts.c
@@ -681,6 +681,7 @@ CheckEntry(struct cmd_syndesc *as, void *arock)
 
 	lids.idlist_val[0] = aentry.owner;
 	lids.idlist_val[1] = aentry.creator;
+	xdr_free((xdrproc_t) xdr_namelist, &lnames);
 	code = pr_IdToName(&lids, &lnames);
 	if (code) {
 	    rcode = code;
diff --git a/src/ptserver/testpt.c b/src/ptserver/testpt.c
index c359f8f465..50f0029f6f 100644
--- a/src/ptserver/testpt.c
+++ b/src/ptserver/testpt.c
@@ -112,6 +112,7 @@ ListUsedIds(struct cmd_syndesc *as, void *arock)
 		startId++;
 	}
 	lids.idlist_len = i;
+	xdr_free((xdrproc_t) xdr_namelist, &lnames);
 	code = pr_IdToName(&lids, &lnames);
 	if (code) {
 	    afs_com_err(whoami, code, "converting id to name");
diff --git a/src/volser/vos.c b/src/volser/vos.c
index 007bfe5064..6efc1c73fd 100644
--- a/src/volser/vos.c
+++ b/src/volser/vos.c
@@ -4520,6 +4520,7 @@ ListVLDB(struct cmd_syndesc *as, void *arock)
 				  &arrayEntries, &nextindex);
 	if (vcode == RXGEN_OPCODE) {
 	    /* Vlserver not running with ListAttributesN2. Fall back */
+	    xdr_free((xdrproc_t) xdr_nbulkentries, &arrayEntries);
 	    vcode =
 		VLDB_ListAttributes(&attributes, &centries, &arrayEntries);
 	    nextindex = -1;
-- 
2.45.2


From 4871f8ad2775e97bb85ff7efc33a4ad8d3f6d9d1 Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Fri, 16 Oct 2020 10:55:15 -0500
Subject: [PATCH 8/9] OPENAFS-SA-2024-003: sys: Don't over-copy RMTSYS_Pioctl
 output data

CVE-2024-10397

Here, 'OutData' only has OutData.rmtbulk_len bytes in it. We know that
OutData.rmtbulk_len is at most data->out_size, but it could be
smaller. So, only copy OutData.rmtbulk_len bytes, not data->out_size,
since data->out_size could be more than the number of bytes we have
allocated in OutData.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15924
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit f31a79d749abc8e64a8d9ac748bb2b5457875099)

Change-Id: Ic05751d05c7c8862770188131110cc602c9b93b7
Reviewed-on: https://gerrit.openafs.org/15946
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/sys/rmtsysc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/sys/rmtsysc.c b/src/sys/rmtsysc.c
index cf16afcea0..0a41489b89 100644
--- a/src/sys/rmtsysc.c
+++ b/src/sys/rmtsysc.c
@@ -260,7 +260,7 @@ pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data, afs_int32 follow)
 	    errno = EINVAL;
 	    errorcode = -1;
 	} else {
-	    memcpy(data->out, OutData.rmtbulk_val, data->out_size);
+	    memcpy(data->out, OutData.rmtbulk_val, OutData.rmtbulk_len);
 	    outparam_conversion(cmd, data->out, 1);
 	}
     }
-- 
2.45.2


From 37e585f0841803cdf3a1f99770034890ba162d7c Mon Sep 17 00:00:00 2001
From: Andrew Deason <adeason@sinenomine.net>
Date: Thu, 15 Oct 2020 21:07:17 -0500
Subject: [PATCH 9/9] OPENAFS-SA-2024-003: xdr: Initialize memory for INOUT
 args

CVE-2024-10397

Currently, there are a few callers of RPCs that specify some data for
an INOUT parameter, but do not initialize the memory for that data.
This can result in the uninitialized memory being sent to the peer
when the argument is processed as an IN argument. Simply clear the
relevant data before running the RPC to avoid this.

The relevant RPCs and arguments are:

- For RMTSYS_Pioctl, the 'OutData' argument.

- For BUDB_GetVolumes, the 'volumes' argument.
-- via DBLookupByVolume -> bcdb_LookupVolume -> ubik_BUDB_GetVolumes
-- and via bc_Restorer -> bcdb_FindVolumes -> ubik_BUDB_GetVolumes

- For KAA_Authenticate_old / KAA_Authenticate, this can happen with
  the 'answer' argument in ka_Authenticate if KAA_AuthenticateV2 or
  KAA_Authenticate return RXGEN_OPCODE, but the server manages to
  populate oanswer.SeqLen with non-zero.

For all of these, make sure the memory is blanked before running the
relevant RPC. For ka_Authenticate, reset oanswer.SeqLen to 0 to avoid
sending any data, but still blank 'answer' and 'answer_old' just to be
safe.

FIXES 135043

Reviewed-on: https://gerrit.openafs.org/15925
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
(cherry picked from commit c4e28c2afe743aa323be57ef3b0faec13027e678)

Change-Id: If44320c1efde98c53eed88099cd978ef89f4c0d8
Reviewed-on: https://gerrit.openafs.org/15947
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
---
 src/afs/afs_pag_call.c | 2 ++
 src/bucoord/commands.c | 2 ++
 src/bucoord/restore.c  | 2 +-
 src/kauth/authclient.c | 4 ++++
 src/sys/rmtsysc.c      | 2 +-
 5 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/afs/afs_pag_call.c b/src/afs/afs_pag_call.c
index 610cfb3a84..679ef3a701 100644
--- a/src/afs/afs_pag_call.c
+++ b/src/afs/afs_pag_call.c
@@ -461,6 +461,8 @@ afs_syscall_pioctl(char *path, unsigned int com, caddr_t cmarg, int follow)
 	goto out_idata;
     }
 
+    memset(odata.rmtbulk_val, 0, out_size);
+
     AFS_GUNLOCK();
     code = RMTSYS_Pioctl(rmtsys_conn, &ccred, abspath, com, follow,
 			 &idata, &odata, &err);
diff --git a/src/bucoord/commands.c b/src/bucoord/commands.c
index 10f088a478..dc7847fd14 100644
--- a/src/bucoord/commands.c
+++ b/src/bucoord/commands.c
@@ -2725,6 +2725,8 @@ DBLookupByVolume(char *volumeName)
     char vname[BU_MAXNAMELEN];
     char ds[50];
 
+    memset(volumeEntry, 0, sizeof(volumeEntry));
+
     for (pass = 0; pass < 2; pass++) {
 	/*p */
 	/* On second pass, search for backup volume */
diff --git a/src/bucoord/restore.c b/src/bucoord/restore.c
index b73a9351f3..b7fb1d4b3f 100644
--- a/src/bucoord/restore.c
+++ b/src/bucoord/restore.c
@@ -191,7 +191,7 @@ bc_Restorer(afs_int32 aindex)
     serverAll = HOSTADDR(&dumpTaskPtr->destServer);
     partitionAll = dumpTaskPtr->destPartition;
 
-    volumeEntries = malloc(MAXTAPESATONCE * sizeof(struct budb_volumeEntry));
+    volumeEntries = calloc(MAXTAPESATONCE, sizeof(struct budb_volumeEntry));
     if (!volumeEntries) {
 	afs_com_err(whoami, BC_NOMEM, NULL);
 	ERROR(BC_NOMEM);
diff --git a/src/kauth/authclient.c b/src/kauth/authclient.c
index 4db0cbc134..c64f330edf 100644
--- a/src/kauth/authclient.c
+++ b/src/kauth/authclient.c
@@ -525,12 +525,15 @@ ka_Authenticate(char *name, char *instance, char *cell, struct ubik_client * con
     oanswer.MaxSeqLen = sizeof(answer);
     oanswer.SeqLen = 0;
     oanswer.SeqBody = (char *)&answer;
+    memset(&answer, 0, sizeof(answer));
+    memset(&answer_old, 0, sizeof(answer_old));
 
     version = 2;
     code =
 	kawrap_ubik_Call(KAA_AuthenticateV2, conn, 0, name, instance,
 			 (void*)(uintptr_t)start, (void*)(uintptr_t)end, &arequest, &oanswer, 0, 0);
     if (code == RXGEN_OPCODE) {
+	oanswer.SeqLen = 0;
 	oanswer.MaxSeqLen = sizeof(answer);
 	oanswer.SeqBody = (char *)&answer;
 	version = 1;
@@ -538,6 +541,7 @@ ka_Authenticate(char *name, char *instance, char *cell, struct ubik_client * con
 	    ubik_KAA_Authenticate(conn, 0, name, instance, start, end,
 				  &arequest, &oanswer);
 	if (code == RXGEN_OPCODE) {
+	    oanswer.SeqLen = 0;
 	    oanswer.MaxSeqLen = sizeof(answer_old);
 	    oanswer.SeqBody = (char *)&answer_old;
 	    version = 0;
diff --git a/src/sys/rmtsysc.c b/src/sys/rmtsysc.c
index 0a41489b89..6881234220 100644
--- a/src/sys/rmtsysc.c
+++ b/src/sys/rmtsysc.c
@@ -214,7 +214,7 @@ pioctl(char *path, afs_int32 cmd, struct ViceIoctl *data, afs_int32 follow)
     inparam_conversion(cmd, InData.rmtbulk_val, 0);
 
     OutData.rmtbulk_len = MAXBUFFERLEN * sizeof(*OutData.rmtbulk_val);
-    OutData.rmtbulk_val = malloc(OutData.rmtbulk_len);
+    OutData.rmtbulk_val = calloc(1, OutData.rmtbulk_len);
     if (!OutData.rmtbulk_val) {
 	free(inbuffer);
 	return -1;
-- 
2.45.2

