-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[libc][mmap] implement mmap in terms of mmap2 for 32b targets #96700
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,56 +8,90 @@ | |
|
|
||
| #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. | ||
| #ifndef __LP64__ | ||
|
|
||
| // 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; | ||
| } | ||
|
Comment on lines
+48
to
+59
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @enh-google any sense how much validation needs to be done here vs is already handled by the kernel?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for LP64, you don't need any of this. (that's why you couldn't find bionic's LP64 code --- it's a trivial generated assembler stub.) for ILP32 ... these are the checks you'll have to do yourself, because these are the checks that tell you whether your translation to mmap2(2) is valid or not. given that you've got everything in the same file,
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds like I should add some 32b only tests then. |
||
|
|
||
| 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 // __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 __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. | ||
|
|
||
| // 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); | ||
| #else | ||
| return mmap64(addr, size, prot, flags, fd, static_cast<off64_t>(offset)); | ||
| #endif // __LP64__ | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably also isn't correct since we don't express in cmake a dependency on
getauxvalformmap(which leads to inf recursion in our cmake). Hence the below question about parameter validation.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you probably don't want to have to search auxv for the page size either. you could stash the page size at libc startup, but bionic currently just has this as
note also that getpagesize() is a public function that you'll end up implementing eventually anyway. (which is one reason why i went this route in bionic rather than the startup special case that iirc musl does.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do, which is what's done for the
LIBC_FULL_BUILDcheck. The issue becomes that llvm-libc supports building with literally justmmapas the only symbol in the resulting libc.a, so we also additionally need to support "getting the page size" without the ability to reach into the system libc's private book keeping.