[CRIU] [PATCH 01/11] cr-check: Add check for mremap() of special mappings

Andrei Vagin avagin at gmail.com
Sat May 25 08:42:15 MSK 2019


On Wed, May 22, 2019 at 07:18:15PM +0100, Dmitry Safonov wrote:
> During restore any VMA that's a subject to ASLR should be moved at the
> same address as was on a checkpoint. Previously, ports to non-x86
> architectures had problems with VDSO mremap(). On those platforms kernel
> needs "landing" for return to userspace in some cases.
> Usually, vdso provides this landing and finishes restoring of registers.
> That's `int80_landing_pad` on ia32. On arm64/arm32 it's sigtrap for
> SA_RESTORER - to proceed after signal processing.
> 
> That's why kernel needs to track the position of landing.
> On modern kernels for platform we support it's already done - however,
> for older kernels some patches needs to be backported for C/R.
> 
> Provide the checks for mremap() of special VMAs: that CRIU has suitable
> kernel to work on and if we'll have some new platforms - that kernel
> tracks the position of landing.
> 
> Signed-off-by: Dmitry Safonov <dima at arista.com>
> ---
>  criu/cr-check.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 148 insertions(+), 1 deletion(-)
> 
> diff --git a/criu/cr-check.c b/criu/cr-check.c
> index e24668305838..54fa44091ad8 100644
> --- a/criu/cr-check.c
> +++ b/criu/cr-check.c
> @@ -582,7 +582,7 @@ static pid_t fork_and_ptrace_attach(int (*child_setup)(void))
>  	return pid;
>  }
>  
> -static int check_ptrace_peeksiginfo()
> +static int check_ptrace_peeksiginfo(void)
>  {
>  	struct ptrace_peeksiginfo_args arg;
>  	siginfo_t siginfo;
> @@ -611,6 +611,152 @@ static int check_ptrace_peeksiginfo()
>  	return ret;
>  }
>  
> +struct special_mapping {
> +	const char	*name;
> +	void		*addr;
> +	size_t		size;
> +};
> +
> +static int parse_special_maps(struct special_mapping *vmas, size_t nr)
> +{
> +	FILE *maps;
> +	char buf[256];
> +	int ret = 0;
> +
> +	maps = fopen_proc(PROC_SELF, "maps");
> +	if (!maps)
> +		return -1;
> +
> +	while (fgets(buf, sizeof(buf), maps)) {
> +		unsigned long start, end;
> +		int r, tail;
> +		size_t i;
> +
> +		r = sscanf(buf, "%lx-%lx %*s %*s %*s %*s %n\n",
> +				&start, &end, &tail);
> +		if (r != 2) {
> +			fclose(maps);
> +			pr_err("Bad maps format %d.%d (%s)\n", r, tail, buf + tail);
> +			return -1;
> +		}
> +
> +		for (i = 0; i < nr; i++) {
> +			if (strcmp(buf + tail, vmas[i].name) != 0)
> +				continue;
> +			if (vmas[i].addr != MAP_FAILED) {
> +				pr_err("Special mapping meet twice: %s\n", vmas[i].name);
> +				ret = -1;
> +				goto out;
> +			}
> +			vmas[i].addr = (void *)start;
> +			vmas[i].size = end - start;
> +		}
> +	}
> +
> +out:
> +	fclose(maps);
> +	return ret;
> +}
> +
> +static void dummy_sighandler(int sig)
> +{
> +}
> +
> +static void check_special_mapping_mremap_child(struct special_mapping *vmas,
> +					       size_t nr)
> +{
> +	size_t i, parking_size = 0;
> +	void *parking_lot;
> +	pid_t self = getpid();
> +
> +	for (i = 0; i < nr; i++) {
> +		if (vmas[i].addr != MAP_FAILED)
> +			parking_size += vmas[i].size;
> +	}
> +

Could you write a comment why we need to handle SIGUSR1 here?

> +	if (signal(SIGUSR1, dummy_sighandler) == SIG_ERR) {
> +		pr_perror("signal() failed");
> +		exit(1);
> +	}
> +
> +	parking_lot = mmap(NULL, parking_size, PROT_NONE,
> +			   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> +	if (parking_lot == MAP_FAILED) {
> +		pr_perror("mmap(%zu) failed", parking_size);
> +		exit(1);
> +	}
> +
> +	for (i = 0; i < nr; i++) {
> +		unsigned long ret;
> +
> +		if (vmas[i].addr == MAP_FAILED)
> +			continue;
> +
> +		ret = syscall(__NR_mremap, (unsigned long)vmas[i].addr,
> +			      vmas[i].size, vmas[i].size,
> +			      MREMAP_FIXED | MREMAP_MAYMOVE,
> +			      (unsigned long)parking_lot);
> +		if (ret != (unsigned long)parking_lot)

if it fails, we probably can log this error

> +			syscall(__NR_exit, 1);
> +		parking_lot += vmas[i].size;
> +	}
> +
> +	syscall(__NR_kill, self, SIGUSR1);
> +	syscall(__NR_exit, 0);
> +}
> +
> +static int check_special_mapping_mremap(void)
> +{
> +	struct special_mapping special_vmas[] = {
> +		{
> +			.name = "[vvar]\n",
> +			.addr = MAP_FAILED,
> +		},
> +		{
> +			.name = "[vdso]\n",
> +			.addr = MAP_FAILED,
> +		},
> +		{
> +			.name = "[sigpage]\n",
> +			.addr = MAP_FAILED,
> +		},
> +		/* XXX: { .name = "[uprobes]\n" }, */
> +		/*
> +		 * Not subjects for ASLR, skipping:
> +		 * { .name = "[vectors]\n", },
> +		 * { .name = "[vsyscall]\n" },
> +		 */
> +	};
> +	size_t vmas_nr = ARRAY_SIZE(special_vmas);
> +	pid_t child;
> +	int stat;
> +
> +	if (parse_special_maps(special_vmas, vmas_nr))
> +		return -1;
> +
> +	child = fork();
> +	if (child < 0) {
> +		pr_perror("%s(): failed to fork()", __func__);
> +		return -1;
> +	}
> +
> +	if (child == 0) {
> +		check_special_mapping_mremap_child(special_vmas, vmas_nr);
		exit(1); /* unreachable */
	}
> +
> +	if (waitpid(child, &stat, 0) != child) {
> +		pr_err("Failed to wait for special mapping mremap() test\n");
> +		kill(child, SIGKILL);
if waitpid failed, we probably doesn't have this child, so you would
prefer to not kill a process with this pid.
> +		return -1;
> +	}
> +
> +	if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0) {
> +		pr_err("mremap() for special mapping did something bad to a child\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
>  static int check_ptrace_suspend_seccomp(void)
>  {
>  	pid_t pid;
> @@ -1172,6 +1318,7 @@ int cr_check(void)
>  	CHECK_CAT1(check_ipc());
>  	CHECK_CAT1(check_sigqueuinfo());
>  	CHECK_CAT1(check_ptrace_peeksiginfo());
> +	CHECK_CAT1(check_special_mapping_mremap());
>  
>  	/*
>  	 * Category 2 - required for specific cases.
> -- 
> 2.21.0
> 
> _______________________________________________
> CRIU mailing list
> CRIU at openvz.org
> https://lists.openvz.org/mailman/listinfo/criu


More information about the CRIU mailing list