[libc-commits] [libc] [libc] Add setgroups and initgroups entrypoints (PR #227272)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Tue Sep 29 03:57:21 PDT 2026
https://github.com/kaladron created https://github.com/llvm/llvm-project/pull/227272
Add the setgroups and initgroups entrypoints from <grp.h> (GNU extensions).
A Linux syscall wrapper is added for setgroups, supporting both SYS_setgroups and SYS_setgroups32. The initgroups entrypoint initialises the supplementary group access list for a specified user by reading the group database using a scoped database stream and setting the resulting groups via setgroups.
Assisted-by: Automated tooling, human reviewed.
>From b0d9c71984e855a6c007a51fbbe62def1b4f8380 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Thu, 17 Sep 2026 16:18:12 +0100
Subject: [PATCH] [libc] Add setgroups and initgroups entrypoints
Add the setgroups and initgroups entrypoints from <grp.h> (GNU
extensions).
A Linux syscall wrapper is added for setgroups, supporting both
SYS_setgroups and SYS_setgroups32. The initgroups entrypoint
initialises the supplementary group access list for a specified user
by reading the group database using a scoped database stream and
setting the resulting groups via setgroups.
Assisted-by: Automated tooling, human reviewed.
---
libc/config/linux/aarch64/entrypoints.txt | 2 +
libc/config/linux/arm/entrypoints.txt | 2 +
libc/config/linux/i386/entrypoints.txt | 2 +
libc/config/linux/riscv/entrypoints.txt | 2 +
libc/config/linux/x86_64/entrypoints.txt | 2 +
libc/include/grp.yaml | 14 +++
.../linux/syscall_wrappers/CMakeLists.txt | 14 +++
.../OSUtil/linux/syscall_wrappers/setgroups.h | 43 ++++++++
libc/src/grp/CMakeLists.txt | 34 ++++++
libc/src/grp/grp_utils.cpp | 93 +++++++++-------
libc/src/grp/grp_utils.h | 5 +
libc/src/grp/initgroups.cpp | 35 ++++++
libc/src/grp/initgroups.h | 26 +++++
libc/src/grp/setgroups.cpp | 37 +++++++
libc/src/grp/setgroups.h | 27 +++++
libc/test/src/grp/CMakeLists.txt | 45 ++++++++
libc/test/src/grp/initgroups_test.cpp | 102 ++++++++++++++++++
libc/test/src/grp/setgroups_test.cpp | 48 +++++++++
18 files changed, 497 insertions(+), 36 deletions(-)
create mode 100644 libc/src/__support/OSUtil/linux/syscall_wrappers/setgroups.h
create mode 100644 libc/src/grp/initgroups.cpp
create mode 100644 libc/src/grp/initgroups.h
create mode 100644 libc/src/grp/setgroups.cpp
create mode 100644 libc/src/grp/setgroups.h
create mode 100644 libc/test/src/grp/initgroups_test.cpp
create mode 100644 libc/test/src/grp/setgroups_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 140f3dde5329c..9a59ec2b93dee 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -54,7 +54,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.grp.getgrouplist
libc.src.grp.getgrnam
libc.src.grp.getgrnam_r
+ libc.src.grp.initgroups
libc.src.grp.setgrent
+ libc.src.grp.setgroups
# poll.h entrypoints
libc.src.poll.poll
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index ed4267fa499cc..5f6f4ee9d558a 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -38,7 +38,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.grp.getgrouplist
libc.src.grp.getgrnam
libc.src.grp.getgrnam_r
+ libc.src.grp.initgroups
libc.src.grp.setgrent
+ libc.src.grp.setgroups
# poll.h entrypoints
libc.src.poll.poll
diff --git a/libc/config/linux/i386/entrypoints.txt b/libc/config/linux/i386/entrypoints.txt
index 9ad8e7f160064..b0562e4227344 100644
--- a/libc/config/linux/i386/entrypoints.txt
+++ b/libc/config/linux/i386/entrypoints.txt
@@ -10,7 +10,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.grp.getgrouplist
libc.src.grp.getgrnam
libc.src.grp.getgrnam_r
+ libc.src.grp.initgroups
libc.src.grp.setgrent
+ libc.src.grp.setgroups
# pwd.h entrypoints
libc.src.pwd.endpwent
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 7e7e90ff0e929..eded9fe955ccf 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -65,7 +65,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.grp.getgrouplist
libc.src.grp.getgrnam
libc.src.grp.getgrnam_r
+ libc.src.grp.initgroups
libc.src.grp.setgrent
+ libc.src.grp.setgroups
# net/if.h entrypoints
libc.src.net.if_indextoname
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e3facf92e81de..11c1093419ce8 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -65,7 +65,9 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.grp.getgrouplist
libc.src.grp.getgrnam
libc.src.grp.getgrnam_r
+ libc.src.grp.initgroups
libc.src.grp.setgrent
+ libc.src.grp.setgroups
# net/if.h entrypoints
libc.src.net.if_indextoname
diff --git a/libc/include/grp.yaml b/libc/include/grp.yaml
index 757c69d006533..a4c9cbc78bc75 100644
--- a/libc/include/grp.yaml
+++ b/libc/include/grp.yaml
@@ -59,8 +59,22 @@ functions:
- type: gid_t
- type: gid_t *
- type: int *
+ - name: initgroups
+ standards:
+ - gnu
+ return_type: int
+ arguments:
+ - type: const char *
+ - type: gid_t
- name: setgrent
standards:
- posix
return_type: void
arguments: []
+ - name: setgroups
+ standards:
+ - gnu
+ return_type: int
+ arguments:
+ - type: size_t
+ - type: const gid_t *
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
index 1af11116862c7..2a24b6b28cab0 100644
--- a/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/CMakeLists.txt
@@ -879,6 +879,20 @@ add_header_library(
libc.src.__support.OSUtil.osutil
)
+add_header_library(
+ setgroups
+ HDRS
+ setgroups.h
+ DEPENDS
+ libc.hdr.types.gid_t
+ libc.hdr.types.size_t
+ libc.include.sys_syscall
+ libc.src.__support.common
+ libc.src.__support.error_or
+ libc.src.__support.macros.config
+ libc.src.__support.OSUtil.osutil
+)
+
add_header_library(
setpgid
HDRS
diff --git a/libc/src/__support/OSUtil/linux/syscall_wrappers/setgroups.h b/libc/src/__support/OSUtil/linux/syscall_wrappers/setgroups.h
new file mode 100644
index 0000000000000..c316d64623fff
--- /dev/null
+++ b/libc/src/__support/OSUtil/linux/syscall_wrappers/setgroups.h
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Syscall wrapper for setgroups.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETGROUPS_H
+#define LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETGROUPS_H
+
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/OSUtil/linux/syscall.h" // syscall_checked
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h> // For syscall numbers
+
+namespace LIBC_NAMESPACE_DECL {
+namespace linux_syscalls {
+
+LIBC_INLINE ErrorOr<int> setgroups(size_t size, const gid_t *list) {
+ // Confusingly, 32-bit is the newer syscall, replacing the 16-bit legacy
+ // version.
+#if defined(SYS_setgroups32)
+ return syscall_checked<int>(SYS_setgroups32, size, list);
+#elif defined(SYS_setgroups)
+ return syscall_checked<int>(SYS_setgroups, size, list);
+#else
+#error "SYS_setgroups and SYS_setgroups32 syscalls not available."
+#endif
+}
+
+} // namespace linux_syscalls
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_OSUTIL_SYSCALL_WRAPPERS_SETGROUPS_H
diff --git a/libc/src/grp/CMakeLists.txt b/libc/src/grp/CMakeLists.txt
index 85ad797b00e24..f7a2c81eea181 100644
--- a/libc/src/grp/CMakeLists.txt
+++ b/libc/src/grp/CMakeLists.txt
@@ -116,6 +116,22 @@ add_entrypoint_object(
.grp_utils
)
+add_entrypoint_object(
+ initgroups
+ SRCS
+ initgroups.cpp
+ HDRS
+ initgroups.h
+ DEPENDS
+ libc.hdr.types.gid_t
+ libc.src.__support.common
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.errno.errno
+ .grp_utils
+)
+
add_entrypoint_object(
setgrent
SRCS
@@ -130,6 +146,23 @@ add_entrypoint_object(
.grp_utils
)
+add_entrypoint_object(
+ setgroups
+ SRCS
+ setgroups.cpp
+ HDRS
+ setgroups.h
+ DEPENDS
+ libc.hdr.types.gid_t
+ libc.hdr.types.size_t
+ libc.src.__support.OSUtil.linux.syscall_wrappers.setgroups
+ libc.src.__support.common
+ libc.src.__support.libc_errno
+ libc.src.__support.macros.config
+ libc.src.__support.macros.null_check
+ libc.src.errno.errno
+)
+
add_object_library(
grp_utils
HDRS
@@ -151,6 +184,7 @@ add_object_library(
libc.src.__support.CPP.string_view
libc.src.__support.File.file
libc.src.__support.File.platform_file
+ libc.src.__support.OSUtil.linux.syscall_wrappers.setgroups
libc.src.__support.ctype_utils
libc.src.__support.error_or
libc.src.__support.libc_assert
diff --git a/libc/src/grp/grp_utils.cpp b/libc/src/grp/grp_utils.cpp
index 771e4c0c721a4..31bcd9f601cb2 100644
--- a/libc/src/grp/grp_utils.cpp
+++ b/libc/src/grp/grp_utils.cpp
@@ -24,6 +24,7 @@
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/span.h"
#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/setgroups.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/error_or.h"
#include "src/__support/libc_assert.h"
@@ -266,12 +267,56 @@ class GidList {
[[nodiscard]] LIBC_INLINE size_t size() const { return count; }
+ [[nodiscard]] LIBC_INLINE const gid_t *data() const { return buf; }
+
[[nodiscard]] LIBC_INLINE gid_t operator[](size_t i) const {
LIBC_ASSERT(i < count);
return buf[i];
}
};
+ErrorOr<void> populate_gid_list(cpp::string_view user, gid_t group,
+ GidList &gid_list, const char *path) {
+ if (!gid_list.push_back(group))
+ return Error(ENOMEM);
+
+ pwd::ScopedFlatFileDatabase<struct group> local_db(path ? path
+ : group_file_path);
+ pwd::ScopedDynamicBuffer buffer;
+ struct group entry = {};
+
+ const auto open_res = local_db.setdb();
+ if (open_res.has_value()) {
+ while (true) {
+ const auto next_res = local_db.getnext(&entry, buffer);
+ if (!next_res.has_value()) {
+ if (next_res.error() == ENOMEM)
+ return Error(ENOMEM);
+ break;
+ }
+ if (!next_res.value())
+ break;
+
+ bool is_member = false;
+ if (entry.gr_mem) {
+ for (char **m = entry.gr_mem; *m != nullptr; ++m) {
+ if (cpp::string_view(*m) == user) {
+ is_member = true;
+ break;
+ }
+ }
+ }
+
+ if (is_member && !gid_list.contains(entry.gr_gid)) {
+ if (!gid_list.push_back(entry.gr_gid))
+ return Error(ENOMEM);
+ }
+ }
+ }
+
+ return {};
+}
+
} // namespace
void TESTONLY_set_group_path(const char *path) {
@@ -334,42 +379,9 @@ ErrorOr<size_t> get_group_list(cpp::string_view user, gid_t group,
gid_t *groups, size_t ngroups,
const char *path) {
GidList gid_list;
- if (!gid_list.push_back(group))
- return Error(ENOMEM);
-
- pwd::ScopedFlatFileDatabase<struct group> local_db(path ? path
- : group_file_path);
- pwd::ScopedDynamicBuffer buffer;
- struct group entry = {};
-
- const auto open_res = local_db.setdb();
- if (open_res.has_value()) {
- while (true) {
- const auto next_res = local_db.getnext(&entry, buffer);
- if (!next_res.has_value()) {
- if (next_res.error() == ENOMEM)
- return Error(ENOMEM);
- break;
- }
- if (!next_res.value())
- break;
-
- bool is_member = false;
- if (entry.gr_mem) {
- for (char **m = entry.gr_mem; *m != nullptr; ++m) {
- if (cpp::string_view(*m) == user) {
- is_member = true;
- break;
- }
- }
- }
-
- if (is_member && !gid_list.contains(entry.gr_gid)) {
- if (!gid_list.push_back(entry.gr_gid))
- return Error(ENOMEM);
- }
- }
- }
+ const auto res = populate_gid_list(user, group, gid_list, path);
+ if (!res.has_value())
+ return Error(res.error());
const size_t copy_count =
ngroups < gid_list.size() ? ngroups : gid_list.size();
@@ -379,5 +391,14 @@ ErrorOr<size_t> get_group_list(cpp::string_view user, gid_t group,
return gid_list.size();
}
+ErrorOr<int> init_groups(cpp::string_view user, gid_t group, const char *path) {
+ GidList gid_list;
+ const auto res = populate_gid_list(user, group, gid_list, path);
+ if (!res.has_value())
+ return Error(res.error());
+
+ return linux_syscalls::setgroups(gid_list.size(), gid_list.data());
+}
+
} // namespace grp
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/grp_utils.h b/libc/src/grp/grp_utils.h
index 70f85791ef79e..69ff3bb306dfb 100644
--- a/libc/src/grp/grp_utils.h
+++ b/libc/src/grp/grp_utils.h
@@ -61,6 +61,11 @@ ErrorOr<size_t> get_group_list(cpp::string_view user, gid_t group,
gid_t *groups, size_t ngroups,
const char *path = nullptr);
+// Sets the supplementary group access list for user, including the specified
+// group ID, by reading the group database.
+ErrorOr<int> init_groups(cpp::string_view user, gid_t group,
+ const char *path = nullptr);
+
} // namespace grp
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/initgroups.cpp b/libc/src/grp/initgroups.cpp
new file mode 100644
index 0000000000000..83211b81497b7
--- /dev/null
+++ b/libc/src/grp/initgroups.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of initgroups.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/grp/initgroups.h"
+#include "hdr/types/gid_t.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+#include "src/grp/grp_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, initgroups, (const char *user, gid_t group)) {
+ LIBC_CRASH_ON_NULLPTR(user);
+
+ const auto res = grp::init_groups(user, group);
+ if (!res.has_value()) {
+ libc_errno = res.error();
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/initgroups.h b/libc/src/grp/initgroups.h
new file mode 100644
index 0000000000000..9a7f98f0171f9
--- /dev/null
+++ b/libc/src/grp/initgroups.h
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header file for initgroups function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GRP_INITGROUPS_H
+#define LLVM_LIBC_SRC_GRP_INITGROUPS_H
+
+#include "hdr/types/gid_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int initgroups(const char *user, gid_t group);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_GRP_INITGROUPS_H
diff --git a/libc/src/grp/setgroups.cpp b/libc/src/grp/setgroups.cpp
new file mode 100644
index 0000000000000..098cdc5ad6315
--- /dev/null
+++ b/libc/src/grp/setgroups.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation of setgroups.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/grp/setgroups.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/OSUtil/linux/syscall_wrappers/setgroups.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/null_check.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, setgroups, (size_t size, const gid_t *list)) {
+ if (size > 0)
+ LIBC_CRASH_ON_NULLPTR(list);
+
+ const auto ret = linux_syscalls::setgroups(size, list);
+ if (!ret) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/grp/setgroups.h b/libc/src/grp/setgroups.h
new file mode 100644
index 0000000000000..0bcf502397a81
--- /dev/null
+++ b/libc/src/grp/setgroups.h
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Header file for setgroups function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_GRP_SETGROUPS_H
+#define LLVM_LIBC_SRC_GRP_SETGROUPS_H
+
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int setgroups(size_t size, const gid_t *list);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_GRP_SETGROUPS_H
diff --git a/libc/test/src/grp/CMakeLists.txt b/libc/test/src/grp/CMakeLists.txt
index 32c532c917a6d..e05586fd8bdc9 100644
--- a/libc/test/src/grp/CMakeLists.txt
+++ b/libc/test/src/grp/CMakeLists.txt
@@ -179,3 +179,48 @@ add_libc_test(
libc.test.UnitTest.ErrnoCheckingTest
libc.test.UnitTest.ErrnoSetterMatcher
)
+
+add_libc_test(
+ initgroups_test
+ SUITE
+ libc_grp_unittests
+ HDRS
+ grp_test_utils.h
+ SRCS
+ initgroups_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.gid_t
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_group
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+ libc.src.grp.endgrent
+ libc.src.grp.getgrent
+ libc.src.grp.grp_utils
+ libc.src.grp.initgroups
+ libc.src.grp.setgrent
+ libc.src.stdio.remove
+ libc.src.string.string_utils
+ libc.src.unistd.getuid
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
+add_libc_test(
+ setgroups_test
+ SUITE
+ libc_grp_unittests
+ SRCS
+ setgroups_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.gid_t
+ libc.hdr.types.size_t
+ libc.src.grp.setgroups
+ libc.src.unistd.getuid
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
diff --git a/libc/test/src/grp/initgroups_test.cpp b/libc/test/src/grp/initgroups_test.cpp
new file mode 100644
index 0000000000000..a024cab96fc05
--- /dev/null
+++ b/libc/test/src/grp/initgroups_test.cpp
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for initgroups.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_group.h"
+#include "src/grp/endgrent.h"
+#include "src/grp/getgrent.h"
+#include "src/grp/grp_utils.h"
+#include "src/grp/initgroups.h"
+#include "src/grp/setgrent.h"
+#include "src/unistd/getuid.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+#include "test/src/grp/grp_test_utils.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+TEST_F(LlvmLibcGrpTest, InitgroupsPrivilegeCheck) {
+ const char *content = "root:x:0:root\n"
+ "users:x:100:user1,user2\n"
+ "admins:x:300:user1\n";
+ ScopedGroupFile test_file(libc_make_test_file_path("initgroups_priv.test"),
+ content);
+
+ if (LIBC_NAMESPACE::getuid() == 0) {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("user1", 1000), Succeeds(0));
+ } else {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("user1", 1000), Fails(EPERM));
+ }
+}
+
+TEST_F(LlvmLibcGrpTest, InitgroupsNonexistentUser) {
+ const char *content = "wheel:x:10:root\n";
+ ScopedGroupFile test_file(
+ libc_make_test_file_path("initgroups_nonexist.test"), content);
+
+ if (LIBC_NAMESPACE::getuid() == 0) {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("nonexistent", 1000), Succeeds(0));
+ } else {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("nonexistent", 1000), Fails(EPERM));
+ }
+}
+
+TEST_F(LlvmLibcGrpTest, InitgroupsNonexistentFile) {
+ LIBC_NAMESPACE::grp::TESTONLY_set_group_path(
+ libc_make_test_file_path("initgroups_missing.test"));
+
+ if (LIBC_NAMESPACE::getuid() == 0) {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("anyuser", 1000), Succeeds(0));
+ } else {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("anyuser", 1000), Fails(EPERM));
+ }
+}
+
+TEST_F(LlvmLibcGrpTest, InitgroupsDoesNotDisturbIteration) {
+ const char *content = "group1:x:1:user1\n"
+ "group2:x:2:user2\n"
+ "group3:x:3:user1\n";
+ ScopedGroupFile test_file(libc_make_test_file_path("initgroups_iter.test"),
+ content);
+
+ LIBC_NAMESPACE::setgrent();
+ const auto first = LIBC_NAMESPACE::grp::read_next();
+ ASSERT_TRUE(first.has_value());
+ ASSERT_NE(first.value(), nullptr);
+ EXPECT_STREQ(first.value()->gr_name, "group1");
+
+ // initgroups reads the database using a scoped stream.
+ if (LIBC_NAMESPACE::getuid() == 0) {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("user2", 50), Succeeds(0));
+ } else {
+ EXPECT_THAT(LIBC_NAMESPACE::initgroups("user2", 50), Fails(EPERM));
+ }
+
+ const auto second = LIBC_NAMESPACE::grp::read_next();
+ ASSERT_TRUE(second.has_value());
+ ASSERT_NE(second.value(), nullptr);
+ EXPECT_STREQ(second.value()->gr_name, "group2");
+
+ LIBC_NAMESPACE::endgrent();
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST_F(LlvmLibcGrpTest, NullUserCrash) {
+ ASSERT_DEATH([] { LIBC_NAMESPACE::initgroups(nullptr, 1000); },
+ WITH_SIGNAL(-1));
+}
+#endif // LIBC_ADD_NULL_CHECKS
diff --git a/libc/test/src/grp/setgroups_test.cpp b/libc/test/src/grp/setgroups_test.cpp
new file mode 100644
index 0000000000000..c17f0029e2199
--- /dev/null
+++ b/libc/test/src/grp/setgroups_test.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unit tests for setgroups.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/signal_macros.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
+#include "src/grp/setgroups.h"
+#include "src/unistd/getuid.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::any_of;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+using LlvmLibcSetgroupsTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+TEST_F(LlvmLibcSetgroupsTest, InvalidSizeReturnsEinval) {
+ gid_t list[1] = {0};
+ EXPECT_THAT(LIBC_NAMESPACE::setgroups(static_cast<size_t>(-1), list),
+ Fails(any_of(EINVAL, EPERM)));
+}
+
+TEST_F(LlvmLibcSetgroupsTest, PrivilegeCheck) {
+ gid_t list[1] = {1000};
+ if (LIBC_NAMESPACE::getuid() == 0) {
+ EXPECT_THAT(LIBC_NAMESPACE::setgroups(1, list), Succeeds(0));
+ } else {
+ EXPECT_THAT(LIBC_NAMESPACE::setgroups(1, list), Fails(EPERM));
+ }
+}
+
+#if defined(LIBC_ADD_NULL_CHECKS)
+TEST_F(LlvmLibcSetgroupsTest, NullPointerCrash) {
+ ASSERT_DEATH([] { LIBC_NAMESPACE::setgroups(1, nullptr); }, WITH_SIGNAL(-1));
+}
+#endif // LIBC_ADD_NULL_CHECKS
More information about the libc-commits
mailing list