blob: 448bdac30f047c45bf6e2c2489ff277dded1fe87 [file] [log] [blame]
// Copyright (c) 2011 The Chromium 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 <fcntl.h>
#include <stdlib.h>
#include <sys/time.h>
#include "library.h"
#include "sandbox.h"
#include "test_runner.h"
extern "C" int my_getpid(void);
extern char my_getpid_end[];
void patch_range(char *start, char *end) {
int maps_fd;
CHECK_SUCCEEDS((maps_fd = open("/proc/self/maps", O_RDONLY, 0)) >= 0);
playground::Maps maps(maps_fd);
playground::Library library;
library.setLibraryInfo(&maps);
char *extra_space = NULL;
int extra_size = 0;
char *page_start = (char *) ((uintptr_t) start & ~(getpagesize() - 1));
CHECK_SUCCEEDS(mprotect(page_start, end - page_start,
PROT_READ | PROT_WRITE | PROT_EXEC) == 0);
library.patchSystemCallsInRange(start, end, &extra_space, &extra_size);
CHECK_SUCCEEDS(close(maps_fd) == 0);
}
TEST(test_patching_syscall) {
int pid = getpid();
CHECK(my_getpid() == pid);
char *func = (char *) my_getpid;
char *func_end = my_getpid_end;
patch_range(func, func_end);
#if defined(__x86_64__)
CHECK(func[0] == '\xe9'); // e9 XX XX XX XX jmp X
CHECK(func[5] == '\x90'); // 90 nop
CHECK(func[6] == '\x90'); // 90 nop
CHECK(func[7] == '\xc3'); // c3 ret (unmodified)
#elif defined(__i386__)
CHECK(func[0] == '\x68'); // 68 XX XX XX XX push $X
CHECK(func[5] == '\xc3'); // c3 ret
CHECK(func[6] == '\x90'); // 90 nop
CHECK(func[7] == '\xc3'); // c3 ret (unmodified)
#else
# error Unsupported target platform
#endif
StartSeccompSandbox();
CHECK(my_getpid() == pid);
}
#if defined(__x86_64__)
// These test cases test patching calls to the vsyscall page, which is
// present on x86-64 only.
// The timer tests below could fail on a heavily loaded machine, but
// we make a generous allowance for this. They could also fail if the
// clock is changed while the test is running.
const int kMaxTime = 30; // Time in seconds
extern "C" int my_vgettimeofday(struct timeval *tv, struct timezone *tz);
extern char my_vgettimeofday_end[];
extern "C" int my_vtime(time_t *time);
extern char my_vtime_end[];
extern "C" int my_vgetcpu(unsigned *cpu, unsigned *node, void *tcache);
extern char my_vgetcpu_end[];
void check_patching_vsyscall(char *func, char *func_end) {
patch_range(func, func_end);
CHECK(func[0] == '\x48'); // 48 83 ec 08 sub $8, %rsp (unmodified)
CHECK(func[1] == '\x83');
CHECK(func[2] == '\xec');
CHECK(func[3] == '\x08');
CHECK(func[4] == '\xe9'); // e9 XX XX XX XX jmp X
CHECK(func[9] == '\x90'); // 90 nop
CHECK(func[10] == '\x90'); // 90 nop
CHECK(func[11] == '\x90'); // 90 nop
CHECK(func[12] == '\x90'); // 90 nop
CHECK(func[13] == '\x48'); // 48 83 c4 08 add $8, %rsp (unmodified)
CHECK(func[14] == '\x83');
CHECK(func[15] == '\xc4');
CHECK(func[16] == '\x08');
CHECK(func[17] == '\xc3'); // c3 ret (unmodified)
}
TEST(test_patching_vsyscall_gettimeofday) {
struct timeval time1;
struct timeval time2;
CHECK_SUCCEEDS(gettimeofday(&time1, NULL) == 0);
CHECK(my_vgettimeofday(&time2, NULL) == 0);
CHECK(time1.tv_sec <= time2.tv_sec && time2.tv_sec < time1.tv_sec + kMaxTime);
check_patching_vsyscall((char *) my_vgettimeofday, my_vgettimeofday_end);
StartSeccompSandbox();
CHECK(my_vgettimeofday(&time2, NULL) == 0);
CHECK(time1.tv_sec <= time2.tv_sec && time2.tv_sec < time1.tv_sec + kMaxTime);
}
TEST(test_patching_vsyscall_time) {
time_t time1;
time_t time2;
CHECK_SUCCEEDS((time1 = time(NULL)) != -1);
time2 = time(NULL);
CHECK(time1 <= time2 && time2 < time1 + kMaxTime);
check_patching_vsyscall((char *) my_vtime, my_vtime_end);
StartSeccompSandbox();
time2 = time(NULL);
CHECK(time1 <= time2 && time2 < time1 + kMaxTime);
}
TEST(test_patching_vsyscall_getcpu) {
CHECK(my_vgetcpu(NULL, NULL, NULL) == 0);
check_patching_vsyscall((char *) my_vgetcpu, my_vgetcpu_end);
StartSeccompSandbox();
// glibc's sched_getcpu() could still succeed if it goes via the
// vdso and just reads memory, but my_vgetcpu() is always redirected
// through the sandbox's handler and is rejected.
CHECK(my_vgetcpu(NULL, NULL, NULL) == -ENOSYS);
}
#endif