Message ID | 20240328180744.2906269-1-josimmon@redhat.com |
---|---|
State | New |
Headers | show |
Series | [v13] posix: Deprecate group_member for Linux | expand |
Ping On Thu, Mar 28, 2024 at 2:07 PM Joe Simmons-Talbott <josimmon@redhat.com> wrote: > > The alloca usage in group_member could lead to stack overflow on Linux. > Removing the alloca usage would require group_member to handle the error > condition where memory could not be allocated and that cannot be done > since group_member returns a boolean value. Thus deprecate group_member. > Add an internal only implementation of __group_member2 using a > scratch_buffer and return -1 for memory allocation errors. Use > __group_member2 in place of __group_member internally. Add testcases > for both group_member and __group_member2. > --- > Changes to v12: > * Rework euidaccess to only call __group_member2 if the euid and egid do > not match. Use is_group_member rather than gm for the temporary > variable name in both euidaccess and faccessat. > > Changes to v11: > * Rework faccessat as suggested by Paul Eggert to avoid duplicate checks > of EACCESS. > > Changes to v10: > * Only call __group_member2 in faccessat if we didn't match the egid or > gid. > * Update copyright year for newly added files to 2024 > > Changes to v9: > * v8 didn't actually include the changes due to a missing 'git add'. > Include those changes. > > Changes to v8: > * Remove duplicate calls to __getegid () and __getgid () and convert > nested ternary operators into if/else. > > Changes to v7: > * rebased to latest master. > > Changes to v6: > * Use the intial scratch_buffer size as the starting point for > determining how much space is needed to store the group list. > * Call getgroups() with a zero size and set the scratch_buffer size > based on the returned number of groups. > > Changes to v5: > * Add __group_member2 and use it internally in the place of the now > deprecated group_member. > * Add a testcase for __group_member2. > > Changes to v4: > * Rebase onto latest commit. > > Changes to v3: > * Fix include guards to match file location _BITS_GROUP_MEMBER_H > * Fix indentation of preprocessor directives > > Changes to v2: > * Move the linux group_member.h to the bits directory > * Include the correct group_member.h in posix/unistd.h > > Changes to v1: > * Add NEWS entry > * Move group_member.h to bits/group_member.h > * Include bits/group_member.h in installed headers > * Add tests to group_member.h files to only be included from unistd.h > > NEWS | 4 ++ > bits/group_member.h | 31 +++++++++++++++ > include/unistd.h | 1 + > posix/Makefile | 8 ++++ > posix/group_member.c | 35 +++++++++++++++++ > posix/tst-group_member.c | 41 ++++++++++++++++++++ > posix/tst-group_member2.c | 43 +++++++++++++++++++++ > posix/unistd.h | 6 +-- > sysdeps/posix/euidaccess.c | 24 ++++++++---- > sysdeps/unix/sysv/linux/bits/group_member.h | 32 +++++++++++++++ > sysdeps/unix/sysv/linux/faccessat.c | 27 ++++++++----- > 11 files changed, 233 insertions(+), 19 deletions(-) > create mode 100644 bits/group_member.h > create mode 100644 posix/tst-group_member.c > create mode 100644 posix/tst-group_member2.c > create mode 100644 sysdeps/unix/sysv/linux/bits/group_member.h > > diff --git a/NEWS b/NEWS > index da4b2223e9..82e1c43306 100644 > --- a/NEWS > +++ b/NEWS > @@ -141,6 +141,10 @@ Deprecated and removed features, and other changes affecting compatibility: > > * The ia64*-*-linux-gnu configurations are no longer supported. > > +* Deprecated group_member on Linux as it uses alloca to allocate a large > + buffer and has no capability for indicating failure for other memory > + allocations. > + > Changes to build and runtime requirements: > > * Building on LoongArch requires at a minimum binutils 2.41 for vector > diff --git a/bits/group_member.h b/bits/group_member.h > new file mode 100644 > index 0000000000..4ec1ef0813 > --- /dev/null > +++ b/bits/group_member.h > @@ -0,0 +1,31 @@ > +/* group_member declaration > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#ifndef _UNISTD_H > +# error "Never use <bits/group_member.h> directly; include <unistd.h> instead." > +#endif > + > +#ifndef _BITS_GROUP_MEMBER_H > +# define _BITS_GROUP_MEMBER_H 1 > + > +# ifdef __USE_GNU > +/* Return nonzero iff the calling process is in group GID. */ > +extern int group_member (__gid_t __gid) __THROW; > +# endif > + > +#endif /* _BITS_GROUP_MEMBER_H */ > diff --git a/include/unistd.h b/include/unistd.h > index e241603b81..39d5bda372 100644 > --- a/include/unistd.h > +++ b/include/unistd.h > @@ -131,6 +131,7 @@ extern __gid_t __getegid (void) attribute_hidden; > extern int __getgroups (int __size, __gid_t __list[]) attribute_hidden; > libc_hidden_proto (__getpgid) > extern int __group_member (__gid_t __gid) attribute_hidden; > +extern int __group_member2 (__gid_t __gid) attribute_hidden; > extern int __setuid (__uid_t __uid); > extern int __setreuid (__uid_t __ruid, __uid_t __euid); > extern int __setgid (__gid_t __gid); > diff --git a/posix/Makefile b/posix/Makefile > index a1e84853a8..b71d6c8750 100644 > --- a/posix/Makefile > +++ b/posix/Makefile > @@ -29,6 +29,7 @@ headers := \ > bits/getopt_core.h \ > bits/getopt_ext.h \ > bits/getopt_posix.h \ > + bits/group_member.h \ > bits/local_lim.h \ > bits/mman_ext.h \ > bits/posix1_lim.h \ > @@ -291,6 +292,7 @@ tests := \ > tst-glob_symlinks \ > tst-gnuglob \ > tst-gnuglob64 \ > + tst-group_member \ > tst-mmap \ > tst-mmap-offset \ > tst-nanosleep \ > @@ -479,6 +481,10 @@ tests-special += \ > # tests-special > endif > > +# This test calls __group_member2 directly, which is not exported from glibc. > +tests-internal += tst-group_member2 > +tests-static += tst-group_member2 > + > include ../Rules > > ifeq ($(run-built-tests),yes) > @@ -606,6 +612,8 @@ bug-glob1-ARGS = "$(objpfx)" > tst-execvp3-ARGS = --test-dir=$(objpfx) > CFLAGS-tst-spawn3.c += -DOBJPFX=\"$(objpfx)\" > > +CFLAGS-tst-group_member.c += -Wno-error=deprecated-declarations > + > # Test voluntarily overflows struct dirent > CFLAGS-bug-glob2.c += $(no-fortify-source) > > diff --git a/posix/group_member.c b/posix/group_member.c > index 9d68f57a68..bb92f4d631 100644 > --- a/posix/group_member.c > +++ b/posix/group_member.c > @@ -18,6 +18,7 @@ > > #include <sys/types.h> > #include <unistd.h> > +#include <scratch_buffer.h> > #include <stdlib.h> > #include <limits.h> > > @@ -47,3 +48,37 @@ __group_member (gid_t gid) > return 0; > } > weak_alias (__group_member, group_member) > + > +int > +__group_member2 (gid_t gid) > +{ > + int n; > + gid_t *groups; > + struct scratch_buffer sbuf; > + scratch_buffer_init (&sbuf); > + groups = sbuf.data; > + > + do > + { > + n = __getgroups (0, NULL); > + if (n > sbuf.length) > + { > + if (!scratch_buffer_set_array_size (&sbuf, sizeof (*groups), n)) > + return -1; > + groups = sbuf.data; > + } > + > + n = __getgroups (n, groups); > + } > + while (n > sbuf.length); > + > + while (n-- > 0) > + if (groups[n] == gid) > + { > + scratch_buffer_free (&sbuf); > + return 1; > + } > + > + scratch_buffer_free (&sbuf); > + return 0; > +} > diff --git a/posix/tst-group_member.c b/posix/tst-group_member.c > new file mode 100644 > index 0000000000..cc7f91618e > --- /dev/null > +++ b/posix/tst-group_member.c > @@ -0,0 +1,41 @@ > +/* Basic tests for group_member. > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#include <alloca.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > + > +#include <support/check.h> > + > +static int do_test (void) > +{ > + int n; > + gid_t *groups; > + > + n = getgroups (0, NULL); > + groups = alloca (n * sizeof (*groups)); > + n = getgroups (n, groups); > + > + while (n-- > 0) > + TEST_COMPARE (1, group_member(groups[n])); > + > + return EXIT_SUCCESS; > +} > + > +#include <support/test-driver.c> > diff --git a/posix/tst-group_member2.c b/posix/tst-group_member2.c > new file mode 100644 > index 0000000000..8f86d5a1e9 > --- /dev/null > +++ b/posix/tst-group_member2.c > @@ -0,0 +1,43 @@ > +/* Basic tests for group_member. > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#include <alloca.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <posix/unistd.h> > + > +#include <support/check.h> > + > +extern int __group_member2 (__gid_t __gid); > + > +static int do_test (void) > +{ > + int n; > + gid_t *groups; > + > + n = getgroups (0, NULL); > + groups = alloca (n * sizeof (*groups)); > + n = getgroups (n, groups); > + > + while (n-- > 0) > + TEST_COMPARE (1, __group_member2(groups[n])); > + > + return EXIT_SUCCESS; > +} > + > +#include <support/test-driver.c> > diff --git a/posix/unistd.h b/posix/unistd.h > index 54d7d7527e..411de1d6d4 100644 > --- a/posix/unistd.h > +++ b/posix/unistd.h > @@ -710,10 +710,10 @@ extern __gid_t getegid (void) __THROW; > of its supplementary groups in LIST and return the number written. */ > extern int getgroups (int __size, __gid_t __list[]) __THROW __wur > __fortified_attr_access (__write_only__, 2, 1); > + > #ifdef __USE_GNU > -/* Return nonzero iff the calling process is in group GID. */ > -extern int group_member (__gid_t __gid) __THROW; > -#endif > +# include <bits/group_member.h> > +#endif > > /* Set the user ID of the calling process to UID. > If the calling process is the super-user, set the real > diff --git a/sysdeps/posix/euidaccess.c b/sysdeps/posix/euidaccess.c > index 4c5c2220bd..da185dec3e 100644 > --- a/sysdeps/posix/euidaccess.c > +++ b/sysdeps/posix/euidaccess.c > @@ -81,7 +81,7 @@ extern int errno; > > #ifdef _LIBC > > -# define group_member __group_member > +# define group_member __group_member2 > # define euidaccess __euidaccess > > #else > @@ -120,7 +120,6 @@ int > euidaccess (const char *path, int mode) > { > struct __stat64_t64 stats; > - int granted; > > #ifdef _LIBC > uid_t euid; > @@ -167,15 +166,26 @@ euidaccess (const char *path, int mode) > || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) > return 0; > > + int shift; > + > if (euid == stats.st_uid) > - granted = (unsigned int) (stats.st_mode & (mode << 6)) >> 6; > - else if (egid == stats.st_gid || group_member (stats.st_gid)) > - granted = (unsigned int) (stats.st_mode & (mode << 3)) >> 3; > + shift = 6; > else > - granted = (stats.st_mode & mode); > + { > + int is_group_member = (egid == stats.st_gid); > + if (!is_group_member) > + { > + is_group_member = group_member (stats.st_gid); > + if (is_group_member < 0) > + return is_group_member; > + } > + shift = is_group_member ? 3 : 0; > + } > + > /* XXX Add support for ACLs. */ > - if (granted == mode) > + if ((stats.st_mode >> shift & mode) == mode) > return 0; > + > __set_errno (EACCESS); > return -1; > } > diff --git a/sysdeps/unix/sysv/linux/bits/group_member.h b/sysdeps/unix/sysv/linux/bits/group_member.h > new file mode 100644 > index 0000000000..ad77e65f9b > --- /dev/null > +++ b/sysdeps/unix/sysv/linux/bits/group_member.h > @@ -0,0 +1,32 @@ > +/* group_member declaration > + Copyright (C) 2024 Free Software Foundation, Inc. > + This file is part of the GNU C Library. > + > + The GNU C Library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + The GNU C Library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with the GNU C Library; if not, see > + <https://www.gnu.org/licenses/>. */ > + > +#ifndef _UNISTD_H > +# error "Never use <bits/group_member.h> directly; include <unistd.h> instead." > +#endif > + > +#ifndef _BITS_GROUP_MEMBER_H > +# define _BITS_GROUP_MEMBER_H 1 > + > +# ifdef __USE_GNU > +/* Return nonzero iff the calling process is in group GID. Deprecated */ > +extern int group_member (__gid_t __gid) __THROW > + __attribute_deprecated_msg__ ("may overflow the stack"); > +# endif > + > +#endif /* _BITS_GROUP_MEMBER_H */ > diff --git a/sysdeps/unix/sysv/linux/faccessat.c b/sysdeps/unix/sysv/linux/faccessat.c > index 2fa57fd63d..03232103b4 100644 > --- a/sysdeps/unix/sysv/linux/faccessat.c > +++ b/sysdeps/unix/sysv/linux/faccessat.c > @@ -59,15 +59,24 @@ __faccessat (int fd, const char *file, int mode, int flag) > || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) > return 0; > > - int granted = (uid == stats.st_uid > - ? (unsigned int) (stats.st_mode & (mode << 6)) >> 6 > - : (stats.st_gid == ((flag & AT_EACCESS) > - ? __getegid () : __getgid ()) > - || __group_member (stats.st_gid)) > - ? (unsigned int) (stats.st_mode & (mode << 3)) >> 3 > - : (stats.st_mode & mode)); > - > - if (granted == mode) > + int shift; > + > + if (uid == stats.st_uid) > + shift = 6; > + else > + { > + int is_group_member = (stats.st_gid > + == (flag & AT_EACCESS ? __getegid () : __getgid ())); > + if (!is_group_member) > + { > + is_group_member = __group_member2 (stats.st_gid); > + if (is_group_member < 0) > + return is_group_member; > + } > + shift = is_group_member ? 3 : 0; > + } > + > + if ((stats.st_mode >> shift & mode) == mode) > return 0; > > return INLINE_SYSCALL_ERROR_RETURN_VALUE (EACCES); > -- > 2.44.0 >
diff --git a/NEWS b/NEWS index da4b2223e9..82e1c43306 100644 --- a/NEWS +++ b/NEWS @@ -141,6 +141,10 @@ Deprecated and removed features, and other changes affecting compatibility: * The ia64*-*-linux-gnu configurations are no longer supported. +* Deprecated group_member on Linux as it uses alloca to allocate a large + buffer and has no capability for indicating failure for other memory + allocations. + Changes to build and runtime requirements: * Building on LoongArch requires at a minimum binutils 2.41 for vector diff --git a/bits/group_member.h b/bits/group_member.h new file mode 100644 index 0000000000..4ec1ef0813 --- /dev/null +++ b/bits/group_member.h @@ -0,0 +1,31 @@ +/* group_member declaration + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _UNISTD_H +# error "Never use <bits/group_member.h> directly; include <unistd.h> instead." +#endif + +#ifndef _BITS_GROUP_MEMBER_H +# define _BITS_GROUP_MEMBER_H 1 + +# ifdef __USE_GNU +/* Return nonzero iff the calling process is in group GID. */ +extern int group_member (__gid_t __gid) __THROW; +# endif + +#endif /* _BITS_GROUP_MEMBER_H */ diff --git a/include/unistd.h b/include/unistd.h index e241603b81..39d5bda372 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -131,6 +131,7 @@ extern __gid_t __getegid (void) attribute_hidden; extern int __getgroups (int __size, __gid_t __list[]) attribute_hidden; libc_hidden_proto (__getpgid) extern int __group_member (__gid_t __gid) attribute_hidden; +extern int __group_member2 (__gid_t __gid) attribute_hidden; extern int __setuid (__uid_t __uid); extern int __setreuid (__uid_t __ruid, __uid_t __euid); extern int __setgid (__gid_t __gid); diff --git a/posix/Makefile b/posix/Makefile index a1e84853a8..b71d6c8750 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -29,6 +29,7 @@ headers := \ bits/getopt_core.h \ bits/getopt_ext.h \ bits/getopt_posix.h \ + bits/group_member.h \ bits/local_lim.h \ bits/mman_ext.h \ bits/posix1_lim.h \ @@ -291,6 +292,7 @@ tests := \ tst-glob_symlinks \ tst-gnuglob \ tst-gnuglob64 \ + tst-group_member \ tst-mmap \ tst-mmap-offset \ tst-nanosleep \ @@ -479,6 +481,10 @@ tests-special += \ # tests-special endif +# This test calls __group_member2 directly, which is not exported from glibc. +tests-internal += tst-group_member2 +tests-static += tst-group_member2 + include ../Rules ifeq ($(run-built-tests),yes) @@ -606,6 +612,8 @@ bug-glob1-ARGS = "$(objpfx)" tst-execvp3-ARGS = --test-dir=$(objpfx) CFLAGS-tst-spawn3.c += -DOBJPFX=\"$(objpfx)\" +CFLAGS-tst-group_member.c += -Wno-error=deprecated-declarations + # Test voluntarily overflows struct dirent CFLAGS-bug-glob2.c += $(no-fortify-source) diff --git a/posix/group_member.c b/posix/group_member.c index 9d68f57a68..bb92f4d631 100644 --- a/posix/group_member.c +++ b/posix/group_member.c @@ -18,6 +18,7 @@ #include <sys/types.h> #include <unistd.h> +#include <scratch_buffer.h> #include <stdlib.h> #include <limits.h> @@ -47,3 +48,37 @@ __group_member (gid_t gid) return 0; } weak_alias (__group_member, group_member) + +int +__group_member2 (gid_t gid) +{ + int n; + gid_t *groups; + struct scratch_buffer sbuf; + scratch_buffer_init (&sbuf); + groups = sbuf.data; + + do + { + n = __getgroups (0, NULL); + if (n > sbuf.length) + { + if (!scratch_buffer_set_array_size (&sbuf, sizeof (*groups), n)) + return -1; + groups = sbuf.data; + } + + n = __getgroups (n, groups); + } + while (n > sbuf.length); + + while (n-- > 0) + if (groups[n] == gid) + { + scratch_buffer_free (&sbuf); + return 1; + } + + scratch_buffer_free (&sbuf); + return 0; +} diff --git a/posix/tst-group_member.c b/posix/tst-group_member.c new file mode 100644 index 0000000000..cc7f91618e --- /dev/null +++ b/posix/tst-group_member.c @@ -0,0 +1,41 @@ +/* Basic tests for group_member. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <alloca.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <support/check.h> + +static int do_test (void) +{ + int n; + gid_t *groups; + + n = getgroups (0, NULL); + groups = alloca (n * sizeof (*groups)); + n = getgroups (n, groups); + + while (n-- > 0) + TEST_COMPARE (1, group_member(groups[n])); + + return EXIT_SUCCESS; +} + +#include <support/test-driver.c> diff --git a/posix/tst-group_member2.c b/posix/tst-group_member2.c new file mode 100644 index 0000000000..8f86d5a1e9 --- /dev/null +++ b/posix/tst-group_member2.c @@ -0,0 +1,43 @@ +/* Basic tests for group_member. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <alloca.h> +#include <stdio.h> +#include <stdlib.h> +#include <posix/unistd.h> + +#include <support/check.h> + +extern int __group_member2 (__gid_t __gid); + +static int do_test (void) +{ + int n; + gid_t *groups; + + n = getgroups (0, NULL); + groups = alloca (n * sizeof (*groups)); + n = getgroups (n, groups); + + while (n-- > 0) + TEST_COMPARE (1, __group_member2(groups[n])); + + return EXIT_SUCCESS; +} + +#include <support/test-driver.c> diff --git a/posix/unistd.h b/posix/unistd.h index 54d7d7527e..411de1d6d4 100644 --- a/posix/unistd.h +++ b/posix/unistd.h @@ -710,10 +710,10 @@ extern __gid_t getegid (void) __THROW; of its supplementary groups in LIST and return the number written. */ extern int getgroups (int __size, __gid_t __list[]) __THROW __wur __fortified_attr_access (__write_only__, 2, 1); + #ifdef __USE_GNU -/* Return nonzero iff the calling process is in group GID. */ -extern int group_member (__gid_t __gid) __THROW; -#endif +# include <bits/group_member.h> +#endif /* Set the user ID of the calling process to UID. If the calling process is the super-user, set the real diff --git a/sysdeps/posix/euidaccess.c b/sysdeps/posix/euidaccess.c index 4c5c2220bd..da185dec3e 100644 --- a/sysdeps/posix/euidaccess.c +++ b/sysdeps/posix/euidaccess.c @@ -81,7 +81,7 @@ extern int errno; #ifdef _LIBC -# define group_member __group_member +# define group_member __group_member2 # define euidaccess __euidaccess #else @@ -120,7 +120,6 @@ int euidaccess (const char *path, int mode) { struct __stat64_t64 stats; - int granted; #ifdef _LIBC uid_t euid; @@ -167,15 +166,26 @@ euidaccess (const char *path, int mode) || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) return 0; + int shift; + if (euid == stats.st_uid) - granted = (unsigned int) (stats.st_mode & (mode << 6)) >> 6; - else if (egid == stats.st_gid || group_member (stats.st_gid)) - granted = (unsigned int) (stats.st_mode & (mode << 3)) >> 3; + shift = 6; else - granted = (stats.st_mode & mode); + { + int is_group_member = (egid == stats.st_gid); + if (!is_group_member) + { + is_group_member = group_member (stats.st_gid); + if (is_group_member < 0) + return is_group_member; + } + shift = is_group_member ? 3 : 0; + } + /* XXX Add support for ACLs. */ - if (granted == mode) + if ((stats.st_mode >> shift & mode) == mode) return 0; + __set_errno (EACCESS); return -1; } diff --git a/sysdeps/unix/sysv/linux/bits/group_member.h b/sysdeps/unix/sysv/linux/bits/group_member.h new file mode 100644 index 0000000000..ad77e65f9b --- /dev/null +++ b/sysdeps/unix/sysv/linux/bits/group_member.h @@ -0,0 +1,32 @@ +/* group_member declaration + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#ifndef _UNISTD_H +# error "Never use <bits/group_member.h> directly; include <unistd.h> instead." +#endif + +#ifndef _BITS_GROUP_MEMBER_H +# define _BITS_GROUP_MEMBER_H 1 + +# ifdef __USE_GNU +/* Return nonzero iff the calling process is in group GID. Deprecated */ +extern int group_member (__gid_t __gid) __THROW + __attribute_deprecated_msg__ ("may overflow the stack"); +# endif + +#endif /* _BITS_GROUP_MEMBER_H */ diff --git a/sysdeps/unix/sysv/linux/faccessat.c b/sysdeps/unix/sysv/linux/faccessat.c index 2fa57fd63d..03232103b4 100644 --- a/sysdeps/unix/sysv/linux/faccessat.c +++ b/sysdeps/unix/sysv/linux/faccessat.c @@ -59,15 +59,24 @@ __faccessat (int fd, const char *file, int mode, int flag) || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) return 0; - int granted = (uid == stats.st_uid - ? (unsigned int) (stats.st_mode & (mode << 6)) >> 6 - : (stats.st_gid == ((flag & AT_EACCESS) - ? __getegid () : __getgid ()) - || __group_member (stats.st_gid)) - ? (unsigned int) (stats.st_mode & (mode << 3)) >> 3 - : (stats.st_mode & mode)); - - if (granted == mode) + int shift; + + if (uid == stats.st_uid) + shift = 6; + else + { + int is_group_member = (stats.st_gid + == (flag & AT_EACCESS ? __getegid () : __getgid ())); + if (!is_group_member) + { + is_group_member = __group_member2 (stats.st_gid); + if (is_group_member < 0) + return is_group_member; + } + shift = is_group_member ? 3 : 0; + } + + if ((stats.st_mode >> shift & mode) == mode) return 0; return INLINE_SYSCALL_ERROR_RETURN_VALUE (EACCES);