| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/test/test_shared_memory_util.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| |
| #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL) |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <unistd.h> |
| #endif |
| |
| #if BUILDFLAG(IS_FUCHSIA) |
| #include <lib/zx/vmar.h> |
| #include <zircon/rights.h> |
| #endif |
| |
| #if BUILDFLAG(IS_APPLE) |
| #include <mach/vm_map.h> |
| #endif |
| |
| #if BUILDFLAG(IS_WIN) |
| #include <aclapi.h> |
| #endif |
| |
| namespace base { |
| |
| #if !BUILDFLAG(IS_NACL) |
| |
| static const size_t kDataSize = 1024; |
| |
| // Common routine used with Posix file descriptors. Check that shared memory |
| // file descriptor |fd| does not allow writable mappings. Return true on |
| // success, false otherwise. |
| #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) |
| static bool CheckReadOnlySharedMemoryFdPosix(int fd) { |
| // Note that the error on Android is EPERM, unlike other platforms where |
| // it will be EACCES. |
| #if BUILDFLAG(IS_ANDROID) |
| const int kExpectedErrno = EPERM; |
| #else |
| const int kExpectedErrno = EACCES; |
| #endif |
| errno = 0; |
| void* address = |
| mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); |
| const bool success = (address != nullptr) && (address != MAP_FAILED); |
| if (success) { |
| LOG(ERROR) << "mmap() should have failed!"; |
| munmap(address, kDataSize); // Cleanup. |
| return false; |
| } |
| if (errno != kExpectedErrno) { |
| LOG(ERROR) << "Expected mmap() to return " << kExpectedErrno |
| << " but returned " << errno << ": " << strerror(errno) << "\n"; |
| return false; |
| } |
| return true; |
| } |
| #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE) |
| |
| #if BUILDFLAG(IS_FUCHSIA) |
| // Fuchsia specific implementation. |
| bool CheckReadOnlySharedMemoryFuchsiaHandle(zx::unowned_vmo handle) { |
| const uint32_t flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; |
| uintptr_t addr; |
| const zx_status_t status = |
| zx::vmar::root_self()->map(flags, 0, *handle, 0U, kDataSize, &addr); |
| if (status == ZX_OK) { |
| LOG(ERROR) << "zx_vmar_map() should have failed!"; |
| zx::vmar::root_self()->unmap(addr, kDataSize); |
| return false; |
| } |
| if (status != ZX_ERR_ACCESS_DENIED) { |
| LOG(ERROR) << "Expected zx_vmar_map() to return " << ZX_ERR_ACCESS_DENIED |
| << " (ZX_ERR_ACCESS_DENIED) but returned " << status << "\n"; |
| return false; |
| } |
| return true; |
| } |
| |
| #elif BUILDFLAG(IS_APPLE) |
| bool CheckReadOnlySharedMemoryMachPort(mach_port_t memory_object) { |
| vm_address_t memory; |
| const kern_return_t kr = |
| vm_map(mach_task_self(), &memory, kDataSize, 0, VM_FLAGS_ANYWHERE, |
| memory_object, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE, |
| VM_PROT_READ | VM_PROT_WRITE | VM_PROT_IS_MASK, VM_INHERIT_NONE); |
| if (kr == KERN_SUCCESS) { |
| LOG(ERROR) << "vm_map() should have failed!"; |
| vm_deallocate(mach_task_self(), memory, kDataSize); // Cleanup. |
| return false; |
| } |
| return true; |
| } |
| |
| #elif BUILDFLAG(IS_WIN) |
| bool CheckReadOnlySharedMemoryWindowsHandle(HANDLE handle) { |
| void* memory = |
| MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kDataSize); |
| if (memory != nullptr) { |
| LOG(ERROR) << "MapViewOfFile() should have failed!"; |
| UnmapViewOfFile(memory); |
| return false; |
| } |
| return true; |
| } |
| #endif |
| |
| bool CheckReadOnlyPlatformSharedMemoryRegionForTesting( |
| subtle::PlatformSharedMemoryRegion region) { |
| if (region.GetMode() != subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) { |
| LOG(ERROR) << "Expected region mode is " |
| << static_cast<int>( |
| subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) |
| << " but actual is " << static_cast<int>(region.GetMode()); |
| return false; |
| } |
| |
| #if BUILDFLAG(IS_APPLE) |
| return CheckReadOnlySharedMemoryMachPort(region.GetPlatformHandle()); |
| #elif BUILDFLAG(IS_FUCHSIA) |
| return CheckReadOnlySharedMemoryFuchsiaHandle(region.GetPlatformHandle()); |
| #elif BUILDFLAG(IS_WIN) |
| return CheckReadOnlySharedMemoryWindowsHandle(region.GetPlatformHandle()); |
| #elif BUILDFLAG(IS_ANDROID) || defined(STARBOARD) |
| return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle()); |
| #else |
| return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle().fd); |
| #endif |
| } |
| |
| #endif // !BUILDFLAG(IS_NACL) |
| |
| WritableSharedMemoryMapping MapForTesting( |
| subtle::PlatformSharedMemoryRegion* region) { |
| return MapAtForTesting(region, 0, region->GetSize()); |
| } |
| |
| WritableSharedMemoryMapping MapAtForTesting( |
| subtle::PlatformSharedMemoryRegion* region, |
| uint64_t offset, |
| size_t size) { |
| SharedMemoryMapper* mapper = SharedMemoryMapper::GetDefaultInstance(); |
| auto result = region->MapAt(offset, size, mapper); |
| if (!result.has_value()) |
| return {}; |
| |
| return WritableSharedMemoryMapping(result.value(), size, region->GetGUID(), |
| mapper); |
| } |
| |
| template <> |
| std::pair<ReadOnlySharedMemoryRegion, WritableSharedMemoryMapping> |
| CreateMappedRegion(size_t size) { |
| MappedReadOnlyRegion mapped_region = ReadOnlySharedMemoryRegion::Create(size); |
| return {std::move(mapped_region.region), std::move(mapped_region.mapping)}; |
| } |
| |
| } // namespace base |