Example #1
0
/**
 * Modify the current syscall of @tracee as described by @modif
 * regarding the given @config.  This function returns whether the
 * syscall was modified or not.
 */
static bool modify_syscall(Tracee *tracee, const Config *config, const Modif *modif)
{
	size_t i, j;
	word_t syscall;

	assert(config != NULL);

	if (!needs_kompat(config, modif->expected_release))
		return false;

	/* Check if this syscall is supported on this architecture.  */
	syscall = detranslate_sysnum(get_abi(tracee), modif->new_sysarg_num);
	if (syscall == SYSCALL_AVOIDER)
		return false;

	set_sysnum(tracee, modif->new_sysarg_num);

	/* Shift syscall arguments.  */
	for (i = 0; i < MAX_ARG_SHIFT; i++) {
		Reg sysarg     = modif->shifts[i].sysarg;
		size_t nb_args = modif->shifts[i].nb_args;
		int offset     = modif->shifts[i].offset;

		for (j = 0; j < nb_args; j++) {
			word_t arg = peek_reg(tracee, CURRENT, sysarg + j);
			poke_reg(tracee, sysarg + j + offset, arg);
		}
	}

	return true;
}
Example #2
0
File: reg.c Project: BobbWu/PRoot
/**
 * Print the value of the current @tracee's registers according
 * to the @verbose_level.  Note: @message is mixed to the output.
 */
void print_current_regs(Tracee *tracee, int verbose_level, const char *message)
{
	if (tracee->verbose < verbose_level)
		return;

	note(tracee, INFO, INTERNAL,
		"pid %d: %s: %s(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx) = 0x%lx [0x%lx, %d]",
		tracee->pid, message,
		stringify_sysnum(get_sysnum(tracee, CURRENT)),
		peek_reg(tracee, CURRENT, SYSARG_1), peek_reg(tracee, CURRENT, SYSARG_2),
		peek_reg(tracee, CURRENT, SYSARG_3), peek_reg(tracee, CURRENT, SYSARG_4),
		peek_reg(tracee, CURRENT, SYSARG_5), peek_reg(tracee, CURRENT, SYSARG_6),
		peek_reg(tracee, CURRENT, SYSARG_RESULT),
		peek_reg(tracee, CURRENT, STACK_POINTER),
		get_abi(tracee));
}
int op_write_abi_to_file(char const * abi_file)
{
	FILE * fp;
	struct op_abi_entry const * abi_entry;

	if ((fp = fopen(abi_file, "w")) == NULL)
		return 0;

	for (abi_entry = get_abi() ; abi_entry->name != NULL; ++abi_entry)
		fprintf(fp, "%s %u\n", abi_entry->name, abi_entry->offset);
	fprintf(fp, "little_endian %d\n", op_little_endian());

	fclose(fp);

	return 1;
}
Example #4
0
/* Print some debugging info */
void
SgAsmGenericFormat::dump(FILE *f, const char *prefix, ssize_t idx) const
{
    char p[4096];
    if (idx>=0) {
        snprintf(p, sizeof(p),"%sFormat[%zd].", prefix, idx);
    } else {
        snprintf(p, sizeof(p),"%sFormat.", prefix);
    }
    const int w = std::max(1, DUMP_FIELD_WIDTH-(int)strlen(p));
    
    fprintf(f, "%s%-*s = %s\n", p, w, "family", to_string(get_family()).c_str());
    fprintf(f, "%s%-*s = %s\n", p, w, "purpose", to_string(get_purpose()).c_str());
    fprintf(f, "%s%-*s = %s\n", p, w, "sex", to_string(get_sex()).c_str());

    fprintf(f, "%s%-*s = %u (%scurrent)\n", p, w, "version", get_version(), get_is_current_version() ? "" : "not-" );
    
    fprintf(f, "%s%-*s = %s\n",  p, w, "ABI",      to_string(get_abi()).c_str());
    fprintf(f, "%s%-*s = %u\n",  p, w, "ABIvers",  get_abi_version());
    fprintf(f, "%s%-*s = %zu\n", p, w, "wordsize", get_word_size());
}
Example #5
0
/**
 * Replace current @tracee's syscall with an older and compatible one
 * whenever it's required, i.e. when the syscall is supported by the
 * kernel as specified by @config->virtual_release but it isn't
 * supported by the actual kernel.
 */
