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 <dlfcn.h> 6#include <link.h> 7#include <zircon/dlfcn.h> 8#include <zircon/process.h> 9#include <zircon/processargs.h> 10#include <zircon/syscalls.h> 11#include <lib/fdio/util.h> 12#include <stdio.h> 13#include <sys/param.h> 14#include <string.h> 15 16#include <unittest/unittest.h> 17 18bool vdso_base_test(void) { 19 BEGIN_TEST; 20 21 char msg[128]; 22 23 struct link_map* lm = dlopen("libzircon.so", RTLD_NOLOAD); 24 snprintf(msg, sizeof(msg), "dlopen(\"libzircon.so\") failed: %s", 25 dlerror()); 26 EXPECT_NONNULL(lm, msg); 27 uintptr_t rtld_vdso_base = lm->l_addr; 28 int ok = dlclose(lm); 29 snprintf(msg, sizeof(msg), "dlclose failed: %s", dlerror()); 30 EXPECT_EQ(ok, 0, msg); 31 32 uintptr_t prop_vdso_base; 33 zx_status_t status = 34 zx_object_get_property(zx_process_self(), 35 ZX_PROP_PROCESS_VDSO_BASE_ADDRESS, 36 &prop_vdso_base, sizeof(prop_vdso_base)); 37 snprintf(msg, sizeof(msg), "zx_object_get_property failed: %d", status); 38 EXPECT_EQ(status, 0, msg); 39 40 EXPECT_EQ(rtld_vdso_base, prop_vdso_base, 41 "rtld reported address != process property reported address"); 42 43 END_TEST; 44} 45 46static int phdr_info_callback(struct dl_phdr_info* info, size_t size, 47 void* data) { 48 struct dl_phdr_info* key = data; 49 if (info->dlpi_addr == key->dlpi_addr) { 50 *key = *info; 51 return 1; 52 } 53 return 0; 54} 55 56bool vdso_unmap_test(void) { 57 BEGIN_TEST; 58 59 char msg[128]; 60 61 uintptr_t prop_vdso_base; 62 zx_status_t status = 63 zx_object_get_property(zx_process_self(), 64 ZX_PROP_PROCESS_VDSO_BASE_ADDRESS, 65 &prop_vdso_base, sizeof(prop_vdso_base)); 66 snprintf(msg, sizeof(msg), "zx_object_get_property failed: %d", status); 67 ASSERT_EQ(status, 0, msg); 68 69 struct dl_phdr_info info = { .dlpi_addr = prop_vdso_base }; 70 int ret = dl_iterate_phdr(&phdr_info_callback, &info); 71 EXPECT_EQ(ret, 1, "dl_iterate_phdr didn't see vDSO?"); 72 73 uintptr_t vdso_code_start = 0; 74 size_t vdso_code_len = 0; 75 for (uint_fast16_t i = 0; i < info.dlpi_phnum; ++i) { 76 if (info.dlpi_phdr[i].p_type == PT_LOAD && 77 (info.dlpi_phdr[i].p_flags & PF_X)) { 78 vdso_code_start = info.dlpi_addr + info.dlpi_phdr[i].p_vaddr; 79 vdso_code_len = info.dlpi_phdr[i].p_memsz; 80 break; 81 } 82 } 83 ASSERT_NE(vdso_code_start, 0u, "vDSO has no code segment?"); 84 ASSERT_NE(vdso_code_len, 0u, "vDSO has no code segment?"); 85 86 // Removing the vDSO code mapping is not allowed. 87 status = zx_vmar_unmap(zx_vmar_root_self(), 88 vdso_code_start, vdso_code_len); 89 EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "unmap vDSO code"); 90 91 // Nor is removing a whole range overlapping the vDSO code. 92 status = zx_vmar_unmap(zx_vmar_root_self(), 93 vdso_code_start - PAGE_SIZE, 94 PAGE_SIZE * 2); 95 EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "unmap range overlapping vDSO code"); 96 97 END_TEST; 98} 99 100bool vdso_map_test(void) { 101 BEGIN_TEST; 102 103 zx_handle_t vmo = zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0)); 104 ASSERT_NE(vmo, ZX_HANDLE_INVALID, "zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0))"); 105 106 // Since we already have a vDSO mapping, loading it again should fail. 107 void* h = dlopen_vmo(vmo, RTLD_LOCAL); 108 EXPECT_NULL(h, "dlopen_vmo on vDSO VMO succeeded"); 109 110 // Create a fresh process that doesn't already have a vDSO mapping. 111 // We can't meaningfully test the other constraints on our own 112 // process, because the "there can be only one" constraint trumps them. 113 const char* name = "vdso_map_test"; 114 zx_handle_t proc, vmar; 115 ASSERT_EQ(zx_process_create(zx_job_default(), 116 name, strlen(name), 0, &proc, &vmar), 117 ZX_OK, "zx_process_create failed"); 118 119 // This should fail because it's an executable mapping of 120 // the wrong portion of the vDSO image (the first page is 121 // rodata including the ELF headers). Only the actual code 122 // segment can be mapped executable. 123 uintptr_t addr; 124 zx_status_t status = zx_vmar_map( 125 vmar, ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 0, vmo, 0, 126 PAGE_SIZE, &addr); 127 EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "map vDSO data as executable"); 128 129 zx_handle_close(proc); 130 zx_handle_close(vmar); 131 132 END_TEST; 133} 134 135BEGIN_TEST_CASE(vdso_base_tests) 136RUN_TEST(vdso_base_test); 137RUN_TEST(vdso_unmap_test); 138RUN_TEST(vdso_map_test); 139END_TEST_CASE(vdso_base_tests) 140 141int main(int argc, char** argv) { 142 bool success = unittest_run_all_tests(argc, argv); 143 return success ? 0 : -1; 144} 145