[libc-commits] [libc] [libc] Add getpwnam_r and getpwuid_r entrypoints (PR #220833)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Thu Sep 3 01:22:21 PDT 2026
https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/220833
>From 6209b30d35ae718fea04e3d675b3417b94329495 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Thu, 3 Sep 2026 08:32:01 +0100
Subject: [PATCH 1/2] [libc] Add getpwnam_r and getpwuid_r entrypoints
Added reentrant getpwnam_r and getpwuid_r functions using the
FlatFileDatabase lookup engine.
* Implemented getpwnam_r and getpwuid_r entrypoints
* Added find_by_name and find_by_uid lookups under namespace passwd
* Added function specifications to include/pwd.yaml
* Registered entrypoints in config/linux/*/entrypoints.txt
* Added unit tests in libc/test/src/pwd/getpwnam_r_test.cpp and
libc/test/src/pwd/getpwuid_r_test.cpp
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/pwd.yaml | 20 +++
libc/src/pwd/CMakeLists.txt | 33 +++++
libc/src/pwd/getpwnam_r.cpp | 44 ++++++
libc/src/pwd/getpwnam_r.h | 29 ++++
libc/src/pwd/getpwuid_r.cpp | 45 ++++++
libc/src/pwd/getpwuid_r.h | 30 ++++
libc/src/pwd/pwd_utils.cpp | 21 +++
libc/src/pwd/pwd_utils.h | 8 +
libc/test/src/pwd/CMakeLists.txt | 37 +++++
libc/test/src/pwd/getpwnam_r_test.cpp | 170 ++++++++++++++++++++++
libc/test/src/pwd/getpwuid_r_test.cpp | 162 +++++++++++++++++++++
16 files changed, 609 insertions(+)
create mode 100644 libc/src/pwd/getpwnam_r.cpp
create mode 100644 libc/src/pwd/getpwnam_r.h
create mode 100644 libc/src/pwd/getpwuid_r.cpp
create mode 100644 libc/src/pwd/getpwuid_r.h
create mode 100644 libc/test/src/pwd/getpwnam_r_test.cpp
create mode 100644 libc/test/src/pwd/getpwuid_r_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index fbd9239728bac..8b58c44e3ba28 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -48,6 +48,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# pwd.h entrypoints
libc.src.pwd.endpwent
libc.src.pwd.getpwent
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.getpwuid_r
libc.src.pwd.setpwent
# sched.h entrypoints
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 3a23742162e46..9368949d96f06 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -36,6 +36,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# pwd.h entrypoints
libc.src.pwd.endpwent
libc.src.pwd.getpwent
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.getpwuid_r
libc.src.pwd.setpwent
# string.h entrypoints
diff --git a/libc/config/linux/i386/entrypoints.txt b/libc/config/linux/i386/entrypoints.txt
index 3942289a2e5b2..1b84b0e7a0519 100644
--- a/libc/config/linux/i386/entrypoints.txt
+++ b/libc/config/linux/i386/entrypoints.txt
@@ -5,6 +5,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# pwd.h entrypoints
libc.src.pwd.endpwent
libc.src.pwd.getpwent
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.getpwuid_r
libc.src.pwd.setpwent
)
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index bd906c6900008..60b77b76a8f6c 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -67,6 +67,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# pwd.h entrypoints
libc.src.pwd.endpwent
libc.src.pwd.getpwent
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.getpwuid_r
libc.src.pwd.setpwent
# sched.h entrypoints
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index e6aeb3abe0672..e9e53e6239f6c 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -67,6 +67,8 @@ set(TARGET_LIBC_ENTRYPOINTS
# pwd.h entrypoints
libc.src.pwd.endpwent
libc.src.pwd.getpwent
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.getpwuid_r
libc.src.pwd.setpwent
# sched.h entrypoints
diff --git a/libc/include/pwd.yaml b/libc/include/pwd.yaml
index 40db5690e98b9..ef3656f3a1042 100644
--- a/libc/include/pwd.yaml
+++ b/libc/include/pwd.yaml
@@ -24,3 +24,23 @@ functions:
- posix
return_type: void
arguments: []
+ - name: getpwnam_r
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: const char *
+ - type: struct passwd *
+ - type: char *
+ - type: size_t
+ - type: struct passwd **
+ - name: getpwuid_r
+ standards:
+ - posix
+ return_type: int
+ arguments:
+ - type: uid_t
+ - type: struct passwd *
+ - type: char *
+ - type: size_t
+ - type: struct passwd **
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index 46aa2cb18d2d0..9ffb5d76c7e22 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -42,6 +42,39 @@ add_entrypoint_object(
.pwd_utils
)
+add_entrypoint_object(
+ getpwnam_r
+ SRCS
+ getpwnam_r.cpp
+ HDRS
+ getpwnam_r.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_passwd
+ libc.src.__support.CPP.span
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ .pwd_utils
+)
+
+add_entrypoint_object(
+ getpwuid_r
+ SRCS
+ getpwuid_r.cpp
+ HDRS
+ getpwuid_r.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_passwd
+ libc.hdr.types.uid_t
+ libc.src.__support.CPP.span
+ libc.src.__support.common
+ libc.src.__support.macros.config
+ .pwd_utils
+)
+
add_object_library(
pwd_utils
HDRS
diff --git a/libc/src/pwd/getpwnam_r.cpp b/libc/src/pwd/getpwnam_r.cpp
new file mode 100644
index 0000000000000..12f8f1a8892fc
--- /dev/null
+++ b/libc/src/pwd/getpwnam_r.cpp
@@ -0,0 +1,44 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwnam_r.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/getpwnam_r.h"
+#include "hdr/errno_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/common.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, getpwnam_r,
+ (const char *name, struct passwd *pwd, char *buffer,
+ size_t bufsize, struct passwd **result)) {
+ if (!result)
+ return EINVAL;
+ *result = nullptr;
+
+ if (!name || !pwd || !buffer || bufsize == 0)
+ return EINVAL;
+
+ auto res = passwd::find_by_name(name, pwd, cpp::span<char>(buffer, bufsize));
+ if (!res.has_value())
+ return res.error();
+
+ if (res.value())
+ *result = pwd;
+
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/getpwnam_r.h b/libc/src/pwd/getpwnam_r.h
new file mode 100644
index 0000000000000..74f7e3f9222ca
--- /dev/null
+++ b/libc/src/pwd/getpwnam_r.h
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwnam_r function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_GETPWNAM_R_H
+#define LLVM_LIBC_SRC_PWD_GETPWNAM_R_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Searches the password database for an entry with the matching username.
+int getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
+ size_t bufsize, struct passwd **result);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_GETPWNAM_R_H
diff --git a/libc/src/pwd/getpwuid_r.cpp b/libc/src/pwd/getpwuid_r.cpp
new file mode 100644
index 0000000000000..854b432339c12
--- /dev/null
+++ b/libc/src/pwd/getpwuid_r.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwuid_r.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/getpwuid_r.h"
+#include "hdr/errno_macros.h"
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_passwd.h"
+#include "hdr/types/uid_t.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/common.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, getpwuid_r,
+ (uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
+ struct passwd **result)) {
+ if (!result)
+ return EINVAL;
+ *result = nullptr;
+
+ if (!pwd || !buffer || bufsize == 0)
+ return EINVAL;
+
+ auto res = passwd::find_by_uid(uid, pwd, cpp::span<char>(buffer, bufsize));
+ if (!res.has_value())
+ return res.error();
+
+ if (res.value())
+ *result = pwd;
+
+ return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/getpwuid_r.h b/libc/src/pwd/getpwuid_r.h
new file mode 100644
index 0000000000000..b8ac51da566a9
--- /dev/null
+++ b/libc/src/pwd/getpwuid_r.h
@@ -0,0 +1,30 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwuid_r function.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_GETPWUID_R_H
+#define LLVM_LIBC_SRC_PWD_GETPWUID_R_H
+
+#include "hdr/types/size_t.h"
+#include "hdr/types/struct_passwd.h"
+#include "hdr/types/uid_t.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+// Searches the password database for an entry with the matching user ID.
+int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize,
+ struct passwd **result);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_GETPWUID_R_H
diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
index e85ced60620bd..31cae798886e1 100644
--- a/libc/src/pwd/pwd_utils.cpp
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -15,6 +15,7 @@
#include "hdr/errno_macros.h"
#include "hdr/types/struct_passwd.h"
#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/macros/attributes.h"
#include "src/pwd/flat_file_db.h"
#include "src/string/string_utils.h"
@@ -64,5 +65,25 @@ ErrorOr<struct passwd *> read_next() {
return &pwd_entry;
}
+ErrorOr<bool> find_by_name(const char *name, struct passwd *pwd,
+ cpp::span<char> buffer) {
+ if (!name || !pwd)
+ return Error(EINVAL);
+ auto matcher = [name](const struct passwd &entry) {
+ return cpp::string_view(entry.pw_name) == name;
+ };
+ return db.lookup(matcher, pwd, buffer);
+}
+
+ErrorOr<bool> find_by_uid(uid_t uid, struct passwd *pwd,
+ cpp::span<char> buffer) {
+ if (!pwd)
+ return Error(EINVAL);
+ auto matcher = [uid](const struct passwd &entry) {
+ return entry.pw_uid == uid;
+ };
+ return db.lookup(matcher, pwd, buffer);
+}
+
} // namespace passwd
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
index 4354f360e692c..2f3551be5d554 100644
--- a/libc/src/pwd/pwd_utils.h
+++ b/libc/src/pwd/pwd_utils.h
@@ -107,6 +107,14 @@ ErrorOr<void> close();
// Reads the next entry from the password database.
ErrorOr<struct passwd *> read_next();
+// Searches for a password entry matching the given username.
+ErrorOr<bool> find_by_name(const char *name, struct passwd *pwd,
+ cpp::span<char> buffer);
+
+// Searches for a password entry matching the given user ID.
+ErrorOr<bool> find_by_uid(uid_t uid, struct passwd *pwd,
+ cpp::span<char> buffer);
+
} // namespace passwd
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
index fb1e6e8ee5fce..634495c98a71c 100644
--- a/libc/test/src/pwd/CMakeLists.txt
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -62,3 +62,40 @@ add_libc_test(
libc.src.stdio.remove
libc.src.string.string_utils
)
+
+add_libc_test(
+ getpwnam_r_test
+ SUITE
+ libc_pwd_unittests
+ SRCS
+ getpwnam_r_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_passwd
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+ libc.src.pwd.getpwnam_r
+ libc.src.pwd.pwd_utils
+ libc.src.stdio.remove
+ libc.src.string.string_utils
+)
+
+add_libc_test(
+ getpwuid_r_test
+ SUITE
+ libc_pwd_unittests
+ SRCS
+ getpwuid_r_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.types.size_t
+ libc.hdr.types.struct_passwd
+ libc.hdr.types.uid_t
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
+ libc.src.pwd.getpwuid_r
+ libc.src.pwd.pwd_utils
+ libc.src.stdio.remove
+ libc.src.string.string_utils
+)
diff --git a/libc/test/src/pwd/getpwnam_r_test.cpp b/libc/test/src/pwd/getpwnam_r_test.cpp
new file mode 100644
index 0000000000000..a33a2bd28aaa5
--- /dev/null
+++ b/libc/test/src/pwd/getpwnam_r_test.cpp
@@ -0,0 +1,170 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwnam_r.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/File/file.h"
+#include "src/pwd/getpwnam_r.h"
+#include "src/pwd/pwd_utils.h"
+#include "src/stdio/remove.h"
+#include "src/string/string_utils.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+class HermeticFile {
+ char path[256];
+
+public:
+ HermeticFile(const char *file_path, const char *content) {
+ LIBC_NAMESPACE::internal::strlcpy(path, file_path, sizeof(path));
+
+ auto file_or = LIBC_NAMESPACE::openfile(path, "w");
+ if (file_or.has_value()) {
+ auto *f = file_or.value();
+ size_t len = LIBC_NAMESPACE::internal::string_length(content);
+ f->write(content, len);
+ f->close();
+ }
+ }
+
+ ~HermeticFile() { LIBC_NAMESPACE::remove(path); }
+
+ const char *get_path() const { return path; }
+};
+
+class LlvmLibcGetpwnamRTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
+
+} // namespace
+
+TEST_F(LlvmLibcGetpwnamRTest, Success) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n"
+ "bin:x:1:1:bin:/bin:/sbin/nologin\n"
+ "daemon:x:2:2:daemon:/sbin:/sbin/nologin\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwnam_r_success.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ int ret = LIBC_NAMESPACE::getpwnam_r("bin", &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "bin");
+ ASSERT_STREQ(pwd.pw_passwd, "x");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(1));
+ ASSERT_EQ(pwd.pw_gid, static_cast<gid_t>(1));
+ ASSERT_STREQ(pwd.pw_gecos, "bin");
+ ASSERT_STREQ(pwd.pw_dir, "/bin");
+ ASSERT_STREQ(pwd.pw_shell, "/sbin/nologin");
+}
+
+TEST_F(LlvmLibcGetpwnamRTest, FirstAndLastEntries) {
+ const char *content = "first:x:100:100:first:/home/first:/bin/sh\n"
+ "middle:x:101:101:middle:/home/middle:/bin/sh\n"
+ "last:x:102:102:last:/home/last:/bin/sh\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwnam_r_boundary.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ // Lookup the first entry
+ int ret = LIBC_NAMESPACE::getpwnam_r("first", &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "first");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(100));
+
+ // Lookup the last entry
+ result = nullptr;
+ ret = LIBC_NAMESPACE::getpwnam_r("last", &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "last");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(102));
+}
+
+TEST_F(LlvmLibcGetpwnamRTest, NotFound) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwnam_r_notfound.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+
+ int ret = LIBC_NAMESPACE::getpwnam_r("nonexistent", &pwd, buffer,
+ sizeof(buffer), &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
+
+TEST_F(LlvmLibcGetpwnamRTest, BufferTooSmall) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwnam_r_toosmall.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char small_buf[8];
+ struct passwd *result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+
+ int ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, small_buf,
+ sizeof(small_buf), &result);
+ ASSERT_EQ(ret, ERANGE);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
+
+TEST_F(LlvmLibcGetpwnamRTest, InvalidArguments) {
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ // Null result pointer
+ int ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, buffer, sizeof(buffer),
+ nullptr);
+ ASSERT_EQ(ret, EINVAL);
+
+ // Null name
+ ret = LIBC_NAMESPACE::getpwnam_r(nullptr, &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Null pwd pointer
+ ret = LIBC_NAMESPACE::getpwnam_r("root", nullptr, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Null buffer pointer
+ ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, nullptr, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Zero buffer size
+ ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, buffer, 0, &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
diff --git a/libc/test/src/pwd/getpwuid_r_test.cpp b/libc/test/src/pwd/getpwuid_r_test.cpp
new file mode 100644
index 0000000000000..711918ea2080e
--- /dev/null
+++ b/libc/test/src/pwd/getpwuid_r_test.cpp
@@ -0,0 +1,162 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwuid_r.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/types/struct_passwd.h"
+#include "hdr/types/uid_t.h"
+#include "src/__support/File/file.h"
+#include "src/pwd/getpwuid_r.h"
+#include "src/pwd/pwd_utils.h"
+#include "src/stdio/remove.h"
+#include "src/string/string_utils.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+namespace {
+
+class HermeticFile {
+ char path[256];
+
+public:
+ HermeticFile(const char *file_path, const char *content) {
+ LIBC_NAMESPACE::internal::strlcpy(path, file_path, sizeof(path));
+
+ auto file_or = LIBC_NAMESPACE::openfile(path, "w");
+ if (file_or.has_value()) {
+ auto *f = file_or.value();
+ size_t len = LIBC_NAMESPACE::internal::string_length(content);
+ f->write(content, len);
+ f->close();
+ }
+ }
+
+ ~HermeticFile() { LIBC_NAMESPACE::remove(path); }
+
+ const char *get_path() const { return path; }
+};
+
+class LlvmLibcGetpwuidRTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
+
+} // namespace
+
+TEST_F(LlvmLibcGetpwuidRTest, Success) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n"
+ "bin:x:1:1:bin:/bin:/sbin/nologin\n"
+ "daemon:x:2:2:daemon:/sbin:/sbin/nologin\n"
+ "nobody:x:65534:65534:nobody:/nonexistent:/bin/false\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwuid_r_success.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ int ret = LIBC_NAMESPACE::getpwuid_r(1, &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "bin");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(1));
+ ASSERT_EQ(pwd.pw_gid, static_cast<gid_t>(1));
+ ASSERT_STREQ(pwd.pw_dir, "/bin");
+ ASSERT_STREQ(pwd.pw_shell, "/sbin/nologin");
+
+ // Lookup high UID (nobody)
+ result = nullptr;
+ ret = LIBC_NAMESPACE::getpwuid_r(65534, &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "nobody");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(65534));
+}
+
+TEST_F(LlvmLibcGetpwuidRTest, RootUidZero) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n"
+ "bin:x:1:1:bin:/bin:/sbin/nologin\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwuid_r_zero.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ int ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, &pwd);
+ ASSERT_STREQ(pwd.pw_name, "root");
+ ASSERT_EQ(pwd.pw_uid, static_cast<uid_t>(0));
+}
+
+TEST_F(LlvmLibcGetpwuidRTest, NotFound) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwuid_r_notfound.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+
+ int ret = LIBC_NAMESPACE::getpwuid_r(999, &pwd, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, 0);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
+
+TEST_F(LlvmLibcGetpwuidRTest, BufferTooSmall) {
+ const char *content = "root:x:0:0:root:/root:/bin/bash\n";
+ HermeticFile test_file(libc_make_test_file_path("getpwuid_r_toosmall.test"),
+ content);
+ LIBC_NAMESPACE::passwd::TESTONLY_set_passwd_path(test_file.get_path());
+
+ struct passwd pwd;
+ char small_buf[8];
+ struct passwd *result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+
+ int ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, small_buf, sizeof(small_buf),
+ &result);
+ ASSERT_EQ(ret, ERANGE);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
+
+TEST_F(LlvmLibcGetpwuidRTest, InvalidArguments) {
+ struct passwd pwd;
+ char buffer[256];
+ struct passwd *result = nullptr;
+
+ // Null result pointer
+ int ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer),
+ nullptr);
+ ASSERT_EQ(ret, EINVAL);
+
+ // Null pwd pointer
+ ret = LIBC_NAMESPACE::getpwuid_r(0, nullptr, buffer, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Null buffer pointer
+ ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, nullptr, sizeof(buffer),
+ &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Zero buffer size
+ ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, 0, &result);
+ ASSERT_EQ(ret, EINVAL);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+}
>From 231a4579acde372199f66702b95cb2605f06afb4 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Thu, 3 Sep 2026 09:22:04 +0100
Subject: [PATCH 2/2] [libc][NFC] Use string_view and reference in pwd lookups
(#220833)
Updated find_by_name to take cpp::string_view for the lookup name.
Updated find_by_name and find_by_uid to take struct passwd by
reference instead of pointer, eliminating redundant null checks.
Assisted-by: Automated tooling, human reviewed.
---
libc/src/pwd/CMakeLists.txt | 1 +
libc/src/pwd/getpwnam_r.cpp | 3 ++-
libc/src/pwd/getpwuid_r.cpp | 2 +-
libc/src/pwd/pwd_utils.cpp | 12 ++++-------
libc/src/pwd/pwd_utils.h | 5 +++--
libc/test/src/pwd/getpwnam_r_test.cpp | 15 +++++++-------
libc/test/src/pwd/getpwuid_r_test.cpp | 29 +++++++++++++--------------
7 files changed, 33 insertions(+), 34 deletions(-)
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index 9ffb5d76c7e22..b12d4be1b6636 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -53,6 +53,7 @@ add_entrypoint_object(
libc.hdr.types.size_t
libc.hdr.types.struct_passwd
libc.src.__support.CPP.span
+ libc.src.__support.CPP.string_view
libc.src.__support.common
libc.src.__support.macros.config
.pwd_utils
diff --git a/libc/src/pwd/getpwnam_r.cpp b/libc/src/pwd/getpwnam_r.cpp
index 12f8f1a8892fc..539d6f11089bf 100644
--- a/libc/src/pwd/getpwnam_r.cpp
+++ b/libc/src/pwd/getpwnam_r.cpp
@@ -16,6 +16,7 @@
#include "hdr/types/size_t.h"
#include "hdr/types/struct_passwd.h"
#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/common.h"
#include "src/pwd/pwd_utils.h"
@@ -31,7 +32,7 @@ LLVM_LIBC_FUNCTION(int, getpwnam_r,
if (!name || !pwd || !buffer || bufsize == 0)
return EINVAL;
- auto res = passwd::find_by_name(name, pwd, cpp::span<char>(buffer, bufsize));
+ auto res = passwd::find_by_name(name, *pwd, cpp::span<char>(buffer, bufsize));
if (!res.has_value())
return res.error();
diff --git a/libc/src/pwd/getpwuid_r.cpp b/libc/src/pwd/getpwuid_r.cpp
index 854b432339c12..9986122356b2c 100644
--- a/libc/src/pwd/getpwuid_r.cpp
+++ b/libc/src/pwd/getpwuid_r.cpp
@@ -32,7 +32,7 @@ LLVM_LIBC_FUNCTION(int, getpwuid_r,
if (!pwd || !buffer || bufsize == 0)
return EINVAL;
- auto res = passwd::find_by_uid(uid, pwd, cpp::span<char>(buffer, bufsize));
+ auto res = passwd::find_by_uid(uid, *pwd, cpp::span<char>(buffer, bufsize));
if (!res.has_value())
return res.error();
diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
index 31cae798886e1..540805e6cc090 100644
--- a/libc/src/pwd/pwd_utils.cpp
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -65,24 +65,20 @@ ErrorOr<struct passwd *> read_next() {
return &pwd_entry;
}
-ErrorOr<bool> find_by_name(const char *name, struct passwd *pwd,
+ErrorOr<bool> find_by_name(cpp::string_view name, struct passwd &pwd,
cpp::span<char> buffer) {
- if (!name || !pwd)
- return Error(EINVAL);
auto matcher = [name](const struct passwd &entry) {
return cpp::string_view(entry.pw_name) == name;
};
- return db.lookup(matcher, pwd, buffer);
+ return db.lookup(matcher, &pwd, buffer);
}
-ErrorOr<bool> find_by_uid(uid_t uid, struct passwd *pwd,
+ErrorOr<bool> find_by_uid(uid_t uid, struct passwd &pwd,
cpp::span<char> buffer) {
- if (!pwd)
- return Error(EINVAL);
auto matcher = [uid](const struct passwd &entry) {
return entry.pw_uid == uid;
};
- return db.lookup(matcher, pwd, buffer);
+ return db.lookup(matcher, &pwd, buffer);
}
} // namespace passwd
diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
index 2f3551be5d554..4865f3f5a4d95 100644
--- a/libc/src/pwd/pwd_utils.h
+++ b/libc/src/pwd/pwd_utils.h
@@ -19,6 +19,7 @@
#include "hdr/types/struct_passwd.h"
#include "hdr/types/uid_t.h"
#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string_view.h"
#include "src/__support/ctype_utils.h"
#include "src/__support/error_or.h"
#include "src/__support/macros/attributes.h"
@@ -108,11 +109,11 @@ ErrorOr<void> close();
ErrorOr<struct passwd *> read_next();
// Searches for a password entry matching the given username.
-ErrorOr<bool> find_by_name(const char *name, struct passwd *pwd,
+ErrorOr<bool> find_by_name(cpp::string_view name, struct passwd &pwd,
cpp::span<char> buffer);
// Searches for a password entry matching the given user ID.
-ErrorOr<bool> find_by_uid(uid_t uid, struct passwd *pwd,
+ErrorOr<bool> find_by_uid(uid_t uid, struct passwd &pwd,
cpp::span<char> buffer);
} // namespace passwd
diff --git a/libc/test/src/pwd/getpwnam_r_test.cpp b/libc/test/src/pwd/getpwnam_r_test.cpp
index a33a2bd28aaa5..5aa5cb96b0555 100644
--- a/libc/test/src/pwd/getpwnam_r_test.cpp
+++ b/libc/test/src/pwd/getpwnam_r_test.cpp
@@ -44,7 +44,8 @@ class HermeticFile {
const char *get_path() const { return path; }
};
-class LlvmLibcGetpwnamRTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
+class LlvmLibcGetpwnamRTest
+ : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
} // namespace
@@ -60,8 +61,8 @@ TEST_F(LlvmLibcGetpwnamRTest, Success) {
char buffer[256];
struct passwd *result = nullptr;
- int ret = LIBC_NAMESPACE::getpwnam_r("bin", &pwd, buffer, sizeof(buffer),
- &result);
+ int ret =
+ LIBC_NAMESPACE::getpwnam_r("bin", &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, &pwd);
ASSERT_STREQ(pwd.pw_name, "bin");
@@ -95,8 +96,8 @@ TEST_F(LlvmLibcGetpwnamRTest, FirstAndLastEntries) {
// Lookup the last entry
result = nullptr;
- ret = LIBC_NAMESPACE::getpwnam_r("last", &pwd, buffer, sizeof(buffer),
- &result);
+ ret =
+ LIBC_NAMESPACE::getpwnam_r("last", &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, &pwd);
ASSERT_STREQ(pwd.pw_name, "last");
@@ -141,8 +142,8 @@ TEST_F(LlvmLibcGetpwnamRTest, InvalidArguments) {
struct passwd *result = nullptr;
// Null result pointer
- int ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, buffer, sizeof(buffer),
- nullptr);
+ int ret =
+ LIBC_NAMESPACE::getpwnam_r("root", &pwd, buffer, sizeof(buffer), nullptr);
ASSERT_EQ(ret, EINVAL);
// Null name
diff --git a/libc/test/src/pwd/getpwuid_r_test.cpp b/libc/test/src/pwd/getpwuid_r_test.cpp
index 711918ea2080e..3a9876001ce30 100644
--- a/libc/test/src/pwd/getpwuid_r_test.cpp
+++ b/libc/test/src/pwd/getpwuid_r_test.cpp
@@ -45,7 +45,8 @@ class HermeticFile {
const char *get_path() const { return path; }
};
-class LlvmLibcGetpwuidRTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
+class LlvmLibcGetpwuidRTest
+ : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {};
} // namespace
@@ -62,8 +63,8 @@ TEST_F(LlvmLibcGetpwuidRTest, Success) {
char buffer[256];
struct passwd *result = nullptr;
- int ret = LIBC_NAMESPACE::getpwuid_r(1, &pwd, buffer, sizeof(buffer),
- &result);
+ int ret =
+ LIBC_NAMESPACE::getpwuid_r(1, &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, &pwd);
ASSERT_STREQ(pwd.pw_name, "bin");
@@ -74,8 +75,8 @@ TEST_F(LlvmLibcGetpwuidRTest, Success) {
// Lookup high UID (nobody)
result = nullptr;
- ret = LIBC_NAMESPACE::getpwuid_r(65534, &pwd, buffer, sizeof(buffer),
- &result);
+ ret =
+ LIBC_NAMESPACE::getpwuid_r(65534, &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, &pwd);
ASSERT_STREQ(pwd.pw_name, "nobody");
@@ -93,8 +94,8 @@ TEST_F(LlvmLibcGetpwuidRTest, RootUidZero) {
char buffer[256];
struct passwd *result = nullptr;
- int ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer),
- &result);
+ int ret =
+ LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, &pwd);
ASSERT_STREQ(pwd.pw_name, "root");
@@ -111,8 +112,8 @@ TEST_F(LlvmLibcGetpwuidRTest, NotFound) {
char buffer[256];
struct passwd *result = reinterpret_cast<struct passwd *>(0xdeadbeef);
- int ret = LIBC_NAMESPACE::getpwuid_r(999, &pwd, buffer, sizeof(buffer),
- &result);
+ int ret =
+ LIBC_NAMESPACE::getpwuid_r(999, &pwd, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, 0);
ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
}
@@ -139,19 +140,17 @@ TEST_F(LlvmLibcGetpwuidRTest, InvalidArguments) {
struct passwd *result = nullptr;
// Null result pointer
- int ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer),
- nullptr);
+ int ret =
+ LIBC_NAMESPACE::getpwuid_r(0, &pwd, buffer, sizeof(buffer), nullptr);
ASSERT_EQ(ret, EINVAL);
// Null pwd pointer
- ret = LIBC_NAMESPACE::getpwuid_r(0, nullptr, buffer, sizeof(buffer),
- &result);
+ ret = LIBC_NAMESPACE::getpwuid_r(0, nullptr, buffer, sizeof(buffer), &result);
ASSERT_EQ(ret, EINVAL);
ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
// Null buffer pointer
- ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, nullptr, sizeof(buffer),
- &result);
+ ret = LIBC_NAMESPACE::getpwuid_r(0, &pwd, nullptr, sizeof(buffer), &result);
ASSERT_EQ(ret, EINVAL);
ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
More information about the libc-commits
mailing list