Bug 18862 - Cloning with CLONE_VFORK | CLONE_VM stops sending signals internally from working.
Summary: Cloning with CLONE_VFORK | CLONE_VM stops sending signals internally from wor...
Status: RESOLVED INVALID
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.19
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-08-23 19:46 UTC by Steven Stewart-Gallus
Modified: 2015-09-08 14:27 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Steven Stewart-Gallus 2015-08-23 19:46:30 UTC
Sending signals internally such as from with abort or pthread_kill goes to the wrong process and gives errors. See the code below. I know vfork is hard to support but as of now posix_spawn simply isn't a mature enough replacement.

#define _GNU_SOURCE

#include <assert.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>

static int fork_routine(void *args);
static size_t small_stack_size(void);

int main(void)
{

	size_t page_size = sysconf(_SC_PAGE_SIZE);
	size_t stack_size = small_stack_size();

	void *stack = mmap(0, stack_size + 2U * page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_GROWSDOWN, -1, 0);
	if (MAP_FAILED == stack) {
		perror("mmap");
		return EXIT_FAILURE;
	}

	if (-1 == mprotect(stack, page_size, PROT_NONE)) {
		perror("mprotect");
		return EXIT_FAILURE;
	}

	if (-1 == mprotect(((char *)stack) + page_size + stack_size, page_size, PROT_NONE)) {
		perror("mprotect");
		return EXIT_FAILURE;
	}
	fprintf(stderr, "before fork\n");

	pid_t child = clone(fork_routine, ((char *)stack) + page_size + stack_size, SIGCHLD | CLONE_VM | CLONE_VFORK, 0);
	if (-1 == child) {
		perror("clone");
		return EXIT_FAILURE;
	}
	siginfo_t info;
	if (-1 == waitid(P_PID, child, &info, WEXITED)) {
		perror("waitid");
		return EXIT_FAILURE;
	}

	fprintf(stderr, "out of fork: %i\n", getpid());
	getchar();
	abort();
	return EXIT_SUCCESS;
}
static int fork_routine(void *args)
{
	write(STDERR_FILENO, "in fork\n", sizeof "in fork\n" - 1U);
	_Exit(EXIT_SUCCESS);
}

static size_t small_stack_size(void)
{
	long maybe_page_size = sysconf(_SC_PAGE_SIZE);
	assert(maybe_page_size >= 0);

	long maybe_stack_min_size = sysconf(_SC_THREAD_STACK_MIN);
	assert(maybe_stack_min_size >= 0);

	size_t page_size = maybe_page_size;
	size_t stack_min_size = maybe_stack_min_size;

	return stack_min_size + 3U * page_size;
}
Comment 1 Florian Weimer 2015-09-04 10:39:54 UTC
fork_routine does not set up a glibc thread descriptor, and calling write and _Exit corrupts the main thread descriptor.  This means that the raise call inside abort uses the wrong PID.

This is not a bug in glibc.  If you call clone directly, you need to make sure that you take care of the thread descriptor situation in some way.
Comment 2 Steven Stewart-Gallus 2015-09-08 14:07:25 UTC
(In reply to Florian Weimer from comment #1)
> fork_routine does not set up a glibc thread descriptor, and calling write
> and _Exit corrupts the main thread descriptor.  This means that the raise
> call inside abort uses the wrong PID.
> 
> This is not a bug in glibc.  If you call clone directly, you need to make
> sure that you take care of the thread descriptor situation in some way.

Why even expose clone if you are not going to support it? I would be willing to accept that only execve and _Exit are supported system calls though I guess. That would be very sparse but proportionate to how vfork is usually used.
Comment 3 Florian Weimer 2015-09-08 14:15:28 UTC
There are use cases which are valid (if the appropriate flags are specified).  We cannot support execve because it needs to set errno on failure, so there has to be a valid thread descriptor.
Comment 4 Steven Stewart-Gallus 2015-09-08 14:27:35 UTC
(In reply to Florian Weimer from comment #3)
> There are use cases which are valid (if the appropriate flags are
> specified).  We cannot support execve because it needs to set errno on
> failure, so there has to be a valid thread descriptor.

I see. I shall look into alternate ways of accomplishing what I want. The whole reason I wanted to use clone with CLONE_VM is so that I could do a vfork with CLONE_UNTRACED.

Thank you for your time.