static int handle_sysenter_end(Tracee *tracee, Config *config)
{
	/* Note: syscalls like "openat" can be replaced by "open" since PRoot
	 * has canonicalized "fd + path" into "path".  */
	switch (get_sysnum(tracee, ORIGINAL)) {
	case PR_accept4: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,28),
			.new_sysarg_num   = PR_accept,
			.shifts		  = NONE
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_dup3: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_dup2,
			.shifts		  = NONE
		};

		/* "If oldfd equals newfd, then dup3() fails with the
		 * error EINVAL" -- man dup3 */
		if (peek_reg(tracee, CURRENT, SYSARG_1) == peek_reg(tracee, CURRENT, SYSARG_2))
			return -EINVAL;

		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_epoll_create1: {
		bool modified;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_epoll_create,
			.shifts		  = NONE
		};

		/* "the size argument is ignored, but must be greater
		 * than zero" -- man epoll_create */
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_1, 1);
		return 0;
	}

	case PR_epoll_pwait: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,19),
			.new_sysarg_num   = PR_epoll_wait,
			.shifts		  = NONE
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_eventfd2: {
		bool modified;
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_eventfd,
			.shifts		  = NONE
		};

		modified = modify_syscall(tracee, config, &modif);
		if (modified) {
			/* EFD_SEMAPHORE can't be emulated with eventfd.  */
			flags = peek_reg(tracee, CURRENT, SYSARG_2);
			if ((flags & EFD_SEMAPHORE) != 0)
				return -EINVAL;
		}
		return 0;
	}

	case PR_faccessat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_access,
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_fchmodat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_chmod,
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_fchownat: {
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 3,
					.offset  = -1 }
			}
		};

		flags = peek_reg(tracee, CURRENT, SYSARG_5);
		modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0
					? PR_lchown
					: PR_chown);

		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_fcntl: {
		word_t command;

		if (!needs_kompat(config, KERNEL_VERSION(2,6,24)))
			return 0;

		command = peek_reg(tracee, ORIGINAL, SYSARG_2);
		if (command == F_DUPFD_CLOEXEC)
			poke_reg(tracee, SYSARG_2, F_DUPFD);

		return 0;
	}

	case PR_newfstatat:
	case PR_fstatat64: {
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};

		flags = peek_reg(tracee, CURRENT, SYSARG_4);
		if ((flags & ~AT_SYMLINK_NOFOLLOW) != 0)
			return -EINVAL; /* Exposed by LTP.  */

#if defined(ARCH_X86_64)
		if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
			modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64);
		else
			modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64);
#else
		if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
			modif.new_sysarg_num = PR_lstat64;
		else
			modif.new_sysarg_num = PR_stat64;
#endif

		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_futex: {
		word_t operation;
		static bool warned = false;

		if (!needs_kompat(config, KERNEL_VERSION(2,6,22)) || config->actual_release == 0)
			return 0;

		operation = peek_reg(tracee, CURRENT, SYSARG_2);
		if ((operation & FUTEX_PRIVATE_FLAG) == 0)
			return 0;

		if (!warned) {
			warned = true;
			note(tracee, WARNING, USER,
				"kompat: this kernel doesn't support private futexes "
				"and PRoot can't emulate them.  Expect some troubles...");
		}

		poke_reg(tracee, SYSARG_2, operation & ~FUTEX_PRIVATE_FLAG);
		return 0;
	}

	case PR_futimesat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_utimes,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_inotify_init1: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_inotify_init,
			.shifts		  = NONE
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_linkat: {
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_link,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 1,
					.offset  = -1 },
				    [1] = {
					.sysarg  = SYSARG_4,
					.nb_args = 1,
					.offset  = -2 }
			}
		};

		flags = peek_reg(tracee, CURRENT, SYSARG_5);
		if ((flags & ~AT_SYMLINK_FOLLOW) != 0)
			return -EINVAL; /* Exposed by LTP.  */

		modify_syscall(tracee, config, &modif);
		return 0;
	}

	case PR_mkdirat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_mkdir,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return 0;
	}
