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