[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:53:04 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/4] [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/4] [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));
>From 11066ff487470380b51cc5cace8f3835ffeeed3a Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Thu, 3 Sep 2026 09:40:32 +0100
Subject: [PATCH 3/4] [libc] Fix ERANGE handling and test dependencies in pwd
(#220833)
Updated read_line to return ERANGE instead of EINVAL when buffer size
is less than 2. Added proxy type headers and missing CMake dependencies
in pwd unit tests, and added tests for single-byte buffer lookups.
Assisted-by: Automated tooling, human reviewed.
---
libc/src/pwd/flat_file_db.h | 4 +++-
libc/test/src/pwd/CMakeLists.txt | 3 +++
libc/test/src/pwd/getpwnam_r_test.cpp | 11 +++++++++++
libc/test/src/pwd/getpwuid_r_test.cpp | 10 ++++++++++
4 files changed, 27 insertions(+), 1 deletion(-)
diff --git a/libc/src/pwd/flat_file_db.h b/libc/src/pwd/flat_file_db.h
index 28e773089828c..a12b03c637a7a 100644
--- a/libc/src/pwd/flat_file_db.h
+++ b/libc/src/pwd/flat_file_db.h
@@ -52,8 +52,10 @@ template <typename EntryType> class FlatFileDatabase {
// fixed, bounded buffer without dynamic heap allocations or realloc.
LIBC_INLINE static ErrorOr<ReadLineResult> read_line(File *f,
cpp::span<char> buf) {
- if (!f || buf.size() < 2)
+ if (!f)
return Error(EINVAL);
+ if (buf.size() < 2)
+ return Error(ERANGE);
f->lock();
size_t bytes_read = 0;
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
index 634495c98a71c..ac9c79ffcbbd0 100644
--- a/libc/test/src/pwd/CMakeLists.txt
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -71,8 +71,10 @@ add_libc_test(
getpwnam_r_test.cpp
DEPENDS
libc.hdr.errno_macros
+ libc.hdr.types.gid_t
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.getpwnam_r
@@ -89,6 +91,7 @@ add_libc_test(
getpwuid_r_test.cpp
DEPENDS
libc.hdr.errno_macros
+ libc.hdr.types.gid_t
libc.hdr.types.size_t
libc.hdr.types.struct_passwd
libc.hdr.types.uid_t
diff --git a/libc/test/src/pwd/getpwnam_r_test.cpp b/libc/test/src/pwd/getpwnam_r_test.cpp
index 5aa5cb96b0555..29c9c2a151480 100644
--- a/libc/test/src/pwd/getpwnam_r_test.cpp
+++ b/libc/test/src/pwd/getpwnam_r_test.cpp
@@ -12,7 +12,10 @@
//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
#include "hdr/types/struct_passwd.h"
+#include "hdr/types/uid_t.h"
#include "src/__support/File/file.h"
#include "src/pwd/getpwnam_r.h"
#include "src/pwd/pwd_utils.h"
@@ -134,6 +137,14 @@ TEST_F(LlvmLibcGetpwnamRTest, BufferTooSmall) {
sizeof(small_buf), &result);
ASSERT_EQ(ret, ERANGE);
ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Single-byte buffer is insufficient and must return ERANGE.
+ char tiny_buf[1];
+ result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+ ret = LIBC_NAMESPACE::getpwnam_r("root", &pwd, tiny_buf, sizeof(tiny_buf),
+ &result);
+ ASSERT_EQ(ret, ERANGE);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
}
TEST_F(LlvmLibcGetpwnamRTest, InvalidArguments) {
diff --git a/libc/test/src/pwd/getpwuid_r_test.cpp b/libc/test/src/pwd/getpwuid_r_test.cpp
index 3a9876001ce30..3b08f49039cd0 100644
--- a/libc/test/src/pwd/getpwuid_r_test.cpp
+++ b/libc/test/src/pwd/getpwuid_r_test.cpp
@@ -12,6 +12,8 @@
//===----------------------------------------------------------------------===//
#include "hdr/errno_macros.h"
+#include "hdr/types/gid_t.h"
+#include "hdr/types/size_t.h"
#include "hdr/types/struct_passwd.h"
#include "hdr/types/uid_t.h"
#include "src/__support/File/file.h"
@@ -132,6 +134,14 @@ TEST_F(LlvmLibcGetpwuidRTest, BufferTooSmall) {
&result);
ASSERT_EQ(ret, ERANGE);
ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
+
+ // Single-byte buffer is insufficient and must return ERANGE.
+ char tiny_buf[1];
+ result = reinterpret_cast<struct passwd *>(0xdeadbeef);
+ ret =
+ LIBC_NAMESPACE::getpwuid_r(0, &pwd, tiny_buf, sizeof(tiny_buf), &result);
+ ASSERT_EQ(ret, ERANGE);
+ ASSERT_EQ(result, static_cast<struct passwd *>(nullptr));
}
TEST_F(LlvmLibcGetpwuidRTest, InvalidArguments) {
>From d1356a03313ce3ca2ae75a3aca25a13c6fc84e7b Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Thu, 3 Sep 2026 09:52:45 +0100
Subject: [PATCH 4/4] [libc] Use local database streams for reentrant pwd
lookups (#220833)
Instantiated a stack-local FlatFileDatabase in find_by_name and
find_by_uid, ensuring each reentrant lookup uses an independent file
stream. This provides MT-Safe thread isolation and prevents interference
with getpwent iteration cursors.
Assisted-by: Automated tooling, human reviewed.
---
libc/src/pwd/pwd_utils.cpp | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
index 540805e6cc090..e9c58e6a7d9f6 100644
--- a/libc/src/pwd/pwd_utils.cpp
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -43,6 +43,8 @@ ErrorOr<struct passwd> parse_passwd_line(char *line) {
namespace passwd {
+static const char *passwd_file_path = LIBC_COPT_PWD_FILE_PATH;
+
static LIBC_CONSTINIT pwd::FlatFileDatabase<struct passwd>
db(LIBC_COPT_PWD_FILE_PATH);
// Note: These static buffers are process-global and NOT protected by a mutex
@@ -50,7 +52,10 @@ static LIBC_CONSTINIT pwd::FlatFileDatabase<struct passwd>
static char line_buffer[1024];
static struct passwd pwd_entry;
-void TESTONLY_set_passwd_path(const char *path) { db.set_path(path); }
+void TESTONLY_set_passwd_path(const char *path) {
+ passwd_file_path = path;
+ db.set_path(path);
+}
ErrorOr<void> open() { return db.setdb(); }
@@ -67,18 +72,24 @@ ErrorOr<struct passwd *> read_next() {
ErrorOr<bool> find_by_name(cpp::string_view name, struct passwd &pwd,
cpp::span<char> buffer) {
+ pwd::FlatFileDatabase<struct passwd> local_db(passwd_file_path);
auto matcher = [name](const struct passwd &entry) {
return cpp::string_view(entry.pw_name) == name;
};
- return db.lookup(matcher, &pwd, buffer);
+ auto res = local_db.lookup(matcher, &pwd, buffer);
+ local_db.enddb();
+ return res;
}
ErrorOr<bool> find_by_uid(uid_t uid, struct passwd &pwd,
cpp::span<char> buffer) {
+ pwd::FlatFileDatabase<struct passwd> local_db(passwd_file_path);
auto matcher = [uid](const struct passwd &entry) {
return entry.pw_uid == uid;
};
- return db.lookup(matcher, &pwd, buffer);
+ auto res = local_db.lookup(matcher, &pwd, buffer);
+ local_db.enddb();
+ return res;
}
} // namespace passwd
More information about the libc-commits
mailing list