Example #6
0
File: exit.c Project: meefik/PRoot
/**
 * Translate the output arguments of the current @tracee's syscall in
 * the @tracee->pid process area. This function sets the result of
 * this syscall to @tracee->status if an error occured previously
 * during the translation, that is, if @tracee->status is less than 0.
 */
void translate_syscall_exit(Tracee *tracee)
{
	word_t syscall_number;
	word_t syscall_result;
	int status;

	status = notify_extensions(tracee, SYSCALL_EXIT_START, 0, 0);
	if (status < 0) {
		poke_reg(tracee, SYSARG_RESULT, (word_t) status);
		goto end;
	}
	if (status > 0)
		return;

	/* Set the tracee's errno if an error occured previously during
	 * the translation. */
	if (tracee->status < 0) {
		poke_reg(tracee, SYSARG_RESULT, (word_t) tracee->status);
		goto end;
	}

	/* Translate output arguments:
	 * - break: update the syscall result register with "status"
	 * - goto end: nothing else to do.
	 */
	syscall_number = get_sysnum(tracee, ORIGINAL);
	syscall_result = peek_reg(tracee, CURRENT, SYSARG_RESULT);
	switch (syscall_number) {
	case PR_brk:
		translate_brk_exit(tracee);
		goto end;

	case PR_getcwd: {
		char path[PATH_MAX];
		size_t new_size;
		size_t size;
		word_t output;

		size = (size_t) peek_reg(tracee, ORIGINAL, SYSARG_2);
		if (size == 0) {
			status = -EINVAL;
			break;
		}

		/* Ensure cwd still exists.  */
		status = translate_path(tracee, path, AT_FDCWD, ".", false);
		if (status < 0)
			break;

		new_size = strlen(tracee->fs->cwd) + 1;
		if (size < new_size) {
			status = -ERANGE;
			break;
		}

		/* Overwrite the path.  */
		output = peek_reg(tracee, ORIGINAL, SYSARG_1);
		status = write_data(tracee, output, tracee->fs->cwd, new_size);
		if (status < 0)
			break;

		/* The value of "status" is used to update the returned value
		 * in translate_syscall_exit().  */
		status = new_size;
		break;
	}

	case PR_accept:
	case PR_accept4:
		/* Nothing special to do if no sockaddr was specified.  */
		if (peek_reg(tracee, ORIGINAL, SYSARG_2) == 0)
			goto end;
		/* Fall through.  */
	case PR_getsockname:
	case PR_getpeername: {
		word_t sock_addr;
		word_t size_addr;
		word_t max_size;

		/* Error reported by the kernel.  */
		if ((int) syscall_result < 0)
			goto end;

		sock_addr = peek_reg(tracee, ORIGINAL, SYSARG_2);
		size_addr = peek_reg(tracee, MODIFIED, SYSARG_3);
		max_size  = peek_reg(tracee, MODIFIED, SYSARG_6);

		status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size);
		if (status < 0)
			break;

		/* Don't overwrite the syscall result.  */
		goto end;
	}

#define SYSARG_ADDR(n) (args_addr + ((n) - 1) * sizeof_word(tracee))

#define POKE_WORD(addr, value)			\
	poke_word(tracee, addr, value);		\
	if (errno != 0)	{			\
		status = -errno;		\
		break;				\
	}

#define PEEK_WORD(addr)				\
	peek_word(tracee, addr);		\
	if (errno != 0) {			\
		status = -errno;		\
		break;				\
	}

	case PR_socketcall: {
		word_t args_addr;
		word_t sock_addr;
		word_t size_addr;
		word_t max_size;

		args_addr = peek_reg(tracee, ORIGINAL, SYSARG_2);

		switch (peek_reg(tracee, ORIGINAL, SYSARG_1)) {
		case SYS_ACCEPT:
		case SYS_ACCEPT4:
			/* Nothing special to do if no sockaddr was specified.  */
			sock_addr = PEEK_WORD(SYSARG_ADDR(2));
			if (sock_addr == 0)
				goto end;
			/* Fall through.  */
		case SYS_GETSOCKNAME:
		case SYS_GETPEERNAME:
			/* Handle these cases below.  */
			status = 1;
			break;

		case SYS_BIND:
		case SYS_CONNECT:
			/* Restore the initial parameters: this memory was
			 * overwritten at the enter stage.  Remember: POKE_WORD
			 * puts -errno in status and breaks if an error
			 * occured.  */
			POKE_WORD(SYSARG_ADDR(2), peek_reg(tracee, MODIFIED, SYSARG_5));
			POKE_WORD(SYSARG_ADDR(3), peek_reg(tracee, MODIFIED, SYSARG_6));

			status = 0;
			break;

		default:
			status = 0;
			break;
		}

		/* Error reported by the kernel or there's nothing else to do.  */
		if ((int) syscall_result < 0 || status == 0)
			goto end;

		/* An error occured in SYS_BIND or SYS_CONNECT.  */
		if (status < 0)
			break;

		/* Remember: PEEK_WORD puts -errno in status and breaks if an
		 * error occured.  */
		sock_addr = PEEK_WORD(SYSARG_ADDR(2));
		size_addr = PEEK_WORD(SYSARG_ADDR(3));
		max_size  = peek_reg(tracee, MODIFIED, SYSARG_6);

		status = translate_socketcall_exit(tracee, sock_addr, size_addr, max_size);
		if (status < 0)
			break;

		/* Don't overwrite the syscall result.  */
		goto end;
	}

#undef SYSARG_ADDR
#undef PEEK_WORD
#undef POKE_WORD

	case PR_fchdir:
	case PR_chdir:
		/* These syscalls are fully emulated, see enter.c for details
		 * (like errors).  */
		status = 0;
		break;

	case PR_rename:
	case PR_renameat: {
		char old_path[PATH_MAX];
		char new_path[PATH_MAX];
		ssize_t old_length;
		ssize_t new_length;
		Comparison comparison;
		Reg old_reg;
		Reg new_reg;
		char *tmp;

		/* Error reported by the kernel.  */
		if ((int) syscall_result < 0)
			goto end;

		if (syscall_number == PR_rename) {
			old_reg = SYSARG_1;
			new_reg = SYSARG_2;
		}
		else {
			old_reg = SYSARG_2;
			new_reg = SYSARG_4;
		}

		/* Get the old path, then convert it to the same
		 * "point-of-view" as tracee->fs->cwd (guest).  */
		status = read_path(tracee, old_path, peek_reg(tracee, MODIFIED, old_reg));
		if (status < 0)
			break;

		status = detranslate_path(tracee, old_path, NULL);
		if (status < 0)
			break;
		old_length = (status > 0 ? status - 1 : (ssize_t) strlen(old_path));

		/* Nothing special to do if the moved path is not the
		 * current working directory.  */
		comparison = compare_paths(old_path, tracee->fs->cwd);
		if (comparison != PATH1_IS_PREFIX && comparison != PATHS_ARE_EQUAL) {
			status = 0;
			break;
		}

		/* Get the new path, then convert it to the same
		 * "point-of-view" as tracee->fs->cwd (guest).  */
		status = read_path(tracee, new_path, peek_reg(tracee, MODIFIED, new_reg));
		if (status < 0)
			break;

		status = detranslate_path(tracee, new_path, NULL);
		if (status < 0)
			break;
		new_length = (status > 0 ? status - 1 : (ssize_t) strlen(new_path));

		/* Sanity check.  */
		if (strlen(tracee->fs->cwd) >= PATH_MAX) {
			status = 0;
			break;
		}
		strcpy(old_path, tracee->fs->cwd);

		/* Update the virtual current working directory.  */
		substitute_path_prefix(old_path, old_length, new_path, new_length);

		tmp = talloc_strdup(tracee->fs, old_path);
		if (tmp == NULL) {
			status = -ENOMEM;
			break;
		}

		TALLOC_FREE(tracee->fs->cwd);
		tracee->fs->cwd = tmp;

		status = 0;
		break;
	}

	case PR_readlink:
	case PR_readlinkat: {
		char referee[PATH_MAX];
		char referer[PATH_MAX];
		size_t old_size;
		size_t new_size;
		size_t max_size;
		word_t input;
		word_t output;

		/* Error reported by the kernel.  */
		if ((int) syscall_result < 0)
			goto end;

		old_size = syscall_result;

		if (syscall_number == PR_readlink) {
			output   = peek_reg(tracee, ORIGINAL, SYSARG_2);
			max_size = peek_reg(tracee, ORIGINAL, SYSARG_3);
			input    = peek_reg(tracee, MODIFIED, SYSARG_1);
		}
		else {
			output   = peek_reg(tracee, ORIGINAL,  SYSARG_3);
			max_size = peek_reg(tracee, ORIGINAL, SYSARG_4);
			input    = peek_reg(tracee, MODIFIED, SYSARG_2);
		}

		if (max_size > PATH_MAX)
			max_size = PATH_MAX;

		if (max_size == 0) {
			status = -EINVAL;
			break;
		}

		/* The kernel does NOT put the NULL terminating byte for
		 * readlink(2).  */
		status = read_data(tracee, referee, output, old_size);
		if (status < 0)
			break;
		referee[old_size] = '\0';

		/* Not optimal but safe (path is fully translated).  */
		status = read_path(tracee, referer, input);
		if (status < 0)
			break;

		if (status >= PATH_MAX) {
			status = -ENAMETOOLONG;
			break;
		}

		status = detranslate_path(tracee, referee, referer);
		if (status < 0)
			break;

		/* The original path doesn't require any transformation, i.e
		 * it is a symetric binding.  */
		if (status == 0)
			goto end;

		/* Overwrite the path.  Note: the output buffer might be
		 * initialized with zeros but it was updated with the kernel
		 * result, and then with the detranslated result.  This later
		 * might be shorter than the former, so it's safier to add a
		 * NULL terminating byte when possible.  This problem was
		 * exposed by IDA Demo 6.3.  */
		if ((size_t) status < max_size) {
			new_size = status - 1;
			status = write_data(tracee, output, referee, status);
		}
		else {
			new_size = max_size;
			status = write_data(tracee, output, referee, max_size);
		}
		if (status < 0)
			break;

		/* The value of "status" is used to update the returned value
		 * in translate_syscall_exit().  */
		status = new_size;
		break;
	}

#if defined(ARCH_X86_64)
	case PR_uname: {
		struct utsname utsname;
		word_t address;
		size_t size;

		if (get_abi(tracee) != ABI_2)
			goto end;

		/* Error reported by the kernel.  */
		if ((int) syscall_result < 0)
			goto end;

		address = peek_reg(tracee, ORIGINAL, SYSARG_1);

		status = read_data(tracee, &utsname, address, sizeof(utsname));
		if (status < 0)
			break;

		/* Some 32-bit programs like package managers can be
		 * confused when the kernel reports "x86_64".  */
		size = sizeof(utsname.machine);
		strncpy(utsname.machine, "i686", size);
		utsname.machine[size - 1] = '\0';

		status = write_data(tracee, address, &utsname, sizeof(utsname));
		if (status < 0)
			break;

		status = 0;
		break;
	}
#endif

	case PR_execve:
		translate_execve_exit(tracee);
		goto end;

	case PR_ptrace:
		status = translate_ptrace_exit(tracee);
		break;

	case PR_wait4:
	case PR_waitpid:
		if (tracee->as_ptracer.waits_in != WAITS_IN_PROOT)
			goto end;

		status = translate_wait_exit(tracee);
		break;

	case PR_setrlimit:
	case PR_prlimit64:
		/* Error reported by the kernel.  */
		if ((int) syscall_result < 0)
			goto end;

		status = translate_setrlimit_exit(tracee, syscall_number == PR_prlimit64);
		if (status < 0)
			break;

		/* Don't overwrite the syscall result.  */
		goto end;

	default:
		goto end;
	}

	poke_reg(tracee, SYSARG_RESULT, (word_t) status);

