summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan McDonald <danmcd@joyent.com>2020-12-09 10:20:57 -0500
committerDan McDonald <danmcd@joyent.com>2020-12-09 10:20:57 -0500
commitb1e92d38ed920e9dcddad6df733657d248710c19 (patch)
tree2b08c2b687826e4b611727137322d145b0a4fb87
parentceb196aa4a6afed87153787b518d4cc7baf4adf1 (diff)
parentf2c438c5058c64b7373448f239156bf60009abcb (diff)
downloadillumos-joyent-b1e92d38ed920e9dcddad6df733657d248710c19.tar.gz
[illumos-gate merge]
commit f2c438c5058c64b7373448f239156bf60009abcb 7024 Add getgrouplist() to illumos commit adc528899caad05412c9b8d397e277900adf786b 13207 Creating an aggr with more than 128 Tx or Rx rings panics
-rw-r--r--manifest1
-rw-r--r--usr/src/head/grp.h11
-rw-r--r--usr/src/lib/libc/port/gen/initgroups.c129
-rw-r--r--usr/src/lib/libc/port/mapfile-vers7
-rw-r--r--usr/src/man/man3c/Makefile3
-rw-r--r--usr/src/man/man3c/getgrouplist.3c207
-rw-r--r--usr/src/pkg/manifests/system-library.man3c.inc3
-rw-r--r--usr/src/uts/common/io/aggr/aggr_grp.c91
8 files changed, 422 insertions, 30 deletions
diff --git a/manifest b/manifest
index 7e7d70d3d4..87cd9c4f39 100644
--- a/manifest
+++ b/manifest
@@ -13510,6 +13510,7 @@ s usr/share/man/man3c/getgrgid.3c=getgrnam.3c
s usr/share/man/man3c/getgrgid_r.3c=getgrnam.3c
f usr/share/man/man3c/getgrnam.3c 0444 root bin
s usr/share/man/man3c/getgrnam_r.3c=getgrnam.3c
+f usr/share/man/man3c/getgrouplist.3c 0444 root bin
s usr/share/man/man3c/gethomelgroup.3c=getcpuid.3c
f usr/share/man/man3c/gethostid.3c 0444 root bin
f usr/share/man/man3c/gethostname.3c 0444 root bin
diff --git a/usr/src/head/grp.h b/usr/src/head/grp.h
index b5cea984f2..1a7e8ffb98 100644
--- a/usr/src/head/grp.h
+++ b/usr/src/head/grp.h
@@ -28,6 +28,8 @@
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ *
+ * Copyright 2020 Joyent, Inc.
*/
#ifndef _GRP_H
@@ -62,6 +64,7 @@ extern struct group *fgetgrent_r(FILE *, struct group *, char *, int);
extern struct group *fgetgrent(FILE *); /* MT-unsafe */
extern int initgroups(const char *, gid_t);
+extern int getgrouplist(const char *, gid_t, gid_t *, int *);
#endif /* defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) */
#if defined(__EXTENSIONS__) || !defined(__XOPEN_OR_POSIX) || defined(_XPG4_2)
@@ -125,13 +128,6 @@ extern int __posix_getgrgid_r(gid_t, struct group *, char *, size_t,
extern int __posix_getgrnam_r(const char *, struct group *, char *, size_t,
struct group **);
-#ifdef __lint
-
-#define getgrgid_r __posix_getgrgid_r
-#define getgrnam_r __posix_getgrnam_r
-
-#else /* !__lint */
-
static int
getgrgid_r(gid_t __gid, struct group *__grp, char *__buf, size_t __len,
struct group **__res)
@@ -145,7 +141,6 @@ getgrnam_r(const char *__cb, struct group *__grp, char *__buf, size_t __len,
return (__posix_getgrnam_r(__cb, __grp, __buf, __len, __res));
}
-#endif /* !__lint */
#endif /* __PRAGMA_REDEFINE_EXTNAME */
#else /* (_POSIX_C_SOURCE - 0 >= 199506L) || ... */
diff --git a/usr/src/lib/libc/port/gen/initgroups.c b/usr/src/lib/libc/port/gen/initgroups.c
index 29e63e6e53..f03f833d13 100644
--- a/usr/src/lib/libc/port/gen/initgroups.c
+++ b/usr/src/lib/libc/port/gen/initgroups.c
@@ -22,19 +22,20 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
+ * Copyright 2020 Joyent, Inc.
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#pragma weak _initgroups = initgroups
-#include "lint.h"
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#include <grp.h>
+#include <limits.h>
+#include <sys/debug.h>
#include <sys/types.h>
#include <sys/param.h>
#include <unistd.h>
@@ -84,3 +85,125 @@ initgroups(const char *uname, gid_t agroup)
errno = errsave;
return (retsave);
}
+
+int
+getgrouplist(const char *uname, gid_t agroup, gid_t *groups, int *ngroups)
+{
+ gid_t *grouplist = NULL;
+ gid_t *grpptr;
+ long ngroups_max;
+ int sz, ret;
+
+ /*
+ * We require sysconf(_SC_NGROUPS_MAX) either returns a sane value (>0)
+ * or fails. If it returns 0, something has gone horribly, horribly
+ * wrong.
+ */
+ ngroups_max = sysconf(_SC_NGROUPS_MAX);
+ if (ngroups_max > INT_MAX)
+ ngroups_max = INT_MAX;
+ else if (ngroups_max < 0)
+ return (-1);
+ VERIFY3S(ngroups_max, >, 0);
+
+ /*
+ * The documented behavior of getgrouplist(3C) on other platforms
+ * (e.g. Linux and FreeBSD) do not list any failures other than
+ * 'groups is too small'. However, examination of some popular
+ * implementations of getgrouplist on those platforms (e.g. glibc and
+ * musl -- both appear to share the same man page for getgrouplist(3))
+ * show that they can in fact fail for other reasons (e.g. ENOMEM,
+ * EIO).
+ *
+ * As such, we don't attempt to catch and deal with any underlying
+ * errors here. Instead, any underlying errors cause getgrouplist(3C)
+ * to fail, and any errno value set is left unmodified for examination
+ * by the caller.
+ *
+ * One small complication is that the internal _getgroupsbymember()
+ * itself doesn't provide any way to report back if the buffer supplied
+ * to _getgroupsbymember() is too small. Instead, we always supply
+ * a buffer large enough to hold _SC_NGROUPS_MAX entries -- either
+ * by allocating one ourselves or using the user supplied buffer if
+ * sufficiently large.
+ *
+ * The system behavior is undefined for any user in more groups than
+ * _SC_NGROUPS_MAX -- initgroups(3C) for example just ignores any
+ * excess groups (and which _SC_NGROUPS_MAX sized subset of groups end
+ * up being set as the secondary groups is non-deterministic), so this
+ * seems reasonable. Modifying _getgroupsbymember() would require
+ * modification of the NSS code (due to the pervasive special handling
+ * of _getgroupsbymember() in the NSS code) as well as modification of
+ * all NSS backends that implement it. As there are at least a few
+ * known third party NSS backends, we've opted to avoid doing this
+ * for now.
+ */
+
+ if ((ngroups == NULL) || (*ngroups <= 0) || (groups == NULL)) {
+ *ngroups = ngroups_max;
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (*ngroups < ngroups_max) {
+ /*
+ * The caller's buffer might be too small, try to use our own
+ * buffer instead.
+ */
+ grouplist = calloc(ngroups_max, sizeof (gid_t));
+ if (grouplist == NULL)
+ return (-1);
+
+ grpptr = grouplist;
+ sz = ngroups_max;
+ } else {
+ /* The caller's buffer is large enough, so use it */
+ grpptr = groups;
+ sz = *ngroups;
+ }
+
+ /*
+ * Always add agroup as the first member -- it should always appear
+ * in the resulting list of groups, and this allows the backends to
+ * skip adding it.
+ */
+ grpptr[0] = agroup;
+
+ ret = _getgroupsbymember(uname, grpptr, sz, 1);
+
+ /*
+ * We passed in 1 group entry. We should at minimum get 1 entry back
+ * from _getgroupsbymember(). If we don't, there is a bug in the NSS
+ * code or a backend. Since the return value is used to size a copy
+ * further below, we hard fail (abort) here if we get back an
+ * impossible value so we're not traipsing all over memory (which would
+ * just make debugging any such problem all the more difficult).
+ */
+ VERIFY3S(ret, >, 0);
+
+ /*
+ * If we used the caller's buffer, it means its size was >= ngroups_max
+ * entries, and we're done.
+ */
+ if (grpptr == groups) {
+ /* Set *ngroups to the number of entries in groups */
+ *ngroups = ret;
+ return (ret);
+ }
+
+ /* We verified earlier *ngroups > 0 */
+ if (ret < *ngroups) {
+ /* Copy as many gids that will fit */
+ (void) memcpy(groups, grpptr, *ngroups * sizeof (gid_t));
+
+ *ngroups = ret;
+ ret = -1;
+ errno = ERANGE;
+ } else {
+ (void) memcpy(groups, grpptr, ret * sizeof (gid_t));
+ *ngroups = ret;
+ }
+
+ free(grouplist);
+ return (ret);
+}
diff --git a/usr/src/lib/libc/port/mapfile-vers b/usr/src/lib/libc/port/mapfile-vers
index 0dec599cf6..b1309a7071 100644
--- a/usr/src/lib/libc/port/mapfile-vers
+++ b/usr/src/lib/libc/port/mapfile-vers
@@ -23,7 +23,7 @@
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright 2018 Nexenta Systems, Inc.
# Copyright (c) 2012 by Delphix. All rights reserved.
-# Copyright 2018 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
# Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright (c) 2013 Gary Mills
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
@@ -78,6 +78,11 @@ $if _x86 && _ELF64
$add amd64
$endif
+SYMBOL_VERSION ILLUMOS_0.38 {
+ protected:
+ getgrouplist;
+} ILLUMOS_0.37;
+
SYMBOL_VERSION ILLUMOS_0.37 {
global:
__stack_chk_guard;
diff --git a/usr/src/man/man3c/Makefile b/usr/src/man/man3c/Makefile
index ea7b4d4834..e9f8f2cafd 100644
--- a/usr/src/man/man3c/Makefile
+++ b/usr/src/man/man3c/Makefile
@@ -14,7 +14,7 @@
# Copyright 2018 Nexenta Systems, Inc.
# Copyright 2013, OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
-# Copyright 2018 Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
# Copyright 2018 Jason King
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
@@ -172,6 +172,7 @@ MANFILES= __fbufsize.3c \
getenv.3c \
getexecname.3c \
getgrnam.3c \
+ getgrouplist.3c \
gethostid.3c \
gethostname.3c \
gethrtime.3c \
diff --git a/usr/src/man/man3c/getgrouplist.3c b/usr/src/man/man3c/getgrouplist.3c
new file mode 100644
index 0000000000..f1e10695e3
--- /dev/null
+++ b/usr/src/man/man3c/getgrouplist.3c
@@ -0,0 +1,207 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 Joyent, Inc.
+.\"
+.Dd November 22, 2020
+.Dt GETGROUPLIST 3C
+.Os
+.Sh NAME
+.Nm getgrouplist
+.Nd calculate group access list
+.Sh SYNOPSIS
+.In grp.h
+.Ft int
+.Fo getgrouplist
+.Fa "const char *user"
+.Fa "gid_t agroup"
+.Fa "gid_t *groups"
+.Fa "int *ngroups"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn getgrouplist
+function queries the group database to obtain the list of groups that
+.Fa user
+belongs to.
+The
+.Fa agroup
+group is always added to the resulting group list.
+This value is typically the primary gid of the user from the
+.Sy passwd
+database.
+.Pp
+When calling
+.Fn getgrouplist ,
+the caller should set the maximum number of groups that
+.Fa groups
+can hold in
+.Fa *ngroups .
+The value of
+.Dv NGROUPS_MAX
+can be used to size
+.Fa groups
+to ensure it can hold any number of groups supported by the system.
+.Pp
+Upon return,
+.Fn getgrouplist
+stores the list of groups that
+.Fa user
+belongs to in
+.Fa groups
+and stores the number of groups
+.Fa user
+belongs to in
+.Fa *ngroups
+.Po
+this may be a smaller than the value passed in when
+calling
+.Fn getgrouplist
+.Pc .
+If
+.Fa groups
+is too small to hold all of the groups
+.Fa user
+belongs to,
+.Fn getgrouplist
+fails and sets
+.Fa *ngroups
+to a value large enough to hold the full result.
+.Sh RETURN VALUES
+On success,
+.Fn getgrouplist
+returns the number of groups
+.Fa user
+belongs to, fills in
+.Fa groups
+with the gids of the groups
+.Fa user
+belongs to, and also sets
+.Fa *ngroups
+to the number of groups
+.Fa user
+belongs to.
+.Pp
+On failure,
+.Fn getgrouplist
+returns -1 and
+.Va errno
+is set.
+.Pp
+The behavior of
+.Fn getgrouplist
+is undefined if the total number of groups a user belongs to exceeds
+.Dv NGROUPS_MAX .
+.Pp
+Note that on
+.Fx ,
+.Fn getgrouplist
+always returns -1 on failure or 0 on success.
+A caller must rely on the value set in
+.Fa *ngroups
+upon return to determine the number of entries in
+.Fa groups .
+.Pp
+On Linux, both glibc and musl return the number of groups
+.Fa user
+belongs to on success and returns -1 on failure.
+.Pp
+None of these other implementations document any
+.Va errno
+values on failure, however their implementations show that
+.Va errno
+may be set on failure.
+Software using
+.Fn getgrouplist
+should be aware of these differences when attemping to write portable
+software.
+.Sh EXAMPLES
+.Sy Example 1
+Print all the groups for a user.
+.Bd -literal
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <err.h>
+
+void
+printgroups(const char *user)
+{
+ struct passwd *pw;
+ gid_t *groups;
+ int ngroups, ret;
+
+ if ((groups = calloc(NGROUPS_MAX, sizeof (gid_t))) == NULL)
+ err(EXIT_FAILURE, "calloc");
+
+ if ((pw = getpwnam(user)) == NULL)
+ err(EXIT_FAILURE, "getpwname");
+
+ ngroups = NGROUPS_MAX;
+ ret = getgrouplist(user, pw->pw_gid, groups, &ngroups);
+ if (ret < 0)
+ err(EXIT_FAILURE, "getgrouplist");
+
+ for (int i = 0; i < ret; i++) {
+ struct group *gr = getgrgid(groups[i]);
+
+ (void) printf("%s ", gr->gr_name);
+ }
+ (void) fputc('\\n', stdout);
+
+ free(groups);
+}
+.Ed
+.Sh ERRORS
+On failure,
+.Fn getgrouplist
+returns -1, and will set errno to one one of the following values:
+.Bl -tag -width Dv
+.It Er ENOMEM
+Not enough memory to complete the request.
+.It Er EINVAL
+One of the parameters is invalid
+.Po
+for example,
+.Fa ngroups
+is
+.Dv NULL
+.Pc .
+.It Dv ERANGE
+The supplied value of
+.Fa *ngroups
+is too small to hold the results.
+.Fa *ngroups
+is set
+.Po
+upon return
+.Pc
+to a value large enough to hold the results, and a partial set of
+results is written to
+.Fa groups .
+The value written to
+.Fa *ngroups
+may be larger than the value returned by a successful call to
+.Fn getgrouplist .
+.El
+.Sh INTERFACE STABILITY
+.Sy Uncommitted
+.Sh MT-LEVEL
+.Sy MT-Safe
+.Sh SEE ALSO
+.Xr groups 1 ,
+.Xr getuid 2 ,
+.Xr getgrnam 3C ,
+.Xr getgroups 3C ,
+.Xr initgroups 3C ,
+.Xr limits.h 3HEAD
diff --git a/usr/src/pkg/manifests/system-library.man3c.inc b/usr/src/pkg/manifests/system-library.man3c.inc
index f892d1551e..70c9189c0f 100644
--- a/usr/src/pkg/manifests/system-library.man3c.inc
+++ b/usr/src/pkg/manifests/system-library.man3c.inc
@@ -15,7 +15,7 @@
# Copyright 2013 OmniTI Computer Consulting, Inc. All rights reserved.
# Copyright 2014 Garrett D'Amore <garrett@damore.org>
# Copyright 2018 Jason King
-# Copyright 2018, Joyent, Inc.
+# Copyright 2020 Joyent, Inc.
# Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
#
@@ -168,6 +168,7 @@ file path=usr/share/man/man3c/getentropy.3c
file path=usr/share/man/man3c/getenv.3c
file path=usr/share/man/man3c/getexecname.3c
file path=usr/share/man/man3c/getgrnam.3c
+file path=usr/share/man/man3c/getgrouplist.3c
file path=usr/share/man/man3c/gethostid.3c
file path=usr/share/man/man3c/gethostname.3c
file path=usr/share/man/man3c/gethrtime.3c
diff --git a/usr/src/uts/common/io/aggr/aggr_grp.c b/usr/src/uts/common/io/aggr/aggr_grp.c
index 199625c377..9a4a936450 100644
--- a/usr/src/uts/common/io/aggr/aggr_grp.c
+++ b/usr/src/uts/common/io/aggr/aggr_grp.c
@@ -1374,11 +1374,14 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
{
aggr_grp_t *grp = NULL;
aggr_port_t *port;
+ aggr_port_t *last_attached = NULL;
mac_register_t *mac;
boolean_t link_state_changed;
- mac_perim_handle_t mph;
+ mac_perim_handle_t mph, pmph;
+ datalink_id_t tempid;
+ boolean_t mac_registered = B_FALSE;
int err;
- int i;
+ int i, j;
kt_did_t tid = 0;
/* need at least one port */
@@ -1525,6 +1528,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
goto bail;
}
+ mac_registered = B_TRUE;
+
mac_perim_enter_by_mh(grp->lg_mh, &mph);
/*
@@ -1554,12 +1559,33 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
* underlying port. Note that this is done after the
* aggr registers its MAC.
*/
- VERIFY3S(aggr_add_pseudo_tx_group(port, &grp->lg_tx_group),
- ==, 0);
+ err = aggr_add_pseudo_tx_group(port, &grp->lg_tx_group);
+
+ if (err != 0) {
+ mac_perim_exit(mph);
+ goto bail;
+ }
for (i = 0; i < grp->lg_rx_group_count; i++) {
- VERIFY3S(aggr_add_pseudo_rx_group(port,
- &grp->lg_rx_groups[i]), ==, 0);
+ err = aggr_add_pseudo_rx_group(port,
+ &grp->lg_rx_groups[i]);
+
+ if (err != 0) {
+ /*
+ * Undo what we have added for the current
+ * port.
+ */
+ aggr_rem_pseudo_tx_group(port,
+ &grp->lg_tx_group);
+
+ for (j = 0; j < i; j++) {
+ aggr_rem_pseudo_rx_group(port,
+ &grp->lg_rx_groups[j]);
+ }
+
+ mac_perim_exit(mph);
+ goto bail;
+ }
}
if (aggr_port_notify_link(grp, port))
@@ -1569,6 +1595,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
* Initialize the callback functions for this port.
*/
aggr_port_init_callbacks(port);
+
+ last_attached = port;
}
if (link_state_changed)
@@ -1585,18 +1613,8 @@ aggr_grp_create(datalink_id_t linkid, uint32_t key, uint_t nports,
return (0);
bail:
-
grp->lg_closing = B_TRUE;
- port = grp->lg_ports;
- while (port != NULL) {
- aggr_port_t *cport;
-
- cport = port->lp_next;
- aggr_port_delete(port);
- port = cport;
- }
-
/*
* Inform the lacp_rx thread to exit.
*/
@@ -1619,6 +1637,47 @@ bail:
if (tid != 0)
thread_join(tid);
+ if (mac_registered) {
+ (void) dls_devnet_destroy(grp->lg_mh, &tempid, B_TRUE);
+ (void) mac_disable(grp->lg_mh);
+
+ if (last_attached != NULL) {
+ /*
+ * Detach and clean up ports added.
+ */
+ mac_perim_enter_by_mh(grp->lg_mh, &mph);
+
+ for (port = grp->lg_ports; ; port = port->lp_next) {
+ mac_perim_enter_by_mh(port->lp_mh, &pmph);
+ (void) aggr_grp_detach_port(grp, port);
+ mac_perim_exit(pmph);
+
+ aggr_rem_pseudo_tx_group(port,
+ &grp->lg_tx_group);
+
+ for (i = 0; i < grp->lg_rx_group_count; i++) {
+ aggr_rem_pseudo_rx_group(port,
+ &grp->lg_rx_groups[i]);
+ }
+ if (port == last_attached)
+ break;
+ }
+
+ mac_perim_exit(mph);
+ }
+
+ (void) mac_unregister(grp->lg_mh);
+ }
+
+ port = grp->lg_ports;
+ while (port != NULL) {
+ aggr_port_t *cport;
+
+ cport = port->lp_next;
+ aggr_port_delete(port);
+ port = cport;
+ }
+
kmem_free(grp->lg_tx_blocked_rings,
(sizeof (mac_ring_handle_t *) * MAX_RINGS_PER_GROUP));
rw_exit(&aggr_grp_lock);