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