end:
	status = notify_extensions(tracee, SYSCALL_EXIT_END, status, 0);
	if (status < 0)
		poke_reg(tracee, SYSARG_RESULT, (word_t) status);
}
Example #7
0
/**
 * Overwrite the @tracee's current syscall number with @sysnum.  Note:
 * this neutral value is automatically converted into the architecture
 * value.
 */
void set_sysnum(Tracee *tracee, Sysnum sysnum)
{
	poke_reg(tracee, SYSARG_NUM, detranslate_sysnum(get_abi(tracee), sysnum));
}
Example #8
0
/**
 * Return the neutral value of the @tracee's current syscall number.
 */
Sysnum get_sysnum(const Tracee *tracee, RegVersion version)
{
	return translate_sysnum(get_abi(tracee), peek_reg(tracee, version, SYSARG_NUM));
}
Example #9
0
File: heap.c Project: ivoire/PRoot
word_t translate_brk_enter(Tracee *tracee)
{
	word_t new_brk_address;
	size_t old_heap_size;
	size_t new_heap_size;

	if (heap_offset == 0) {
		heap_offset = sysconf(_SC_PAGE_SIZE);
		if ((int) heap_offset <= 0)
			heap_offset = 0x1000;
	}

	/* Non-fixed mmap pages might be placed right after the
	 * emulated heap on some architectures.  A solution is to
	 * preallocate some space to ensure a minimal heap size.  */
	if (tracee->heap->prealloc_size == 0)
		tracee->heap->prealloc_size = MAX(PREALLOCATED_HEAP_SIZE, heap_offset);

	new_brk_address = peek_reg(tracee, CURRENT, SYSARG_1);
	DEBUG_BRK("brk(0x%lx)\n", new_brk_address);

	/* Allocate a new mapping for the emulated heap.  */
	if (tracee->heap->base == 0) {
		Sysnum sysnum;

		if (new_brk_address != 0)
			notice(tracee, WARNING, INTERNAL,
				"process %d is doing suspicious brk()",	tracee->pid);

		/* I don't understand yet why mmap(2) fails (EFAULT)
		 * on architectures that also have mmap2(2).  Maybe
		 * this former implies MAP_FIXED in such cases.  */
		sysnum = detranslate_sysnum(get_abi(tracee), PR_mmap2) != SYSCALL_AVOIDER
			? PR_mmap2
			: PR_mmap;

		set_sysnum(tracee, sysnum);
		poke_reg(tracee, SYSARG_1 /* address */, 0);
		poke_reg(tracee, SYSARG_2 /* length  */, heap_offset + tracee->heap->prealloc_size);
		poke_reg(tracee, SYSARG_3 /* prot    */, PROT_READ | PROT_WRITE);
		poke_reg(tracee, SYSARG_4 /* flags   */, MAP_PRIVATE | MAP_ANONYMOUS);
		poke_reg(tracee, SYSARG_5 /* fd      */, -1);
		poke_reg(tracee, SYSARG_6 /* offset  */, 0);

		return 0;
	}

	/* The size of the heap can't be negative.  */
	if (new_brk_address < tracee->heap->base) {
		set_sysnum(tracee, PR_void);
		return 0;
	}

	new_heap_size = new_brk_address - tracee->heap->base;
	old_heap_size = tracee->heap->size;

	/* Clear the released memory in preallocated space, so it will be
	 * in the expected state next time it will be reallocated.  */
	if (new_heap_size < old_heap_size && new_heap_size < tracee->heap->prealloc_size) {
		(void) clear_mem(tracee,
				tracee->heap->base + new_heap_size,
				MIN(old_heap_size, tracee->heap->prealloc_size) - new_heap_size);
	}

	/* No need to use mremap when both old size and new size are
	 * in the preallocated space.  */
	if (   new_heap_size <= tracee->heap->prealloc_size
	    && old_heap_size <= tracee->heap->prealloc_size) {
		tracee->heap->size = new_heap_size;
		set_sysnum(tracee, PR_void);
		return 0;
	}

	/* Ensure the preallocated space will never be released.  */
	new_heap_size = MAX(new_heap_size, tracee->heap->prealloc_size);
	old_heap_size = MAX(old_heap_size, tracee->heap->prealloc_size);

	/* Actually resizing.  */
	set_sysnum(tracee, PR_mremap);
	poke_reg(tracee, SYSARG_1 /* old_address */, tracee->heap->base - heap_offset);
	poke_reg(tracee, SYSARG_2 /* old_size    */, old_heap_size + heap_offset);
	poke_reg(tracee, SYSARG_3 /* new_size    */, new_heap_size + heap_offset);
	poke_reg(tracee, SYSARG_4 /* flags       */, 0);
	poke_reg(tracee, SYSARG_5 /* new_address */, 0);

	return 0;
}
Example #10
0
File: kompat.c Project: AQBoy/PRoot
/**
 * Replace current @tracee's syscall with an older and compatible one
 * whenever it's required, i.e. when the syscall is supported by the
 * kernel as specified by @config->release but it isn't supported by
 * the actual kernel.
 */
