| //===----------------------------------------------------------------------===// |
| // |
| // 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 getgrouplist. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #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/__support/CPP/array.h" |
| #include "src/__support/CPP/stringstream.h" |
| #include "src/grp/endgrent.h" |
| #include "src/grp/getgrent.h" |
| #include "src/grp/getgrouplist.h" |
| #include "src/grp/grp_utils.h" |
| #include "src/grp/setgrent.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, GetgrouplistSuccess) { |
| const char *content = "root:x:0:root\n" |
| "bin:x:1:bin,daemon\n" |
| "users:x:100:user1,user2\n" |
| "developers:x:200:user2,user3\n" |
| "admins:x:300:user1,user3\n"; |
| ScopedGroupFile test_file( |
| libc_make_test_file_path("getgrouplist_success.test"), content); |
| |
| gid_t groups[10]; |
| int ngroups = 10; |
| |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("user1", 1000, groups, &ngroups), |
| Succeeds(3)); |
| EXPECT_EQ(ngroups, 3); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(1000)); |
| EXPECT_EQ(groups[1], static_cast<gid_t>(100)); |
| EXPECT_EQ(groups[2], static_cast<gid_t>(300)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, BaseGroupAlreadyInDatabase) { |
| const char *content = "staff:x:50:user1\n" |
| "devel:x:60:user1\n"; |
| ScopedGroupFile test_file( |
| libc_make_test_file_path("getgrouplist_base_dup.test"), content); |
| |
| gid_t groups[10]; |
| int ngroups = 10; |
| |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("user1", 50, groups, &ngroups), |
| Succeeds(2)); |
| EXPECT_EQ(ngroups, 2); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(50)); |
| EXPECT_EQ(groups[1], static_cast<gid_t>(60)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, DuplicateGidInDatabase) { |
| const char *content = "team1:x:100:user1\n" |
| "team2:x:100:user1\n" |
| "team3:x:200:user1\n"; |
| ScopedGroupFile test_file( |
| libc_make_test_file_path("getgrouplist_dup_gid.test"), content); |
| |
| gid_t groups[10]; |
| int ngroups = 10; |
| |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("user1", 500, groups, &ngroups), |
| Succeeds(3)); |
| EXPECT_EQ(ngroups, 3); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(500)); |
| EXPECT_EQ(groups[1], static_cast<gid_t>(100)); |
| EXPECT_EQ(groups[2], static_cast<gid_t>(200)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, BufferTooSmall) { |
| const char *content = "users:x:100:user1\n" |
| "admins:x:200:user1\n"; |
| ScopedGroupFile test_file(libc_make_test_file_path("getgrouplist_small.test"), |
| content); |
| |
| gid_t groups[1] = {0}; |
| int ngroups = 1; |
| |
| EXPECT_EQ(LIBC_NAMESPACE::getgrouplist("user1", 10, groups, &ngroups), -1); |
| EXPECT_EQ(ngroups, 3); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(10)); |
| ASSERT_ERRNO_SUCCESS(); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, ZeroBufferQuery) { |
| const char *content = "users:x:100:user1\n" |
| "admins:x:200:user1\n"; |
| ScopedGroupFile test_file(libc_make_test_file_path("getgrouplist_zero.test"), |
| content); |
| |
| int ngroups = 0; |
| |
| EXPECT_EQ(LIBC_NAMESPACE::getgrouplist("user1", 10, nullptr, &ngroups), -1); |
| EXPECT_EQ(ngroups, 3); |
| ASSERT_ERRNO_SUCCESS(); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, UserNotFound) { |
| const char *content = "wheel:x:10:root\n"; |
| ScopedGroupFile test_file( |
| libc_make_test_file_path("getgrouplist_not_found.test"), content); |
| |
| gid_t groups[5]; |
| int ngroups = 5; |
| |
| ASSERT_THAT( |
| LIBC_NAMESPACE::getgrouplist("nonexistent", 1000, groups, &ngroups), |
| Succeeds(1)); |
| EXPECT_EQ(ngroups, 1); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(1000)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, NonexistentFile) { |
| LIBC_NAMESPACE::grp::TESTONLY_set_group_path( |
| libc_make_test_file_path("getgrouplist_missing.test")); |
| |
| gid_t groups[5]; |
| int ngroups = 5; |
| |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("anyuser", 1000, groups, &ngroups), |
| Succeeds(1)); |
| EXPECT_EQ(ngroups, 1); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(1000)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, DoesNotDisturbIteration) { |
| 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("getgrouplist_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"); |
| |
| gid_t groups[5]; |
| int ngroups = 5; |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("user2", 50, groups, &ngroups), |
| Succeeds(2)); |
| EXPECT_EQ(ngroups, 2); |
| |
| 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(); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, UserWithMoreThan32Groups) { |
| // Construct a group file containing 35 groups with testuser. |
| // This exercises dynamic reallocation in GidList. |
| LIBC_NAMESPACE::cpp::array<char, 2048> content; |
| LIBC_NAMESPACE::cpp::StringStream stream(content); |
| for (int i = 1; i <= 35; ++i) |
| stream << "grp" << i << ":x:" << (1000 + i) << ":testuser\n"; |
| stream << LIBC_NAMESPACE::cpp::StringStream::ENDS; |
| ASSERT_FALSE(stream.overflow()); |
| |
| ScopedGroupFile test_file(libc_make_test_file_path("getgrouplist_many.test"), |
| content.data()); |
| |
| gid_t groups[64]; |
| int ngroups = 64; |
| |
| ASSERT_THAT(LIBC_NAMESPACE::getgrouplist("testuser", 5000, groups, &ngroups), |
| Succeeds(36)); |
| EXPECT_EQ(ngroups, 36); |
| EXPECT_EQ(groups[0], static_cast<gid_t>(5000)); |
| for (int i = 1; i <= 35; ++i) |
| EXPECT_EQ(groups[i], static_cast<gid_t>(1000 + i)); |
| } |
| |
| TEST_F(LlvmLibcGrpTest, NegativeNgroupsReturnsEinval) { |
| int ngroups = -1; |
| gid_t groups[5]; |
| EXPECT_THAT(LIBC_NAMESPACE::getgrouplist("user", 100, groups, &ngroups), |
| Fails(EINVAL)); |
| } |
| |
| #if defined(LIBC_ADD_NULL_CHECKS) |
| TEST_F(LlvmLibcGrpTest, NullPointerCrash) { |
| int ngroups = 5; |
| gid_t groups[5]; |
| |
| ASSERT_DEATH( |
| [&] { LIBC_NAMESPACE::getgrouplist(nullptr, 100, groups, &ngroups); }, |
| WITH_SIGNAL(-1)); |
| ASSERT_DEATH( |
| [&] { LIBC_NAMESPACE::getgrouplist("user", 100, groups, nullptr); }, |
| WITH_SIGNAL(-1)); |
| ASSERT_DEATH( |
| [&] { LIBC_NAMESPACE::getgrouplist("user", 100, nullptr, &ngroups); }, |
| WITH_SIGNAL(-1)); |
| } |
| #endif // LIBC_ADD_NULL_CHECKS |