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
vdso_base_test(void)18 bool 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
phdr_info_callback(struct dl_phdr_info * info,size_t size,void * data)46 static 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
vdso_unmap_test(void)56 bool 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
vdso_map_test(void)100 bool 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
135 BEGIN_TEST_CASE(vdso_base_tests)
136 RUN_TEST(vdso_base_test);
137 RUN_TEST(vdso_unmap_test);
138 RUN_TEST(vdso_map_test);
END_TEST_CASE(vdso_base_tests)139 END_TEST_CASE(vdso_base_tests)
140
141 int main(int argc, char** argv) {
142 bool success = unittest_run_all_tests(argc, argv);
143 return success ? 0 : -1;
144 }
145