1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <elfload/elfload.h> 6#include <limits.h> 7#include <zircon/process.h> 8#include <zircon/processargs.h> 9#include <zircon/syscalls.h> 10#include <zxcpp/new.h> 11#include <lib/zx/process.h> 12#include <lib/zx/vmar.h> 13#include <lib/zx/vmo.h> 14#include <fbl/array.h> 15#include <string.h> 16#include <unittest/unittest.h> 17 18static const zx::vmo vdso_vmo{zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0))}; 19 20class ScratchPad { 21public: 22 ScratchPad() = delete; 23 ScratchPad(const char* name) { 24 EXPECT_EQ(zx_process_create( 25 zx_job_default(), 26 name, static_cast<uint32_t>(strlen(name)), 0, 27 process_.reset_and_get_address(), 28 root_vmar_.reset_and_get_address()), 29 ZX_OK, "zx_process_create"); 30 } 31 32 const zx::vmar& root_vmar() const { return root_vmar_; } 33 uintptr_t vdso_base() const { return vdso_base_; } 34 uintptr_t vdso_code_offset() const { return vdso_code_offset_; } 35 uintptr_t vdso_code_size() const { return vdso_code_size_; } 36 uintptr_t vdso_code_address() const { 37 return vdso_base_ + vdso_code_offset_; 38 } 39 uintptr_t vdso_total_size() const { 40 return vdso_code_offset_ + vdso_code_size_; 41 } 42 43 zx_status_t load_vdso(zx::vmar* segments_vmar = nullptr, 44 bool really_load = true) { 45 elf_load_header_t header; 46 uintptr_t phoff; 47 zx_status_t status = elf_load_prepare(vdso_vmo.get(), nullptr, 0, 48 &header, &phoff); 49 if (status == ZX_OK) { 50 fbl::Array<elf_phdr_t> phdrs(new elf_phdr_t[header.e_phnum], 51 header.e_phnum); 52 status = elf_load_read_phdrs(vdso_vmo.get(), phdrs.get(), phoff, 53 header.e_phnum); 54 if (status == ZX_OK) { 55 for (const auto& ph : phdrs) { 56 if (ph.p_type == PT_LOAD && (ph.p_type & PF_X)) { 57 vdso_code_offset_ = ph.p_vaddr; 58 vdso_code_size_ = ph.p_memsz; 59 } 60 } 61 if (really_load) { 62 status = elf_load_map_segments( 63 root_vmar_.get(), &header, phdrs.get(), vdso_vmo.get(), 64 segments_vmar ? segments_vmar->reset_and_get_address() : nullptr, 65 &vdso_base_, nullptr); 66 } 67 } 68 } 69 return status; 70 } 71 72 zx_status_t compute_vdso_sizes() { 73 return load_vdso(nullptr, false); 74 } 75 76private: 77 zx::process process_; 78 zx::vmar root_vmar_; 79 uintptr_t vdso_base_ = 0; 80 uintptr_t vdso_code_offset_ = 0; 81 uintptr_t vdso_code_size_ = 0; 82}; 83 84bool vdso_map_twice_test() { 85 BEGIN_TEST; 86 87 ScratchPad scratch(__func__); 88 89 // Load the vDSO once. That's on me. 90 EXPECT_EQ(scratch.load_vdso(), ZX_OK, "load vDSO into empty process"); 91 92 // Load the vDSO twice. Can't get loaded again. 93 EXPECT_EQ(scratch.load_vdso(), ZX_ERR_ACCESS_DENIED, "load vDSO second time"); 94 95 END_TEST; 96} 97 98bool vdso_map_change_test() { 99 BEGIN_TEST; 100 101 ScratchPad scratch(__func__); 102 103 // Load the vDSO and hold onto the sub-VMAR. 104 zx::vmar vdso_vmar; 105 EXPECT_EQ(scratch.load_vdso(&vdso_vmar), ZX_OK, "load vDSO"); 106 107 // Changing protections on the code pages is forbidden. 108 EXPECT_EQ(vdso_vmar.protect(scratch.vdso_code_address(), 109 scratch.vdso_code_size(), 110 ZX_VM_PERM_READ), 111 ZX_ERR_ACCESS_DENIED, "zx_vmar_protect on vDSO code"); 112 113 zx::vmo vmo; 114 ASSERT_EQ(zx::vmo::create(scratch.vdso_total_size(), 0, &vmo), 115 ZX_OK, "zx_vmo_create"); 116 117 // Implicit unmapping by overwriting the mapping is forbidden. 118 uintptr_t addr = 0; 119 EXPECT_EQ(vdso_vmar.map(0, vmo, 0, scratch.vdso_total_size(), 120 ZX_VM_PERM_READ | 121 ZX_VM_SPECIFIC_OVERWRITE, 122 &addr), 123 ZX_ERR_ACCESS_DENIED, "zx_vmar_map to overmap vDSO"); 124 EXPECT_EQ(addr, 0, "zx_vmar_map to overmap vDSO"); 125 126 // Also forbidden if done from a parent VMAR. 127 zx_info_vmar_t root_vmar_info; 128 ASSERT_EQ(scratch.root_vmar().get_info(ZX_INFO_VMAR, &root_vmar_info, 129 sizeof(root_vmar_info), 130 nullptr, nullptr), 131 ZX_OK, "zx_object_get_info on root VMAR"); 132 EXPECT_EQ(scratch.root_vmar(). 133 map(scratch.vdso_base() - root_vmar_info.base, vmo, 134 0, scratch.vdso_total_size(), 135 ZX_VM_PERM_READ | ZX_VM_SPECIFIC_OVERWRITE, 136 &addr), 137 ZX_ERR_ACCESS_DENIED, "zx_vmar_map to overmap vDSO from root"); 138 EXPECT_EQ(addr, 0, "zx_vmar_map to overmap vDSO from root"); 139 140 // Explicit unmapping covering the vDSO code region is forbidden. 141 EXPECT_EQ(scratch.root_vmar().unmap(scratch.vdso_base(), 142 scratch.vdso_total_size()), 143 ZX_ERR_ACCESS_DENIED, "zx_vmar_unmap to unmap vDSO"); 144 145 // Implicit unmapping by destroying a containing VMAR is forbidden. 146 EXPECT_EQ(vdso_vmar.destroy(), 147 ZX_ERR_ACCESS_DENIED, "zx_vmar_destroy to unmap vDSO"); 148 EXPECT_EQ(scratch.root_vmar().destroy(), 149 ZX_ERR_ACCESS_DENIED, "zx_vmar_destroy on root to unmap vDSO"); 150 151 END_TEST; 152} 153 154bool vdso_map_code_wrong_test() { 155 BEGIN_TEST; 156 157 ScratchPad scratch(__func__); 158 159 ASSERT_EQ(scratch.compute_vdso_sizes(), ZX_OK, 160 "cannot read vDSO program headers"); 161 162 // Try to map the first page, which is not the code, as executable. 163 uintptr_t addr; 164 EXPECT_EQ(scratch.root_vmar(). 165 map(0, vdso_vmo, 0, PAGE_SIZE, 166 ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, &addr), 167 ZX_ERR_ACCESS_DENIED, "executable mapping of wrong part of vDSO"); 168 169 // Try to map only part of the code, not the whole code segment. 170 ASSERT_GE(scratch.vdso_code_size(), PAGE_SIZE, "vDSO code < page??"); 171 if (scratch.vdso_code_size() > PAGE_SIZE) { 172 ASSERT_EQ(scratch.vdso_code_size() % PAGE_SIZE, 0); 173 EXPECT_EQ(scratch.root_vmar(). 174 map(0, vdso_vmo, scratch.vdso_code_offset(), PAGE_SIZE, 175 ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, &addr), 176 ZX_ERR_ACCESS_DENIED, 177 "executable mapping of subset of vDSO code"); 178 } 179 180 END_TEST; 181} 182 183BEGIN_TEST_CASE(vdso_tests) 184RUN_TEST(vdso_map_twice_test); 185RUN_TEST(vdso_map_code_wrong_test); 186RUN_TEST(vdso_map_change_test); 187END_TEST_CASE(vdso_tests) 188 189int main(int argc, char** argv) { 190 return unittest_run_all_tests(argc, argv) ? 0 : -1; 191} 192