[libc] [llvm] [libc] Implement pkey_alloc/free/get/set/mprotect for x86_64 linux (PR #162362)
Jackson Stogel via llvm-commits
llvm-commits at lists.llvm.org
Thu Nov 6 12:25:59 PST 2025
https://github.com/jtstogel updated https://github.com/llvm/llvm-project/pull/162362
>From 6eb5a45044aaa138d7b1d62f5587bb6332357c30 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Tue, 7 Oct 2025 20:04:14 +0000
Subject: [PATCH 1/8] Implement pkey alloc/free/get/set/mprotect for x86_64
linux
---
libc/config/linux/x86_64/entrypoints.txt | 5 +
libc/src/sys/mman/CMakeLists.txt | 35 +++
libc/src/sys/mman/linux/CMakeLists.txt | 75 ++++++
.../src/sys/mman/linux/generic/CMakeLists.txt | 9 +
libc/src/sys/mman/linux/generic/pkey_common.h | 25 ++
libc/src/sys/mman/linux/pkey_alloc.cpp | 37 +++
libc/src/sys/mman/linux/pkey_free.cpp | 35 +++
libc/src/sys/mman/linux/pkey_get.cpp | 35 +++
libc/src/sys/mman/linux/pkey_mprotect.cpp | 45 ++++
libc/src/sys/mman/linux/pkey_set.cpp | 35 +++
libc/src/sys/mman/linux/x86_64/CMakeLists.txt | 10 +
libc/src/sys/mman/linux/x86_64/pkey_common.h | 71 ++++++
libc/src/sys/mman/pkey_alloc.h | 20 ++
libc/src/sys/mman/pkey_free.h | 20 ++
libc/src/sys/mman/pkey_get.h | 20 ++
libc/src/sys/mman/pkey_mprotect.h | 21 ++
libc/src/sys/mman/pkey_set.h | 20 ++
libc/test/src/sys/mman/linux/CMakeLists.txt | 21 ++
libc/test/src/sys/mman/linux/pkey_test.cpp | 241 ++++++++++++++++++
.../llvm-project-overlay/libc/BUILD.bazel | 75 ++++++
.../libc/test/UnitTest/BUILD.bazel | 1 +
.../libc/test/src/sys/mman/BUILD.bazel | 18 ++
22 files changed, 874 insertions(+)
create mode 100644 libc/src/sys/mman/linux/generic/CMakeLists.txt
create mode 100644 libc/src/sys/mman/linux/generic/pkey_common.h
create mode 100644 libc/src/sys/mman/linux/pkey_alloc.cpp
create mode 100644 libc/src/sys/mman/linux/pkey_free.cpp
create mode 100644 libc/src/sys/mman/linux/pkey_get.cpp
create mode 100644 libc/src/sys/mman/linux/pkey_mprotect.cpp
create mode 100644 libc/src/sys/mman/linux/pkey_set.cpp
create mode 100644 libc/src/sys/mman/linux/x86_64/CMakeLists.txt
create mode 100644 libc/src/sys/mman/linux/x86_64/pkey_common.h
create mode 100644 libc/src/sys/mman/pkey_alloc.h
create mode 100644 libc/src/sys/mman/pkey_free.h
create mode 100644 libc/src/sys/mman/pkey_get.h
create mode 100644 libc/src/sys/mman/pkey_mprotect.h
create mode 100644 libc/src/sys/mman/pkey_set.h
create mode 100644 libc/test/src/sys/mman/linux/pkey_test.cpp
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 87b78a337b875..4c56d23d96877 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -264,6 +264,11 @@ set(TARGET_LIBC_ENTRYPOINTS
libc.src.sys.mman.munlock
libc.src.sys.mman.munlockall
libc.src.sys.mman.munmap
+ libc.src.sys.mman.pkey_alloc
+ libc.src.sys.mman.pkey_free
+ libc.src.sys.mman.pkey_get
+ libc.src.sys.mman.pkey_mprotect
+ libc.src.sys.mman.pkey_set
libc.src.sys.mman.remap_file_pages
libc.src.sys.mman.posix_madvise
libc.src.sys.mman.shm_open
diff --git a/libc/src/sys/mman/CMakeLists.txt b/libc/src/sys/mman/CMakeLists.txt
index 4d4c2ad376050..c7be1eddacb5e 100644
--- a/libc/src/sys/mman/CMakeLists.txt
+++ b/libc/src/sys/mman/CMakeLists.txt
@@ -86,6 +86,41 @@ add_entrypoint_object(
.${LIBC_TARGET_OS}.msync
)
+add_entrypoint_object(
+ pkey_alloc
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pkey_alloc
+)
+
+add_entrypoint_object(
+ pkey_free
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pkey_free
+)
+
+add_entrypoint_object(
+ pkey_get
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pkey_get
+)
+
+add_entrypoint_object(
+ pkey_mprotect
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pkey_mprotect
+)
+
+add_entrypoint_object(
+ pkey_set
+ ALIAS
+ DEPENDS
+ .${LIBC_TARGET_OS}.pkey_set
+)
+
add_entrypoint_object(
remap_file_pages
ALIAS
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 7181bb98a187f..1c79180cbcabb 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -1,3 +1,10 @@
+add_subdirectory(generic)
+set(ARCH_SUBDIRECTORY generic)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+ add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
+ set(ARCH_SUBDIRECTORY ${LIBC_TARGET_ARCHITECTURE})
+endif()
+
add_entrypoint_object(
madvise
SRCS
@@ -166,6 +173,74 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_entrypoint_object(
+ pkey_alloc
+ SRCS
+ pkey_alloc.cpp
+ HDRS
+ ../pkey_alloc.h
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ pkey_free
+ SRCS
+ pkey_free.cpp
+ HDRS
+ ../pkey_free.h
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ pkey_get
+ SRCS
+ pkey_get.cpp
+ HDRS
+ ../pkey_get.h
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+ .${ARCH_SUBDIRECTORY}.pkey_common
+)
+
+add_entrypoint_object(
+ pkey_mprotect
+ SRCS
+ pkey_mprotect.cpp
+ HDRS
+ ../pkey_mprotect.h
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.sys.mman.mprotect
+ libc.src.errno.errno
+)
+
+add_entrypoint_object(
+ pkey_set
+ SRCS
+ pkey_set.cpp
+ HDRS
+ ../pkey_set.h
+ DEPENDS
+ libc.include.sys_mman
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+ .${ARCH_SUBDIRECTORY}.pkey_common
+)
+
add_entrypoint_object(
remap_file_pages
SRCS
diff --git a/libc/src/sys/mman/linux/generic/CMakeLists.txt b/libc/src/sys/mman/linux/generic/CMakeLists.txt
new file mode 100644
index 0000000000000..42b6d96c8387e
--- /dev/null
+++ b/libc/src/sys/mman/linux/generic/CMakeLists.txt
@@ -0,0 +1,9 @@
+add_header_library(
+ pkey_common
+ HDRS
+ pkey_common.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.src.__support.common
+ libc.src.__support.error_or
+)
diff --git a/libc/src/sys/mman/linux/generic/pkey_common.h b/libc/src/sys/mman/linux/generic/pkey_common.h
new file mode 100644
index 0000000000000..0811cfb77d4b0
--- /dev/null
+++ b/libc/src/sys/mman/linux/generic/pkey_common.h
@@ -0,0 +1,25 @@
+#ifndef LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
+#define LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace pkey_common {
+
+LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
+ (void)pkey;
+ return Error(ENOSYS);
+}
+
+LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
+ (void)pkey;
+ (void)access_rights;
+ return Error(ENOSYS);
+}
+
+} // namespace pkey_common
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
diff --git a/libc/src/sys/mman/linux/pkey_alloc.cpp b/libc/src/sys/mman/linux/pkey_alloc.cpp
new file mode 100644
index 0000000000000..baf32013bc5c7
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_alloc.cpp
@@ -0,0 +1,37 @@
+//===---------- Linux implementation of the Linux pkey_alloc function -----===//
+//
+// 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/sys/mman/pkey_alloc.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_alloc,
+ (unsigned int flags, unsigned int access_rights)) {
+#if !defined(SYS_pkey_alloc)
+ libc_errno = ENOSYS;
+ return -1;
+#else
+ int ret =
+ LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_alloc, flags, access_rights);
+ if (ret < 0) {
+ libc_errno = static_cast<int>(-ret);
+ return -1;
+ }
+ return static_cast<int>(ret);
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_free.cpp b/libc/src/sys/mman/linux/pkey_free.cpp
new file mode 100644
index 0000000000000..0228971bd10f6
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_free.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_free function ------===//
+//
+// 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/sys/mman/pkey_free.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_free, (int pkey)) {
+#if !defined(SYS_pkey_free)
+ libc_errno = ENOSYS;
+ return -1;
+#else
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_free, pkey);
+ if (ret < 0) {
+ libc_errno = static_cast<int>(-ret);
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_get.cpp b/libc/src/sys/mman/linux/pkey_get.cpp
new file mode 100644
index 0000000000000..623b7930c7a23
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_get.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// 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/sys/mman/pkey_get.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/macros/properties/architectures.h"
+
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#include "src/sys/mman/linux/x86_64/pkey_common.h"
+#else
+#include "src/sys/mman/linux/generic/pkey_common.h"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_get, (int pkey)) {
+ ErrorOr<int> ret = LIBC_NAMESPACE::pkey_common::pkey_get(pkey);
+ if (!ret.has_value()) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ return ret.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_mprotect.cpp b/libc/src/sys/mman/linux/pkey_mprotect.cpp
new file mode 100644
index 0000000000000..15c5d9db39b33
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_mprotect.cpp
@@ -0,0 +1,45 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// 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/sys/mman/pkey_mprotect.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "hdr/types/size_t.h"
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/common.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/config.h"
+#include "src/sys/mman/mprotect.h"
+
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_mprotect,
+ (void *addr, size_t len, int prot, int pkey)) {
+ // Fall back to mprotect if pkey is -1
+ // to maintain compatibility with kernel versions that don't support pkey.
+ if (pkey == -1) {
+ return LIBC_NAMESPACE::mprotect(addr, len, prot);
+ }
+
+#if !defined(SYS_pkey_mprotect)
+ libc_errno = ENOSYS;
+ return -1;
+#else
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_mprotect, addr, len,
+ prot, pkey);
+ if (ret < 0) {
+ libc_errno = -ret;
+ return -1;
+ }
+ return 0;
+#endif
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_set.cpp b/libc/src/sys/mman/linux/pkey_set.cpp
new file mode 100644
index 0000000000000..7921443f688d3
--- /dev/null
+++ b/libc/src/sys/mman/linux/pkey_set.cpp
@@ -0,0 +1,35 @@
+//===---------- Linux implementation of the Linux pkey_mprotect function --===//
+//
+// 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/sys/mman/pkey_set.h"
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+#if defined(LIBC_TARGET_ARCH_IS_X86_64)
+#include "src/sys/mman/linux/x86_64/pkey_common.h"
+#else
+#include "src/sys/mman/linux/generic/pkey_common.h"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+
+LLVM_LIBC_FUNCTION(int, pkey_set, (int pkey, unsigned int access_rights)) {
+ ErrorOr<int> ret = LIBC_NAMESPACE::pkey_common::pkey_set(pkey, access_rights);
+ if (!ret.has_value()) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ return ret.value();
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/x86_64/CMakeLists.txt b/libc/src/sys/mman/linux/x86_64/CMakeLists.txt
new file mode 100644
index 0000000000000..1ce23af6dbd2a
--- /dev/null
+++ b/libc/src/sys/mman/linux/x86_64/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_header_library(
+ pkey_common
+ HDRS
+ pkey_common.h
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.stdint_proxy
+ libc.src.__support.common
+ libc.src.__support.error_or
+)
diff --git a/libc/src/sys/mman/linux/x86_64/pkey_common.h b/libc/src/sys/mman/linux/x86_64/pkey_common.h
new file mode 100644
index 0000000000000..bffa9feaed06c
--- /dev/null
+++ b/libc/src/sys/mman/linux/x86_64/pkey_common.h
@@ -0,0 +1,71 @@
+#ifndef LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
+#define LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
+
+#include "hdr/errno_macros.h" // For ENOSYS
+#include "hdr/stdint_proxy.h"
+#include "src/__support/common.h"
+#include "src/__support/error_or.h"
+
+#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
+#error "Invalid include"
+#endif
+
+namespace LIBC_NAMESPACE_DECL {
+namespace pkey_common {
+namespace internal {
+
+constexpr int MAX_KEY = 15;
+constexpr int KEY_MASK = 0x3;
+constexpr int BITS_PER_KEY = 2;
+
+// This will SIGILL on CPUs that don't support PKU / OSPKE,
+// but this case should never be reached as a prior pkey_alloc invocation
+// would have failed more gracefully.
+LIBC_INLINE uint32_t read_prku() {
+ uint32_t pkru = 0;
+ uint32_t edx = 0;
+ LIBC_INLINE_ASM("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
+ return pkru;
+}
+
+// This will SIGILL on CPUs that don't support PKU / OSPKE,
+// but this case should never be reached as a prior pkey_alloc invocation
+// would have failed more gracefully.
+LIBC_INLINE void write_prku(uint32_t pkru) {
+ LIBC_INLINE_ASM("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
+}
+
+} // namespace internal
+
+// x86_64 implementation of pkey_get.
+// Returns the access rights for the given pkey on success, errno otherwise.
+LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
+ if (pkey < 0 || pkey > internal::MAX_KEY) {
+ return Error(EINVAL);
+ }
+
+ uint32_t pkru = internal::read_prku();
+ return (pkru >> (pkey * internal::BITS_PER_KEY)) & internal::KEY_MASK;
+}
+
+// x86_64 implementation of pkey_set.
+// Returns 0 on success, errno otherwise.
+LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
+ if (pkey < 0 || pkey > internal::MAX_KEY ||
+ access_rights > internal::KEY_MASK) {
+ return Error(EINVAL);
+ }
+
+ uint32_t pkru = internal::read_prku();
+ pkru &= ~(internal::KEY_MASK << (pkey * internal::BITS_PER_KEY));
+ pkru |=
+ ((access_rights & internal::KEY_MASK) << (pkey * internal::BITS_PER_KEY));
+ internal::write_prku(pkru);
+
+ return 0;
+}
+
+} // namespace pkey_common
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
diff --git a/libc/src/sys/mman/pkey_alloc.h b/libc/src/sys/mman/pkey_alloc.h
new file mode 100644
index 0000000000000..c63c6a36c8021
--- /dev/null
+++ b/libc/src/sys/mman/pkey_alloc.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_alloc function -----------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_alloc(unsigned int flags, unsigned int access_rights);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_ALLOC_H
diff --git a/libc/src/sys/mman/pkey_free.h b/libc/src/sys/mman/pkey_free.h
new file mode 100644
index 0000000000000..a357e9b8c847b
--- /dev/null
+++ b/libc/src/sys/mman/pkey_free.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_free function ------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_free(int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_FREE_H
diff --git a/libc/src/sys/mman/pkey_get.h b/libc/src/sys/mman/pkey_get.h
new file mode 100644
index 0000000000000..d41afe08ae371
--- /dev/null
+++ b/libc/src/sys/mman/pkey_get.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_get function -------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_get(int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_GET_H
diff --git a/libc/src/sys/mman/pkey_mprotect.h b/libc/src/sys/mman/pkey_mprotect.h
new file mode 100644
index 0000000000000..4d19348ef09db
--- /dev/null
+++ b/libc/src/sys/mman/pkey_mprotect.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pkey_mprotect function --------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
+
+#include "src/__support/macros/config.h"
+#include "hdr/types/size_t.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_mprotect(void *addr, size_t len, int prot, int pkey);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
diff --git a/libc/src/sys/mman/pkey_set.h b/libc/src/sys/mman/pkey_set.h
new file mode 100644
index 0000000000000..55bafbd11d709
--- /dev/null
+++ b/libc/src/sys/mman/pkey_set.h
@@ -0,0 +1,20 @@
+//===-- Implementation header for pkey_set function -------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
+#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
+
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pkey_set(int pkey, unsigned int access_rights);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_SYS_MMAN_PKEY_SET_H
diff --git a/libc/test/src/sys/mman/linux/CMakeLists.txt b/libc/test/src/sys/mman/linux/CMakeLists.txt
index a362c1cf61cbc..721f89961f7c0 100644
--- a/libc/test/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/test/src/sys/mman/linux/CMakeLists.txt
@@ -67,6 +67,27 @@ add_libc_unittest(
)
+add_libc_unittest(
+ pkey_test
+ SUITE
+ libc_sys_mman_unittests
+ SRCS
+ pkey_test.cpp
+ DEPENDS
+ libc.hdr.errno_macros
+ libc.hdr.signal_macros
+ libc.hdr.types.size_t
+ libc.src.sys.mman.mmap
+ libc.src.sys.mman.munmap
+ libc.src.sys.mman.pkey_alloc
+ libc.src.sys.mman.pkey_free
+ libc.src.sys.mman.pkey_get
+ libc.src.sys.mman.pkey_mprotect
+ libc.src.sys.mman.pkey_set
+ libc.test.UnitTest.ErrnoCheckingTest
+ libc.test.UnitTest.ErrnoSetterMatcher
+)
+
add_libc_unittest(
posix_madvise_test
SUITE
diff --git a/libc/test/src/sys/mman/linux/pkey_test.cpp b/libc/test/src/sys/mman/linux/pkey_test.cpp
new file mode 100644
index 0000000000000..9c6feae2d457b
--- /dev/null
+++ b/libc/test/src/sys/mman/linux/pkey_test.cpp
@@ -0,0 +1,241 @@
+//===-- Unit tests for pkey functions -------------------------------------===//
+//
+// 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 "hdr/signal_macros.h"
+#include "hdr/types/size_t.h"
+#include "src/sys/mman/mmap.h"
+#include "src/sys/mman/munmap.h"
+#include "src/sys/mman/pkey_alloc.h"
+#include "src/sys/mman/pkey_free.h"
+#include "src/sys/mman/pkey_get.h"
+#include "src/sys/mman/pkey_mprotect.h"
+#include "src/sys/mman/pkey_set.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/LibcTest.h"
+#include "test/UnitTest/TestLogger.h"
+
+#include <linux/param.h> // For EXEC_PAGESIZE.
+
+using LIBC_NAMESPACE::testing::tlog;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Fails;
+using LIBC_NAMESPACE::testing::ErrnoSetterMatcher::Succeeds;
+
+using LlvmLibcProtectionKeyTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
+constexpr size_t MMAP_SIZE = EXEC_PAGESIZE;
+
+// Wrapper around a pkey to ensure it is freed.
+class PKeyGuard {
+public:
+ int key;
+
+ PKeyGuard() : key(-1) {}
+
+ PKeyGuard(int key) : key(key) {}
+
+ ~PKeyGuard() {
+ if (key != -1) {
+ LIBC_NAMESPACE::pkey_free(key);
+ }
+ }
+};
+
+// Wrapper around mmap to ensure munmap is called.
+class MMapPageGuard {
+public:
+ void *addr = nullptr;
+ size_t size = 0;
+
+ static MMapPageGuard mmap(int prot) {
+ void *addr = LIBC_NAMESPACE::mmap(nullptr, MMAP_SIZE, prot,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (addr == MAP_FAILED) {
+ return MMapPageGuard(nullptr, 0);
+ }
+ return MMapPageGuard(addr, MMAP_SIZE);
+ }
+
+ MMapPageGuard(void *addr, size_t size) : addr(addr), size(size) {}
+
+ ~MMapPageGuard() {
+ if (addr != nullptr) {
+ LIBC_NAMESPACE::munmap(addr, size);
+ }
+ }
+};
+
+bool protection_keys_supported() {
+ static bool supported = []() {
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, 0));
+ int err = libc_errno;
+ libc_errno = 0;
+
+ if (pkey.key < 0 || (err == ENOSPC || err == ENOSYS || err == EINVAL)) {
+ tlog << "pkey_alloc failed with errno=" << err << "\n";
+ return false;
+ }
+
+ int access_rights = LIBC_NAMESPACE::pkey_get(pkey.key);
+ err = libc_errno;
+ libc_errno = 0;
+ if (access_rights < 0 || err == ENOSYS) {
+ tlog << "pkey_get failed with errno=" << err << "\n";
+ return false;
+ }
+
+ return true;
+ }();
+ return supported;
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, MProtectWithPKeyDisablesWrite) {
+ if (!protection_keys_supported()) {
+ tlog << "Skipping test: pkey is not available\n";
+ return;
+ }
+
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, PKEY_DISABLE_WRITE));
+ ASSERT_NE(pkey.key, -1);
+
+ MMapPageGuard page = MMapPageGuard::mmap(PROT_READ | PROT_WRITE);
+ ASSERT_NE(page.addr, nullptr);
+
+ volatile char *data = (char *)page.addr;
+ data[0] = 'a';
+
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_mprotect(page.addr, page.size,
+ PROT_READ | PROT_WRITE, pkey.key),
+ Succeeds());
+
+ // Read is still allowed.
+ EXPECT_EQ(data[0], 'a');
+
+ // Write is not allowed.
+ EXPECT_DEATH([&data]() { data[0] = 'b'; }, WITH_SIGNAL(SIGSEGV));
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, PKeySetChangesAccessRights) {
+ if (!protection_keys_supported()) {
+ tlog << "Skipping test: pkey is not available\n";
+ return;
+ }
+
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, 0));
+ ASSERT_NE(pkey.key, -1);
+
+ MMapPageGuard page = MMapPageGuard::mmap(PROT_READ | PROT_WRITE);
+ ASSERT_NE(page.addr, nullptr);
+
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_mprotect(page.addr, page.size,
+ PROT_READ | PROT_WRITE, pkey.key),
+ Succeeds());
+
+ // Write is allowed by default.
+ volatile char *data = (char *)page.addr;
+ data[0] = 'a';
+
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(pkey.key, PKEY_DISABLE_WRITE),
+ Succeeds());
+
+ // Now read is allowed but write is not.
+ EXPECT_EQ(data[0], 'a');
+ EXPECT_DEATH([&data]() { data[0] = 'b'; }, WITH_SIGNAL(SIGSEGV));
+
+ // Now neither read nor write is allowed.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(pkey.key, PKEY_DISABLE_ACCESS |
+ PKEY_DISABLE_WRITE),
+ Succeeds());
+ EXPECT_DEATH([&data]() { (void)data[0]; }, WITH_SIGNAL(SIGSEGV));
+ EXPECT_DEATH([&data]() { data[0] = 'b'; }, WITH_SIGNAL(SIGSEGV));
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, FallsBackToMProtectForInvalidPKey) {
+ MMapPageGuard page = MMapPageGuard::mmap(PROT_READ | PROT_WRITE);
+ ASSERT_NE(page.addr, nullptr);
+
+ volatile char *data = (char *)page.addr;
+ data[0] = 'a';
+
+ EXPECT_THAT(
+ LIBC_NAMESPACE::pkey_mprotect(page.addr, page.size, PROT_READ, -1),
+ Succeeds());
+
+ // Read is still allowed.
+ EXPECT_EQ(data[0], 'a');
+
+ // Write is not allowed.
+ EXPECT_DEATH([&data]() { data[0] = 'b'; }, WITH_SIGNAL(SIGSEGV));
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, ExhaustedKeysFailsWithENOSPC) {
+ if (!protection_keys_supported()) {
+ tlog << "Skipping test: pkey is not available\n";
+ return;
+ }
+
+ // Use an unreasonably large limit to ensure test is cross-platform.
+ // This limit is intended to be much larger than the actual hardware limit.
+ constexpr int MAX_PKEYS = 64;
+ PKeyGuard pkeys[MAX_PKEYS];
+ for (int i = 0; i < MAX_PKEYS; ++i) {
+ pkeys[i].key = LIBC_NAMESPACE::pkey_alloc(0, 0);
+ }
+
+ // pkey allocation should eventually fail with ENOSPC.
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, 0));
+ EXPECT_THAT(pkey.key, Fails(ENOSPC));
+ libc_errno = 0;
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, Accessors) {
+ if (!protection_keys_supported()) {
+ tlog << "Skipping test: pkey is not available\n";
+ return;
+ }
+
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, PKEY_DISABLE_WRITE));
+ ASSERT_NE(pkey.key, -1);
+
+ // Check that pkey_alloc sets the access rights.
+ EXPECT_EQ(LIBC_NAMESPACE::pkey_get(pkey.key), PKEY_DISABLE_WRITE);
+
+ // Check that pkey_set changes the access rights.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(pkey.key, PKEY_DISABLE_ACCESS),
+ Succeeds());
+ EXPECT_EQ(LIBC_NAMESPACE::pkey_get(pkey.key), PKEY_DISABLE_ACCESS);
+}
+
+TEST_F(LlvmLibcProtectionKeyTest, AccessorsErrorForInvalidValues) {
+ if (!protection_keys_supported()) {
+ tlog << "Skipping test: pkey is not available\n";
+ return;
+ }
+
+ PKeyGuard pkey(LIBC_NAMESPACE::pkey_alloc(0, PKEY_DISABLE_WRITE));
+ ASSERT_NE(pkey.key, -1);
+
+ // Pkey is out of bounds in pkey_get.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_get(100), Fails(EINVAL));
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_get(-1234), Fails(EINVAL));
+
+ // Pkey is out of bounds in pkey_set.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(100, PKEY_DISABLE_ACCESS),
+ Fails(EINVAL));
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(-1234, PKEY_DISABLE_ACCESS),
+ Fails(EINVAL));
+
+ // Non-zero flags are not supported in pkey_alloc.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_alloc(123, PKEY_DISABLE_WRITE),
+ Fails(EINVAL));
+
+ // Access rights are out of bounds.
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_alloc(0, 1000), Fails(EINVAL));
+ EXPECT_THAT(LIBC_NAMESPACE::pkey_set(pkey.key, 1000), Fails(EINVAL));
+}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index a9675f4b02565..5bd45df678085 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -6899,6 +6899,81 @@ libc_function(
],
)
+libc_function(
+ name = "pkey_alloc",
+ srcs = ["src/sys/mman/linux/pkey_alloc.cpp"],
+ hdrs = ["src/sys/mman/pkey_alloc.h"],
+ deps = [
+ ":__support_common",
+ ":__support_osutil_syscall",
+ ":errno",
+ ],
+)
+
+libc_function(
+ name = "pkey_free",
+ srcs = ["src/sys/mman/linux/pkey_free.cpp"],
+ hdrs = ["src/sys/mman/pkey_free.h"],
+ deps = [
+ ":__support_common",
+ ":__support_osutil_syscall",
+ ":errno",
+ ],
+)
+
+libc_function(
+ name = "pkey_get",
+ srcs = ["src/sys/mman/linux/pkey_get.cpp"],
+ hdrs = ["src/sys/mman/pkey_get.h"],
+ deps = [
+ ":__support_common",
+ ":__support_error_or",
+ ":__support_osutil_syscall",
+ ":errno",
+ ":pkey_common",
+ ],
+)
+
+libc_function(
+ name = "pkey_mprotect",
+ srcs = ["src/sys/mman/linux/pkey_mprotect.cpp"],
+ hdrs = ["src/sys/mman/pkey_mprotect.h"],
+ deps = [
+ ":__support_common",
+ ":__support_osutil_syscall",
+ ":errno",
+ ":mprotect",
+ ":types_size_t",
+ ],
+)
+
+libc_function(
+ name = "pkey_set",
+ srcs = ["src/sys/mman/linux/pkey_set.cpp"],
+ hdrs = ["src/sys/mman/pkey_set.h"],
+ deps = [
+ ":__support_common",
+ ":__support_error_or",
+ ":__support_osutil_syscall",
+ ":errno",
+ ":pkey_common",
+ ],
+)
+
+libc_support_library(
+ name = "pkey_common",
+ hdrs = select({
+ "@platforms//cpu:x86_64": ["src/sys/mman/linux/x86_64/pkey_common.h"],
+ "//conditions:default": ["src/sys/mman/linux/generic/pkey_common.h"],
+ }),
+ deps = [
+ ":__support_common",
+ ":__support_error_or",
+ ":hdr_errno_macros",
+ ":hdr_stdint_proxy",
+ ],
+)
+
libc_function(
name = "posix_madvise",
srcs = ["src/sys/mman/linux/posix_madvise.cpp"],
diff --git a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
index b44273123dcad..4a0a81a4b2057 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/UnitTest/BUILD.bazel
@@ -35,6 +35,7 @@ libc_test_library(
srcs = [
"BazelFilePath.cpp",
"ExecuteFunctionUnix.cpp",
+ "LibcDeathTestExecutors.cpp",
"LibcTest.cpp",
"LibcTestMain.cpp",
],
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
index e2c7f7a8bf60b..13353e2a1722b 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
@@ -92,6 +92,24 @@ libc_test(
],
)
+libc_test(
+ name = "pkey_test",
+ srcs = ["linux/pkey_test.cpp"],
+ deps = [
+ "//libc:hdr_errno_macros",
+ "//libc:hdr_signal_macros",
+ "//libc:mmap",
+ "//libc:munmap",
+ "//libc:pkey_alloc",
+ "//libc:pkey_free",
+ "//libc:pkey_get",
+ "//libc:pkey_mprotect",
+ "//libc:pkey_set",
+ "//libc:types_size_t",
+ "//libc/test/UnitTest:test_logger",
+ ],
+)
+
libc_test(
name = "posix_madvise_test",
srcs = ["linux/posix_madvise_test.cpp"],
>From 7147a2adc7c151e51d01ba912caba4300ea69228 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Tue, 7 Oct 2025 21:07:37 +0000
Subject: [PATCH 2/8] Add pkey_* to generated sys/mman.h. Fix formatting.
---
libc/include/sys/mman.yaml | 35 +++++++++++++++++++++++++++++++
libc/src/sys/mman/pkey_mprotect.h | 2 +-
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/libc/include/sys/mman.yaml b/libc/include/sys/mman.yaml
index 8c207552f9805..91b0f17313a26 100644
--- a/libc/include/sys/mman.yaml
+++ b/libc/include/sys/mman.yaml
@@ -101,6 +101,41 @@ functions:
arguments:
- type: void *
- type: size_t
+ - name: pkey_alloc
+ standards:
+ - Linux
+ return_type: int
+ arguments:
+ - type: unsigned int
+ - type: unsigned int
+ - name: pkey_free
+ standards:
+ - Linux
+ return_type: int
+ arguments:
+ - type: int
+ - name: pkey_get
+ standards:
+ - GNUExtensions
+ return_type: int
+ arguments:
+ - type: int
+ - name: pkey_mprotect
+ standards:
+ - Linux
+ return_type: int
+ arguments:
+ - type: void *
+ - type: size_t
+ - type: int
+ - type: int
+ - name: pkey_set
+ standards:
+ - GNUExtensions
+ return_type: int
+ arguments:
+ - type: int
+ - type: unsigned int
- name: posix_madvise
standards:
- POSIX
diff --git a/libc/src/sys/mman/pkey_mprotect.h b/libc/src/sys/mman/pkey_mprotect.h
index 4d19348ef09db..c02c61594ecc6 100644
--- a/libc/src/sys/mman/pkey_mprotect.h
+++ b/libc/src/sys/mman/pkey_mprotect.h
@@ -9,8 +9,8 @@
#ifndef LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
#define LLVM_LIBC_SRC_SYS_MMAN_PKEY_MPROTECT_H
-#include "src/__support/macros/config.h"
#include "hdr/types/size_t.h"
+#include "src/__support/macros/config.h"
namespace LIBC_NAMESPACE_DECL {
>From 50f264e7b6fc8322530d428310988d81f81ce773 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Tue, 7 Oct 2025 21:10:21 +0000
Subject: [PATCH 3/8] Add license headers.
---
libc/src/sys/mman/linux/generic/pkey_common.h | 8 ++++++++
libc/src/sys/mman/linux/x86_64/pkey_common.h | 8 ++++++++
2 files changed, 16 insertions(+)
diff --git a/libc/src/sys/mman/linux/generic/pkey_common.h b/libc/src/sys/mman/linux/generic/pkey_common.h
index 0811cfb77d4b0..51ffe870ce901 100644
--- a/libc/src/sys/mman/linux/generic/pkey_common.h
+++ b/libc/src/sys/mman/linux/generic/pkey_common.h
@@ -1,3 +1,11 @@
+//===---------- Generic stub implementations for pkey functionality. ------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
#ifndef LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
#define LLVM_SYS_MMAN_LINUX_GENERIC_PKEY_COMMON_H_
diff --git a/libc/src/sys/mman/linux/x86_64/pkey_common.h b/libc/src/sys/mman/linux/x86_64/pkey_common.h
index bffa9feaed06c..9535c9a37a8e8 100644
--- a/libc/src/sys/mman/linux/x86_64/pkey_common.h
+++ b/libc/src/sys/mman/linux/x86_64/pkey_common.h
@@ -1,3 +1,11 @@
+//===---------- x86_64-specific implementations for pkey_{get,set}. -------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
#ifndef LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
#define LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
>From e67b0c5d10d947613a27e08d4198cbd670fd3315 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Tue, 7 Oct 2025 21:36:34 +0000
Subject: [PATCH 4/8] Prefer direct use of 'asm volatile' over LIBC_INLINE_ASM.
---
libc/src/sys/mman/linux/x86_64/pkey_common.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/sys/mman/linux/x86_64/pkey_common.h b/libc/src/sys/mman/linux/x86_64/pkey_common.h
index 9535c9a37a8e8..17b0e3684c60d 100644
--- a/libc/src/sys/mman/linux/x86_64/pkey_common.h
+++ b/libc/src/sys/mman/linux/x86_64/pkey_common.h
@@ -32,7 +32,7 @@ constexpr int BITS_PER_KEY = 2;
LIBC_INLINE uint32_t read_prku() {
uint32_t pkru = 0;
uint32_t edx = 0;
- LIBC_INLINE_ASM("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
+ asm volatile("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
return pkru;
}
@@ -40,7 +40,7 @@ LIBC_INLINE uint32_t read_prku() {
// but this case should never be reached as a prior pkey_alloc invocation
// would have failed more gracefully.
LIBC_INLINE void write_prku(uint32_t pkru) {
- LIBC_INLINE_ASM("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
+ asm volatile("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
}
} // namespace internal
>From 30b1d224b9c636506294343a0314d236ca1a9669 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Mon, 3 Nov 2025 20:36:24 +0000
Subject: [PATCH 5/8] Use instrinsics instead of asm. Don't depend on mprotect
entrypoint.
---
libc/src/sys/mman/linux/CMakeLists.txt | 13 ++++++
libc/src/sys/mman/linux/generic/pkey_common.h | 8 ++--
libc/src/sys/mman/linux/mprotect.cpp | 18 +++-----
libc/src/sys/mman/linux/mprotect_common.h | 38 ++++++++++++++++
libc/src/sys/mman/linux/pkey_alloc.cpp | 2 +-
libc/src/sys/mman/linux/pkey_free.cpp | 2 +-
libc/src/sys/mman/linux/pkey_get.cpp | 1 -
libc/src/sys/mman/linux/pkey_mprotect.cpp | 29 ++++++++----
libc/src/sys/mman/linux/pkey_set.cpp | 1 -
libc/src/sys/mman/linux/x86_64/pkey_common.h | 44 ++++++-------------
.../llvm-project-overlay/libc/BUILD.bazel | 13 ++++++
.../libc/test/src/sys/mman/BUILD.bazel | 2 +-
12 files changed, 111 insertions(+), 60 deletions(-)
create mode 100644 libc/src/sys/mman/linux/mprotect_common.h
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 1c79180cbcabb..603c758127db0 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -57,6 +57,17 @@ add_entrypoint_object(
libc.src.errno.errno
)
+add_header_library(
+ mprotect_common
+ HDRS
+ mprotect_common.h
+ DEPENDS
+ libc.include.sys_syscall
+ libc.src.__support.OSUtil.osutil
+ libc.src.errno.errno
+ libc.src.__support.error_or
+)
+
add_entrypoint_object(
mprotect
SRCS
@@ -68,6 +79,7 @@ add_entrypoint_object(
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
libc.src.errno.errno
+ .mprotect_common
)
add_entrypoint_object(
@@ -225,6 +237,7 @@ add_entrypoint_object(
libc.src.__support.OSUtil.osutil
libc.src.sys.mman.mprotect
libc.src.errno.errno
+ .mprotect_common
)
add_entrypoint_object(
diff --git a/libc/src/sys/mman/linux/generic/pkey_common.h b/libc/src/sys/mman/linux/generic/pkey_common.h
index 51ffe870ce901..95f9a464fbd4a 100644
--- a/libc/src/sys/mman/linux/generic/pkey_common.h
+++ b/libc/src/sys/mman/linux/generic/pkey_common.h
@@ -16,14 +16,12 @@
namespace LIBC_NAMESPACE_DECL {
namespace pkey_common {
-LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
- (void)pkey;
+LIBC_INLINE ErrorOr<int> pkey_get([[maybe_unused]] int pkey) {
return Error(ENOSYS);
}
-LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
- (void)pkey;
- (void)access_rights;
+LIBC_INLINE ErrorOr<int> pkey_set([[maybe_unused]] int pkey,
+ [[maybe_unused]] unsigned int access_rights) {
return Error(ENOSYS);
}
diff --git a/libc/src/sys/mman/linux/mprotect.cpp b/libc/src/sys/mman/linux/mprotect.cpp
index 6b14915b60c94..c891f03a4713c 100644
--- a/libc/src/sys/mman/linux/mprotect.cpp
+++ b/libc/src/sys/mman/linux/mprotect.cpp
@@ -11,26 +11,22 @@
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
+#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
+#include "src/sys/mman/linux/mprotect_common.h"
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
-// This function is currently linux only. It has to be refactored suitably if
-// mprotect is to be supported on non-linux operating systems also.
LLVM_LIBC_FUNCTION(int, mprotect, (void *addr, size_t size, int prot)) {
- int ret = LIBC_NAMESPACE::syscall_impl<int>(
- SYS_mprotect, reinterpret_cast<long>(addr), size, prot);
-
- // A negative return value indicates an error with the magnitude of the
- // value being the error code.
- if (ret < 0) {
- libc_errno = -ret;
+ ErrorOr<int> result =
+ LIBC_NAMESPACE::mprotect_common::mprotect_impl(addr, size, prot);
+ if (!result.has_value()) {
+ libc_errno = result.error();
return -1;
}
-
- return 0;
+ return result.value();
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/mprotect_common.h b/libc/src/sys/mman/linux/mprotect_common.h
new file mode 100644
index 0000000000000..467a287614a38
--- /dev/null
+++ b/libc/src/sys/mman/linux/mprotect_common.h
@@ -0,0 +1,38 @@
+//===---------- Shared Linux implementation of POSIX mprotect. ------------===//
+//
+// 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/__support/common.h"
+#include "src/__support/error_or.h"
+#include "src/__support/libc_errno.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include <sys/syscall.h> // For syscall numbers.
+
+namespace LIBC_NAMESPACE_DECL {
+
+namespace mprotect_common {
+
+// This function is currently linux only. It has to be refactored suitably if
+// mprotect is to be supported on non-linux operating systems also.
+LIBC_INLINE ErrorOr<int> mprotect_impl(void *addr, size_t size, int prot) {
+ int ret = LIBC_NAMESPACE::syscall_impl<int>(
+ SYS_mprotect, reinterpret_cast<long>(addr), size, prot);
+
+ // A negative return value indicates an error with the magnitude of the
+ // value being the error code.
+ if (ret < 0) {
+ return Error(-ret);
+ }
+
+ return 0;
+}
+
+} // namespace mprotect_common
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_alloc.cpp b/libc/src/sys/mman/linux/pkey_alloc.cpp
index baf32013bc5c7..6ad65f342eb5e 100644
--- a/libc/src/sys/mman/linux/pkey_alloc.cpp
+++ b/libc/src/sys/mman/linux/pkey_alloc.cpp
@@ -27,7 +27,7 @@ LLVM_LIBC_FUNCTION(int, pkey_alloc,
int ret =
LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_alloc, flags, access_rights);
if (ret < 0) {
- libc_errno = static_cast<int>(-ret);
+ libc_errno = -ret;
return -1;
}
return static_cast<int>(ret);
diff --git a/libc/src/sys/mman/linux/pkey_free.cpp b/libc/src/sys/mman/linux/pkey_free.cpp
index 0228971bd10f6..328ba0468252e 100644
--- a/libc/src/sys/mman/linux/pkey_free.cpp
+++ b/libc/src/sys/mman/linux/pkey_free.cpp
@@ -25,7 +25,7 @@ LLVM_LIBC_FUNCTION(int, pkey_free, (int pkey)) {
#else
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_free, pkey);
if (ret < 0) {
- libc_errno = static_cast<int>(-ret);
+ libc_errno = -ret;
return -1;
}
return 0;
diff --git a/libc/src/sys/mman/linux/pkey_get.cpp b/libc/src/sys/mman/linux/pkey_get.cpp
index 623b7930c7a23..9e0ea107c9052 100644
--- a/libc/src/sys/mman/linux/pkey_get.cpp
+++ b/libc/src/sys/mman/linux/pkey_get.cpp
@@ -8,7 +8,6 @@
#include "src/sys/mman/pkey_get.h"
-#include "hdr/errno_macros.h" // For ENOSYS
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
diff --git a/libc/src/sys/mman/linux/pkey_mprotect.cpp b/libc/src/sys/mman/linux/pkey_mprotect.cpp
index 15c5d9db39b33..daa12fa927f87 100644
--- a/libc/src/sys/mman/linux/pkey_mprotect.cpp
+++ b/libc/src/sys/mman/linux/pkey_mprotect.cpp
@@ -12,34 +12,47 @@
#include "hdr/types/size_t.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
+#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/config.h"
-#include "src/sys/mman/mprotect.h"
+#include "src/sys/mman/linux/mprotect_common.h"
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
+namespace internal {
-LLVM_LIBC_FUNCTION(int, pkey_mprotect,
- (void *addr, size_t len, int prot, int pkey)) {
+LIBC_INLINE ErrorOr<int> pkey_mprotect_impl(void *addr, size_t len, int prot,
+ int pkey) {
// Fall back to mprotect if pkey is -1
// to maintain compatibility with kernel versions that don't support pkey.
if (pkey == -1) {
- return LIBC_NAMESPACE::mprotect(addr, len, prot);
+ return LIBC_NAMESPACE::mprotect_common::mprotect_impl(addr, len, prot);
}
#if !defined(SYS_pkey_mprotect)
- libc_errno = ENOSYS;
- return -1;
+ return Error(ENOSYS);
#else
int ret = LIBC_NAMESPACE::syscall_impl<int>(SYS_pkey_mprotect, addr, len,
prot, pkey);
if (ret < 0) {
- libc_errno = -ret;
- return -1;
+ return Error(-ret);
}
return 0;
#endif
}
+} // namespace internal
+
+LLVM_LIBC_FUNCTION(int, pkey_mprotect,
+ (void *addr, size_t len, int prot, int pkey)) {
+ ErrorOr<int> ret =
+ LIBC_NAMESPACE::internal::pkey_mprotect_impl(addr, len, prot, pkey);
+ if (!ret.has_value()) {
+ libc_errno = ret.error();
+ return -1;
+ }
+ return ret.value();
+}
+
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/sys/mman/linux/pkey_set.cpp b/libc/src/sys/mman/linux/pkey_set.cpp
index 7921443f688d3..60527877f39c1 100644
--- a/libc/src/sys/mman/linux/pkey_set.cpp
+++ b/libc/src/sys/mman/linux/pkey_set.cpp
@@ -8,7 +8,6 @@
#include "src/sys/mman/pkey_set.h"
-#include "hdr/errno_macros.h" // For ENOSYS
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
diff --git a/libc/src/sys/mman/linux/x86_64/pkey_common.h b/libc/src/sys/mman/linux/x86_64/pkey_common.h
index 17b0e3684c60d..cb657750112c9 100644
--- a/libc/src/sys/mman/linux/x86_64/pkey_common.h
+++ b/libc/src/sys/mman/linux/x86_64/pkey_common.h
@@ -9,6 +9,8 @@
#ifndef LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
#define LLVM_SYS_MMAN_LINUX_X86_64_PKEY_COMMON_H_
+#include <immintrin.h>
+
#include "hdr/errno_macros.h" // For ENOSYS
#include "hdr/stdint_proxy.h"
#include "src/__support/common.h"
@@ -20,55 +22,35 @@
namespace LIBC_NAMESPACE_DECL {
namespace pkey_common {
-namespace internal {
-constexpr int MAX_KEY = 15;
+constexpr int KEY_COUNT = 16;
constexpr int KEY_MASK = 0x3;
constexpr int BITS_PER_KEY = 2;
-// This will SIGILL on CPUs that don't support PKU / OSPKE,
-// but this case should never be reached as a prior pkey_alloc invocation
-// would have failed more gracefully.
-LIBC_INLINE uint32_t read_prku() {
- uint32_t pkru = 0;
- uint32_t edx = 0;
- asm volatile("rdpkru" : "=a"(pkru), "=d"(edx) : "c"(0));
- return pkru;
-}
-
-// This will SIGILL on CPUs that don't support PKU / OSPKE,
-// but this case should never be reached as a prior pkey_alloc invocation
-// would have failed more gracefully.
-LIBC_INLINE void write_prku(uint32_t pkru) {
- asm volatile("wrpkru" : : "a"(pkru), "d"(0), "c"(0));
-}
-
-} // namespace internal
-
// x86_64 implementation of pkey_get.
// Returns the access rights for the given pkey on success, errno otherwise.
+[[gnu::target("pku")]]
LIBC_INLINE ErrorOr<int> pkey_get(int pkey) {
- if (pkey < 0 || pkey > internal::MAX_KEY) {
+ if (pkey < 0 || pkey >= KEY_COUNT) {
return Error(EINVAL);
}
- uint32_t pkru = internal::read_prku();
- return (pkru >> (pkey * internal::BITS_PER_KEY)) & internal::KEY_MASK;
+ uint32_t pkru = _rdpkru_u32();
+ return (pkru >> (pkey * BITS_PER_KEY)) & KEY_MASK;
}
// x86_64 implementation of pkey_set.
// Returns 0 on success, errno otherwise.
+[[gnu::target("pku")]]
LIBC_INLINE ErrorOr<int> pkey_set(int pkey, unsigned int access_rights) {
- if (pkey < 0 || pkey > internal::MAX_KEY ||
- access_rights > internal::KEY_MASK) {
+ if (pkey < 0 || pkey >= KEY_COUNT || access_rights > KEY_MASK) {
return Error(EINVAL);
}
- uint32_t pkru = internal::read_prku();
- pkru &= ~(internal::KEY_MASK << (pkey * internal::BITS_PER_KEY));
- pkru |=
- ((access_rights & internal::KEY_MASK) << (pkey * internal::BITS_PER_KEY));
- internal::write_prku(pkru);
+ uint32_t pkru = _rdpkru_u32();
+ pkru &= ~(KEY_MASK << (pkey * BITS_PER_KEY));
+ pkru |= ((access_rights & KEY_MASK) << (pkey * BITS_PER_KEY));
+ _wrpkru(pkru);
return 0;
}
diff --git a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
index 5bd45df678085..2b3a4b8a4ac1b 100644
--- a/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/BUILD.bazel
@@ -6833,6 +6833,17 @@ libc_function(
],
)
+libc_support_library(
+ name = "mprotect_common",
+ hdrs = ["src/sys/mman/linux/mprotect_common.h"],
+ deps = [
+ ":__support_common",
+ ":__support_error_or",
+ ":__support_osutil_syscall",
+ ":errno",
+ ],
+)
+
libc_function(
name = "mprotect",
srcs = ["src/sys/mman/linux/mprotect.cpp"],
@@ -6841,6 +6852,7 @@ libc_function(
":__support_common",
":__support_osutil_syscall",
":errno",
+ ":mprotect_common",
],
)
@@ -6943,6 +6955,7 @@ libc_function(
":__support_osutil_syscall",
":errno",
":mprotect",
+ ":mprotect_common",
":types_size_t",
],
)
diff --git a/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel b/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
index 13353e2a1722b..1468ec95dfe5a 100644
--- a/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
+++ b/utils/bazel/llvm-project-overlay/libc/test/src/sys/mman/BUILD.bazel
@@ -2,7 +2,7 @@
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-# Tests for LLVM libc socket.h functions.
+# Tests for LLVM libc mman.h functions.
load("//libc/test:libc_test_rules.bzl", "libc_test")
>From 178002701605b48875f6a2ec1f03a6558ed99634 Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Mon, 3 Nov 2025 20:48:10 +0000
Subject: [PATCH 6/8] Remove dependency on mprotect entrypoint from
pkey_mprotect.
---
libc/src/sys/mman/linux/CMakeLists.txt | 1 -
1 file changed, 1 deletion(-)
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 603c758127db0..fce00f0839728 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -235,7 +235,6 @@ add_entrypoint_object(
libc.include.sys_mman
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
- libc.src.sys.mman.mprotect
libc.src.errno.errno
.mprotect_common
)
>From ee0b3127f2b04f868670de64e1840e011deba42f Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Mon, 3 Nov 2025 20:57:20 +0000
Subject: [PATCH 7/8] Format files.
---
libc/src/sys/mman/linux/mprotect_common.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/sys/mman/linux/mprotect_common.h b/libc/src/sys/mman/linux/mprotect_common.h
index 467a287614a38..5cd354f9919dd 100644
--- a/libc/src/sys/mman/linux/mprotect_common.h
+++ b/libc/src/sys/mman/linux/mprotect_common.h
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
+#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include "src/__support/common.h"
#include "src/__support/error_or.h"
#include "src/__support/libc_errno.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
-#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE_DECL {
>From 75b4ab691059180fd815b1259d31662d934880cf Mon Sep 17 00:00:00 2001
From: Jackson Stogel <jtstogel at gmail.com>
Date: Thu, 6 Nov 2025 20:25:37 +0000
Subject: [PATCH 8/8] Fix standard specification in mman.yaml
---
libc/include/sys/mman.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/include/sys/mman.yaml b/libc/include/sys/mman.yaml
index 91b0f17313a26..f9ab0c1001c3d 100644
--- a/libc/include/sys/mman.yaml
+++ b/libc/include/sys/mman.yaml
@@ -116,7 +116,7 @@ functions:
- type: int
- name: pkey_get
standards:
- - GNUExtensions
+ - GNU
return_type: int
arguments:
- type: int
@@ -131,7 +131,7 @@ functions:
- type: int
- name: pkey_set
standards:
- - GNUExtensions
+ - GNU
return_type: int
arguments:
- type: int
More information about the llvm-commits
mailing list