blob: b7ecef34fa7ce0e01055e9fb9c77affa9d20cfcd [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2011-2016 Google, Inc. All rights reserved.
* **********************************************************/
/* Dr. Memory: the memory debugger
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License, and no later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/uio.h>
#ifdef ANDROID
/* Android declares this inside sys/syscall.h instead of unistd.h */
extern long syscall(long number, ...);
#endif
#include "sysnum_linux.h"
#define BUFSZ 1024
#define MAX_PATH 1024
#define CHECKERRNO() do { \
if (errno) { \
perror(__func__); \
} \
errno = 0; \
} while (0)
/* Put some padding around the memory used for the string so native malloc
* libraries don't scribble over our string after we free it.
*/
typedef struct _padded_str_t {
char pad1[BUFSZ];
char str[BUFSZ];
char pad2[BUFSZ];
} padded_str_t;
/* Create an unaddr usage passed to the open syscall. We try to make this work
* when run natively by not using malloc between the free and the use.
*/
static void
unaddr_open(void)
{
const char devnull[] = "/dev/null";
padded_str_t *dangling_ptr;
int fd;
dangling_ptr = malloc(sizeof(padded_str_t));
memcpy(dangling_ptr->str, devnull, sizeof(devnull));
free(dangling_ptr);
/* open unaddr use */
fd = open(dangling_ptr->str, O_WRONLY);
close(fd);
}
static void
wild_open(void)
{
int fd;
/* pick a "wild address" to test umbra on non-app addresses (i#1641) */
#ifdef X64
# define WILD_ADDR 0x0000812300001234 /* non-canonical */
#else
# define WILD_ADDR 0x4 /* umbra handles everything, so just ensure it's unaddr */
#endif
fd = open((const char *)WILD_ADDR, O_RDONLY);
close(fd);
}
/* Create an uninit usage passed to the open syscall. We try to make this work
* when run natively by initializing it on the stack once, calling it again, and
* praying that the previous stack frame lines up with the current one.
*/
static void
uninit_open(int initialize)
{
const char devnull[] = "/dev/null";
padded_str_t stack_str;
int fd;
if (initialize) {
memcpy(stack_str.str, devnull, sizeof(devnull));
}
/* open uninit use */
fd = open(stack_str.str, O_WRONLY);
close(fd);
}
static void
access_filesystem(void)
{
char tmpdir[] = "syscallsXXXXXX";
int fd;
char buf[BUFSZ];
int n;
char orig_dir[MAX_PATH];
getcwd(orig_dir, MAX_PATH);
errno = 0;
chdir("/tmp/"); CHECKERRNO();
mkdtemp(tmpdir); CHECKERRNO();
chdir(tmpdir); CHECKERRNO();
fd = creat("foo.txt", S_IRWXU); CHECKERRNO();
write(fd, "asdf\n", 5); CHECKERRNO();
close(fd); CHECKERRNO();
chmod("foo.txt", S_IRUSR|S_IWUSR); CHECKERRNO();
fd = open("foo.txt", O_RDONLY); CHECKERRNO();
n = read(fd, buf, BUFSZ); CHECKERRNO();
if (n != 5 || strncmp(buf, "asdf\n", 5) != 0) {
printf("foo.txt didn't round trip!\n");
}
link("foo.txt", "bar.txt"); CHECKERRNO();
rename("bar.txt", "baz.txt"); CHECKERRNO();
symlink("foo.txt", "qux.txt"); CHECKERRNO();
n = readlink("qux.txt", buf, BUFSZ);
if (n != 7 || strncmp(buf, "foo.txt", n) != 0) {
printf("readlink provided wrong answer!\n");
}
unlink("foo.txt"); CHECKERRNO();
unlink("baz.txt"); CHECKERRNO();
unlink("qux.txt"); CHECKERRNO();
chdir(".."); CHECKERRNO();
rmdir(tmpdir); CHECKERRNO();
chdir(orig_dir);
}
static void
uninit_finit_module(int initialize)
{
padded_str_t stack_str;
int fd;
char kmod_file[] = "kernel_module/kernel_module.ko";
fd = open(kmod_file, O_RDONLY);
if (fd == -1) {
printf("warning: kernel module file not found\n");
/* keep going -- we'll still get our uninit and the test will pass */
}
if (initialize) {
strncpy(stack_str.str, "param=dummy", 12);
}
/* XXX: If root, we drop to uid of nobody,
* non-standard but 99 many distros */
if (geteuid() == 0) {
setuid(99);
}
syscall(SYS_finit_module, fd, stack_str.str, 0);
}
static void
unaddr_finit_module(void)
{
padded_str_t *dangling_ptr;
dangling_ptr = malloc(sizeof(padded_str_t));
strncpy(dangling_ptr->str, "param=dummy", 12);
free(dangling_ptr);
syscall(SYS_finit_module, -1, dangling_ptr->str, 0);
}
static void
unaddr_uninit_execve(void)
{
char *argv[] = {
malloc(10 * sizeof(char)),
NULL
};
free(argv[0]);
char *envp[] = {
malloc(10 * sizeof(char)),
NULL
};
execve("", argv, envp);
free(envp[0]);
}
static void
unaddr_process_vm_readv_writev(int readv)
{
struct iovec *local;
struct iovec *remote;
char *buf_local1;
char *buf_local2;
char *buf_remote;
buf_local1 = malloc(10 * sizeof(char));
buf_local2 = malloc(10 * sizeof(char));
buf_remote = malloc(20 * sizeof(char));
local = malloc(2*sizeof(struct iovec));
remote = malloc(sizeof(struct iovec));
local[0].iov_base = buf_local1;
local[0].iov_len = 12;
local[1].iov_base = buf_local2;
local[1].iov_len = 10;
remote[0].iov_base = buf_remote;
remote[0].iov_len = 20;
free(buf_local2);
ssize_t n;
if (readv) {
n = syscall(SYS_process_vm_readv, getpid(), local, 2, remote, 1, 0);
} else {
n = syscall(SYS_process_vm_writev, getpid(), local, 2, remote, 1, 0);
}
printf("transferred %ld bytes\n", (long)n);
free(local);
free(remote);
free(buf_local1);
free(buf_remote);
}
int main(void)
{
access_filesystem();
unaddr_open();
wild_open();
uninit_open(1);
uninit_open(0);
unaddr_finit_module();
uninit_finit_module(0);
uninit_finit_module(1);
unaddr_uninit_execve();
unaddr_process_vm_readv_writev(1 /*readv*/);
unaddr_process_vm_readv_writev(0 /*writev*/);
printf("done\n");
return 0;
}