git: d7d71341ae7d - main - acl_from_text.c: Allow negative uid/gid numbers to be handled

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Mon, 06 Jul 2026 19:35:40 UTC
The branch main has been updated by rmacklem:

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

commit d7d71341ae7d79886143a9ce427dca0e858eda97
Author:     Peter Eriksson <pen_lysator.liu.se>
AuthorDate: 2026-07-06 19:33:22 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-07-06 19:33:22 +0000

    acl_from_text.c: Allow negative uid/gid numbers to be handled
    
    getfacl / acl_to_text() incorrectly prints uid/gid numbers as signed integers.
    This causes uid / gid numbers larger than 2G (2147483648) to print as
    negative numbers.
    The libc acl_from_text() function does not handle negative numbers.
    This diff adds a backwards compatiblity fix to allow negative numbers...
    
    Reviewed by:    rmacklem
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D57180
---
 lib/libc/posix1e/acl_from_text.c | 41 +++++++++++++++++++++++++++++++---------
 1 file changed, 32 insertions(+), 9 deletions(-)

diff --git a/lib/libc/posix1e/acl_from_text.c b/lib/libc/posix1e/acl_from_text.c
index 765b58290a04..746338b37f0c 100644
--- a/lib/libc/posix1e/acl_from_text.c
+++ b/lib/libc/posix1e/acl_from_text.c
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <inttypes.h>
 #include <assert.h>
 
 #include "acl_support.h"
@@ -263,6 +264,24 @@ error_label:
 	return(NULL);
 }
 
+/*
+ * Make sure the number given fits inside an uid_t or gid_t.
+ * Currently (2026-05-23) uid_t & gid_t is an uint32_t.
+ * Special case handle uid_t/gid_t numbers specified as negative numbers.
+ * Assumes that uid_t and gid_t are the same types.
+ */
+static int
+_invalid_uidgid(intmax_t v) {
+	if (v < 0) {
+		if ((-v) & ~(uintmax_t)((~(uid_t)0)>>1))
+			return (2); /* Underflow, does not fit into uid_t */
+	} else {
+		if (v & ~(uintmax_t)(~(uid_t)0))
+			return (1); /* Overflow, does not fit into uid_t */
+	}
+	return (0);
+}
+
 /*
  * Given a username/groupname from a text form of an ACL, return the uid/gid
  * XXX NOT THREAD SAFE, RELIES ON GETPWNAM, GETGRNAM
@@ -274,19 +293,21 @@ _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
 {
 	struct group	*g;
 	struct passwd	*p;
-	unsigned long	l;
+	intmax_t        v;
 	char 		*endp;
 
 	switch(tag) {
 	case ACL_USER:
 		p = getpwnam(name);
 		if (p == NULL) {
-			l = strtoul(name, &endp, 0);
-			if (*endp != '\0' || l != (unsigned long)(uid_t)l) {
-				errno = EINVAL;
+			errno = 0;
+			v = strtoimax(name, &endp, 0);
+			if (name == endp || *endp != '\0' ||
+			    errno == ERANGE || _invalid_uidgid(v) != 0) {
+				errno = EINVAL; /* No or invalid number */
 				return (-1);
 			}
-			*id = (uid_t)l;
+			*id = v;
 			return (0);
 		}
 		*id = p->pw_uid;
@@ -295,12 +316,14 @@ _acl_name_to_id(acl_tag_t tag, char *name, uid_t *id)
 	case ACL_GROUP:
 		g = getgrnam(name);
 		if (g == NULL) {
-			l = strtoul(name, &endp, 0);
-			if (*endp != '\0' || l != (unsigned long)(gid_t)l) {
-				errno = EINVAL;
+			errno = 0;
+			v = strtoimax(name, &endp, 0);
+			if (name == endp || *endp != '\0' ||
+			    errno == ERANGE || _invalid_uidgid(v) != 0) {
+				errno = EINVAL; /* No or invalid number */
 				return (-1);
 			}
-			*id = (gid_t)l;
+			*id = v;
 			return (0);
 		}
 		*id = g->gr_gid;