diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt index fc2b0e91c128..f5ba34141176 100644 --- a/libc/config/linux/aarch64/entrypoints.txt +++ b/libc/config/linux/aarch64/entrypoints.txt @@ -322,6 +322,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.unistd.fsync libc.src.unistd.ftruncate libc.src.unistd.getcwd + libc.src.unistd.getentropy libc.src.unistd.geteuid libc.src.unistd.getpid libc.src.unistd.getppid diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt index 723853b2230a..0c1ae9561a7e 100644 --- a/libc/config/linux/x86_64/entrypoints.txt +++ b/libc/config/linux/x86_64/entrypoints.txt @@ -321,6 +321,7 @@ set(TARGET_LIBC_ENTRYPOINTS libc.src.unistd.fsync libc.src.unistd.ftruncate libc.src.unistd.getcwd + libc.src.unistd.getentropy libc.src.unistd.geteuid libc.src.unistd.getpid libc.src.unistd.getppid diff --git a/libc/config/windows/entrypoints.txt b/libc/config/windows/entrypoints.txt index 4ecc3ada9c76..aad320995d33 100644 --- a/libc/config/windows/entrypoints.txt +++ b/libc/config/windows/entrypoints.txt @@ -101,6 +101,9 @@ set(TARGET_LIBC_ENTRYPOINTS # time.h entrypoints libc.src.time.time libc.src.time.clock_getres + + # unistd.h entrypoints + libc.src.unistd.getentropy ) set(TARGET_LIBM_ENTRYPOINTS diff --git a/libc/config/windows/headers.txt b/libc/config/windows/headers.txt index bccc04f7697e..6d9aae927692 100644 --- a/libc/config/windows/headers.txt +++ b/libc/config/windows/headers.txt @@ -6,4 +6,5 @@ set(TARGET_PUBLIC_HEADERS libc.include.errno libc.include.fenv libc.include.math + libc.include.unistd ) diff --git a/libc/include/sys/random.yaml b/libc/include/sys/random.yaml index 4efb2fbb4473..a97266a5481d 100644 --- a/libc/include/sys/random.yaml +++ b/libc/include/sys/random.yaml @@ -15,3 +15,10 @@ functions: - type: void * - type: size_t - type: unsigned int + - name: getentropy + standards: + - GNUExtensions + return_type: int + arguments: + - type: void * + - type: size_t diff --git a/libc/include/unistd.yaml b/libc/include/unistd.yaml index fada365e0103..c1901be446fe 100644 --- a/libc/include/unistd.yaml +++ b/libc/include/unistd.yaml @@ -128,6 +128,13 @@ functions: arguments: - type: char * - type: size_t + - name: getentropy + standards: + - GNUExtensions + return_type: int + arguments: + - type: void * + - type: size_t - name: geteuid standards: - POSIX diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt index 32308ba14794..41183429f67a 100644 --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_subdirectory(string) add_subdirectory(strings) add_subdirectory(wchar) add_subdirectory(time) +add_subdirectory(unistd) if(${LIBC_TARGET_OS} STREQUAL "linux") add_subdirectory(dirent) @@ -23,7 +24,6 @@ if(${LIBC_TARGET_OS} STREQUAL "linux") add_subdirectory(sched) add_subdirectory(sys) add_subdirectory(termios) - add_subdirectory(unistd) endif() if(NOT LLVM_LIBC_FULL_BUILD) diff --git a/libc/src/unistd/CMakeLists.txt b/libc/src/unistd/CMakeLists.txt index 1a0b2e3293d0..6bdea0c7693b 100644 --- a/libc/src/unistd/CMakeLists.txt +++ b/libc/src/unistd/CMakeLists.txt @@ -350,3 +350,10 @@ add_entrypoint_object( DEPENDS libc.src.__support.threads.identifier ) + +add_entrypoint_object( + getentropy + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.getentropy +) diff --git a/libc/src/unistd/getentropy.h b/libc/src/unistd/getentropy.h new file mode 100644 index 000000000000..27e13d2352d8 --- /dev/null +++ b/libc/src/unistd/getentropy.h @@ -0,0 +1,19 @@ +//===-- Implementation header for getentropy ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/types/size_t.h" +#include "src/__support/common.h" + +#ifndef LLVM_LIBC_SRC_UNISTD_GETENTROPY_H +#define LLVM_LIBC_SRC_UNISTD_GETENTROPY_H + +namespace LIBC_NAMESPACE_DECL { +int getentropy(void *buffer, size_t length); +} + +#endif // LLVM_LIBC_SRC_UNISTD_GETENTROPY_H diff --git a/libc/src/unistd/linux/CMakeLists.txt b/libc/src/unistd/linux/CMakeLists.txt index ed360c73354a..2bb17f56f7b3 100644 --- a/libc/src/unistd/linux/CMakeLists.txt +++ b/libc/src/unistd/linux/CMakeLists.txt @@ -570,3 +570,18 @@ add_entrypoint_object( libc.src.__support.OSUtil.osutil libc.src.errno.errno ) + +add_entrypoint_object( + getentropy + SRCS + getentropy.cpp + HDRS + ../getentropy.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.types.ssize_t + libc.hdr.errno_macros + libc.include.sys_syscall + libc.src.__support.OSUtil.osutil + libc.src.errno.errno +) diff --git a/libc/src/unistd/linux/getentropy.cpp b/libc/src/unistd/linux/getentropy.cpp new file mode 100644 index 000000000000..168a1197734e --- /dev/null +++ b/libc/src/unistd/linux/getentropy.cpp @@ -0,0 +1,51 @@ +//===-- Linux implementation of getentropy --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/unistd/getentropy.h" +#include "hdr/errno_macros.h" +#include "src/__support/OSUtil/syscall.h" +#include "src/__support/common.h" +#include "src/errno/libc_errno.h" + +#include // For syscall numbers. + +namespace LIBC_NAMESPACE_DECL { +LLVM_LIBC_FUNCTION(int, getentropy, (void *buffer, size_t length)) { + // check the length limit + if (length > 256) { + libc_errno = EIO; + return -1; + } + + char *cursor = static_cast(buffer); + while (length != 0) { + // 0 flag means urandom and blocking, which meets the assumption of + // getentropy + auto ret = syscall_impl(SYS_getrandom, cursor, length, 0); + + // on success, advance the buffer pointer + if (ret >= 0) { + length -= static_cast(ret); + cursor += ret; + continue; + } + + auto error = -static_cast(ret); + + // on EINTR, try again + if (error == EINTR) + continue; + + // on ENOSYS, forward errno and exit; + // otherwise, set EIO and exit + libc_errno = (error == ENOSYS) ? ENOSYS : EIO; + return -1; + } + return 0; +} +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/unistd/windows/CMakeLists.txt b/libc/src/unistd/windows/CMakeLists.txt new file mode 100644 index 000000000000..195d98cdb51d --- /dev/null +++ b/libc/src/unistd/windows/CMakeLists.txt @@ -0,0 +1,11 @@ +add_entrypoint_object( + getentropy + SRCS + getentropy.cpp + HDRS + ../getentropy.h + DEPENDS + libc.hdr.types.size_t + libc.hdr.errno_macros + libc.src.errno.errno +) diff --git a/libc/src/unistd/windows/getentropy.cpp b/libc/src/unistd/windows/getentropy.cpp new file mode 100644 index 000000000000..bfaec723ac63 --- /dev/null +++ b/libc/src/unistd/windows/getentropy.cpp @@ -0,0 +1,42 @@ +//===-- Windows implementation of getentropy ------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/unistd/getentropy.h" +#include "hdr/errno_macros.h" +#include "src/__support/common.h" +#include "src/errno/libc_errno.h" + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#pragma comment(lib, "bcrypt.lib") + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(int, getentropy, (void *buffer, size_t length)) { + __try { + // check the length limit + if (length > 256) + __leave; + + NTSTATUS result = ::BCryptGenRandom(nullptr, static_cast(buffer), + static_cast(length), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + + if (result == STATUS_SUCCESS) + return 0; + + } __except (EXCEPTION_EXECUTE_HANDLER) { + // no need to handle exceptions specially + } + + libc_errno = EIO; + return -1; +} +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 31008508d649..22ec43588f74 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -61,6 +61,7 @@ add_subdirectory(string) add_subdirectory(strings) add_subdirectory(wchar) add_subdirectory(time) +add_subdirectory(unistd) # Depends on utilities in stdlib add_subdirectory(inttypes) @@ -70,7 +71,6 @@ if(${LIBC_TARGET_OS} STREQUAL "linux") add_subdirectory(sched) add_subdirectory(sys) add_subdirectory(termios) - add_subdirectory(unistd) endif() if(NOT LLVM_LIBC_FULL_BUILD) diff --git a/libc/test/src/unistd/CMakeLists.txt b/libc/test/src/unistd/CMakeLists.txt index e036e09cde70..c3eebdf2a877 100644 --- a/libc/test/src/unistd/CMakeLists.txt +++ b/libc/test/src/unistd/CMakeLists.txt @@ -488,6 +488,18 @@ add_libc_test( libc.src.stdio.fflush ) +add_libc_test( + getentropy_test + SUITE + libc_unistd_unittests + SRCS + getentropy_test.cpp + DEPENDS + libc.src.unistd.getentropy + libc.src.errno.errno + libc.test.UnitTest.ErrnoSetterMatcher +) + if(LLVM_LIBC_FULL_BUILD) add_libc_test( _exit_test diff --git a/libc/test/src/unistd/getentropy_test.cpp b/libc/test/src/unistd/getentropy_test.cpp new file mode 100644 index 000000000000..f7329ae41932 --- /dev/null +++ b/libc/test/src/unistd/getentropy_test.cpp @@ -0,0 +1,28 @@ +//===-- Unittests for getentropy ------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "hdr/errno_macros.h" +#include "src/unistd/getentropy.h" +#include "test/UnitTest/ErrnoSetterMatcher.h" +#include "test/UnitTest/Test.h" + +using namespace LIBC_NAMESPACE::testing::ErrnoSetterMatcher; + +TEST(LlvmLibcUnistdGetEntropyTest, LengthTooLong) { + char buf[1024]; + ASSERT_THAT(LIBC_NAMESPACE::getentropy(buf, 257), Fails(EIO)); +} + +TEST(LlvmLibcUnistdGetEntropyTest, SmokeTest) { + char buf[256]; + ASSERT_THAT(LIBC_NAMESPACE::getentropy(buf, 256), Succeeds()); +} + +TEST(LlvmLibcUnistdGetEntropyTest, OtherError) { + ASSERT_THAT(LIBC_NAMESPACE::getentropy(nullptr, 1), Fails(EIO)); +}