[libc-commits] [libc] [libc] Add getpwent, setpwent, and endpwent functions (PR #206064)
Jeff Bailey via libc-commits
libc-commits at lists.llvm.org
Tue Jul 14 06:36:47 PDT 2026
https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/206064
>From 07bfd00ffd3b8b55c275c365f1442c1405b6be90 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 26 Jun 2026 12:42:06 +0100
Subject: [PATCH 1/5] [libc] Add getpwent, setpwent, and endpwent functions
Implemented `getpwent`, `setpwent`, and `endpwent` functions for
accessing the password database.
* Added `struct passwd` definition in `llvm-libc-types`.
* Added `pwd.yaml` interface definition with POSIX standards.
* Implemented `getpwent`, `setpwent`, and `endpwent` entrypoints.
* Implemented `getpwent` to robustly skip malformed lines by looping.
* Implemented passwd line parsing in an internal helper
`parse_passwd_line` in `pwd_utils`.
* Used `strtointeger` for robust parsing of UID and GID fields.
* Added `struct_passwd` proxy header to avoid direct system includes.
* Centrally declared internal implementation helpers in `pwd_utils.h`.
* Added unit tests for `getpwent` and `setpwent` / `endpwent` behaviour.
Assisted-by: Automated tooling, human reviewed.
---
libc/config/linux/aarch64/entrypoints.txt | 5 ++
libc/config/linux/arm/entrypoints.txt | 5 ++
libc/config/linux/riscv/entrypoints.txt | 5 ++
libc/config/linux/x86_64/entrypoints.txt | 5 ++
libc/hdr/types/CMakeLists.txt | 8 +++
libc/hdr/types/struct_passwd.h | 27 +++++++
libc/include/CMakeLists.txt | 12 ++++
libc/include/llvm-libc-types/CMakeLists.txt | 5 ++
libc/include/llvm-libc-types/struct_passwd.h | 30 ++++++++
libc/include/pwd.yaml | 27 +++++++
libc/src/CMakeLists.txt | 1 +
libc/src/pwd/CMakeLists.txt | 62 ++++++++++++++++
libc/src/pwd/endpwent.cpp | 23 ++++++
libc/src/pwd/endpwent.h | 25 +++++++
libc/src/pwd/getpwent.cpp | 65 +++++++++++++++++
libc/src/pwd/getpwent.h | 26 +++++++
libc/src/pwd/pwd_utils.cpp | 75 ++++++++++++++++++++
libc/src/pwd/pwd_utils.h | 32 +++++++++
libc/src/pwd/setpwent.cpp | 23 ++++++
libc/src/pwd/setpwent.h | 25 +++++++
libc/test/src/CMakeLists.txt | 1 +
libc/test/src/pwd/CMakeLists.txt | 22 ++++++
libc/test/src/pwd/getpwent_test.cpp | 66 +++++++++++++++++
23 files changed, 575 insertions(+)
create mode 100644 libc/hdr/types/struct_passwd.h
create mode 100644 libc/include/llvm-libc-types/struct_passwd.h
create mode 100644 libc/include/pwd.yaml
create mode 100644 libc/src/pwd/CMakeLists.txt
create mode 100644 libc/src/pwd/endpwent.cpp
create mode 100644 libc/src/pwd/endpwent.h
create mode 100644 libc/src/pwd/getpwent.cpp
create mode 100644 libc/src/pwd/getpwent.h
create mode 100644 libc/src/pwd/pwd_utils.cpp
create mode 100644 libc/src/pwd/pwd_utils.h
create mode 100644 libc/src/pwd/setpwent.cpp
create mode 100644 libc/src/pwd/setpwent.h
create mode 100644 libc/test/src/pwd/CMakeLists.txt
create mode 100644 libc/test/src/pwd/getpwent_test.cpp
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 97625b28a2555..cd33bbb044182 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -45,6 +45,11 @@ set(TARGET_LIBC_ENTRYPOINTS
# poll.h entrypoints
libc.src.poll.poll
+ # pwd.h entrypoints
+ libc.src.pwd.endpwent
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+
# sched.h entrypoints
libc.src.sched.sched_get_priority_max
libc.src.sched.sched_get_priority_min
diff --git a/libc/config/linux/arm/entrypoints.txt b/libc/config/linux/arm/entrypoints.txt
index 1986d6a5347dc..f80a0acaf7c10 100644
--- a/libc/config/linux/arm/entrypoints.txt
+++ b/libc/config/linux/arm/entrypoints.txt
@@ -33,6 +33,11 @@ set(TARGET_LIBC_ENTRYPOINTS
# poll.h entrypoints
libc.src.poll.poll
+ # pwd.h entrypoints
+ libc.src.pwd.endpwent
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+
# string.h entrypoints
libc.src.string.memccpy
libc.src.string.memchr
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index 4395484968baa..f006015a9408c 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -63,6 +63,11 @@ set(TARGET_LIBC_ENTRYPOINTS
# poll.h entrypoints
libc.src.poll.poll
+ # pwd.h entrypoints
+ libc.src.pwd.endpwent
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+
# sched.h entrypoints
libc.src.sched.getcpu
libc.src.sched.sched_get_priority_max
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 1d00b995c85d7..a5cbe1aa692a0 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -63,6 +63,11 @@ set(TARGET_LIBC_ENTRYPOINTS
# poll.h entrypoints
libc.src.poll.poll
+ # pwd.h entrypoints
+ libc.src.pwd.endpwent
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+
# sched.h entrypoints
libc.src.sched.getcpu
libc.src.sched.sched_get_priority_max
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 5a6b17ef67f2d..f21f1c7fd2c0b 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -1173,3 +1173,11 @@ add_proxy_header_library(
FULL_BUILD_DEPENDS
libc.include.llvm-libc-types.regmatch_t
)
+
+add_proxy_header_library(
+ struct_passwd
+ HDRS
+ struct_passwd.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-types.struct_passwd
+)
diff --git a/libc/hdr/types/struct_passwd.h b/libc/hdr/types/struct_passwd.h
new file mode 100644
index 0000000000000..e55667e7172d2
--- /dev/null
+++ b/libc/hdr/types/struct_passwd.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
+/// Proxy for struct passwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
+#define LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/struct_passwd.h"
+
+#else
+
+#include <pwd.h>
+
+#endif // LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index b856097c6bd3c..b2aaee78a6ec2 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -1059,6 +1059,18 @@ add_header_macro(
.llvm-libc-types.nl_catd
)
+add_header_macro(
+ pwd
+ ../libc/include/pwd.yaml
+ pwd.h
+ DEPENDS
+ .llvm_libc_common_h
+ .llvm-libc-types.struct_passwd
+ .llvm-libc-types.uid_t
+ .llvm-libc-types.gid_t
+ .llvm-libc-types.size_t
+)
+
# UEFI spec references "Uefi.h" so we use that name for compatibility
add_header_macro(
uefi
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index 5ea8ca0634834..bc6d0e98645fc 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -179,6 +179,11 @@ add_header(
.dev_t .ino_t .mode_t .nlink_t .uid_t .gid_t .off_t .struct_timespec
.blksize_t .blkcnt_t
)
+add_header(
+ struct_passwd
+ HDR struct_passwd.h
+ DEPENDS .gid_t .uid_t
+)
add_header(struct_tm HDR struct_tm.h)
add_header(struct_utsname HDR struct_utsname.h)
add_header(thrd_start_t HDR thrd_start_t.h)
diff --git a/libc/include/llvm-libc-types/struct_passwd.h b/libc/include/llvm-libc-types/struct_passwd.h
new file mode 100644
index 0000000000000..7b41be6edbbe0
--- /dev/null
+++ b/libc/include/llvm-libc-types/struct_passwd.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
+/// Definition of struct passwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#define __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+
+#include "gid_t.h"
+#include "uid_t.h"
+
+struct passwd {
+ char *pw_name;
+ char *pw_passwd;
+ uid_t pw_uid;
+ gid_t pw_gid;
+ char *pw_gecos;
+ char *pw_dir;
+ char *pw_shell;
+};
+
+#endif // __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
diff --git a/libc/include/pwd.yaml b/libc/include/pwd.yaml
new file mode 100644
index 0000000000000..f8a9500937ed1
--- /dev/null
+++ b/libc/include/pwd.yaml
@@ -0,0 +1,27 @@
+header: pwd.h
+standards:
+ - posix
+macros: []
+types:
+ - type_name: struct_passwd
+ - type_name: uid_t
+ - type_name: gid_t
+ - type_name: size_t
+enums: []
+objects: []
+functions:
+ - name: getpwent
+ standards:
+ - posix
+ return_type: struct passwd *
+ arguments: []
+ - name: setpwent
+ standards:
+ - posix
+ return_type: void
+ arguments: []
+ - name: endpwent
+ standards:
+ - posix
+ return_type: void
+ arguments: []
diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt
index 2c7c4d00f150a..847183e2bf597 100644
--- a/libc/src/CMakeLists.txt
+++ b/libc/src/CMakeLists.txt
@@ -30,6 +30,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(err)
add_subdirectory(fcntl)
add_subdirectory(poll)
+ add_subdirectory(pwd)
add_subdirectory(pthread)
add_subdirectory(sched)
add_subdirectory(semaphore)
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
new file mode 100644
index 0000000000000..b808f59492e11
--- /dev/null
+++ b/libc/src/pwd/CMakeLists.txt
@@ -0,0 +1,62 @@
+#===-- CMakeLists.txt ----------------------------------------------------===#
+#
+# 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
+#
+#===----------------------------------------------------------------------===#
+
+add_object_library(
+ pwd_utils
+ SRCS
+ pwd_utils.cpp
+ HDRS
+ pwd_utils.h
+ DEPENDS
+ libc.hdr.types.struct_passwd
+ libc.src.__support.str_to_integer
+ libc.src.string.string_utils
+)
+
+add_entrypoint_object(
+ getpwent
+ SRCS
+ getpwent.cpp
+ HDRS
+ getpwent.h
+ DEPENDS
+ libc.include.pwd
+ libc.hdr.types.struct_passwd
+ libc.src.errno.errno
+ libc.src.stdio.fopen
+ libc.src.stdio.fclose
+ libc.src.stdio.fgets
+ libc.src.stdio.fseek
+ libc.src.string.string_utils
+ libc.hdr.stdio_macros
+ .pwd_utils
+)
+
+add_entrypoint_object(
+ setpwent
+ SRCS
+ setpwent.cpp
+ HDRS
+ setpwent.h
+ DEPENDS
+ libc.include.pwd
+ .getpwent
+ .pwd_utils
+)
+
+add_entrypoint_object(
+ endpwent
+ SRCS
+ endpwent.cpp
+ HDRS
+ endpwent.h
+ DEPENDS
+ libc.include.pwd
+ .getpwent
+ .pwd_utils
+)
diff --git a/libc/src/pwd/endpwent.cpp b/libc/src/pwd/endpwent.cpp
new file mode 100644
index 0000000000000..948eac2b72349
--- /dev/null
+++ b/libc/src/pwd/endpwent.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 endpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/endpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, endpwent, ()) { endpwent_impl(); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/endpwent.h b/libc/src/pwd/endpwent.h
new file mode 100644
index 0000000000000..b5a8262cc7d33
--- /dev/null
+++ b/libc/src/pwd/endpwent.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of endpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_ENDPWENT_H
+#define LLVM_LIBC_SRC_PWD_ENDPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+void endpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_ENDPWENT_H
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
new file mode 100644
index 0000000000000..83bbe1ade4137
--- /dev/null
+++ b/libc/src/pwd/getpwent.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/getpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+#include "src/stdio/fclose.h"
+#include "src/stdio/fgets.h"
+#include "src/stdio/fopen.h"
+#include "src/string/string_utils.h"
+
+#include "hdr/stdio_macros.h"
+#include "src/stdio/fseek.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static FILE *pwd_file = nullptr;
+static char line_buffer[1024];
+static struct passwd pwd_entry;
+
+void setpwent_impl() {
+ if (pwd_file)
+ LIBC_NAMESPACE::fseek(pwd_file, 0, SEEK_SET);
+}
+
+void endpwent_impl() {
+ if (pwd_file) {
+ LIBC_NAMESPACE::fclose(pwd_file);
+ pwd_file = nullptr;
+ }
+}
+
+LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
+ if (!pwd_file) {
+ pwd_file = LIBC_NAMESPACE::fopen("/etc/passwd", "r");
+ if (!pwd_file)
+ return nullptr;
+ }
+
+ while (LIBC_NAMESPACE::fgets(line_buffer, sizeof(line_buffer), pwd_file)) {
+ // Remove newline
+ size_t len = LIBC_NAMESPACE::internal::string_length(line_buffer);
+ if (len > 0 && line_buffer[len - 1] == '\n')
+ line_buffer[len - 1] = '\0';
+
+ if (internal::parse_passwd_line(line_buffer, &pwd_entry))
+ return &pwd_entry;
+ }
+
+ return nullptr;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/getpwent.h b/libc/src/pwd/getpwent.h
new file mode 100644
index 0000000000000..5e8e96b8f6ba1
--- /dev/null
+++ b/libc/src/pwd/getpwent.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
+/// Declarations of getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_GETPWENT_H
+#define LLVM_LIBC_SRC_PWD_GETPWENT_H
+
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+struct passwd *getpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_GETPWENT_H
diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
new file mode 100644
index 0000000000000..6a993c1129c5a
--- /dev/null
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Helper functions for pwd.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/pwd_utils.h"
+#include "src/__support/str_to_integer.h"
+#include "src/string/string_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+bool parse_passwd_line(char *line, struct passwd *pwd) {
+ if (!line || !pwd)
+ return false;
+
+ char *context = line;
+
+ pwd->pw_name =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_name)
+ return false;
+
+ pwd->pw_passwd =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_passwd)
+ return false;
+
+ char *uid_str =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!uid_str)
+ return false;
+ auto uid_res = LIBC_NAMESPACE::internal::strtointeger<uid_t>(uid_str, 10);
+ if (uid_res.has_error())
+ return false;
+ pwd->pw_uid = uid_res;
+
+ char *gid_str =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!gid_str)
+ return false;
+ auto gid_res = LIBC_NAMESPACE::internal::strtointeger<gid_t>(gid_str, 10);
+ if (gid_res.has_error())
+ return false;
+ pwd->pw_gid = gid_res;
+
+ pwd->pw_gecos =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_gecos)
+ return false;
+
+ pwd->pw_dir =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_dir)
+ return false;
+
+ // shell
+ pwd->pw_shell =
+ LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
+ if (!pwd->pw_shell)
+ return false;
+
+ return true;
+}
+
+} // namespace internal
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
new file mode 100644
index 0000000000000..2ff5b3211dbb6
--- /dev/null
+++ b/libc/src/pwd/pwd_utils.h
@@ -0,0 +1,32 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of helper functions for pwd.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_PWD_UTILS_H
+#define LLVM_LIBC_SRC_PWD_PWD_UTILS_H
+
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace internal {
+
+bool parse_passwd_line(char *line, struct passwd *pwd);
+
+} // namespace internal
+
+void setpwent_impl();
+void endpwent_impl();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_PWD_UTILS_H
diff --git a/libc/src/pwd/setpwent.cpp b/libc/src/pwd/setpwent.cpp
new file mode 100644
index 0000000000000..a4efcdacba2dc
--- /dev/null
+++ b/libc/src/pwd/setpwent.cpp
@@ -0,0 +1,23 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 setpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/setpwent.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/config.h"
+#include "src/pwd/pwd_utils.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(void, setpwent, ()) { setpwent_impl(); }
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/setpwent.h b/libc/src/pwd/setpwent.h
new file mode 100644
index 0000000000000..91cb263b41d90
--- /dev/null
+++ b/libc/src/pwd/setpwent.h
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Declarations of setpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_PWD_SETPWENT_H
+#define LLVM_LIBC_SRC_PWD_SETPWENT_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+void setpwent();
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PWD_SETPWENT_H
diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt
index 25a5c920036c3..5453c83744afa 100644
--- a/libc/test/src/CMakeLists.txt
+++ b/libc/test/src/CMakeLists.txt
@@ -89,6 +89,7 @@ if(${LIBC_TARGET_OS} STREQUAL "linux")
add_subdirectory(err)
add_subdirectory(fcntl)
add_subdirectory(poll)
+ add_subdirectory(pwd)
add_subdirectory(sched)
add_subdirectory(sys)
add_subdirectory(termios)
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
new file mode 100644
index 0000000000000..b5bf000443b38
--- /dev/null
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -0,0 +1,22 @@
+#===-- CMakeLists.txt ----------------------------------------------------===#
+#
+# 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
+#
+#===----------------------------------------------------------------------===#
+
+add_custom_target(libc_pwd_unittests)
+
+add_libc_unittest(
+ getpwent_test
+ SUITE
+ libc_pwd_unittests
+ SRCS
+ getpwent_test.cpp
+ DEPENDS
+ libc.src.pwd.getpwent
+ libc.src.pwd.setpwent
+ libc.src.pwd.endpwent
+ libc.src.string.strcmp
+)
diff --git a/libc/test/src/pwd/getpwent_test.cpp b/libc/test/src/pwd/getpwent_test.cpp
new file mode 100644
index 0000000000000..eabc09e502f69
--- /dev/null
+++ b/libc/test/src/pwd/getpwent_test.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Unittests for getpwent.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/pwd/endpwent.h"
+#include "src/pwd/getpwent.h"
+#include "src/pwd/setpwent.h"
+#include "src/string/strcmp.h"
+#include "test/UnitTest/Test.h"
+
+TEST(LlvmLibcPwdTest, GetPwentTest) {
+ // We assume /etc/passwd exists and contains at least "root" and "daemon".
+ // We also assume it does NOT contain "baduser".
+
+ bool found_root = false;
+ bool found_daemon = false;
+ bool found_baduser = false;
+
+ LIBC_NAMESPACE::setpwent();
+
+ struct passwd *pw;
+ while ((pw = LIBC_NAMESPACE::getpwent()) != nullptr) {
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "root") == 0) {
+ found_root = true;
+ ASSERT_EQ(pw->pw_uid, static_cast<uid_t>(0));
+ }
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "daemon") == 0) {
+ found_daemon = true;
+ }
+ if (LIBC_NAMESPACE::strcmp(pw->pw_name, "baduser") == 0) {
+ found_baduser = true;
+ }
+ }
+
+ ASSERT_TRUE(found_root);
+ ASSERT_TRUE(found_daemon);
+ ASSERT_FALSE(found_baduser);
+
+ LIBC_NAMESPACE::endpwent();
+}
+
+TEST(LlvmLibcPwdTest, SetPwentTest) {
+ // Read first entry
+ LIBC_NAMESPACE::setpwent();
+ struct passwd *pw1 = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw1 != nullptr);
+
+ // Rewind
+ LIBC_NAMESPACE::setpwent();
+ struct passwd *pw2 = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw2 != nullptr);
+
+ // Should be the same entry
+ ASSERT_STREQ(pw1->pw_name, pw2->pw_name);
+
+ LIBC_NAMESPACE::endpwent();
+}
>From 61a6cafe00c57b390ce68fed534d84a7c788567b Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 3 Jul 2026 16:17:34 +0100
Subject: [PATCH 2/5] [libc] Use internal File class in getpwent
Rewrite getpwent to use the internal File class instead of public
entrypoints (fopen, fgets, fclose, fseek). This resolves link errors
in overlay mode where these stdio entrypoints are not built.
Also update struct_passwd.h type header guard to remove double
underscores.
Assisted-by: Automated tooling, human reviewed.
---
libc/include/llvm-libc-types/struct_passwd.h | 6 +--
libc/src/pwd/CMakeLists.txt | 6 +--
libc/src/pwd/getpwent.cpp | 49 ++++++++++++++++----
3 files changed, 44 insertions(+), 17 deletions(-)
diff --git a/libc/include/llvm-libc-types/struct_passwd.h b/libc/include/llvm-libc-types/struct_passwd.h
index 7b41be6edbbe0..0f0baf743627b 100644
--- a/libc/include/llvm-libc-types/struct_passwd.h
+++ b/libc/include/llvm-libc-types/struct_passwd.h
@@ -11,8 +11,8 @@
///
//===----------------------------------------------------------------------===//
-#ifndef __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
-#define __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#ifndef LLVM_LIBC_TYPES_STRUCT_PASSWD_H
+#define LLVM_LIBC_TYPES_STRUCT_PASSWD_H
#include "gid_t.h"
#include "uid_t.h"
@@ -27,4 +27,4 @@ struct passwd {
char *pw_shell;
};
-#endif // __LLVM_LIBC_TYPES_STRUCT_PASSWD_H__
+#endif // LLVM_LIBC_TYPES_STRUCT_PASSWD_H
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index b808f59492e11..0d1706bd81d03 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -28,10 +28,8 @@ add_entrypoint_object(
libc.include.pwd
libc.hdr.types.struct_passwd
libc.src.errno.errno
- libc.src.stdio.fopen
- libc.src.stdio.fclose
- libc.src.stdio.fgets
- libc.src.stdio.fseek
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
libc.src.string.string_utils
libc.hdr.stdio_macros
.pwd_utils
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
index 83bbe1ade4137..7e6982101a6c2 100644
--- a/libc/src/pwd/getpwent.cpp
+++ b/libc/src/pwd/getpwent.cpp
@@ -12,44 +12,73 @@
//===----------------------------------------------------------------------===//
#include "src/pwd/getpwent.h"
+#include "src/__support/File/file.h"
#include "src/__support/common.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/pwd/pwd_utils.h"
-#include "src/stdio/fclose.h"
-#include "src/stdio/fgets.h"
-#include "src/stdio/fopen.h"
#include "src/string/string_utils.h"
#include "hdr/stdio_macros.h"
-#include "src/stdio/fseek.h"
namespace LIBC_NAMESPACE_DECL {
-static FILE *pwd_file = nullptr;
+static File *pwd_file = nullptr;
static char line_buffer[1024];
static struct passwd pwd_entry;
void setpwent_impl() {
if (pwd_file)
- LIBC_NAMESPACE::fseek(pwd_file, 0, SEEK_SET);
+ pwd_file->seek(0, SEEK_SET);
}
void endpwent_impl() {
if (pwd_file) {
- LIBC_NAMESPACE::fclose(pwd_file);
+ pwd_file->close();
pwd_file = nullptr;
}
}
+static bool read_line(File *f, char *buf, size_t max_len) {
+ if (max_len < 1)
+ return false;
+
+ unsigned char c = '\0';
+ f->lock();
+ size_t i = 0;
+ FileIOResult result(0);
+ for (; i < (max_len - 1) && c != '\n'; ++i) {
+ result = f->read_unlocked(&c, 1);
+ if (result.has_error()) {
+ libc_errno = result.error;
+ break;
+ }
+ if (result.value != 1)
+ break;
+ buf[i] = c;
+ }
+ bool has_error = f->error_unlocked();
+ bool has_eof = f->iseof_unlocked();
+ f->unlock();
+
+ if (has_error || (i == 0 && has_eof))
+ return false;
+
+ buf[i] = '\0';
+ return true;
+}
+
LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
if (!pwd_file) {
- pwd_file = LIBC_NAMESPACE::fopen("/etc/passwd", "r");
- if (!pwd_file)
+ auto result = LIBC_NAMESPACE::openfile("/etc/passwd", "r");
+ if (!result.has_value()) {
+ libc_errno = result.error();
return nullptr;
+ }
+ pwd_file = result.value();
}
- while (LIBC_NAMESPACE::fgets(line_buffer, sizeof(line_buffer), pwd_file)) {
+ while (read_line(pwd_file, line_buffer, sizeof(line_buffer))) {
// Remove newline
size_t len = LIBC_NAMESPACE::internal::string_length(line_buffer);
if (len > 0 && line_buffer[len - 1] == '\n')
>From 808f8289b574ff2df81a886c659cc1a64e644996 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 3 Jul 2026 17:43:10 +0100
Subject: [PATCH 3/5] [libc] Add comment explaining getpwent helper design
Explain why setpwent_impl and endpwent_impl are defined in getpwent.cpp
instead of directly in their entrypoint files.
Assisted-by: Automated tooling, human reviewed.
---
libc/src/pwd/getpwent.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
index 7e6982101a6c2..c275483d3b22f 100644
--- a/libc/src/pwd/getpwent.cpp
+++ b/libc/src/pwd/getpwent.cpp
@@ -27,6 +27,11 @@ static File *pwd_file = nullptr;
static char line_buffer[1024];
static struct passwd pwd_entry;
+// Helper implementations for setpwent and endpwent.
+// These are defined here because they need to access the static pwd_file state,
+// which is encapsulated in this file. The actual entrypoints are defined in
+// separate files (setpwent.cpp, endpwent.cpp) to comply with LLVM-libc's
+// one-function-per-file rule.
void setpwent_impl() {
if (pwd_file)
pwd_file->seek(0, SEEK_SET);
>From 459872f37ae3740231b4889e8127a5b132e67544 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Fri, 3 Jul 2026 17:54:31 +0100
Subject: [PATCH 4/5] [libc] Fix code review findings for pwd
Address review comments and improve implementation quality:
- Remove redundant comment in pwd_utils.cpp.
- Propagate errors from read_line helper and set errno at the edge in getpwent.
- Set errno on setpwent and endpwent failures by checking seek/close returns.
- Handle long lines (>1023 chars) in getpwent by truncating and discarding.
- Add set_passwd_path helper for hermetic testing.
- Rewrite getpwent_test to use hermetic test files and fix SetPwentTest bug.
- Add pwd_utils_test to directly test parse_passwd_line.
- Fix CMake dependencies for pwd targets and tests.
- Improve parse_passwd_line to strictly validate UID and GID fields.
Assisted-by: Automated tooling, human reviewed.
---
libc/src/pwd/CMakeLists.txt | 8 ++
libc/src/pwd/endpwent.cpp | 7 +-
libc/src/pwd/getpwent.cpp | 102 ++++++++++++++----
libc/src/pwd/pwd_utils.cpp | 11 +-
libc/src/pwd/pwd_utils.h | 5 +-
libc/src/pwd/setpwent.cpp | 7 +-
libc/test/src/pwd/CMakeLists.txt | 16 +++
libc/test/src/pwd/getpwent_test.cpp | 148 +++++++++++++++++++++------
libc/test/src/pwd/pwd_utils_test.cpp | 65 ++++++++++++
9 files changed, 313 insertions(+), 56 deletions(-)
create mode 100644 libc/test/src/pwd/pwd_utils_test.cpp
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index 0d1706bd81d03..87b363fea2e8f 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -28,6 +28,8 @@ add_entrypoint_object(
libc.include.pwd
libc.hdr.types.struct_passwd
libc.src.errno.errno
+ libc.src.__support.common
+ libc.src.__support.macros.config
libc.src.__support.File.file
libc.src.__support.File.platform_file
libc.src.string.string_utils
@@ -43,6 +45,9 @@ add_entrypoint_object(
setpwent.h
DEPENDS
libc.include.pwd
+ libc.src.errno.errno
+ libc.src.__support.common
+ libc.src.__support.macros.config
.getpwent
.pwd_utils
)
@@ -55,6 +60,9 @@ add_entrypoint_object(
endpwent.h
DEPENDS
libc.include.pwd
+ libc.src.errno.errno
+ libc.src.__support.common
+ libc.src.__support.macros.config
.getpwent
.pwd_utils
)
diff --git a/libc/src/pwd/endpwent.cpp b/libc/src/pwd/endpwent.cpp
index 948eac2b72349..948708a77a932 100644
--- a/libc/src/pwd/endpwent.cpp
+++ b/libc/src/pwd/endpwent.cpp
@@ -13,11 +13,16 @@
#include "src/pwd/endpwent.h"
#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/pwd/pwd_utils.h"
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(void, endpwent, ()) { endpwent_impl(); }
+LLVM_LIBC_FUNCTION(void, endpwent, ()) {
+ int result = endpwent_impl();
+ if (result != 0)
+ libc_errno = result;
+}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
index c275483d3b22f..d5d2958bf5ec9 100644
--- a/libc/src/pwd/getpwent.cpp
+++ b/libc/src/pwd/getpwent.cpp
@@ -21,61 +21,116 @@
#include "hdr/stdio_macros.h"
+#ifndef LIBC_COPT_PWD_FILE_PATH
+#define LIBC_COPT_PWD_FILE_PATH "/etc/passwd"
+#endif
+
namespace LIBC_NAMESPACE_DECL {
static File *pwd_file = nullptr;
+static const char *pwd_file_path = LIBC_COPT_PWD_FILE_PATH;
static char line_buffer[1024];
static struct passwd pwd_entry;
+namespace internal {
+void set_passwd_path(const char *path) {
+ if (pwd_file) {
+ pwd_file->close();
+ pwd_file = nullptr;
+ }
+ pwd_file_path = path;
+}
+} // namespace internal
+
// Helper implementations for setpwent and endpwent.
// These are defined here because they need to access the static pwd_file state,
// which is encapsulated in this file. The actual entrypoints are defined in
// separate files (setpwent.cpp, endpwent.cpp) to comply with LLVM-libc's
// one-function-per-file rule.
-void setpwent_impl() {
- if (pwd_file)
- pwd_file->seek(0, SEEK_SET);
+int setpwent_impl() {
+ if (!pwd_file) {
+ auto result = LIBC_NAMESPACE::openfile(pwd_file_path, "r");
+ if (!result.has_value())
+ return result.error();
+ pwd_file = result.value();
+ } else {
+ auto result = pwd_file->seek(0, SEEK_SET);
+ if (!result.has_value())
+ return result.error();
+ }
+ return 0;
}
-void endpwent_impl() {
+int endpwent_impl() {
if (pwd_file) {
- pwd_file->close();
+ int result = pwd_file->close();
pwd_file = nullptr;
+ return result;
}
+ return 0;
}
-static bool read_line(File *f, char *buf, size_t max_len) {
+struct ReadLineResult {
+ size_t bytes_read;
+ bool truncated;
+};
+
+static ErrorOr<ReadLineResult> read_line(File *f, char *buf, size_t max_len) {
if (max_len < 1)
- return false;
+ return Error(EINVAL);
unsigned char c = '\0';
f->lock();
size_t i = 0;
FileIOResult result(0);
- for (; i < (max_len - 1) && c != '\n'; ++i) {
+ bool truncated = false;
+ for (; i < (max_len - 1); ++i) {
result = f->read_unlocked(&c, 1);
if (result.has_error()) {
- libc_errno = result.error;
- break;
+ f->unlock();
+ return Error(result.error);
}
if (result.value != 1)
break;
buf[i] = c;
+ if (c == '\n') {
+ i++; // Include '\n' in bytes_read count
+ break;
+ }
}
+
+ // Check for truncation
+ if (i == max_len - 1 && c != '\n') {
+ truncated = true;
+ // Discard the rest of the line
+ while (true) {
+ result = f->read_unlocked(&c, 1);
+ if (result.has_error()) {
+ f->unlock();
+ return Error(result.error);
+ }
+ if (result.value != 1 || c == '\n')
+ break;
+ }
+ }
+
bool has_error = f->error_unlocked();
bool has_eof = f->iseof_unlocked();
f->unlock();
- if (has_error || (i == 0 && has_eof))
- return false;
+ if (has_error)
+ return Error(EIO);
+
+ if (i == 0 && has_eof)
+ return ReadLineResult{0, false};
buf[i] = '\0';
- return true;
+ return ReadLineResult{i, truncated};
}
LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
if (!pwd_file) {
- auto result = LIBC_NAMESPACE::openfile("/etc/passwd", "r");
+ auto result = LIBC_NAMESPACE::openfile(pwd_file_path, "r");
if (!result.has_value()) {
libc_errno = result.error();
return nullptr;
@@ -83,17 +138,28 @@ LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
pwd_file = result.value();
}
- while (read_line(pwd_file, line_buffer, sizeof(line_buffer))) {
+ while (true) {
+ auto result = read_line(pwd_file, line_buffer, sizeof(line_buffer));
+ if (!result.has_value()) {
+ libc_errno = result.error();
+ return nullptr;
+ }
+
+ ReadLineResult res = result.value();
+ if (res.bytes_read == 0)
+ return nullptr;
+
+ if (res.truncated)
+ continue;
+
// Remove newline
- size_t len = LIBC_NAMESPACE::internal::string_length(line_buffer);
+ size_t len = res.bytes_read;
if (len > 0 && line_buffer[len - 1] == '\n')
line_buffer[len - 1] = '\0';
if (internal::parse_passwd_line(line_buffer, &pwd_entry))
return &pwd_entry;
}
-
- return nullptr;
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp
index 6a993c1129c5a..0512f874b0ad9 100644
--- a/libc/src/pwd/pwd_utils.cpp
+++ b/libc/src/pwd/pwd_utils.cpp
@@ -39,18 +39,20 @@ bool parse_passwd_line(char *line, struct passwd *pwd) {
if (!uid_str)
return false;
auto uid_res = LIBC_NAMESPACE::internal::strtointeger<uid_t>(uid_str, 10);
- if (uid_res.has_error())
+ if (uid_res.has_error() || uid_res.parsed_len == 0 ||
+ uid_str[uid_res.parsed_len] != '\0')
return false;
- pwd->pw_uid = uid_res;
+ pwd->pw_uid = uid_res.value;
char *gid_str =
LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
if (!gid_str)
return false;
auto gid_res = LIBC_NAMESPACE::internal::strtointeger<gid_t>(gid_str, 10);
- if (gid_res.has_error())
+ if (gid_res.has_error() || gid_res.parsed_len == 0 ||
+ gid_str[gid_res.parsed_len] != '\0')
return false;
- pwd->pw_gid = gid_res;
+ pwd->pw_gid = gid_res.value;
pwd->pw_gecos =
LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
@@ -62,7 +64,6 @@ bool parse_passwd_line(char *line, struct passwd *pwd) {
if (!pwd->pw_dir)
return false;
- // shell
pwd->pw_shell =
LIBC_NAMESPACE::internal::string_token<false>(nullptr, ":", &context);
if (!pwd->pw_shell)
diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h
index 2ff5b3211dbb6..c7510e17f8506 100644
--- a/libc/src/pwd/pwd_utils.h
+++ b/libc/src/pwd/pwd_utils.h
@@ -21,11 +21,12 @@ namespace LIBC_NAMESPACE_DECL {
namespace internal {
bool parse_passwd_line(char *line, struct passwd *pwd);
+void set_passwd_path(const char *path);
} // namespace internal
-void setpwent_impl();
-void endpwent_impl();
+int setpwent_impl();
+int endpwent_impl();
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pwd/setpwent.cpp b/libc/src/pwd/setpwent.cpp
index a4efcdacba2dc..077bccf8087d1 100644
--- a/libc/src/pwd/setpwent.cpp
+++ b/libc/src/pwd/setpwent.cpp
@@ -13,11 +13,16 @@
#include "src/pwd/setpwent.h"
#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
#include "src/pwd/pwd_utils.h"
namespace LIBC_NAMESPACE_DECL {
-LLVM_LIBC_FUNCTION(void, setpwent, ()) { setpwent_impl(); }
+LLVM_LIBC_FUNCTION(void, setpwent, ()) {
+ int result = setpwent_impl();
+ if (result != 0)
+ libc_errno = result;
+}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
index b5bf000443b38..f21b8d3551189 100644
--- a/libc/test/src/pwd/CMakeLists.txt
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -15,8 +15,24 @@ add_libc_unittest(
SRCS
getpwent_test.cpp
DEPENDS
+ libc.include.pwd
+ libc.hdr.types.struct_passwd
libc.src.pwd.getpwent
libc.src.pwd.setpwent
libc.src.pwd.endpwent
+ libc.src.pwd.pwd_utils
+ libc.src.__support.File.file
+ libc.src.__support.File.platform_file
libc.src.string.strcmp
)
+
+add_libc_unittest(
+ pwd_utils_test
+ SUITE
+ libc_pwd_unittests
+ SRCS
+ pwd_utils_test.cpp
+ DEPENDS
+ libc.hdr.types.struct_passwd
+ libc.src.pwd.pwd_utils
+)
diff --git a/libc/test/src/pwd/getpwent_test.cpp b/libc/test/src/pwd/getpwent_test.cpp
index eabc09e502f69..8db17230fee30 100644
--- a/libc/test/src/pwd/getpwent_test.cpp
+++ b/libc/test/src/pwd/getpwent_test.cpp
@@ -11,56 +11,146 @@
///
//===----------------------------------------------------------------------===//
+#include "hdr/types/struct_passwd.h"
+#include "src/__support/File/file.h"
#include "src/pwd/endpwent.h"
#include "src/pwd/getpwent.h"
+#include "src/pwd/pwd_utils.h"
#include "src/pwd/setpwent.h"
-#include "src/string/strcmp.h"
+#include "test/UnitTest/LibcTest.h"
#include "test/UnitTest/Test.h"
-TEST(LlvmLibcPwdTest, GetPwentTest) {
- // We assume /etc/passwd exists and contains at least "root" and "daemon".
- // We also assume it does NOT contain "baduser".
+static const char *test_passwd_data =
+ "root:x:0:0:root:/root:/bin/bash\n"
+ "daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n"
+ "baduser:x:invalid:invalid:::\n" // Should be skipped due to parse error
+ "user2:x:3:3:user2:/home/user2:/bin/sh\n";
- bool found_root = false;
- bool found_daemon = false;
- bool found_baduser = false;
+static bool create_test_file(const char *path, const char *data) {
+ auto result = LIBC_NAMESPACE::openfile(path, "w");
+ if (!result.has_value())
+ return false;
+ auto f = result.value();
+ size_t len = 0;
+ while (data[len] != '\0')
+ len++;
+ auto write_result = f->write(data, len);
+ if (write_result.value != len) {
+ f->close();
+ return false;
+ }
+ f->close();
+ return true;
+}
- LIBC_NAMESPACE::setpwent();
+TEST(LlvmLibcPwdTest, GetPwentTestHermetic) {
+ auto TEST_FILE = libc_make_test_file_path("getpwent_hermetic.test");
+ ASSERT_TRUE(create_test_file(TEST_FILE, test_passwd_data));
- struct passwd *pw;
- while ((pw = LIBC_NAMESPACE::getpwent()) != nullptr) {
- if (LIBC_NAMESPACE::strcmp(pw->pw_name, "root") == 0) {
- found_root = true;
- ASSERT_EQ(pw->pw_uid, static_cast<uid_t>(0));
- }
- if (LIBC_NAMESPACE::strcmp(pw->pw_name, "daemon") == 0) {
- found_daemon = true;
- }
- if (LIBC_NAMESPACE::strcmp(pw->pw_name, "baduser") == 0) {
- found_baduser = true;
- }
- }
+ LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
+
+ // First entry: root
+ struct passwd *pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "root");
+ EXPECT_STREQ(pw->pw_passwd, "x");
+ EXPECT_EQ(pw->pw_uid, 0u);
+ EXPECT_EQ(pw->pw_gid, 0u);
+ EXPECT_STREQ(pw->pw_gecos, "root");
+ EXPECT_STREQ(pw->pw_dir, "/root");
+ EXPECT_STREQ(pw->pw_shell, "/bin/bash");
+
+ // Second entry: daemon (skip baduser)
+ pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "daemon");
+ EXPECT_EQ(pw->pw_uid, 1u);
+
+ // Third entry: user2 (skip baduser because of parse error)
+ pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "user2");
+ EXPECT_EQ(pw->pw_uid, 3u);
- ASSERT_TRUE(found_root);
- ASSERT_TRUE(found_daemon);
- ASSERT_FALSE(found_baduser);
+ // End of file
+ pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw == nullptr);
LIBC_NAMESPACE::endpwent();
}
-TEST(LlvmLibcPwdTest, SetPwentTest) {
+TEST(LlvmLibcPwdTest, SetPwentTestHermetic) {
+ auto TEST_FILE = libc_make_test_file_path("setpwent_hermetic.test");
+ ASSERT_TRUE(create_test_file(TEST_FILE, test_passwd_data));
+
+ LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
+
// Read first entry
LIBC_NAMESPACE::setpwent();
struct passwd *pw1 = LIBC_NAMESPACE::getpwent();
ASSERT_TRUE(pw1 != nullptr);
- // Rewind
- LIBC_NAMESPACE::setpwent();
+ // Copy pw1->pw_name
+ char pw1_name[256];
+ size_t i = 0;
+ for (; pw1->pw_name[i] != '\0' && i < sizeof(pw1_name) - 1; ++i) {
+ pw1_name[i] = pw1->pw_name[i];
+ }
+ pw1_name[i] = '\0';
+
+ // Read second entry
struct passwd *pw2 = LIBC_NAMESPACE::getpwent();
ASSERT_TRUE(pw2 != nullptr);
+ EXPECT_STRNE(pw1_name, pw2->pw_name); // Should be different
+
+ // Rewind
+ LIBC_NAMESPACE::setpwent();
+ struct passwd *pw3 = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw3 != nullptr);
+ EXPECT_STREQ(pw1_name, pw3->pw_name); // Should be first entry again
+
+ LIBC_NAMESPACE::endpwent();
+}
+
+TEST(LlvmLibcPwdTest, GetPwentTruncationTest) {
+ auto TEST_FILE = libc_make_test_file_path("getpwent_truncation.test");
+
+ char test_data[2048];
+ size_t idx = 0;
+
+ // "longuser:x:4:4:"
+ const char *prefix = "longuser:x:4:4:";
+ for (size_t i = 0; prefix[i] != '\0'; ++i)
+ test_data[idx++] = prefix[i];
+
+ // 1100 'a's (gecos field) - triggers truncation (>1023 chars)
+ for (int i = 0; i < 1100; ++i)
+ test_data[idx++] = 'a';
+
+ // ":/home/longuser:/bin/sh\n"
+ const char *suffix = ":/home/longuser:/bin/sh\n";
+ for (size_t i = 0; suffix[i] != '\0'; ++i)
+ test_data[idx++] = suffix[i];
+
+ // "nextuser:x:5:5:next:/home/next:/bin/sh\n"
+ const char *next = "nextuser:x:5:5:next:/home/next:/bin/sh\n";
+ for (size_t i = 0; next[i] != '\0'; ++i)
+ test_data[idx++] = next[i];
+
+ test_data[idx] = '\0';
+
+ ASSERT_TRUE(create_test_file(TEST_FILE, test_data));
+ LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
+
+ // The long line should be truncated, discarded, and skipped.
+ // We should directly get "nextuser".
+ struct passwd *pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "nextuser");
+ EXPECT_EQ(pw->pw_uid, 5u);
- // Should be the same entry
- ASSERT_STREQ(pw1->pw_name, pw2->pw_name);
+ pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw == nullptr);
LIBC_NAMESPACE::endpwent();
}
diff --git a/libc/test/src/pwd/pwd_utils_test.cpp b/libc/test/src/pwd/pwd_utils_test.cpp
new file mode 100644
index 0000000000000..f8e07cec83c95
--- /dev/null
+++ b/libc/test/src/pwd/pwd_utils_test.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+/// Unittests for parse_passwd_line.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/types/struct_passwd.h"
+#include "src/pwd/pwd_utils.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::internal::parse_passwd_line;
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_Success) {
+ char line[] = "root:x:0:0:root:/root:/bin/bash";
+ struct passwd pwd;
+ ASSERT_TRUE(parse_passwd_line(line, &pwd));
+ ASSERT_STREQ(pwd.pw_name, "root");
+ ASSERT_STREQ(pwd.pw_passwd, "x");
+ ASSERT_EQ(pwd.pw_uid, 0u);
+ ASSERT_EQ(pwd.pw_gid, 0u);
+ ASSERT_STREQ(pwd.pw_gecos, "root");
+ ASSERT_STREQ(pwd.pw_dir, "/root");
+ ASSERT_STREQ(pwd.pw_shell, "/bin/bash");
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_EmptyFields) {
+ char line[] = "root::0:0::/root:";
+ struct passwd pwd;
+ ASSERT_TRUE(parse_passwd_line(line, &pwd));
+ ASSERT_STREQ(pwd.pw_name, "root");
+ ASSERT_STREQ(pwd.pw_passwd, "");
+ ASSERT_EQ(pwd.pw_uid, 0u);
+ ASSERT_EQ(pwd.pw_gid, 0u);
+ ASSERT_STREQ(pwd.pw_gecos, "");
+ ASSERT_STREQ(pwd.pw_dir, "/root");
+ ASSERT_STREQ(pwd.pw_shell, "");
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_InvalidNumeric) {
+ char line1[] = "root:x:abc:0:root:/root:/bin/bash";
+ struct passwd pwd;
+ ASSERT_FALSE(parse_passwd_line(line1, &pwd));
+
+ char line2[] = "root:x:0:def:root:/root:/bin/bash";
+ ASSERT_FALSE(parse_passwd_line(line2, &pwd));
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_MissingFields) {
+ char line[] = "root:x:0:0:root:/root"; // Only 6 fields
+ struct passwd pwd;
+ ASSERT_FALSE(parse_passwd_line(line, &pwd));
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_NullInput) {
+ struct passwd pwd;
+ ASSERT_FALSE(parse_passwd_line(nullptr, &pwd));
+ ASSERT_FALSE(parse_passwd_line(nullptr, nullptr));
+}
>From 0ad65d448a6c9e455efa6a4a3829ca712b648ec0 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 14 Jul 2026 14:36:33 +0100
Subject: [PATCH 5/5] [libc] Fix spec compliance and improve test quality for
pwd
- Modify getpwent to stop and report EINVAL error on parse or truncation
failures, rather than silently skipping.
- Fix namespace rule violation in pwd_utils_test.cpp by using explicit
LIBC_NAMESPACE:: prefix.
- Remove redundant LibcTest.h include from getpwent_test.cpp.
- Add missing libc.src.__support.macros.config dependency to pwd_utils,
and remove redundant strcmp dependency from getpwent_test.
- Add test coverage for reopening database after endpwent, and file open
failures in getpwent_test.cpp.
- Add test coverage for trailing garbage and numeric overflow in
pwd_utils_test.cpp.
---
libc/src/pwd/CMakeLists.txt | 1 +
libc/src/pwd/getpwent.cpp | 9 ++-
libc/test/src/pwd/CMakeLists.txt | 2 +-
libc/test/src/pwd/getpwent_test.cpp | 82 +++++++++++++++++++++-------
libc/test/src/pwd/pwd_utils_test.cpp | 35 +++++++++---
5 files changed, 98 insertions(+), 31 deletions(-)
diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt
index 87b363fea2e8f..e177c3506b225 100644
--- a/libc/src/pwd/CMakeLists.txt
+++ b/libc/src/pwd/CMakeLists.txt
@@ -16,6 +16,7 @@ add_object_library(
libc.hdr.types.struct_passwd
libc.src.__support.str_to_integer
libc.src.string.string_utils
+ libc.src.__support.macros.config
)
add_entrypoint_object(
diff --git a/libc/src/pwd/getpwent.cpp b/libc/src/pwd/getpwent.cpp
index d5d2958bf5ec9..9a4ae4af89414 100644
--- a/libc/src/pwd/getpwent.cpp
+++ b/libc/src/pwd/getpwent.cpp
@@ -149,8 +149,10 @@ LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
if (res.bytes_read == 0)
return nullptr;
- if (res.truncated)
- continue;
+ if (res.truncated) {
+ libc_errno = EINVAL;
+ return nullptr;
+ }
// Remove newline
size_t len = res.bytes_read;
@@ -159,6 +161,9 @@ LLVM_LIBC_FUNCTION(struct passwd *, getpwent, ()) {
if (internal::parse_passwd_line(line_buffer, &pwd_entry))
return &pwd_entry;
+
+ libc_errno = EINVAL;
+ return nullptr;
}
}
diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt
index f21b8d3551189..55e1a8fcd6fe4 100644
--- a/libc/test/src/pwd/CMakeLists.txt
+++ b/libc/test/src/pwd/CMakeLists.txt
@@ -23,7 +23,7 @@ add_libc_unittest(
libc.src.pwd.pwd_utils
libc.src.__support.File.file
libc.src.__support.File.platform_file
- libc.src.string.strcmp
+ libc.src.__support.libc_errno
)
add_libc_unittest(
diff --git a/libc/test/src/pwd/getpwent_test.cpp b/libc/test/src/pwd/getpwent_test.cpp
index 8db17230fee30..2839b6df36162 100644
--- a/libc/test/src/pwd/getpwent_test.cpp
+++ b/libc/test/src/pwd/getpwent_test.cpp
@@ -13,19 +13,22 @@
#include "hdr/types/struct_passwd.h"
#include "src/__support/File/file.h"
+#include "src/__support/libc_errno.h"
#include "src/pwd/endpwent.h"
#include "src/pwd/getpwent.h"
#include "src/pwd/pwd_utils.h"
#include "src/pwd/setpwent.h"
-#include "test/UnitTest/LibcTest.h"
#include "test/UnitTest/Test.h"
-static const char *test_passwd_data =
+static const char *valid_passwd_data =
"root:x:0:0:root:/root:/bin/bash\n"
"daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin\n"
- "baduser:x:invalid:invalid:::\n" // Should be skipped due to parse error
"user2:x:3:3:user2:/home/user2:/bin/sh\n";
+static const char *bad_passwd_data = "root:x:0:0:root:/root:/bin/bash\n"
+ "baduser:x:invalid:invalid:::\n"
+ "user2:x:3:3:user2:/home/user2:/bin/sh\n";
+
static bool create_test_file(const char *path, const char *data) {
auto result = LIBC_NAMESPACE::openfile(path, "w");
if (!result.has_value())
@@ -43,9 +46,9 @@ static bool create_test_file(const char *path, const char *data) {
return true;
}
-TEST(LlvmLibcPwdTest, GetPwentTestHermetic) {
- auto TEST_FILE = libc_make_test_file_path("getpwent_hermetic.test");
- ASSERT_TRUE(create_test_file(TEST_FILE, test_passwd_data));
+TEST(LlvmLibcPwdTest, GetPwentTestSuccess) {
+ auto TEST_FILE = libc_make_test_file_path("getpwent_success.test");
+ ASSERT_TRUE(create_test_file(TEST_FILE, valid_passwd_data));
LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
@@ -60,13 +63,13 @@ TEST(LlvmLibcPwdTest, GetPwentTestHermetic) {
EXPECT_STREQ(pw->pw_dir, "/root");
EXPECT_STREQ(pw->pw_shell, "/bin/bash");
- // Second entry: daemon (skip baduser)
+ // Second entry: daemon
pw = LIBC_NAMESPACE::getpwent();
ASSERT_TRUE(pw != nullptr);
EXPECT_STREQ(pw->pw_name, "daemon");
EXPECT_EQ(pw->pw_uid, 1u);
- // Third entry: user2 (skip baduser because of parse error)
+ // Third entry: user2
pw = LIBC_NAMESPACE::getpwent();
ASSERT_TRUE(pw != nullptr);
EXPECT_STREQ(pw->pw_name, "user2");
@@ -79,9 +82,29 @@ TEST(LlvmLibcPwdTest, GetPwentTestHermetic) {
LIBC_NAMESPACE::endpwent();
}
+TEST(LlvmLibcPwdTest, GetPwentTestFailure) {
+ auto TEST_FILE = libc_make_test_file_path("getpwent_failure.test");
+ ASSERT_TRUE(create_test_file(TEST_FILE, bad_passwd_data));
+
+ LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
+
+ // First entry: root (valid)
+ struct passwd *pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "root");
+
+ // Second entry: baduser (invalid) -> should fail
+ libc_errno = 0;
+ pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw == nullptr);
+ EXPECT_EQ(static_cast<int>(libc_errno), EINVAL);
+
+ LIBC_NAMESPACE::endpwent();
+}
+
TEST(LlvmLibcPwdTest, SetPwentTestHermetic) {
auto TEST_FILE = libc_make_test_file_path("setpwent_hermetic.test");
- ASSERT_TRUE(create_test_file(TEST_FILE, test_passwd_data));
+ ASSERT_TRUE(create_test_file(TEST_FILE, valid_passwd_data));
LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
@@ -132,25 +155,46 @@ TEST(LlvmLibcPwdTest, GetPwentTruncationTest) {
for (size_t i = 0; suffix[i] != '\0'; ++i)
test_data[idx++] = suffix[i];
- // "nextuser:x:5:5:next:/home/next:/bin/sh\n"
- const char *next = "nextuser:x:5:5:next:/home/next:/bin/sh\n";
- for (size_t i = 0; next[i] != '\0'; ++i)
- test_data[idx++] = next[i];
-
test_data[idx] = '\0';
ASSERT_TRUE(create_test_file(TEST_FILE, test_data));
LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
- // The long line should be truncated, discarded, and skipped.
- // We should directly get "nextuser".
+ // The long line should be truncated and cause failure
+ libc_errno = 0;
+ struct passwd *pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw == nullptr);
+ EXPECT_EQ(static_cast<int>(libc_errno), EINVAL);
+
+ LIBC_NAMESPACE::endpwent();
+}
+
+TEST(LlvmLibcPwdTest, ReopenAfterEndpwent) {
+ auto TEST_FILE = libc_make_test_file_path("getpwent_reopen.test");
+ ASSERT_TRUE(create_test_file(TEST_FILE, valid_passwd_data));
+ LIBC_NAMESPACE::internal::set_passwd_path(TEST_FILE);
+
+ // Read first entry
struct passwd *pw = LIBC_NAMESPACE::getpwent();
ASSERT_TRUE(pw != nullptr);
- EXPECT_STREQ(pw->pw_name, "nextuser");
- EXPECT_EQ(pw->pw_uid, 5u);
+ EXPECT_STREQ(pw->pw_name, "root");
+
+ // Close database
+ LIBC_NAMESPACE::endpwent();
+ // Read again -> should reopen and start from root again
pw = LIBC_NAMESPACE::getpwent();
- ASSERT_TRUE(pw == nullptr);
+ ASSERT_TRUE(pw != nullptr);
+ EXPECT_STREQ(pw->pw_name, "root");
LIBC_NAMESPACE::endpwent();
}
+
+TEST(LlvmLibcPwdTest, FileOpenFailure) {
+ LIBC_NAMESPACE::internal::set_passwd_path("/nonexistent_file_pwd_test");
+
+ libc_errno = 0;
+ struct passwd *pw = LIBC_NAMESPACE::getpwent();
+ ASSERT_TRUE(pw == nullptr);
+ EXPECT_EQ(static_cast<int>(libc_errno), ENOENT);
+}
diff --git a/libc/test/src/pwd/pwd_utils_test.cpp b/libc/test/src/pwd/pwd_utils_test.cpp
index f8e07cec83c95..d8626be81a3bf 100644
--- a/libc/test/src/pwd/pwd_utils_test.cpp
+++ b/libc/test/src/pwd/pwd_utils_test.cpp
@@ -15,12 +15,10 @@
#include "src/pwd/pwd_utils.h"
#include "test/UnitTest/Test.h"
-using LIBC_NAMESPACE::internal::parse_passwd_line;
-
TEST(LlvmLibcPwdTest, ParsePasswdLine_Success) {
char line[] = "root:x:0:0:root:/root:/bin/bash";
struct passwd pwd;
- ASSERT_TRUE(parse_passwd_line(line, &pwd));
+ ASSERT_TRUE(LIBC_NAMESPACE::internal::parse_passwd_line(line, &pwd));
ASSERT_STREQ(pwd.pw_name, "root");
ASSERT_STREQ(pwd.pw_passwd, "x");
ASSERT_EQ(pwd.pw_uid, 0u);
@@ -33,7 +31,7 @@ TEST(LlvmLibcPwdTest, ParsePasswdLine_Success) {
TEST(LlvmLibcPwdTest, ParsePasswdLine_EmptyFields) {
char line[] = "root::0:0::/root:";
struct passwd pwd;
- ASSERT_TRUE(parse_passwd_line(line, &pwd));
+ ASSERT_TRUE(LIBC_NAMESPACE::internal::parse_passwd_line(line, &pwd));
ASSERT_STREQ(pwd.pw_name, "root");
ASSERT_STREQ(pwd.pw_passwd, "");
ASSERT_EQ(pwd.pw_uid, 0u);
@@ -46,20 +44,39 @@ TEST(LlvmLibcPwdTest, ParsePasswdLine_EmptyFields) {
TEST(LlvmLibcPwdTest, ParsePasswdLine_InvalidNumeric) {
char line1[] = "root:x:abc:0:root:/root:/bin/bash";
struct passwd pwd;
- ASSERT_FALSE(parse_passwd_line(line1, &pwd));
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line1, &pwd));
char line2[] = "root:x:0:def:root:/root:/bin/bash";
- ASSERT_FALSE(parse_passwd_line(line2, &pwd));
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line2, &pwd));
}
TEST(LlvmLibcPwdTest, ParsePasswdLine_MissingFields) {
char line[] = "root:x:0:0:root:/root"; // Only 6 fields
struct passwd pwd;
- ASSERT_FALSE(parse_passwd_line(line, &pwd));
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line, &pwd));
}
TEST(LlvmLibcPwdTest, ParsePasswdLine_NullInput) {
struct passwd pwd;
- ASSERT_FALSE(parse_passwd_line(nullptr, &pwd));
- ASSERT_FALSE(parse_passwd_line(nullptr, nullptr));
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(nullptr, &pwd));
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(nullptr, nullptr));
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_TrailingGarbage) {
+ char line1[] = "root:x:0a:0:root:/root:/bin/bash";
+ struct passwd pwd;
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line1, &pwd));
+
+ char line2[] = "root:x:0:0b:root:/root:/bin/bash";
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line2, &pwd));
+}
+
+TEST(LlvmLibcPwdTest, ParsePasswdLine_Overflow) {
+ // 4294967296 is 2^32, which overflows 32-bit unsigned.
+ char line1[] = "root:x:4294967296:0:root:/root:/bin/bash";
+ struct passwd pwd;
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line1, &pwd));
+
+ char line2[] = "root:x:0:4294967296:root:/root:/bin/bash";
+ ASSERT_FALSE(LIBC_NAMESPACE::internal::parse_passwd_line(line2, &pwd));
}
More information about the libc-commits
mailing list