static void handle_sysenter_end(Tracee *tracee, Config *config)
{
	bool modified;

	/* Note: syscalls like "openat" can be replaced by "open" since PRoot
	 * has canonicalized "fd + path" into "path".  */
	switch (get_sysnum(tracee, ORIGINAL)) {
	case PR_accept4: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,28),
			.new_sysarg_num   = PR_accept,
			.shifts		  = {{0}}
		};
		modify_syscall(tracee, config, &modif);
		return;
	}

	case PR_dup3: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_dup2,
			.shifts		  = {{0}}
		};
		modify_syscall(tracee, config, &modif);
		return;
	}

	case PR_epoll_create1: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_epoll_create,
			.shifts		  = {{0}}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_1, 0); /* Force "size" to 0.  */
		return;
	}

	case PR_epoll_pwait: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,19),
			.new_sysarg_num   = PR_epoll_wait,
			.shifts		  = {{0}}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_5, 0); /* Force "sigmask" to 0.  */
		return;
	}

	case PR_eventfd2: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_eventfd,
			.shifts		  = {{0}}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_2, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_faccessat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_access,
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_fchmodat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_chmod,
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_fchownat: {
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.shifts	= { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 3,
					.offset  = -1 }
			}
		};

		flags = peek_reg(tracee, CURRENT, SYSARG_5);
		modif.new_sysarg_num = ((flags & AT_SYMLINK_NOFOLLOW) != 0
					? PR_lchown
					: PR_chown);

		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_newfstatat:
	case PR_fstatat64: {
		word_t flags;
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};

		flags = peek_reg(tracee, CURRENT, SYSARG_4);
