git: e11ad014d146 - main - libc: return partial sysctl() result if buffer is too small

From: Stefan Eßer <se_at_FreeBSD.org>
Date: Fri, 04 Feb 2022 13:10:36 UTC
The branch main has been updated by se:

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

commit e11ad014d1468729ecf758ab3709618a78feae1b
Author:     Stefan Eßer <se@FreeBSD.org>
AuthorDate: 2022-02-04 12:44:20 +0000
Commit:     Stefan Eßer <se@FreeBSD.org>
CommitDate: 2022-02-04 12:44:20 +0000

    libc: return partial sysctl() result if buffer is too small
    
    Testing of a new feature revealed that calling sysctl() to retrieve
    the value of the user.localbase variable passing too low a buffer size
    could leave the result buffer unchanged.
    
    The behavior in the normal case of a sufficiently large buffer was
    correct.
    
    All known callers pass a sufficiently large buffer and have thus not
    been affected by this issue. If a non-default value had been assigned
    to this variable, the result was as documented, too.
    
    Fix the function to fill the buffer with a partial result, if the
    passed in buffer size is too low to hold the full result.
    
    MFC after:      3 days
---
 lib/libc/gen/sysctl.c | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/lib/libc/gen/sysctl.c b/lib/libc/gen/sysctl.c
index cdd2d3e391e6..9dd80ddfc0d8 100644
--- a/lib/libc/gen/sysctl.c
+++ b/lib/libc/gen/sysctl.c
@@ -74,17 +74,18 @@ sysctl(const int *name, u_int namelen, void *oldp, size_t *oldlenp,
 	/* Variables under CLT_USER that may be overridden by kernel values */
 	switch (name[1]) {
 	case USER_LOCALBASE:
-		if (oldlenp == NULL || *oldlenp != 1)
-			return (0);
-		if (oldp != NULL) {
-			if (orig_oldlen < sizeof(_PATH_LOCALBASE)) {
-				errno = ENOMEM;
-				return (-1);
+		if (oldlenp != NULL && *oldlenp == 1) {
+			*oldlenp = sizeof(_PATH_LOCALBASE);
+			if (oldp != NULL) {
+				if (*oldlenp > orig_oldlen) {
+					*oldlenp = orig_oldlen;
+					errno = ENOMEM;
+					retval = -1;
+				}
+				memmove(oldp, _PATH_LOCALBASE, *oldlenp);
 			}
-			memmove(oldp, _PATH_LOCALBASE, sizeof(_PATH_LOCALBASE));
 		}
-		*oldlenp = sizeof(_PATH_LOCALBASE);
-		return (0);
+		return (retval);
 	}
 
 	/* Variables under CLT_USER whose values are immutably defined below */