All pastes #1180559 Raw Edit

SUA 6.0 fork() fix

public c v1 · immutable
#1180559 ·published 2008-08-21 11:16 UTC
rendered paste body
#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <dlfcn.h>#include <errno.h>#include <string.h>#include <interix/security.h>#include <stdarg.h>#ifndef FORK_RETRY#define FORK_RETRY 32#endif#ifndef LIBC_NAME#error Please define LIBC_NAME to the filename of your libc (RTLD_NEXT not working on interix somehow)#endif#ifndef INSTALL_LIBNAME#error Please define INSTALL_LIBNAME to the final destination filename of this preload library!#endif#ifdef DEBUG#define DBG fprintf#else#define DBG(...)#endifint     __argc = 0;char ** __argv = NULL;extern char * const * environ;void* __get_libc() {	static void* __libc = NULL;	if(!__libc) {		if(!(__libc = dlopen(LIBC_NAME, RTLD_LOCAL))) {			perror("dlopen");			abort();		}	}	return __libc;}int _fork() {	static int(*__fork)() = NULL;	int rv = -1, i = 0;	DBG(stderr, "ENTER: fork (%d)\n", getpid());	if(!__fork) {		if(!(__fork = dlsym(__get_libc(), "fork"))) {			perror("dlsym");			abort();		}	}	while(i <= FORK_RETRY) {		rv = __fork();		if(rv >= 0 || errno != EAGAIN) {			DBG(stderr, "LEAVE: fork (success) (%d)\n", getpid());			return rv;		}		++i;		fprintf(stderr, "!!! %d => fork: retrying (%d)\n", getpid(), i);	}	DBG(stderr, "LEAVE: fork (failed) (%d)\n", getpid());	return -1;}static char const * __lookup(char const * p) {	char const * pth = getenv("PATH");	char * path;	char * start, * end;	char buf[260];	DBG(stderr, "ENTER: lookup (%s) (%d)\n", pth, getpid());	if(!pth) {		DBG(stderr, "LEAVE: lookup (PATH empty) (%d)\n", getpid());		return p;	}	/* just to make sure in any case ... */	path = strdup(pth);	start = path, end = path;	while(end && *end != '\0') {		int sep = 0;		while(*end != ':' && *end != '\0')			++end;		if(*end == ':') {			*end = '\0';			sep = 1;		}		if(strlen(start) > 260) {			fprintf(stderr, "!!! ERROR: PATH contains a path that is longer than 260 chars!\n");			abort();		}		if(strlen(start) + strlen(p) + 2 > 260) {			fprintf(stderr, "!!! WARNING: PATH component + lookup component is longer than 260 chars, skipping\n");			goto docont;		}		strcpy(buf, start);		strcat(buf, "/");		strcat(buf, p);		DBG(stderr, "STATUS: checking X_OK on %s (%d) [%d]\n", buf, getpid(), sep);		if(access(buf, X_OK) == 0) {			DBG(stderr, "LEAVE: lookup (found %s) (%d)\n", buf, getpid());			free(path);			return strdup(buf);		}	docont:		if(sep)			*end = ':';		else			break;		start = ++end;	}	free(path);	DBG(stderr, "LEAVE: lookup (unsure, using %s) (%d)\n", p, getpid());	return p;}int _execve(const char * p, char * const a[], char * const e[]) {	static int(*__execve)(const char *, char * const[], char * const[]) = NULL;	char * const * pe = e;	char ** ppe = NULL;	int cnt = 0;	int alloced = 0;	int status = -1;	DBG(stderr, "ENTER: execve (%s %s ...) (%d)\n", a[0] ? a[0] : "(not set)", a[1] ? a[1] : "(not set)", getpid());	if(!__execve) {		if(!(__execve = dlsym(__get_libc(), "execve"))) {			perror("dlsym");			abort();		}	}	while(pe && *pe) {		++cnt;		if(strstr(*pe, "LD_PRELOAD") && strstr(*pe, INSTALL_LIBNAME)) {			ppe = (char **)e;			goto doit;		}		++pe;	}	fprintf(stderr, "!!! %d => execve: re-adding fork preload!\n", getpid());	ppe = malloc(sizeof(char const *) * cnt + 2);	alloced = 1;	pe = e;	cnt = 0;		while(pe && *pe) {		ppe[cnt++] = *pe++;	}	ppe[cnt++] = "LD_PRELOAD=" INSTALL_LIBNAME;	ppe[cnt] = NULL;doit:	DBG(stderr, "STATUS: execve (replacing process) (%d)\n", getpid());	status = __execve(p, a, ppe);	if(alloced)		free(ppe);	DBG(stderr, "LEAVE: execve (failed) (%d)\n", getpid());	return status;}int _execv(const char* p, char * const a[]) {	int status;	DBG(stderr, "ENTER: execv (%s %s ...) (%d)\n", a[0] ? a[0] : "(not set)", a[1] ? a[1] : "(not set)", getpid());	status = _execve(p, a, environ);	DBG(stderr, "LEAVE: execv (failed) (%d)\n", getpid());	return status;}int _execvp(const char* p, char* const a[]) {	char const * f;	int status;	DBG(stderr, "ENTER: execvp (%s %s ...) (%d)\n", a[0] ? a[0] : "(not set)", a[1] ? a[1] : "(not set)", getpid());		f = __lookup(p);	status = _execve(f, a, environ);	if(f != p)		free((char*)f);	DBG(stderr, "LEAVE: execvp (failed) (%d)\n", getpid());	return status;}#define EXECL_TO_V		\	va_list l;			\	char * tmp[4096];	\	int cnt = 1;		\	va_start(l, f);		\	tmp[0] = (char*)f;	\	while((tmp[cnt++] = va_arg(l, char* const)) != NULL) {}	\	tmp[cnt] = NULL;	\int _execle(const char* p, const char* f, ...) {	int status;	EXECL_TO_V;	DBG(stderr, "ENTER: execle (%s %s ...) (%d)\n", p, f, getpid());	status = _execve(p, tmp, va_arg(l, char * const *));	va_end(l);	DBG(stderr, "LEAVE: execle (failed) (%d)\n", getpid());	return status;}int _execl(const char* p, const char* f, ...) {	int status;	EXECL_TO_V;	DBG(stderr, "ENTER: execl (%s %s ...) (%d)\n", p, f, getpid());	status = _execv(p, tmp);	va_end(l);	DBG(stderr, "LEAVE: execl (failed) (%d)\n", getpid());	return status;}int _execlp(const char* p, const char* f, ...) {	int status;	EXECL_TO_V;	DBG(stderr, "ENTER: execlp (%s %s ...) (%d)\n", p, f, getpid());	status = _execvp(p, tmp);	va_end(l);	DBG(stderr, "LEAVE: execlp (failed) (%d)\n", getpid());	return status;}/* * vfork() would require some magic. we would have to save the * return address of this vfork() replacement in some means of * global storage (not stack!) and make the parent jump to that * address after the real call to vfork immediately! * * Below is a first go at it, but things aren't quite right yet: * vfork works, and both parent and child return properly - *if* * the parent isn't too fast. seems the parent isn't blocked until * the child does an exec* call (as described in the linux man page). * This leads to a crash if the parent continues immediately after * vfork. if you delay the program using a debugger, things work. * However in my test program the exit code of the program was some * seemingly random value, even though everything seemed ok... * Maybe someone has ideas? *int _vfork () {	static void* __libc = NULL;	static int(*__vfork)() = NULL;	int rv = -1, i = 0;	int ret_addr;	if(!__libc) {		if(!(__libc = dlopen("libc.so.5.2", RTLD_LOCAL))) {			perror("dlopen");			abort();		}	}	if(!__vfork) {		if(!(__vfork = dlsym(__libc, "vfork"))) {			perror("dlsym");			abort();		}	}	asm ( "movl 0x4(%%ebp), %0": "=b" (ret_addr) );	while(i <= FORK_RETRY) {		rv = __vfork();		if(rv >= 0 || errno != EAGAIN) {			switch(rv) {				case -1:					return -1;				case 0:					return 0;				default:					// need to jump directly to return address, since					// child took care of cleaning up the shared stack					asm ( "jmp *%0": : "g" (ret_addr) );			}		}		++i;		fprintf(stderr, "!!! %d => vfork: retrying (%d)\n", getpid(), i);	}	return -1;} */