[libc-commits] [libc] [libc][mmap] implement mmap in terms of mmap2 for 32b targets (PR #96700)
Nick Desaulniers via libc-commits
libc-commits at lists.llvm.org
Tue Jun 25 14:48:00 PDT 2024
https://github.com/nickdesaulniers updated https://github.com/llvm/llvm-project/pull/96700
>From b50692594164c34e99506e88747086d0e6e176d7 Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Tue, 25 Jun 2024 10:58:20 -0700
Subject: [PATCH 1/2] [libc][mmap] implement mmap in terms of mmap2 for 32b
targets
Port Bionic's mmap64 implementation.
Link: https://android.googlesource.com/platform/bionic/+/main/libc/bionic/legacy_32_bit_support.cpp#117
---
libc/hdr/CMakeLists.txt | 16 ++++
libc/hdr/sys_auxv_macros.h | 22 ++++++
libc/hdr/sys_mman_macros.h | 22 ++++++
libc/hdr/types/CMakeLists.txt | 12 +++
libc/hdr/types/off64_t.h | 22 ++++++
libc/hdr/types/off_t.h | 22 ++++++
libc/src/sys/auxv/getauxval.h | 2 -
libc/src/sys/mman/linux/CMakeLists.txt | 5 ++
libc/src/sys/mman/linux/mmap.cpp | 91 +++++++++++++++-------
libc/src/sys/mman/mmap.h | 4 +-
libc/test/src/sys/mman/linux/mmap_test.cpp | 14 ++++
11 files changed, 201 insertions(+), 31 deletions(-)
create mode 100644 libc/hdr/sys_auxv_macros.h
create mode 100644 libc/hdr/sys_mman_macros.h
create mode 100644 libc/hdr/types/off64_t.h
create mode 100644 libc/hdr/types/off_t.h
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index 66b82c84dac49..b7c887e9b3da2 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -97,4 +97,20 @@ add_proxy_header_library(
libc.include.float
)
+add_proxy_header_library(
+ sys_mman_macros
+ HDRS
+ sys_mman_macros.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-macros.sys_mman_macros
+)
+
+add_proxy_header_library(
+ sys_auxv_macros
+ HDRS
+ sys_auxv_macros.h
+ FULL_BUILD_DEPENDS
+ libc.include.llvm-libc-macros.sys_auxv_macros
+)
+
add_subdirectory(types)
diff --git a/libc/hdr/sys_auxv_macros.h b/libc/hdr/sys_auxv_macros.h
new file mode 100644
index 0000000000000..7fae034937a81
--- /dev/null
+++ b/libc/hdr/sys_auxv_macros.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from sys/auxv.h ------------------------------===//
+//
+// 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_HDR_SYS_AUXV_MACROS_H
+#define LLVM_LIBC_HDR_SYS_AUXV_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-auxv-macros.h"
+
+#else // Overlay mode
+
+#include <sys/auxv.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS/AUXV_MACROS_H
diff --git a/libc/hdr/sys_mman_macros.h b/libc/hdr/sys_mman_macros.h
new file mode 100644
index 0000000000000..a656db48e96c3
--- /dev/null
+++ b/libc/hdr/sys_mman_macros.h
@@ -0,0 +1,22 @@
+//===-- Definition of macros from sys/mman.h ------------------------------===//
+//
+// 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_HDR_SYS_MMAN_MACROS_H
+#define LLVM_LIBC_HDR_SYS_MMAN_MACROS_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-macros/sys-mman-macros.h"
+
+#else // Overlay mode
+
+#include <sys/mman.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_SYS/MMAN_MACROS_H
diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt
index 9b3373a0ca390..b4da1da4b1bda 100644
--- a/libc/hdr/types/CMakeLists.txt
+++ b/libc/hdr/types/CMakeLists.txt
@@ -126,3 +126,15 @@ add_proxy_header_library(
libc.include.llvm-libc-types.atexithandler_t
libc.include.stdlib
)
+
+add_proxy_header_library(
+ off_t
+ HDRS
+ off_t.h
+)
+
+add_proxy_header_library(
+ off64_t
+ HDRS
+ off64_t.h
+)
diff --git a/libc/hdr/types/off64_t.h b/libc/hdr/types/off64_t.h
new file mode 100644
index 0000000000000..183d58e6141a1
--- /dev/null
+++ b/libc/hdr/types/off64_t.h
@@ -0,0 +1,22 @@
+//===-- Proxy header for the off64_t type ---------------------------------===//
+//
+// 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_HDR_OFF64_T_H
+#define LLVM_LIBC_HDR_OFF64_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/off64_t.h"
+
+#else // overlay mode
+
+#include <sys/types.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_OFF64_T_H
diff --git a/libc/hdr/types/off_t.h b/libc/hdr/types/off_t.h
new file mode 100644
index 0000000000000..cbeee1c7eec97
--- /dev/null
+++ b/libc/hdr/types/off_t.h
@@ -0,0 +1,22 @@
+//===-- Proxy header for the off_t type -----------------------------------===//
+//
+// 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_HDR_OFF_T_H
+#define LLVM_LIBC_HDR_OFF_T_H
+
+#ifdef LIBC_FULL_BUILD
+
+#include "include/llvm-libc-types/off_t.h"
+
+#else // overlay mode
+
+#include <sys/types.h>
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_OFF_T_H
diff --git a/libc/src/sys/auxv/getauxval.h b/libc/src/sys/auxv/getauxval.h
index 7c9fb846e9198..40b7a934083c3 100644
--- a/libc/src/sys/auxv/getauxval.h
+++ b/libc/src/sys/auxv/getauxval.h
@@ -9,8 +9,6 @@
#ifndef LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
#define LLVM_LIBC_SRC_SYS_AUXV_GETAUXVAL_H
-#include <sys/auxv.h>
-
namespace LIBC_NAMESPACE {
unsigned long getauxval(unsigned long id);
diff --git a/libc/src/sys/mman/linux/CMakeLists.txt b/libc/src/sys/mman/linux/CMakeLists.txt
index 00f4f0e64ec06..7bc5f16702c8e 100644
--- a/libc/src/sys/mman/linux/CMakeLists.txt
+++ b/libc/src/sys/mman/linux/CMakeLists.txt
@@ -18,9 +18,14 @@ add_entrypoint_object(
HDRS
../mmap.h
DEPENDS
+ libc.hdr.sys_auxv_macros
+ libc.hdr.sys_mman_macros
+ libc.hdr.types.off64_t
+ libc.hdr.types.off_t
libc.include.sys_mman
libc.include.sys_syscall
libc.src.__support.OSUtil.osutil
+ libc.src.__support.block
libc.src.errno.errno
)
diff --git a/libc/src/sys/mman/linux/mmap.cpp b/libc/src/sys/mman/linux/mmap.cpp
index 16111c66859f5..f5b065411b994 100644
--- a/libc/src/sys/mman/linux/mmap.cpp
+++ b/libc/src/sys/mman/linux/mmap.cpp
@@ -8,56 +8,91 @@
#include "src/sys/mman/mmap.h"
+#include "config/linux/app.h" // app
+#include "hdr/sys_auxv_macros.h" // AT_PAGESZ
+#include "hdr/sys_mman_macros.h" // MAP_FAILED
+#include "hdr/types/off64_t.h"
+#include "hdr/types/off_t.h"
#include "src/__support/OSUtil/syscall.h" // For internal syscall function.
+#include "src/__support/block.h" // align_up
#include "src/__support/common.h"
-
#include "src/errno/libc_errno.h"
-#include <linux/param.h> // For EXEC_PAGESIZE.
+#include "src/sys/auxv/getauxval.h"
+
+#include <stddef.h> // size_t
+#include <stdint.h> // PTRDIFF_MAX
#include <sys/syscall.h> // For syscall numbers.
namespace LIBC_NAMESPACE {
+// Older 32b systems generally have a SYS_mmap2 that accepts a 32b value which
+// was a 64b value shifted down by 12; this magic constant isn't exposed via
+// UAPI headers, but its in kernel sources for mmap2 implementations.
+#ifdef SYS_mmap2
+
+// TODO: move these helpers to OSUtil?
+#ifdef LIBC_FULL_BUILD
+unsigned long get_page_size() { return app.page_size; }
+#else
+unsigned long get_page_size() {
+ // TODO: is it ok for mmap to call getauxval in overlay mode? Or is there a
+ // risk of infinite recursion?
+ return ::getauxval(AT_PAGESZ);
+}
+#endif // LIBC_FULL_BUILD
+
+void *mmap64(void *addr, size_t size, int prot, int flags, int fd,
+ off64_t offset) {
+ constexpr size_t MMAP2_SHIFT = 12;
+
+ if (offset < 0 || offset % (1UL << MMAP2_SHIFT)) {
+ libc_errno = EINVAL;
+ return MAP_FAILED;
+ }
+
+ // Prevent allocations large enough for `end - start` to overflow,
+ // to avoid security bugs.
+ size_t rounded = align_up(size, get_page_size());
+ if (rounded < size || rounded > PTRDIFF_MAX) {
+ libc_errno = ENOMEM;
+ return MAP_FAILED;
+ }
+
+ long ret = syscall_impl(SYS_mmap2, reinterpret_cast<long>(addr), size, prot,
+ flags, fd, static_cast<long>(offset >> MMAP2_SHIFT));
+
+ if (ret < 0) {
+ libc_errno = static_cast<int>(-ret);
+ return MAP_FAILED;
+ }
+
+ return reinterpret_cast<void *>(ret);
+}
+#endif // SYS_mmap2
+
// This function is currently linux only. It has to be refactored suitably if
// mmap is to be supported on non-linux operating systems also.
LLVM_LIBC_FUNCTION(void *, mmap,
(void *addr, size_t size, int prot, int flags, int fd,
off_t offset)) {
+#ifdef SYS_mmap2
+ return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset));
+#else
+
// A lot of POSIX standard prescribed validation of the parameters is not
// done in this function as modern linux versions do it in the syscall.
// TODO: Perform argument validation not done by the linux syscall.
- // EXEC_PAGESIZE is used for the page size. While this is OK for x86_64, it
- // might not be correct in general.
- // TODO: Use pagesize read from the ELF aux vector instead of EXEC_PAGESIZE.
+ long ret = LIBC_NAMESPACE::syscall_impl(
+ SYS_mmap, reinterpret_cast<long>(addr), size, prot, flags, fd, offset);
-#ifdef SYS_mmap2
- offset /= EXEC_PAGESIZE;
- long syscall_number = SYS_mmap2;
-#elif defined(SYS_mmap)
- long syscall_number = SYS_mmap;
-#else
-#error "mmap or mmap2 syscalls not available."
-#endif
-
- long ret =
- LIBC_NAMESPACE::syscall_impl(syscall_number, reinterpret_cast<long>(addr),
- size, prot, flags, fd, offset);
-
- // The mmap/mmap2 syscalls return negative values on error. These negative
- // values are actually the negative values of the error codes. So, fix them
- // up in case an error code is detected.
- //
- // A point to keep in mind for the fix up is that a negative return value
- // from the syscall can also be an error-free value returned by the syscall.
- // However, since a valid return address cannot be within the last page, a
- // return value corresponding to a location in the last page is an error
- // value.
- if (ret < 0 && ret > -EXEC_PAGESIZE) {
+ if (ret < 0) {
libc_errno = static_cast<int>(-ret);
return MAP_FAILED;
}
return reinterpret_cast<void *>(ret);
+#endif // SYS_mmap2
}
} // namespace LIBC_NAMESPACE
diff --git a/libc/src/sys/mman/mmap.h b/libc/src/sys/mman/mmap.h
index 4425019c4ee1a..cab8f863a66f0 100644
--- a/libc/src/sys/mman/mmap.h
+++ b/libc/src/sys/mman/mmap.h
@@ -9,7 +9,9 @@
#ifndef LLVM_LIBC_SRC_SYS_MMAN_MMAP_H
#define LLVM_LIBC_SRC_SYS_MMAN_MMAP_H
-#include <sys/mman.h> // For size_t and off_t
+#include "hdr/types/off_t.h"
+
+#include <stddef.h> // size_t
namespace LIBC_NAMESPACE {
diff --git a/libc/test/src/sys/mman/linux/mmap_test.cpp b/libc/test/src/sys/mman/linux/mmap_test.cpp
index dcbc75808f13c..2d4fd4468f81e 100644
--- a/libc/test/src/sys/mman/linux/mmap_test.cpp
+++ b/libc/test/src/sys/mman/linux/mmap_test.cpp
@@ -41,3 +41,17 @@ TEST(LlvmLibcMMapTest, Error_InvalidSize) {
EXPECT_THAT(LIBC_NAMESPACE::munmap(0, 0), Fails(EINVAL));
}
+
+TEST(LlvmLibcMMapTest, Error_NegativeOffset) {
+ LIBC_NAMESPACE::libc_errno = 0;
+ void *addr = LIBC_NAMESPACE::mmap(nullptr, 128, PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, -42);
+ EXPECT_THAT(addr, Fails(EINVAL, MAP_FAILED));
+}
+
+TEST(LlvmLibcMMapTest, Error_NonPageSizeMultipleOffset) {
+ LIBC_NAMESPACE::libc_errno = 0;
+ void *addr = LIBC_NAMESPACE::mmap(nullptr, 128, PROT_READ,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 7);
+ EXPECT_THAT(addr, Fails(EINVAL, MAP_FAILED));
+}
>From 825f18edab556f9fda1a662c0c16f61526ef66ff Mon Sep 17 00:00:00 2001
From: Nick Desaulniers <ndesaulniers at google.com>
Date: Tue, 25 Jun 2024 14:47:29 -0700
Subject: [PATCH 2/2] s/SYS_mmap2/!__LP64__
---
libc/src/sys/mman/linux/mmap.cpp | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/libc/src/sys/mman/linux/mmap.cpp b/libc/src/sys/mman/linux/mmap.cpp
index f5b065411b994..f7deab6689203 100644
--- a/libc/src/sys/mman/linux/mmap.cpp
+++ b/libc/src/sys/mman/linux/mmap.cpp
@@ -28,7 +28,7 @@ namespace LIBC_NAMESPACE {
// Older 32b systems generally have a SYS_mmap2 that accepts a 32b value which
// was a 64b value shifted down by 12; this magic constant isn't exposed via
// UAPI headers, but its in kernel sources for mmap2 implementations.
-#ifdef SYS_mmap2
+#ifndef __LP64__
// TODO: move these helpers to OSUtil?
#ifdef LIBC_FULL_BUILD
@@ -68,17 +68,14 @@ void *mmap64(void *addr, size_t size, int prot, int flags, int fd,
return reinterpret_cast<void *>(ret);
}
-#endif // SYS_mmap2
+#endif // __LP64__
// This function is currently linux only. It has to be refactored suitably if
// mmap is to be supported on non-linux operating systems also.
LLVM_LIBC_FUNCTION(void *, mmap,
(void *addr, size_t size, int prot, int flags, int fd,
off_t offset)) {
-#ifdef SYS_mmap2
- return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset));
-#else
-
+#ifdef __LP64__
// A lot of POSIX standard prescribed validation of the parameters is not
// done in this function as modern linux versions do it in the syscall.
// TODO: Perform argument validation not done by the linux syscall.
@@ -92,7 +89,9 @@ LLVM_LIBC_FUNCTION(void *, mmap,
}
return reinterpret_cast<void *>(ret);
-#endif // SYS_mmap2
+#else
+ return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset));
+#endif // __LP64__
}
} // namespace LIBC_NAMESPACE
More information about the libc-commits
mailing list