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