分类: LINUX
2008-09-09 07:40:44
/********************************************************************************
* Program: mmsupervisor
* Purpose: Configure quota of memory usage for certain program before it
* started.
*
* File: mmsupervisor.c
*
* Author: ZC Miao <>
* Date: 2008-09-07
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if 1
# define debugf(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
#endif
/* architecture dependent functions */
static long int get_syscall(struct user_regs_struct *pregs);
static void get_mmap2_args(
struct user_regs_struct *pregs,
void **pmmap2_start, size_t *pmmap2_length, int *pmmap2_prot,
int *pmmap2_flags, int *pmmap2_fd, off_t *pmmap2_pgoffset);
static long int get_ret(struct user_regs_struct *pregs);
/* i386 depedent implementation */
#if __i386__
static inline long int get_syscall(struct user_regs_struct *pregs) {
return pregs->orig_eax;
}
static inline void get_mmap2_args(
struct user_regs_struct *pregs,
void **pmmap2_start, size_t *pmmap2_length, int *pmmap2_prot,
int *pmmap2_flags, int *pmmap2_fd, off_t *pmmap2_pgoffset) {
*pmmap2_start = (void*)pregs->ebx;
*pmmap2_length = (size_t)pregs->ecx;
*pmmap2_prot = (int)pregs->edx;
*pmmap2_flags = (int)pregs->esi;
*pmmap2_fd = (int)pregs->edi;
*pmmap2_pgoffset = (off_t)pregs->ebp;
}
static inline long int get_ret(struct user_regs_struct *pregs){
return pregs->eax;
}
# define MMSUPERVISOR_ARCH_IMPLEMENTED 1
#endif
#if ! MMSUPERVISOR_ARCH_IMPLEMENTED
# error mmsupervisor for this architecture is not implemented
#endif
static int mmsupervisor(int child) {
struct user_regs_struct regs, regs_return;
int syscall_num;
int syscall_interested = 1;
int status;
/* mmap2 */
void *mmap2_start;
size_t mmap2_length;
int mmap2_prot;
int mmap2_flags;
int mmap2_fd;
off_t mmap2_pgoffset;
void *mmap2_return;
/* get system call name and interested arguments */
if (ptrace(PTRACE_GETREGS, child, NULL, ®s)) {
perror("ptrace PTRACE_GETREGS failed");
return 1;
}
syscall_num = get_syscall(®s);
if (syscall_num == SYS_mmap2) {
get_mmap2_args(®s,
&mmap2_start, &mmap2_length, &mmap2_prot,
&mmap2_flags, &mmap2_fd, &mmap2_pgoffset);
debugf("%d: mmap2(%p, %u, %d, %d, %d, %lu) = ", child,
mmap2_start, mmap2_length, mmap2_prot,
mmap2_flags, mmap2_fd, mmap2_pgoffset);
} else {
syscall_interested = 0;
}
if (syscall_interested) {
/* get return value */
if (ptrace(PTRACE_SYSCALL, child, NULL, NULL)) {
perror("ptrace PTRACE_SYSCALL failed");
/* abnormal state*/
return 1;
}
if (waitpid(child, &status, 0) != child) {
perror("waitpid failed");
/* abnormal state*/
return 1;
}
if (WIFSTOPPED(status)) {
if (WSTOPSIG(status) & SIGTRAP) {
if (ptrace(PTRACE_GETREGS, child, NULL, ®s_return)) {
perror("ptrace PTRACE_GETREGS failed");
return 1;
}
if (get_syscall(®s_return) == syscall_num) {
if (syscall_num == SYS_mmap2) {
mmap2_return = (void*)get_ret(®s_return);
debugf("%p\n", mmap2_return);
if (mmap2_return) {
}
}
} else {
fprintf(stderr, "unmatched entry/return syscall\n");
}
}
}
}
if (ptrace(PTRACE_SYSCALL, child, NULL, NULL)) {
perror("ptrace PTRACE_SYSCALL failed");
return 1;
}
return 0;
}
static void usage(void) {
fprintf(stderr, "Usage: mmsupervisor command args...\n");
}
int main(int argc, char *const argv[]) {
int setoption = 0;
pid_t bigchild;
pid_t child_waited;
pid_t new_child;
int status;
int event;
int exit_status = 1;
/* parse args */
if (argc < 2) {
usage();
return 1;
}
if ((bigchild = fork()) == 0) {
if (ptrace(PTRACE_TRACEME, 0, NULL, NULL)) {
perror("ptrace PTRACE_TRACEME failed");
return 1;
}
if (execvp(argv[1], argv+1)) {
perror("execvp failed");
return 1;
}
} else {
debugf("bigchild %d started\n", bigchild);
while (1) {
child_waited = wait(&status);
if (setoption == 0) {
if (ptrace(PTRACE_SETOPTIONS, bigchild, NULL,
PTRACE_O_TRACEFORK |
PTRACE_O_TRACEVFORK |
PTRACE_O_TRACECLONE)) {
perror("ptrace PTRACE_SETOPTIONS failed");
return 1;
}
setoption = 1;
}
if (child_waited == -1) {
break;
} else if (WIFEXITED(status)) {
debugf("child %d exited with status %d\n",
child_waited, WEXITSTATUS(status));
if (child_waited == bigchild) exit_status = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
debugf("child %d killed by signal %d\n",
child_waited, WTERMSIG(status));
if (child_waited == bigchild) exit_status = WEXITSTATUS(status);
} else if (WIFSTOPPED(status)) {
event = status >> 16;
if (WSTOPSIG(status) & SIGTRAP) {
if ((event == PTRACE_EVENT_FORK) ||
(event == PTRACE_EVENT_VFORK) ||
(event == PTRACE_EVENT_CLONE)) {
if (ptrace(PTRACE_GETEVENTMSG, child_waited, NULL, &new_child)) {
perror("ptrace PTRACE_GETEVENTMSG failed");
}
debugf("new child %d\n", new_child);
if (ptrace(PTRACE_SYSCALL, child_waited, NULL, NULL)) {
perror("ptrace PTRACE_SYSCALL failed");
status = 1;
break;
}
} else {
if (mmsupervisor(child_waited)) {
/* mmsupervisor is angry */
debugf("mmsupervisor is angry");
}
}
} else {
debugf("child %d stopped by signal %d\n",
child_waited, WSTOPSIG(status));
ptrace(PTRACE_DETACH, child_waited, NULL, NULL);
kill(child_waited, WSTOPSIG(status));
}
} else if (WIFCONTINUED(status)) {
debugf("child %d resumed\n", child_waited);
} else {
debugf("what status : %d\n", status);
}
}
}
return exit_status;
}