#if defined(ARCH_X86_64)
		if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
			modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_lstat : PR_lstat64);
		else
			modif.new_sysarg_num = (get_abi(tracee) != ABI_2 ? PR_stat : PR_stat64);
#else
		if ((flags & AT_SYMLINK_NOFOLLOW) != 0)
			modif.new_sysarg_num = PR_lstat64;
		else
			modif.new_sysarg_num = PR_stat64;
#endif
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_4, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_futimesat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_utimes,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return;
	}

	case PR_inotify_init1: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,27),
			.new_sysarg_num   = PR_inotify_init,
			.shifts		  = {{0}}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_1, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_linkat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_link,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 1,
					.offset  = -1 },
				    [1] = {
					    .sysarg  = SYSARG_4,
					    .nb_args = 1,
					    .offset  = -2 }
			}
		};
		modified = modify_syscall(tracee, config, &modif);
		if (modified)
			poke_reg(tracee, SYSARG_5, 0); /* Force "flags" to 0.  */
		return;
	}

	case PR_mkdirat: {
		Modif modif = {
			.expected_release = KERNEL_VERSION(2,6,16),
			.new_sysarg_num   = PR_mkdir,
			.shifts = { [0] = {
					.sysarg  = SYSARG_2,
					.nb_args = 2,
					.offset  = -1 }
			}
		};
		modify_syscall(tracee, config, &modif);
		return;
	}