|
|
/*
|
|
|
This sandbox module is from [Fossil
|
|
|
grader](http://code.google.com/p/fossil-grader/).
|
|
|
|
|
|
This library is a modification from a program called trun, taken
|
|
|
from an unknown source. (FIX THIS)
|
|
|
|
|
|
When compiling with Mingw, add "-lpsapi" to link Windows'memory stat
|
|
|
library.
|
|
|
*/
|
|
|
#include <windows.h>
|
|
|
#include <psapi.h>
|
|
|
#include <tlhelp32.h>
|
|
|
#include <stdio.h>
|
|
|
#include "execute.h"
|
|
|
|
|
|
#define INITIAL_WAIT_FOR_MEM_CHECK 100
|
|
|
|
|
|
/*
|
|
|
==How execute works==
|
|
|
|
|
|
===Start up===
|
|
|
Set up basic configurations: input file, output file
|
|
|
into STARTUPINFO struct to be passed to CreateProcess.
|
|
|
|
|
|
Create a child process with CreateProcess.
|
|
|
|
|
|
===Wait===
|
|
|
Use WaitForSingleObject to wait.
|
|
|
|
|
|
===Killing chile process===
|
|
|
This process is really involved, because (1) programs in
|
|
|
DOS mode actually runs inside NTVDM so killing them
|
|
|
requires to kill NTVDM, (2) something a program crashes
|
|
|
NTVDM and a dialog box pops up, and we need to close
|
|
|
that dialog box MANUALLY, and (3) for Win32 apps that crash,
|
|
|
some reporting service in Windows opens a dialog box,
|
|
|
and it has to be killed.
|
|
|
|
|
|
Those extra steps are what's exactly done here:
|
|
|
1. Kill the process if there's any
|
|
|
2. In case that there's no real process, find NTVDM
|
|
|
and kill it (repeatedly until it's gone)
|
|
|
3. Check if NTVDM crashed and some warning dialog opens,
|
|
|
if there's any, signal the user and wait.
|
|
|
4. For real Win32 apps, find process "dwwin.exe" which
|
|
|
represents an agent for reporting service and also
|
|
|
opens a dialog. If finds it, kill it (repeatedly)
|
|
|
until it's gone.
|
|
|
|
|
|
Step 4. might be problematic --- dwwin.exe might not
|
|
|
be a universal process for error reporting services???
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
These are routines that check NTVDM crash dialog.
|
|
|
It works by enumerating all window titles, and
|
|
|
checks for "16 bit" or something with ".exe" somewhere
|
|
|
and starts with "cmd.exe".
|
|
|
*/
|
|
|
bool NTVDMcrashed_found;
|
|
|
|
|
|
/* this is a callback for window title enumeration */
|
|
|
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
|
|
|
{
|
|
|
char buffer[256];
|
|
|
GetWindowText(hWnd, buffer, 256);
|
|
|
|
|
|
if(strlen(buffer)!=0) {
|
|
|
if(strstr(buffer,"16 bit")!=0) {
|
|
|
NTVDMcrashed_found = true;
|
|
|
}
|
|
|
if((strstr(buffer,".exe")!=0) &&
|
|
|
(strstr(buffer,"cmd.exe")==buffer)) {
|
|
|
NTVDMcrashed_found = true;
|
|
|
printf("Title: %s\n",buffer);
|
|
|
}
|
|
|
}
|
|
|
return TRUE;
|
|
|
}
|
|
|
|
|
|
bool check_ntvdm_dialog()
|
|
|
{
|
|
|
NTVDMcrashed_found = false;
|
|
|
|
|
|
FARPROC EnumProcInstance = MakeProcInstance((FARPROC)EnumWindowsProc,
|
|
|
AfxGetInstanceHandle());
|
|
|
EnumWindows((WNDENUMPROC)EnumProcInstance, (LPARAM)0);
|
|
|
FreeProcInstance(EnumProcInstance);
|
|
|
|
|
|
return NTVDMcrashed_found;
|
|
|
}
|
|
|
|
|
|
DWORD get_process_id(char *pname)
|
|
|
{
|
|
|
HANDLE hProcessSnap;
|
|
|
HANDLE hProcess;
|
|
|
PROCESSENTRY32 pe32;
|
|
|
DWORD dwPriorityClass;
|
|
|
DWORD pid=0;
|
|
|
|
|
|
hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 );
|
|
|
if( hProcessSnap == INVALID_HANDLE_VALUE ) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
pe32.dwSize = sizeof( PROCESSENTRY32 );
|
|
|
if( !Process32First( hProcessSnap, &pe32 ) ) {
|
|
|
CloseHandle( hProcessSnap );
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
do {
|
|
|
if(strcasecmp(pe32.szExeFile ,pname)==0)
|
|
|
pid = pe32.th32ProcessID;
|
|
|
} while( Process32Next( hProcessSnap, &pe32 ) );
|
|
|
|
|
|
CloseHandle( hProcessSnap );
|
|
|
return pid;
|
|
|
}
|
|
|
|
|
|
DWORD get_ntvdm_pid()
|
|
|
{
|
|
|
return get_process_id("ntvdm.exe");
|
|
|
}
|
|
|
|
|
|
void kill_error_report()
|
|
|
{
|
|
|
DWORD pid;
|
|
|
do {
|
|
|
if((pid = get_process_id("dwwin.exe"))!=0) {
|
|
|
fprintf(stderr," -- with error report (pid: %ld)\n",pid);
|
|
|
HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid);
|
|
|
if(hProcess!=NULL) {
|
|
|
TerminateProcess(hProcess, 0);
|
|
|
Sleep(500);
|
|
|
while(get_process_id("dwwin.exe")==pid) {
|
|
|
fprintf(stderr,"wait for dwwin.exe to die...\n");
|
|
|
Sleep(500);
|
|
|
}
|
|
|
} else
|
|
|
fprintf(stderr,"do not have permission (%d)\n",
|
|
|
GetLastError());
|
|
|
}
|
|
|
} while(get_process_id("dwwin.exe")!=0);
|
|
|
}
|
|
|
|
|
|
void wait_dialog()
|
|
|
{
|
|
|
kill_error_report();
|
|
|
if(check_ntvdm_dialog()) {
|
|
|
fprintf(stderr,"Some dialog opens; please MANUALLY kill it.");
|
|
|
fflush(stderr);
|
|
|
do {
|
|
|
Sleep(1000);
|
|
|
} while(check_ntvdm_dialog());
|
|
|
fprintf(stderr,"... done\n");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void setstartupinfo(STARTUPINFO *si, char *inname, char *outname)
|
|
|
{
|
|
|
SECURITY_ATTRIBUTES sa;
|
|
|
|
|
|
ZeroMemory(&sa, sizeof(sa));
|
|
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
sa.lpSecurityDescriptor = NULL;
|
|
|
sa.bInheritHandle = TRUE;
|
|
|
|
|
|
si->dwFlags = STARTF_USESTDHANDLES;
|
|
|
if((inname!=0) && (strcmp(inname,"-")!=0)) {
|
|
|
si->hStdInput = CreateFile(inname,
|
|
|
FILE_READ_DATA,
|
|
|
FILE_SHARE_READ,
|
|
|
&sa,
|
|
|
OPEN_EXISTING,
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
NULL);
|
|
|
} else
|
|
|
si->hStdInput = NULL;
|
|
|
|
|
|
if((outname!=0) && (strcmp(outname,"-")!=0)) {
|
|
|
si->hStdOutput = CreateFile(outname,
|
|
|
FILE_WRITE_DATA,
|
|
|
FILE_SHARE_READ,
|
|
|
&sa,
|
|
|
CREATE_ALWAYS,
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
NULL);
|
|
|
} else
|
|
|
si->hStdOutput = NULL;
|
|
|
|
|
|
si->hStdError = NULL;
|
|
|
}
|
|
|
|
|
|
// taken from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx
|
|
|
void PrintMemoryInfo(DWORD processID)
|
|
|
{
|
|
|
HANDLE hProcess;
|
|
|
PROCESS_MEMORY_COUNTERS pmc;
|
|
|
|
|
|
// Print the process identifier.
|
|
|
|
|
|
printf("\nProcess ID: %u\n", processID);
|
|
|
|
|
|
// Print information about the memory usage of the process.
|
|
|
|
|
|
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
|
|
|
PROCESS_VM_READ,
|
|
|
FALSE,processID);
|
|
|
if(hProcess == NULL)
|
|
|
return;
|
|
|
|
|
|
if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
|
|
|
printf("\tPageFaultCount: %d\n",pmc.PageFaultCount);
|
|
|
printf("\tPeakWorkingSetSize: %d\n",
|
|
|
pmc.PeakWorkingSetSize);
|
|
|
printf("\tWorkingSetSize: %d\n",pmc.WorkingSetSize);
|
|
|
printf("\tQuotaPeakPagedPoolUsage: %d\n",
|
|
|
pmc.QuotaPeakPagedPoolUsage);
|
|
|
printf("\tQuotaPagedPoolUsage: %d\n",
|
|
|
pmc.QuotaPagedPoolUsage);
|
|
|
printf("\tQuotaPeakNonPagedPoolUsage: %d\n",
|
|
|
pmc.QuotaPeakNonPagedPoolUsage);
|
|
|
printf("\tQuotaNonPagedPoolUsage: %d\n",
|
|
|
pmc.QuotaNonPagedPoolUsage);
|
|
|
printf("\tPagefileUsage: %d\n",pmc.PagefileUsage);
|
|
|
printf("\tPeakPagefileUsage: %d\n",
|
|
|
pmc.PeakPagefileUsage);
|
|
|
}
|
|
|
CloseHandle( hProcess );
|
|
|
}
|
|
|
|
|
|
int check_memory_usage(DWORD pid, int max_mem, int *actual_usage) {
|
|
|
// modified from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx
|
|
|
//PrintMemoryInfo(pid);
|
|
|
HANDLE hProcess;
|
|
|
PROCESS_MEMORY_COUNTERS pmc;
|
|
|
|
|
|
if((max_mem==0) || (pid==0))
|
|
|
return 1;
|
|
|
|
|
|
if(pid == get_ntvdm_pid()) {
|
|
|
fprintf(stderr,"ntvdm: ignored\n");
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
|
|
|
PROCESS_VM_READ,
|
|
|
FALSE, pid);
|
|
|
if(hProcess == NULL)
|
|
|
return 1;
|
|
|
|
|
|
int max_mem_usage = 0;
|
|
|
if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
|
|
|
max_mem_usage = pmc.PeakWorkingSetSize;
|
|
|
if(pmc.PeakPagefileUsage > max_mem_usage)
|
|
|
max_mem_usage = pmc.PeakPagefileUsage;
|
|
|
}
|
|
|
CloseHandle(hProcess);
|
|
|
if(actual_usage != NULL)
|
|
|
(*actual_usage) = max_mem_usage;
|
|
|
return (max_mem_usage <= max_mem);
|
|
|
}
|
|
|
|
|
|
int execute(char *exname, char *inname, char *outname, double t, int max_mem)
|
|
|
{
|
|
|
STARTUPINFO si;
|
|
|
PROCESS_INFORMATION pi;
|
|
|
int ifsuccess = EXE_RESULT_OK;
|
|
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
|
si.cb = sizeof(si);
|
|
|
ZeroMemory(&pi, sizeof(pi));
|
|
|
|
|
|
setstartupinfo(&si, inname, outname);
|
|
|
|
|
|
if(!CreateProcess( NULL, // No module name (use command line).
|
|
|
TEXT(exname), // Command line.
|
|
|
NULL, // Process handle not inheritable.
|
|
|
NULL, // Thread handle not inheritable.
|
|
|
TRUE, // Set handle inheritance to FALSE.
|
|
|
0, // No creation flags.
|
|
|
NULL, // Use parent's environment block.
|
|
|
NULL, // Use parent's starting directory.
|
|
|
&si, // Pointer to STARTUPINFO structure.
|
|
|
&pi)) // Pointer to PROCESS_INFORMATION structure.
|
|
|
{
|
|
|
//printf( "CreateProcess failed (%d).\n", GetLastError() );
|
|
|
}
|
|
|
//fprintf(stderr,"Process ID: %ld\n",pi.dwProcessId);
|
|
|
//fprintf(stderr,"time limit = %d\n",t);
|
|
|
|
|
|
// checking memory usage
|
|
|
// wait 0.1 sec before checking mem usage
|
|
|
|
|
|
int actual_memory_usage = 0;
|
|
|
|
|
|
Sleep(INITIAL_WAIT_FOR_MEM_CHECK);
|
|
|
if(!check_memory_usage(pi.dwProcessId,max_mem,&actual_memory_usage)) {
|
|
|
// using too much memory
|
|
|
fprintf(stderr,"Memory limit exceeded.\n");
|
|
|
//PrintMemoryInfo(pi.dwProcessId);
|
|
|
ifsuccess = EXE_RESULT_MEMORY;
|
|
|
}
|
|
|
|
|
|
if((ifsuccess == EXE_RESULT_MEMORY) ||
|
|
|
(WaitForSingleObject(pi.hProcess,
|
|
|
(int)(t*1000) + 1
|
|
|
- INITIAL_WAIT_FOR_MEM_CHECK)==WAIT_TIMEOUT)) {
|
|
|
// need to kill...
|
|
|
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId);
|
|
|
|
|
|
if(ifsuccess != EXE_RESULT_MEMORY)
|
|
|
fprintf(stderr,"Time limit exceeded.\n");
|
|
|
if(hProcess != NULL) {
|
|
|
fprintf(stderr,"killing pid: %ld\n",pi.dwProcessId);
|
|
|
TerminateProcess(hProcess, 0);
|
|
|
wait_dialog();
|
|
|
} else {
|
|
|
DWORD dwNtvdmId = get_ntvdm_pid();
|
|
|
fprintf(stderr,"killing (ntvdm) pid: %ld\n",dwNtvdmId);
|
|
|
if(dwNtvdmId!=0) {
|
|
|
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwNtvdmId);
|
|
|
TerminateProcess(hProcess, 0);
|
|
|
} else {
|
|
|
fprintf(stderr,"killing process error\n");
|
|
|
}
|
|
|
|
|
|
if(get_ntvdm_pid()!=0) {
|
|
|
fprintf(stderr,"killing error, ntvdm.exe still remains;");
|
|
|
fprintf(stderr,"please MANUALLY kill it.");
|
|
|
fflush(stderr);
|
|
|
do {
|
|
|
Sleep(1000);
|
|
|
} while(get_ntvdm_pid()!=0);
|
|
|
fprintf(stderr,"... done\n");
|
|
|
wait_dialog();
|
|
|
}
|
|
|
}
|
|
|
if(ifsuccess != EXE_RESULT_MEMORY)
|
|
|
ifsuccess = EXE_RESULT_TIMEOUT;
|
|
|
}
|
|
|
if((ifsuccess==EXE_RESULT_OK) &&
|
|
|
(!check_memory_usage(pi.dwProcessId,max_mem, &actual_memory_usage))) {
|
|
|
// using too much memory
|
|
|
ifsuccess = EXE_RESULT_MEMORY;
|
|
|
}
|
|
|
wait_dialog();
|
|
|
if(si.hStdInput!=NULL)
|
|
|
CloseHandle(si.hStdInput);
|
|
|
if(si.hStdOutput!=NULL)
|
|
|
CloseHandle(si.hStdOutput);
|
|
|
|
|
|
if(ifsuccess==EXE_RESULT_OK)
|
|
|
fprintf(stderr,"OK\n");
|
|
|
else if(ifsuccess==EXE_RESULT_TIMEOUT)
|
|
|
fprintf(stderr,"Time limit exceeded.\n");
|
|
|
else
|
|
|
fprintf(stderr,"Memory limit exceeded.\n");
|
|
|
|
|
|
double actual_time_usage;
|
|
|
if(ifsuccess==EXE_RESULT_TIMEOUT)
|
|
|
actual_time_usage = t+1;
|
|
|
else
|
|
|
actual_time_usage = t;
|
|
|
|
|
|
fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n",
|
|
|
actual_time_usage,
|
|
|
actual_time_usage, (double)0,
|
|
|
(actual_memory_usage + 1023)/1024);
|
|
|
|
|
|
return ifsuccess;
|
|
|
}
|
|
|
|
|
|
|