on win32, use a named pipe and IOCP to read stdout, stderr and debugfd correctly.

This commit is contained in:
Kévin Szkudłapski 2012-07-11 13:01:20 +00:00 committed by wisk
parent 714c203ece
commit 8349e4746d
6 changed files with 263 additions and 53 deletions

View File

@ -65,6 +65,62 @@ static void mydebug(char const *format, va_list args);
char debugbuffer[BUFSIZ];
size_t debugcount = 1;
#ifdef _WIN32
CRITICAL_SECTION _zz_pipe_cs; /* Initialized in DllMain */
void _zz_debug(char const *format, ...)
{
va_list args;
char buf[0x100];
DWORD written;
va_start(args, format);
//if (_zz_debuglevel >= 1) // LATER:
{
HANDLE dbg_hdl = (HANDLE)_get_osfhandle(_zz_debugfd);
int ret = _vsnprintf(buf, sizeof(buf), format, args);
if (ret <= 0) return; /* if _snprintf failed, we send nothing */
if (buf[0] == '\0') return; /* if the buf is empty, we don't bother to send it to zzuf */
/* FIXME: if len >= count, no null-terminator is appended, so we may erased the last character */
if (ret >= sizeof(buf)) buf[ret - 1] = '\n';
else buf[ret++] = '\n';
EnterCriticalSection(&_zz_pipe_cs);
WriteFile(dbg_hdl, buf, ret, &written, NULL);
LeaveCriticalSection(&_zz_pipe_cs);
}
va_end(args);
fflush(NULL); /* flush all streams to make sure zzuf gotta catch 'em all */
}
void _zz_debug2(char const *format, ...)
{
va_list args;
char buf[0x100];
DWORD written;
va_start(args, format);
//if (_zz_debuglevel >= 1) // LATER:
{
HANDLE dbg_hdl = (HANDLE)_get_osfhandle(_zz_debugfd);
int ret = _vsnprintf(buf, sizeof(buf), format, args);
if (ret <= 0) return; /* if _snprintf failed, we send nothing */
if (buf[0] == '\0') return; /* if the buf is empty, we don't bother to send it to zzuf */
/* FIXME: if len >= count, no null-terminator is appended, so we may erased the last character */
if (ret >= sizeof(buf)) buf[ret - 1] = '\n';
else buf[ret++] = '\n';
EnterCriticalSection(&_zz_pipe_cs);
WriteFile(dbg_hdl, buf, ret, &written, NULL);
LeaveCriticalSection(&_zz_pipe_cs);
}
va_end(args);
fflush(NULL); /* flush all streams to make sure zzuf gotta catch 'em all */
}
#else
void _zz_debug(char const *format, ...)
{
va_list args;
@ -82,6 +138,7 @@ void _zz_debug2(char const *format, ...)
mydebug(format, args);
va_end(args);
}
#endif
/**
* Format a string, printf-like, and write the resulting data to zzuf's

View File

@ -122,8 +122,12 @@ BOOL __stdcall NEW(ReadFile)(HANDLE hFile, LPVOID lpBuffer,
DWORD nNumberOfBytesToRead, LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped)
{
return ORIG(ReadFile)(hFile, lpBuffer, nNumberOfBytesToRead,
BOOL ret;
ret = ORIG(ReadFile)(hFile, lpBuffer, nNumberOfBytesToRead,
lpNumberOfBytesRead, lpOverlapped);
debug("ReadFile(%#08x, %#08x, %#08x, %#08x, %#08x) = %s",
hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped, (ret ? "TRUE" : "FALSE"));
return ret;
}
#endif
@ -136,7 +140,7 @@ BOOL __stdcall NEW(CloseHandle)(HANDLE hObject)
{
BOOL ret;
ret = ORIG(CloseHandle)(hObject);
debug("CloseHandle(%i) = %i", (int)hObject, ret);
debug("CloseHandle(%i) = %s", (int)hObject, (ret ? "TRUE" : "FALSE"));
return ret;
}
#endif

View File

@ -229,10 +229,12 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, PVOID impLoad)
switch(reason)
{
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&_zz_pipe_cs);
_zz_init();
break;
case DLL_PROCESS_DETACH:
_zz_fini();
DeleteCriticalSection(&_zz_pipe_cs);
break;
}

View File

@ -36,3 +36,7 @@ extern void _zz_fini(void) __attribute__((destructor));
/* This function is needed to initialise memory functions */
extern void _zz_mem_init(void);
#ifdef _WIN32
# include <Windows.h>
extern CRITICAL_SECTION _zz_pipe_cs;
#endif

View File

