blob: 8e60e390b210cc69b1f719f35210d620c3528095 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <dlfcn.h>
#include <link.h>
#include <zircon/dlfcn.h>
#include <zircon/process.h>
#include <zircon/processargs.h>
#include <zircon/syscalls.h>
#include <lib/fdio/fd.h>
#include <lib/fdio/fdio.h>
#include <lib/fdio/directory.h>
#include <stdio.h>
#include <sys/param.h>
#include <string.h>
#include <unittest/unittest.h>
bool vdso_base_test(void) {
BEGIN_TEST;
char msg[128];
struct link_map* lm = dlopen("libzircon.so", RTLD_NOLOAD);
snprintf(msg, sizeof(msg), "dlopen(\"libzircon.so\") failed: %s",
dlerror());
EXPECT_NONNULL(lm, msg);
uintptr_t rtld_vdso_base = lm->l_addr;
int ok = dlclose(lm);
snprintf(msg, sizeof(msg), "dlclose failed: %s", dlerror());
EXPECT_EQ(ok, 0, msg);
uintptr_t prop_vdso_base;
zx_status_t status =
zx_object_get_property(zx_process_self(),
ZX_PROP_PROCESS_VDSO_BASE_ADDRESS,
&prop_vdso_base, sizeof(prop_vdso_base));
snprintf(msg, sizeof(msg), "zx_object_get_property failed: %d", status);
EXPECT_EQ(status, 0, msg);
EXPECT_EQ(rtld_vdso_base, prop_vdso_base,
"rtld reported address != process property reported address");
END_TEST;
}
static int phdr_info_callback(struct dl_phdr_info* info, size_t size,
void* data) {
struct dl_phdr_info* key = data;
if (info->dlpi_addr == key->dlpi_addr) {
*key = *info;
return 1;
}
return 0;
}
bool vdso_unmap_test(void) {
BEGIN_TEST;
char msg[128];
uintptr_t prop_vdso_base;
zx_status_t status =
zx_object_get_property(zx_process_self(),
ZX_PROP_PROCESS_VDSO_BASE_ADDRESS,
&prop_vdso_base, sizeof(prop_vdso_base));
snprintf(msg, sizeof(msg), "zx_object_get_property failed: %d", status);
ASSERT_EQ(status, 0, msg);
struct dl_phdr_info info = { .dlpi_addr = prop_vdso_base };
int ret = dl_iterate_phdr(&phdr_info_callback, &info);
EXPECT_EQ(ret, 1, "dl_iterate_phdr didn't see vDSO?");
uintptr_t vdso_code_start = 0;
size_t vdso_code_len = 0;
for (uint_fast16_t i = 0; i < info.dlpi_phnum; ++i) {
if (info.dlpi_phdr[i].p_type == PT_LOAD &&
(info.dlpi_phdr[i].p_flags & PF_X)) {
vdso_code_start = info.dlpi_addr + info.dlpi_phdr[i].p_vaddr;
vdso_code_len = info.dlpi_phdr[i].p_memsz;
break;
}
}
ASSERT_NE(vdso_code_start, 0u, "vDSO has no code segment?");
ASSERT_NE(vdso_code_len, 0u, "vDSO has no code segment?");
// Removing the vDSO code mapping is not allowed.
status = zx_vmar_unmap(zx_vmar_root_self(),
vdso_code_start, vdso_code_len);
EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "unmap vDSO code");
// Nor is removing a whole range overlapping the vDSO code.
status = zx_vmar_unmap(zx_vmar_root_self(),
vdso_code_start - PAGE_SIZE,
PAGE_SIZE * 2);
EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "unmap range overlapping vDSO code");
END_TEST;
}
bool vdso_map_test(void) {
BEGIN_TEST;
zx_handle_t vmo = zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0));
ASSERT_NE(vmo, ZX_HANDLE_INVALID, "zx_take_startup_handle(PA_HND(PA_VMO_VDSO, 0))");
// Since we already have a vDSO mapping, loading it again should fail.
void* h = dlopen_vmo(vmo, RTLD_LOCAL);
EXPECT_NULL(h, "dlopen_vmo on vDSO VMO succeeded");
// Create a fresh process that doesn't already have a vDSO mapping.
// We can't meaningfully test the other constraints on our own
// process, because the "there can be only one" constraint trumps them.
const char* name = "vdso_map_test";
zx_handle_t proc, vmar;
ASSERT_EQ(zx_process_create(zx_job_default(),
name, strlen(name), 0, &proc, &vmar),
ZX_OK, "zx_process_create failed");
// This should fail because it's an executable mapping of
// the wrong portion of the vDSO image (the first page is
// rodata including the ELF headers). Only the actual code
// segment can be mapped executable.
uintptr_t addr;
zx_status_t status = zx_vmar_map(
vmar, ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 0, vmo, 0,
PAGE_SIZE, &addr);
EXPECT_EQ(status, ZX_ERR_ACCESS_DENIED, "map vDSO data as executable");
zx_handle_close(proc);
zx_handle_close(vmar);
END_TEST;
}
BEGIN_TEST_CASE(vdso_base_tests)
RUN_TEST(vdso_base_test);
RUN_TEST(vdso_unmap_test);
RUN_TEST(vdso_map_test);
END_TEST_CASE(vdso_base_tests)