@ -80,50 +80,104 @@ static int dll_inject(PROCESS_INFORMATION *, char const *);
static void *get_proc_address(void *, DWORD, char const *);
#endif
int myfork(struct child *child, struct opts *opts)
{
int pipes[3][2];
pid_t pid;
int i;
/* Prepare communication pipe */
for(i = 0; i < 3; i++)
{
int ret;
#if defined HAVE_PIPE
ret = pipe(pipes[i]);
#elif defined HAVE__PIPE
int tmp;
/* The pipe is created with NOINHERIT otherwise both parts are
* inherited. We then duplicate the part we want. */
ret = _pipe(pipes[i], 512, _O_BINARY | O_NOINHERIT);
tmp = _dup(pipes[i][1]);
close(pipes[i][1]);
pipes[i][1] = tmp;
#endif
if(ret < 0)
{
perror("pipe");
return -1;
}
}
pid = run_process(child, opts, pipes);
if(pid < 0)
{
/* FIXME: close pipes */
fprintf(stderr, "error launching `%s'\n", child->newargv[0]);
return -1;
}
child->pid = pid;
for(i = 0; i < 3; i++)
{
close(pipes[i][1]);
child->fd[i] = pipes[i][0];
}
return 0;
int myfork(struct child *child, struct opts *opts)
{
int pipes[3][2];
pid_t pid;
int i;
/* Prepare communication pipe */
for(i = 0; i < 3; i++)
{
int ret;
#if defined HAVE_PIPE
ret = pipe(pipes[i]);
#elif defined HAVE__PIPE && !defined _WIN32
int tmp;
/* The pipe is created with NOINHERIT otherwise both parts are
* inherited. We then duplicate the part we want. */
ret = _pipe(pipes[i], 512, _O_BINARY | O_NOINHERIT);
tmp = _dup(pipes[i][1]);
close(pipes[i][1]);
pipes[i][1] = tmp;
#elif defined _WIN32
// http://www.daniweb.com/software-development/cpp/threads/295780/using-named-pipes-with-asynchronous-io-redirection-to-winapi
{
static int pipe_cnt = 0;
char pipe_name[BUFSIZ];
HANDLE pipe_hdl[2]; /* [0] read | [1] write */
HANDLE new_hdl;
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
ret = 0;
/* Since we have to use a named pipe, we have to make sure the name is unique */
_snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\Pipe\\zzuf.%08x.%d", GetCurrentProcessId(), pipe_cnt++);
/* At this time, the HANDLE is inheritable and can both read/write */
if ((pipe_hdl[0] = CreateNamedPipeA(
pipe_name,
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
1,
BUFSIZ,
BUFSIZ,
0,
&sa)) == INVALID_HANDLE_VALUE ||
/* Create a new handle for writing access only and it must be inheritable */
(pipe_hdl[1] = CreateFileA(
pipe_name,
GENERIC_WRITE,
0,
&sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL)) == INVALID_HANDLE_VALUE)
ret = -1;
/* Now we create a new handle for the listener which is not inheritable */
if (!DuplicateHandle(
GetCurrentProcess(), pipe_hdl[0],
GetCurrentProcess(), &new_hdl,
0, FALSE,
DUPLICATE_SAME_ACCESS))
ret = -1;
/* Finally we can safetly close the pipe handle */
CloseHandle(pipe_hdl[0]);
/* Now we convert handle to fd */
pipes[i][0] = _open_osfhandle((intptr_t)new_hdl, 0x0);
pipes[i][1] = _open_osfhandle((intptr_t)pipe_hdl[1], 0x0);
}
#endif
if(ret < 0)
{
perror("pipe");
return -1;
}
}
pid = run_process(child, opts, pipes);
if(pid < 0)
{
/* FIXME: close pipes */
fprintf(stderr, "error launching `%s'\n", child->newargv[0]);
return -1;
}
child->pid = pid;
for(i = 0; i < 3; i++)
{
close(pipes[i][1]);
child->fd[i] = pipes[i][0];
}
return 0;
}
#if !defined HAVE_SETENV

View File

@ -112,6 +112,11 @@ static void usage(void);
#define ZZUF_FD_ISSET(fd, p_fdset) \
((fd >= 0) && (FD_ISSET(fd, p_fdset)))
#if defined _WIN32
# include <Windows.h>
static CRITICAL_SECTION _zz_pipe_cs;
#endif
int main(int argc, char *argv[])
{
struct opts _opts, *opts = &_opts;
@ -123,6 +128,10 @@ int main(int argc, char *argv[])
int debug = 0, network = 0;
int i;
#if defined _WIN32
InitializeCriticalSection(&_zz_pipe_cs);
#endif
_zz_opts_init(opts);
for(;;)
@ -536,6 +545,10 @@ int main(int argc, char *argv[])
_zz_fd_fini();
_zz_opts_fini(opts);
#if defined _WIN32
DeleteCriticalSection(&_zz_pipe_cs);
#endif
return opts->crashes ? EXIT_FAILURE : EXIT_SUCCESS;
}
@ -914,6 +927,88 @@ static void clean_children(struct opts *opts)
}
}
#ifdef _WIN32
/* This structure contains useful information about data sent from fuzzed applications */
struct child_overlapped
{
OVERLAPPED overlapped;
char buf[BUFSIZ];
struct opts * opts;
int child_no;
int fd_no;
};
/* This callback is called when fuzzed applications write in fd out, err or debug */
static void _stdcall read_child(DWORD err_code, DWORD nbr_of_bytes_transfered, LPOVERLAPPED overlapped)
{
struct child_overlapped * co = (struct child_overlapped *)overlapped;
/* TODO: handle more cases like ERROR_MORE_DATA */
if (err_code != ERROR_SUCCESS) return;
EnterCriticalSection(&_zz_pipe_cs);
switch (co->fd_no)
{
case 0: /* debug fd */
write(1, "dbg: ", 4);
case 1: /* out */
write(1, co->buf, nbr_of_bytes_transfered); break;
case 2: /* err */
write(2, co->buf, nbr_of_bytes_transfered); break;
default: break;
}
LeaveCriticalSection(&_zz_pipe_cs);
if(co->fd_no != 0) /* either out or err fd */
co->opts->child[co->child_no].bytes += nbr_of_bytes_transfered;
if(co->opts->md5 && co->fd_no == 2)
_zz_md5_add(co->opts->child[co->child_no].ctx, co->buf, nbr_of_bytes_transfered);
free(co); /* clean up allocated data */
}
/* Since on windows select doesn't support file HANDLE, we use IOCP */
static void read_children(struct opts *opts)
{
size_t i, j;
HANDLE *children_handle, * cur_child_handle;
size_t fd_number = opts->maxchild * 3;
cur_child_handle = children_handle = malloc(sizeof(*children_handle) * fd_number);
for(i = 0; i < fd_number; i++)
children_handle[i] = INVALID_HANDLE_VALUE;
/* XXX: cute (i, j) iterating hack */
for(i = 0, j = 0; i < (size_t)opts->maxchild; i += (j == 2), j = (j + 1) % 3)
{
struct child_overlapped * co;
HANDLE h;
if(opts->child[i].status != STATUS_RUNNING)
continue;
co = malloc(sizeof(*co));
ZeroMemory(co, sizeof(*co));
*cur_child_handle = co->overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
co->child_no = i;
co->fd_no = j;
co->opts = opts;
h = (HANDLE)_get_osfhandle(opts->child[i].fd[j]);
/* FIXME: handle error */
ReadFileEx(h, co->buf, sizeof(co->buf), (LPOVERLAPPED)co, read_child);
cur_child_handle++;
}
/* FIXME: handle error */
WaitForMultipleObjectsEx(fd_number, children_handle, FALSE, INFINITE, TRUE);
}
#else
static void read_children(struct opts *opts)
{
struct timeval tv;
@ -933,15 +1028,12 @@ static void read_children(struct opts *opts)
tv.tv_sec = 0;
tv.tv_usec = 1000;
#if !defined _WIN32
/* Win32 does not support select() on non-sockets */
errno = 0;
ret = select(maxfd + 1, &fdset, NULL, NULL, &tv);
if(ret < 0 && errno)
perror("select");
if(ret <= 0)
return;
#endif
/* XXX: cute (i, j) iterating hack */
for(i = 0, j = 0; i < opts->maxchild; i += (j == 2), j = (j + 1) % 3)
@ -951,10 +1043,8 @@ static void read_children(struct opts *opts)
if(opts->child[i].status != STATUS_RUNNING)
continue;
#if !defined _WIN32
if(!ZZUF_FD_ISSET(opts->child[i].fd[j], &fdset))
continue;
#endif
ret = read(opts->child[i].fd[j], buf, BUFSIZ - 1);
if(ret > 0)
@ -968,7 +1058,6 @@ static void read_children(struct opts *opts)
else if(!opts->quiet || j == 0)
write((j < 2) ? STDERR_FILENO : STDOUT_FILENO, buf, ret);
}
#if !defined _WIN32
else if(ret == 0)
{
/* End of file reached */
@ -980,9 +1069,9 @@ static void read_children(struct opts *opts)
&& opts->child[i].fd[2] == -1)
opts->child[i].status = STATUS_EOF;
}
#endif
}
}
#endif
#if !defined HAVE_SETENV
static void setenv(char const *name, char const *value, int overwrite)