/* anbexec.c *********************************************** Anubis Virtual Machine. 'main' function for anbexec. ************************************************************/ #include "AnubisSupport.h" #include #include //#include #include #include #include #include #include #include #include #include "vm.h" #include "bytecode.h" #include "cipher.h" #include "minver.h" #include "DebugLog.h" #include "dependencies.h" #include "AnubisProcess.h" #include "AnubisAllocator.h" #include "IniFile.h" #include "tools.h" #include "debugger.h" #include "FileIO.h" #include "MultiPath.h" #include "../../../third_dev/sqlite3/sqlite3.h" #include "perf.h" #if defined (_LINUX_) || (__BEOS__) #include #include #endif #ifdef _LINUX_ #include //#include #include #endif #ifdef WIN32 #include "process.h" #include //need under mingw for usleep #endif #ifdef _WITH_SSL_ #include #include #include #endif #ifdef WITH_SQLAPI #include "SQLAPI.h" #endif USING_NAMESPACE(CM); #ifdef WIN32 #define SIGPIPE (SIGABRT) #endif #ifdef __BEOS__ //BApplication #endif #ifdef _CRYPTADM_ U8 *id_card_ms; FILE *fp; char adm_encryption_key[50]; char *djed = NULL; U32 decrypted_code_length_1; U32 decrypted_code_length_2; #endif MultiPath modulePaths; #ifdef WATCH_CODE U8 *original_code = NULL; U8 *duplicate_code = NULL; U32 watched_code_size = 0; #endif U32 get_file_size(FILE *fp) { U32 result; U32 pos; pos = ftell(fp); /* remember current position */ fseek(fp,0,SEEK_END); /* go to end of file */ result = (U32)ftell(fp); /* get position, i.e. size of file */ fseek(fp,pos,SEEK_SET); /* return to initial position */ return result; } /* If the next flag is on, anbexec will restart instead of exiting 'main'. */ U32 must_restart_flag = 0; U32 max_steps = 500; U32 plineno = 1; FILE *nsc_file = NULL; #define anbexecbufsize (3000) char anbexecbuf[anbexecbufsize]; struct SegmentDescription *seg_descrs; U8* IP = NULL; U8* code = NULL; jmp_buf setjmp_env; U32 setjmp_set = 0; void handle_sigsegv(int sig) { if (!setjmp_set) { Debugger::DumpProcesses(); #ifdef debug_vm printf("Segmentation fault in anbexec at IP=%u.\n",IPcode); #else printf("Segmentation fault in anbexec.\n"); #endif my_exit(1); } else { longjmp(setjmp_env,1); } } bool compute_perf = false; U32 security_zone_size = 0; String vmStartDirectory; String anubis_directory; String my_anubis_directory; String anubisUserDirectory; /* SSL */ String trusted_certs_directory; String certificate_file_name; String ssl_password; String random_file; String profile_output_format; String csv_separator = ","; bool quitingVM = false; fd_set rfds; fd_set wfds; fd_set efds; struct timeval time_to_wait; struct timeval current_time; struct timeval next_tick; U32 alarm_set; U32 show_waiting_descriptors = 0; U32 show_module_loading = 0; extern U32 anubis_empty_string; extern U32 anubis_empty_byte_array; pid_t mypid; U32 euid; U32 realuid; char *msgbuf = NULL; U32 max_connection = 0; FILE *modfile = NULL; U8 *byte_code = NULL; char *modname = NULL; U32 code_size; U32 starting_point; U32 console = 0; U32 show_info = 0; #ifdef _WITH_FREETYPE_ FT_Library freetype_library; #endif #ifdef record_allocations static void show_allocated_segments(void) { U32 i; U32 n = 0; for (i = 0; i < max_seg_descr; i++) if (seg_descrs[i].seg != NULL) { n++; printf("%p (%s:%d) cnt=%d IP=%d\n", seg_descrs[i].seg, seg_descrs[i].filename, seg_descrs[i].line, (seg_descrs[i].seg)[0], seg_descrs[i].IP); } printf("%d segments currently allocated.\n",n); } #endif void show_fd_set(char *name, fd_set *addr) { int f = *((int *)addr); fprintf(stdout,"%15s: %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",name, (f & 1) ? '1' : '0', (f & 2) ? '1' : '0', (f & 4) ? '1' : '0', (f & 8) ? '1' : '0', (f & 16) ? '1' : '0', (f & 32) ? '1' : '0', (f & 64) ? '1' : '0', (f & 128) ? '1' : '0', (f & 256) ? '1' : '0', (f & 512) ? '1' : '0', (f & 1024) ? '1' : '0', (f & 2048) ? '1' : '0', (f & 4096) ? '1' : '0', (f & 8192) ? '1' : '0', (f & 16384) ? '1' : '0', (f & 32768) ? '1' : '0'); } U32 read32(FILE *fp) { U32 result; U32 i; for (i = 0; i < 4; i++) ((char *)&result)[i] = getc(fp); return result; } void syntax(void) { LOGINFO("anbexec version %d.%d.%d.%d\n" " (Build date: %s %s)\n" #ifdef _WITH_SSL_ " (%s)\n" #endif #ifdef WITH_SQLAPI " (SQLAPI v %d.%d.%d)\n" #endif " (SQLITE %s)\n" "Usage: anbexec [arguments] [options]\n" " is the name of an '.adm' file (cannot begin by '-').\n" " [arguments] are the arguments to be passed to the module.\n" " Options are:\n" " --verbose\n" " --info show informations about the module (can be secondary)\n" " which is not executed.\n" " --cdir:path sets the path of the 'anubis' directory.\n" " --pdir:path sets the path of the 'my_anubis' directory.\n" " --credit:n where 'n' is an integer, sets the maximum number\n" " of instructions that can be executed by a process\n" " in a single slice of time\n" " --MB:n where 'n' is an integer, sets the\n" " maximum memory to be used in megabytes\n" " --main_seg_size:n where 'n' is the default size of main allocator\n" " segments in number of 32 bits words\n" " --128bits_ciphers allow only cryptography with keys of at least 128 bits\n" #ifdef _WITH_SSL_ #endif " --profile: defines output format for profiling data.\n" " Supported values are 'text' or 'csv'.\n" " --csv_sep: defines which separator to use when outputing profiling\n" " data as CSV. Default is ',' (comma).\n" " --perf activate performance computing (load average)\n" , maj_version, min_version, rel_version, build_version, __DATE__,__TIME__, #ifdef _WITH_SSL_ OPENSSL_VERSION_TEXT, #endif #ifdef WITH_SQLAPI SQLAPI_VER_MAJOR, SQLAPI_VER_MINOR, SQLAPI_VER_BUILD, #endif SQLITE_VERSION ); my_exit(1); } /* load the module from the file returns : 0 cannot open file 1 not enough memory 2 module corrupted 3 bad version number 4 ok 5 bad adm signature 6 secondary modules */ U32 load_module(struct Exec_Mod_struct *mod, char *filename) { String tmpFileName = filename; String realFileName; if(tmpFileName.EndsWithIgnoreCase(".adm") == false) { tmpFileName << ".adm"; } //printf("tmp file name: %s\n",tmpFileName.Cstr()); realFileName = modulePaths.CheckFile(tmpFileName); if(realFileName.Length() == 0) { return 0; // means cannot open file } //printf("real file name: %s\n",realFileName.Cstr()); FILE *fp = fopen(realFileName.Cstr(),"rb"); U32 adm_signature_length = strlen(adm_signature); U32 version; U32 date; U32 type_desc_size; /* (added in version 1.13) */ U32 code_size; U32 starting_point; U8 ident[20]; U8 *ccheck; U8 rcheck[20]; char *type_desc; /* (added in version 1.13) */ U8 *code; U8 *all; U32 k; char *sign; if (fp == NULL) return 0; /* read signature */ if ((sign = (char *)malloc(adm_signature_length+1)) == NULL) { fprintf(stderr,"Not enough memory (9).\n"); return 1; } //printf("adm_signature_length = %d\n",adm_signature_length); for (k = 0; k < adm_signature_length; k++) { sign[k] = getc(fp); } sign[k] = 0; //printf("Signature just read: %s\n",sign); if (strcmp(sign,adm_signature)) { fclose(fp); return 5; } /* read module version, date and flags */ version = read32(fp); date = read32(fp); mod->flags = read32(fp); /* read type description size */ type_desc_size = read32(fp); //printf("load_module: type_desc_size = %lu\n",type_desc_size); /* read code size and starting point */ code_size = read32(fp); mod->starting_point = starting_point = read32(fp); /* read identification */ for (k = 0; k < 20; k++) ident[k] = getc(fp); /* read type description */ if ((type_desc = (char *)malloc(type_desc_size+1)) == NULL) { fprintf(stderr,"Not enough memory (9a).\n"); return 1; } for (k = 0; k < type_desc_size; k++) { type_desc[k] = getc(fp); } type_desc[k] = 0; /* allocate memory for byte code (if adm is encrypted, code size includes padding and extra byte) */ if ((mod->byte_code = code = (U8 *)malloc(((size_t)code_size)+8)) == NULL) { fclose(fp); return 1; } /* code segment must be aligned on 0 mod 4 */ assert((((U32)code)&3) == 0); /* make the code a permanent byte array */ ((U32 *)code)[0] = 0; /* put the size at its place */ ((U32 *)code)[1] = code_size; #ifdef debug_vm common_code_begin = code+8; /* actual beginning of code */ #endif /* allocate memory for computing the checksum (of module with non encrypted code) */ if ((all = (U8 *)malloc(4+4+4+4+4+4+20+type_desc_size+code_size)) == NULL) { fprintf(stderr,"Not enough memory (1).\n"); free(code); fclose(fp); return 1; } /* store data for checksumming */ *((U32 *)(all)) = version; *((U32 *)(all+4)) = date; *((U32 *)(all+4+4)) = mod->flags; *((U32 *)(all+4+4+4)) = type_desc_size; *((U32 *)(all+4+4+4+4)) = code_size; *((U32 *)(all+4+4+4+4+4)) = starting_point; for (k = 0; k < 20; k++) (all+4+4+4+4+4+4)[k] = ident[k]; /* copy type description */ for (k = 0; k < type_desc_size; k++) (all+4+4+4+4+4+4+20)[k] = type_desc[k]; /* read code into both arrays */ for (k = 0; k < code_size; k++) { (all+4+4+4+4+4+4+20+type_desc_size)[k] = (code+8)[k] = getc(fp); } /* compute the checksum */ ccheck = (U8*)sha1((char *)all,4+4+4+4+4+4+20+type_desc_size+code_size); /* read the checksum from the file */ for (k = 0; k < 20; k++) rcheck[k] = getc(fp); if (console) { LOGINFO("\n| read checksum: %s", sha1_to_ascii(rcheck)); LOGINFO("\n| computed checksum: %s", sha1_to_ascii(ccheck)); fflush(stdout); } // here we don't need file data anymore, hence we close the file if (fp) { fclose(fp); } /* check version */ if ( get_maj_version(version) != maj_version || get_min_version(version) != min_version ) { LOGERROR("ADM version check failed.\n"); LOGERROR(" '%s' ADM has been built by Anubis Compiler version %d.%d.%d.%d\n", filename, get_maj_version(version), get_min_version(version), get_rel_version(version), get_build_number(version)); LOGERROR(" Current Virtual Machine version is %d.%d.%d.%d\n", maj_version, min_version, rel_version, build_version); return 3; } /* compare the two checksums */ if (!same_sha1(ccheck,rcheck)) { return 2; } if (console || show_info) { LOGINFO("\nanbexec: %s module '%s' " "\n| version: %u.%u.%u.%u" "\n| date: %s" "\n| using SSL: %s" "\n| using graphism: %s" "\n| size of type description: %u" "\n| size of code: %u" "\n| starting point: %u" "\ntype of module: \n%s", (mod->flags & mf_secondary_adm) ? "secondary" : "primary", filename, get_maj_version(version), get_min_version(version), get_rel_version(version), get_build_number(version), format_date(date), (mod->flags & mf_using_ssl) ? "yes" : "no", (mod->flags & mf_using_graphism) ? "yes" : "no", type_desc_size, code_size, starting_point, type_desc); fflush(stdout); } if (show_info) { my_exit(0); } /* from now on, the module must not be secondary */ if ((mod->flags) & mf_secondary_adm) { return 6; } if (console) { printf("\n| module successfully loaded"); } if (nsc_file == stdout) { assert(modname != NULL); /* snprintf(anbexecbuf,anbexecbufsize-10,"%s.nsc",modname); if ((nsc_file = fopen(anbexecbuf,"wt")) == NULL) { fprintf(stderr,"Cannot open file %s\n",anbexecbuf); exit(1); } */ fprintf(nsc_file,"Dump of %s\n\n\n",modname); } /* relocate */ link_globals_and_relocate(code+8,code_size); /* free 'all' (or keep it) */ #ifdef WATCH_CODE original_code = code+8; duplicate_code = all; watched_code_size = code_size; /* copy the relocated code to 'all' (which is big enough) */ { U32 i; for (i = 0; i < watched_code_size; i++) duplicate_code[i] = original_code[i]; } #else free(all); #endif if (nsc_file != NULL) { fclose(nsc_file); // if asked for a pseudo .sc file don't execute the module my_exit(1); } if (console) { LOGINFO("\n| module successfully relocated" "\n|______________________\n"); } return 4; } static U32 timevalless(struct timeval *t1, struct timeval *t2) { if (t1->tv_sec < t2->tv_sec) return 1; if (t1->tv_sec > t2->tv_sec) return 0; /* from here the above are equal */ if (t1->tv_usec < t2->tv_usec) return 1; else return 0; } /* the next function is copy-pasted from the GNU info for libc (struct timeval) */ int timeval_substract (struct timeval *result, struct timeval *x, struct timeval *y) { /* Perform the carry for the later subtraction by updating Y. */ if (x->tv_usec < y->tv_usec) { int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; y->tv_usec -= 1000000 * nsec; y->tv_sec += nsec; } if (x->tv_usec - y->tv_usec > 1000000) { int nsec = (x->tv_usec - y->tv_usec) / 1000000; y->tv_usec += 1000000 * nsec; y->tv_sec -= nsec; } /* Compute the time remaining to wait. `tv_usec' is certainly positive. */ result->tv_sec = x->tv_sec - y->tv_sec; result->tv_usec = x->tv_usec - y->tv_usec; /* Return 1 if result is negative. */ return x->tv_sec < y->tv_sec; } struct Exec_Mod_struct the_primary_module; U32 used_machines = 0; U32 module_check_sum = 0; void dump_module(void) { U32 i; //for (i = 0; i < the_primary_module.code_size; i++) for (i = 0; i < ((U32 *)(the_primary_module.byte_code))[1]; i++) { if ((i&15) == 0) printf("\n%8d: ",i); printf("%3d ",(the_primary_module.byte_code)[i]); } } void get_module_check_sum(void) { U32 i; module_check_sum = 0; for (i = 0; i < ((U32 *)(the_primary_module.byte_code))[1]; i++) { module_check_sum += (the_primary_module.byte_code)[i]; if ((the_primary_module.byte_code)[i] == i_protect) i++; /* avoid variable byte */ } } void check_module_integrity(void) { U32 i; U32 j; j = 0; for (i = 0; i < ((U32 *)(the_primary_module.byte_code))[1]; i++) { j += (the_primary_module.byte_code)[i]; if ((the_primary_module.byte_code)[i] == i_protect) i++; /* avoid variable byte */ } //printf("\n<<<%d %d>>>",module_check_sum,j); if (module_check_sum != j) { LOGERROR("code is corrupted.\n"); my_exit(1); } } U32 avenrun[3]; static void calc_load(U32 ticks, U32 active_tasks /* fixed-point */, bigtime_t * slice) { static int32_t count = LOAD_FREQ; //U32 active_tasks_fixed = active_tasks << FSHIFT; /* fixed-point */ count -= ticks; if (count < 0) { count += LOAD_FREQ; CALC_LOAD(avenrun[0], EXP_1, active_tasks); CALC_LOAD(avenrun[1], EXP_5, active_tasks); CALC_LOAD(avenrun[2], EXP_15, active_tasks); //printf("Load = %.1f %.1f %.1f %.1f \n", (float)active_tasks / FIXED_1, (float)avenrun[0] / FIXED_1, (float)avenrun[1] / FIXED_1, (float)avenrun[2] / FIXED_1); //printf("Load = %d %d %d %d \n", active_tasks, avenrun[0], avenrun[1], avenrun[2]); U32 nbProcess = TheAnubisProcessList->GetCount(); AnubisProcess * processItem = NULL; U32 ul_slice = (U32)((*slice << FSHIFT) / 1000000); // in seconds U32 sum_load = 0; float sum = 0.0f; for (U32 i = 0; i < nbProcess; i++) { processItem = TheAnubisProcessList->GetProcessAt(i); if (!processItem) { continue; } int64_t t = (processItem->GetPerfSum() << FSHIFT) / 1000000 ; // convert into fixed-point seconds U32 load = 0; sum += t; if(ul_slice != 0) load = (U32)((t << FSHIFT) / ul_slice); sum_load += load; CALC_LOAD(processItem->m_perf_1, EXP_1, load); CALC_LOAD(processItem->m_perf_5, EXP_5, load); CALC_LOAD(processItem->m_perf_15, EXP_15, load); //printf("[%d] Load = %.3f %.3f %.3f %.3f\n", processItem->GetPid(), (float)load / FIXED_1, (float)processItem->GetAverageLoad1() / FIXED_1, (float)processItem->GetAverageLoad5() / FIXED_1, (float)processItem->GetAverageLoad15() / FIXED_1); processItem->ResetPerfSum(); } //printf("Sum = %.3f / %.3f / %.3f / %.3f\n", (float)sum / FIXED_1, (float)ul_slice / FIXED_1, (float)sum_load/ FIXED_1, (float)(((int64)LOAD_FREQ << FSHIFT) / HZ) / FIXED_1); *slice = 0; } } /* Computing the maximum of the priority levels of all running (non sleeping) machines */ static U8 max_priority_of_runnings(void) { U32 i; U8 result = 0; U8 p; AnubisProcess * processItem; U32 nbProcess = TheAnubisProcessList->GetCount(); for (i = nbProcess - 1; i != ~(U32)0; i--) // i-- with i unsigned and i = 0 is 111111111...1111 = ~0 { processItem = TheAnubisProcessList->GetProcessAt(i); if (!processItem) continue; TheAnubisProcessList->SetRunningProcess(processItem); AnubisProcess::anbStatus st = processItem->GetStatus(); p = processItem->GetPriority(); switch (st) { case AnubisProcess::running: /* case AnubisProcess::waiting_for_event: case AnubisProcess::waiting_for_condition: // machine in a 'wait for' loop case AnubisProcess::waiting_for_completion: // of a child process ('execute') */ result = sup(p,result); break; default: break; } } return result; } /* computing the credit for running a machine */ static U32 compute_credit(U8 M, /* maximum of credit of all running machines */ U8 L) /* priority of the machine */ { /* the formula for M - 16 < L < M: M - L result = D(1 - -----) = ((D<<4) - D(M-L))>>4 16 where D = default maximal credit (max_steps) M = max priority of running processes L = priority of process */ #define D (max_steps) int32 result; do { if (L >= M) result = max_steps; else if (L <= (M - 16)) result = 0; else result = ((D<<4) - D*(M-L))>>4; } while(0); #undef D return (U32)result; } /* The scheduler */ void schedul(void) { U32 i; U32 running_machines; U32 waiting_machines; U32 waiting_for_completion_machines; U32 max_running_priority; /* the maximum of the priority levels of all running (non sleeping) machines */ U32 steps = 0; U32 activity = 0; U32 wmm, wsm, maf; bigtime_t load_previous_time = system_time(); bigtime_t perf_t0 = 0, perf_sum = 0; avenrun[0] = avenrun[1] = avenrun[2] = 0; while(!quitingVM) { if (TheAnubisProcessList->GetCount() == 0) { #ifdef record_allocations // TheAnubisAllocator->shoshow_allocated_segments(); TheAnubisAllocator->ShowHeap(); #endif return; /* exit scheduler */ } /* generate a tick if needed */ /* gettimeofday(¤t_time,NULL); if (timevalless(&next_tick,¤t_time)) { #ifdef _WITH_GRAPHISM_ generate_tick(); #endif next_tick.tv_sec = current_time.tv_sec; next_tick.tv_usec = current_time.tv_usec + 40000; // 25 ticks per second if (next_tick.tv_usec >= 1000000) { next_tick.tv_sec++; next_tick.tv_usec -= 1000000; } } */ #ifdef _WITH_GRAPHISM_ /* get all window events and dispatch them to the windows */ if (the_primary_module.flags & mf_using_graphism) queue_host_window_events(); #endif /* count machines which are currently running (and those which are waiting) */ running_machines = 0; waiting_machines = 0; waiting_for_completion_machines = 0; activity = 0; steps = 0; U32 nbProcess = TheAnubisProcessList->GetCount(); AnubisProcess * processItem; max_running_priority = max_priority_of_runnings(); for (i = nbProcess - 1; i != ~(U32)0; i--) // i-- with i unsigned and i = (U32)0 = 11111...1111 { processItem = TheAnubisProcessList->GetProcessAt(i); if (!processItem) continue; if(compute_perf) perf_t0 = system_time(); TheAnubisProcessList->SetRunningProcess(processItem); AnubisProcess::anbStatus st = processItem->GetStatus(); switch (st) { case CM::AnubisProcess::machine_not_used: break; case AnubisProcess::running: #if 0 { /* debugging tool */ U8 p = processItem->GetPriority(); U32 c = compute_credit(max_running_priority,p); if (p != 255) { printf("<%d|%d>",p,c); fflush(stdout); } } #endif // run this machine for as many steps as its credit allows: steps = processItem->RunMachine(compute_credit(max_running_priority,processItem->GetPriority())) ; break; case AnubisProcess::waiting_for_event: // set the machine status to 'running' and run the machine: processItem->SetStatus(AnubisProcess::running); steps = processItem->RunMachine(compute_credit(max_running_priority,processItem->GetPriority())); break; case AnubisProcess::waiting_for_condition: // machine in a 'wait for' loop // check if it is time to run this machine: gettimeofday(¤t_time,NULL); if (timevalless(processItem->GetAlarm(),¤t_time)) { /* It is time: run it ! */ processItem->SetStatus(AnubisProcess::running); steps = processItem->RunMachine(compute_credit(max_running_priority,processItem->GetPriority())); } break; case AnubisProcess::waiting_for_completion: // of a child process ('execute') // run the machine which will give up immediately if the child did not finish: steps = processItem->RunMachine(compute_credit(max_running_priority,processItem->GetPriority())); break; case AnubisProcess::need_bigger_stack: processItem->EnlargeStack(); break; #if 0 // This is obsolete. case AnubisProcess::need_bigger_locked_files_stack: LOGINFO("need_bigger_locked_files_stack deprecated function %s %d", __FILE__, __LINE__); break; #endif case AnubisProcess::need_more_memory: // Enlarge the memory allocator and reset machine status to 'running'. if(processItem->GetAllocator()->EnlargeMemory()) { processItem->SetStatus(AnubisProcess::running); } break; case AnubisProcess::finished: // This machine has finished. Delete it. TheAnubisProcessList->DeleteProcess(processItem->GetPid()); TheAnubisProcessList->SetRunningProcess(NULL); continue; break; case AnubisProcess::invalid_IP: // This should never happen. LOGERROR("invalid IP value: %d\n", relative_IP(processItem->GetIP())); assert(0); break; case AnubisProcess::invalid_instruction: // This should never happen. LOGERROR("invalid instruction at offset %d while %s\n", relative_IP(processItem->GetIP()), work_sort_string[processItem->GetWorkSort()]); assert(0); break; // There is no other case: default: assert(0); } activity += (steps << FSHIFT) / max_steps; if(compute_perf) { bigtime_t t1 = system_time(); processItem->PerfSumTime(t1 - perf_t0); perf_sum += t1 - perf_t0; } st = processItem->GetStatus(); if (st == AnubisProcess::running || st == AnubisProcess::need_bigger_stack || st == AnubisProcess::need_bigger_locked_files_stack || st == AnubisProcess::need_more_memory || st == AnubisProcess::waiting_for_event || st == AnubisProcess::waiting_for_condition || st == AnubisProcess::waiting_for_completion || st == AnubisProcess::finished) running_machines++; if (st == AnubisProcess::waiting_for_event || st == AnubisProcess::waiting_for_condition || st == AnubisProcess::waiting_for_completion) waiting_machines++; if (st == AnubisProcess::waiting_for_completion) waiting_for_completion_machines++; TheAnubisProcessList->SetRunningProcess(NULL); } if(compute_perf) { bigtime_t now = system_time(); calc_load((U32)(now - load_previous_time), activity, &perf_sum); load_previous_time = now; } //printf("1"); fflush(stdout); if (running_machines == 0) // no machine running (nor waiting) { //printf("2"); fflush(stdout); maf = wmm = wsm = 0; nbProcess = TheAnubisProcessList->GetCount(); for (i = 0; i < nbProcess; i++) { processItem = TheAnubisProcessList->GetProcessAt(i); U32 mStatus = processItem->GetStatus(); if (mStatus == AnubisProcess::need_bigger_stack) wsm++; if (mStatus == AnubisProcess::need_bigger_locked_files_stack) wsm++; if (mStatus == AnubisProcess::need_more_memory) wmm++; if (mStatus == AnubisProcess::finished) maf++; } if (maf+wmm+wsm) { LOGINFO("--- No virtual machine is still running !\n" " Memory currently used: %d bytes\n" " Maximum allowed: %d bytes\n", current_memory,max_memory); LOGINFO(" %d machine(s) need more memory.\n",wmm); LOGINFO(" %d machine(s) need a bigger stack.\n",wsm); LOGINFO(" %d machine(s) have finished.\n",maf); syntax(); } } else { //printf("3"); fflush(stdout); /* at least one machine is running */ if (running_machines == waiting_machines) { /* all machines waiting */ struct timeval alarm_time; //printf("4"); fflush(stdout); /* all running machines are waiting: anbexec must sleep until an event occurs or until the waiting time expires. */ memcpy(&rfds,&descriptors_waited_for_input,sizeof(fd_set)); memcpy(&wfds,&descriptors_waited_for_input,sizeof(fd_set)); memcpy(&efds,&descriptors_waited_for_input,sizeof(fd_set)); /* compute alarm time (minimum of machines alarm times) */ alarm_time.tv_sec = LONG_MAX; alarm_time.tv_usec = LONG_MAX; alarm_set = 0; nbProcess = TheAnubisProcessList->GetCount(); for (i = 0; i < nbProcess; i++) { processItem = TheAnubisProcessList->GetProcessAt(i); if (processItem->GetStatus() == AnubisProcess::waiting_for_condition) { if (show_waiting_descriptors) { LOGINFO("machine %d is waiting for condition.\n",i); } if (processItem->GetAlarm(),&alarm_time) { alarm_time.tv_sec = processItem->GetAlarm()->tv_sec; alarm_time.tv_usec = processItem->GetAlarm()->tv_usec; alarm_set = 1; } } } if (timevalless(&next_tick,&alarm_time)) { alarm_time.tv_sec = next_tick.tv_sec; alarm_time.tv_usec = next_tick.tv_usec; alarm_set = 1; } /* make the difference: alarm_time - current_time (don't wait if negative) */ if (alarm_set) { gettimeofday(¤t_time,NULL); if (timeval_substract(&time_to_wait,&alarm_time,¤t_time)) { /* negative! don't wait */ time_to_wait.tv_sec = 0; time_to_wait.tv_usec = 0; } if (waiting_for_completion_machines) { /* if there is a machine waiting for completion of an 'execute' command, don't wait more than 1/10 second. */ time_to_wait.tv_sec = inf(0,time_to_wait.tv_sec); time_to_wait.tv_usec = inf(100000,time_to_wait.tv_usec); } } if (show_waiting_descriptors) { if (alarm_set) { LOGINFO("alarm set for %d seconds %d microseconds.\n", (int)time_to_wait.tv_sec, (int)time_to_wait.tv_usec); } show_fd_set((char *)"rfds",&rfds); } //#if defined (_LINUX_) || (__BEOS__) //printf("."); fflush(stdout); if(select(FD_SETSIZE,&rfds,NULL,NULL,alarm_set ? (&time_to_wait) : NULL) < 0) { //LOGERROR("CRITICAL ERROR: Main VM select() failed with error [%d].\n", LAST_SOCKET_ERROR); usleep(1000); // this will ensure that we won't take all the CPU time if a socket is // invalid (bug encountered on 2007-08-15 by CR) } //#else // select(FD_SETSIZE,&rfds,NULL,NULL,alarm_set ? (&time_to_wait) : NULL); //Sleep(1); // select(FD_SETSIZE,&rfds,&wfds,&efds,alarm_set ? (&time_to_wait) : NULL); //#endif /* * * wait ... for ... event ... or ... alarm ... * */ /* wake up ! */ /* all machines who were waiting for event should now be running */ nbProcess = TheAnubisProcessList->GetCount(); for (i = 0; i < nbProcess; i++) { processItem = TheAnubisProcessList->GetProcessAt(i); if (processItem->GetStatus() == AnubisProcess::waiting_for_event) { processItem->SetStatus(AnubisProcess::running); } } } /* all machines waiting */ } /* at least one machine running */ } /* while(1) */ } /* schedul() */ #if 1 U32 anubis_string(char *s, AnubisAllocator *allocator) { U32 n = strlen(s); U32 i; U32 result; U32 size = byte_size_to_word_size(n+5); if ((result = allocator->AllocateDataSegment(size)) == 0) { if (allocator->GetMemorySegSize() < size) allocator->SetMemorySegSize(size+100); allocator->EnlargeMemory(); result = allocator->AllocateDataSegment(size); } if (result == 0) { printf("Cannot make a string.\n"); return anubis_empty_string; } *((U32 *)result) = 1; /* counter */ for (i = 0; i < n; i++) ((char *)result)[i+4] = s[i]; assert(s[i] == 0); ((char *)result)[n+4] = 0; return result; } #endif static bigtime_t profiling_start_time = 0; #if 0 struct profile_info_struct { U32 offset; char * description; ProfInfo * next; } typedef struct profile_info_struct ProfInfo; char *read_line(FILE *fp) { char * result = (char *)malloc(1000); if while (get(fp) != '\n') } ProfInfo *read_profile_info_line(File *fp) { ProfInfo *result = NULL; if ((result = (ProfInfo *)malloc(sizeof(struct profile_info_struct))) == NULL) { fprintf(stderr,"Not enough memory for reading profile info file.\n"); return NULL; } fscanf(fp,"",&(result->offset),&(result->description)); result -> next = NULL; return result; } ProfInfo *get_profile_infos() { FILE *fp; ProfInfo *result = NULL; ProfInfo *line = NULL; char buf[1000]; snprintf(buf,995,"%s/modules/%s.info",my_anubis_directory,modname); if ((fp = fopen(buf,"rt")) == NULL) { fprintf(stderr,"file '%s' not found.\n",buf); return NULL; } while ((line = read_profile_info_line(fp)) != NULL) { line -> next = result; result = line; } return result; } #endif void OutputProfiling() { // first simulate a return for unclosed functions AnubisProcessList * processes = AnubisProcessList::GetInstance(); U32 procCount = processes->GetCount(); for(U32 i = 0; i < procCount; i++) { AnubisProcess * process = processes->GetProcessAt(i); FunctionCall * fCall = process->CurrentFunction(); while(fCall) { printf("[VM %u] Closing unreturned function '%s'\n", (U32)process->GetPid(), fCall->Summary()->Name()); FunctionCall * parent = fCall->Parent(); fCall->Return(); fCall = parent; } } U32 countProfilingFunctions = AnubisProcess::GetFunctionsList().GetCount(); if(countProfilingFunctions > 0) { bigtime_t full_time = system_time() - profiling_start_time; if(profile_output_format == "csv") { fprintf(stderr, "Function"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Offset"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Hits"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Time (excl. children)"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Time %%"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Max (inc. children)"); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "Max %%"); fprintf(stderr, "\n"); for(U32 i = 0; i < countProfilingFunctions; i++) { FunctionSummary * sum = AnubisProcess::GetFunctionsList()[i]; fprintf(stderr, "\"%s\"", sum->Name()); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%d", sum->Label()); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%d", (int)sum->Hits()); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%.6f", (float)sum->TotalTimeWithoutChildren() / 1000000.0f); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%.1f", (float)sum->TotalTimeWithoutChildren() / full_time * 100.0f); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%.6f", (float)sum->MaxTimeWithChildren() / 1000000.0f); fprintf(stderr, "%s", csv_separator.Cstr()); fprintf(stderr, "%.1f", (float)sum->MaxTimeWithChildren() / full_time * 100.0f); fprintf(stderr, "\n"); } } else // text format by default { fprintf(stderr, "*** PROFILING DATA ***\n"); fprintf(stderr, "*** Full executing time = %.3f s\n", (float)full_time / 1000000.0f); for(U32 i = 0; i < countProfilingFunctions; i++) { FunctionSummary * sum = AnubisProcess::GetFunctionsList()[i]; fprintf(stderr, "Function %s [offset = %d]:\n", sum->Name(), sum->Label()); fprintf(stderr, " hits = %d\n", sum->Hits()); fprintf(stderr, " time (excluding children) = %.3f s (%.1f%%)\n", (float)sum->TotalTimeWithoutChildren() / 1000000.0f, (float)sum->TotalTimeWithoutChildren() / full_time * 100.0f); fprintf(stderr, " max (including children) = %.3f s (%.1f%%)\n", (float)sum->MaxTimeWithChildren() / 1000000.0f, (float)sum->MaxTimeWithChildren() / full_time * 100.0f); } } } } void handle_broken_pipe(int sig) { //printf("broken pipe\n"); fflush(stdout); // DO NOT CLOSE THE VM HERE !!! // This signal is sent by sockets when the client doesn't close gracefully the socket. // This is not a fatal error. We should simply ignore it. } void handle_sigusr1(int sig) { #ifdef record_allocations show_allocated_segments(); #endif } void handle_sigusr2(int sig) { Debugger::DumpProcesses(); } #ifdef WIN32 BOOL CtrlHandler(DWORD fdwCtrlType) { switch (fdwCtrlType) { // Handle the CTRL+C signal. case CTRL_C_EVENT: Beep(1000, 1); OutputProfiling(); // Debugger::DumpProcesses(); // my_exit(1); quitingVM = true; return TRUE; // CTRL+CLOSE: confirm that the user wants to exit. case CTRL_CLOSE_EVENT: // OutputProfiling(); // my_exit(1); quitingVM = true; return TRUE; case CTRL_BREAK_EVENT: //OutputProfiling(); quitingVM = true; return TRUE; // Pass other signals to the next handler. case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: default: return FALSE; } } #endif void LoadConfigurationFile(IniFile *configFile) { configFile->ReadString("SSL","certificate", "georges.pem", certificate_file_name); configFile->ReadString("SSL","trustedDir", "trusted_certs", trusted_certs_directory); configFile->ReadString("SSL","password","georges", ssl_password); configFile->ReadString("SSL", "randomFile", "anb_random", random_file); random_file = my_anubis_directory + "/" + random_file; } /* 'main' function, executing a module */ int main(int argc, char **argv) { int i; /* The list of arguments to the module given on the command line must become an Anubis datum of type 'List(String)'. We prepare this list in the variable 'args', beginning by an empty list. Since 'List(String)' is a mixed type, and the empty list is the first alternative (without component), the empty list is just represented by the unsigned integer 0. */ U32 args = 0; initftime(); #ifdef debug_vm LOGINFO("anbexec 'debug' version.\n"); #endif /* check some sizes (used only by us) */ assert(sizeof(double) <= 8); #ifdef WIN32 assert(sizeof(long) == 4); #endif /* anbexec *MUST* receive the name of the module as the *FIRST* argument. This name cannot begin by '-'. If it is not the case, we just recall the syntax and exit. */ if (argc < 2 || argv[1][0] == '-') { syntax(); my_exit(1); } else { /* Otherwise, we got the name of the module (with or without ".adm"). */ modname = argv[1]; } /* initialize some 'constants'. The system needs to have a permanent empty string and a permanent empty byte array at hand. These data are made permanent by putting 0 in the reference counter. */ if ((anubis_empty_string = (U32)malloc(5)) == (U32)NULL || (anubis_empty_byte_array = (U32)malloc(8)) == (U32)NULL) { LOGERROR("Not enough memory (3).\n"); my_exit(1); } for (i = 0; i < 5; i++) ((U8 *)anubis_empty_string)[i] = 0; for (i = 0; i < 8; i++) ((U8 *)anubis_empty_byte_array)[i] = 0; /* initialise the input descriptors (by which anbexec must be woken up) */ FD_ZERO(&descriptors_waited_for_input); //here we make all host OS dependencies init, before entering in kernel land kernelInit(); anubisUserDirectory = GetUserDir(); anubisUserDirectory << "/my_anubis"; #ifdef record_allocations if ((segs_descrs = (struct SegmentDescription *)malloc(sizeof(struct SegmentDescription)*max_seg_descr)) == NULL) {s LOGERROR("Not enough memory (2)."); my_exit(1); } { U32 i; for (i = 0; i < max_seg_descr; i++) { seg_descrs[i].filename = NULL; /* added 27/02/2004 */ seg_descrs[i].seg = NULL; } } #endif /* read options and arguments to the module. Recall that the name of the module is already read as argv[1]. Hence, we read all other argv[i] but only for i >= 2 (i.e. i > 1). We read them in reverse order (beginning by the last one), so that the list of arguments to the module will be in the right order (no need to reverse it, since each argument read is put in front of the list, hence the last one read is the head of list, as required). */ for (i = argc-1; i > 1; i--) { if (!strcmp(argv[i],"--debug_mem")) debug_mem = 1; else if (!strcmp(argv[i],"--debug_sqlite")) debug_sqlite = 1; #ifdef debug_vm else if (!strcmp(argv[i],"--debug")) { debugging = 1; } #endif #ifdef record_allocations else if (!strncmp(argv[i],"--seg:",6)) passed_seg_IP = atoi(argv[i]+6); #endif else if (!strcmp(argv[i],"--console")) console = 1; else if (!strcmp(argv[i],"--verbose")) console = 1; else if (!strcmp(argv[i],"--info")) show_info = 1; else if (!strncmp(argv[i],"--pdir:",7)) my_anubis_directory = argv[i]+7; else if (!strncmp(argv[i],"--cdir:",7)) anubis_directory = argv[i]+7; else if (!strncmp(argv[i],"--credit:",9)) max_steps = sup(10,atoi(argv[i]+9)); else if (!strncmp(argv[i],"--MB:",5)) max_memory = (atoi(argv[i]+5))<<20; else if (!strncmp(argv[i],"--main_seg_size:",16)) TheAnubisAllocator->SetMemorySegSize(atoi(argv[i]+16)); else if (!strcmp(argv[i],"--heaptest")) heap_test = 1; else if (!strcmp(argv[i],"--syscalls")) show_syscalls = 1; else if (!strncmp(argv[i],"--szone:",8)) security_zone_size = atoi(argv[i]+8); else if (!strcmp(argv[i],"--wfd")) show_waiting_descriptors = 1; else if (!strcmp(argv[i],"--nsc")) nsc_file = stdout; else if (!strcmp(argv[i],"--perf")) compute_perf = true; else if (!strcmp(argv[i],"--show_module_loading")) show_module_loading = true; else if (!strncmp(argv[i],"--profile:", 10)) profile_output_format = argv[i]+10; else if (!strncmp(argv[i],"--csv_sep:", 10)) csv_separator = argv[i]+10; #ifdef _WITH_SSL_ else if (!strncmp(argv[i],"--cert:",7)) certificate_file_name = argv[i]+7; else if (!strncmp(argv[i],"--ca:",5)) trusted_certs_directory = argv[i] + 5; else if (!strcmp(argv[i],"--128bits_ciphers")) allow_all_ciphers = 0; #endif #ifdef debug_vm else if (!strcmp(argv[i],"--step")) step = 1; else if (!strncmp(argv[i],"--start_debug:",14)) { start_end_debug = 1; start_debug = atoi(argv[i]+14); } else if (!strncmp(argv[i],"--end_debug:",12)) { start_end_debug = 1; end_debug = atoi(argv[i]+12); } #endif else if (argv[i][0] == '-' && argv[i][1] == '-') /* Note: argv[i] has always at least 2 characters, including the trailing 0 */ { LOGERROR("Unknown option: %s\n",argv[i]); my_exit(1); } else { /* record an argument in the list of arguments */ args = anubis_cons(anubis_string(argv[i],TheAnubisAllocator),args,1,TheAnubisAllocator); // LOGINFO("arg %d [%s]\n",i, argv[i]); } } #ifdef debug_vm if (start_end_debug && end_debug <= start_debug) { LOGERROR("When using option '--start_debug', you must also use '--end_debug' and conversely.\n"); my_exit(1); } if (debugging) LOGINFO("Debugging mode !\n"); #endif /* * Creation de l'objet config qui permet de lire et d'ecrire la configuration d'Anubis * La methode Initcheck() renvoie B_NO_ERROR, si l'ouverture c'est bien deroule et que * l'on peut lire ou ecrire dans ce fichier. */ IniFile config( anubisUserDirectory + "/anubis.conf"); if(config.InitCheck() != B_NO_ERROR) { LOGERROR("Can't find anubis.conf file"); my_exit(1); } vmStartDirectory = GetProgramRootDir(argv[0]); if (anubis_directory.Length() == 0) config.ReadString("PATH", "ANUBIS", vmStartDirectory.Cstr(), anubis_directory); if (my_anubis_directory.Length() == 0) config.ReadString("PATH", "MY_ANUBIS", "", my_anubis_directory); if (my_anubis_directory.Length() == 0) { LOGERROR( "No --pdir: option, and the 'anubis.conf' variable 'MY_ANUBIS' has not been defined.\n"); my_exit(1); } modulePaths.AddPath("."); modulePaths.AddPath(my_anubis_directory + "/modules/"); modulePaths.AddPath(anubis_directory + "/modules/"); modulePaths.AddPath(anubisUserDirectory + "/modules/"); #ifdef _LINUX_ mypid = getpid(); euid = geteuid(); realuid = getuid(); if (realuid == 0) realuid = getgid(); if (realuid == 0) realuid = getegid(); if (seteuid(realuid)) { LOGERROR("Cannot release root privileges.\n"); my_exit(1); } #endif //printf("my_anubis_directory = %s\n",my_anubis_directory.Cstr()); LoadConfigurationFile(&config); srand(time(0)); if (signal(SIGPIPE,handle_broken_pipe) == SIG_ERR) { LOGERROR("Cannot trap SIGPIPE signal."); } #if defined (_LINUX_) || (__BEOS__) //DR Win32 can't handle this signal if (signal(SIGUSR1,handle_sigusr1) == SIG_ERR) { LOGERROR("Cannot trap SIGUSR1 signal."); } #endif if (signal(SIGSEGV,handle_sigsegv) == SIG_ERR) { LOGERROR("Cannot trap SIGSEGV signal."); } if (signal(SIGFPE,handle_sigfpe) == SIG_ERR) { LOGERROR("Cannot trap SIGFPE signal."); } #ifdef WIN32 if(SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE) == FALSE) { LOGERROR("Cannot set WIN32 signal handler."); } #endif if (modname == NULL) { LOGERROR("No module file name on command line.\n"); my_exit(1); } /* (added in version 1.13) */ if (!resize_modules_array()) /* this initializes the array of dynamic modules */ { LOGERROR("Not enough memory (resize_modules_array).\n"); my_exit(1); } /* load (and relocate) the module (must be excuted after resize_modules_array !) */ switch (load_module(&the_primary_module,modname)) { case 0: LOGERROR("Cannot find file '%s'.\n",modname); my_exit(1); break; case 1: LOGERROR("Not enough memory (8).\n"); my_exit(1); break; case 2: LOGERROR("Module '%s' is corrupted.\n",modname); my_exit(1); break; case 3: LOGERROR("Bad version number for '%s'.\n",modname); my_exit(1); break; case 4: break; case 5: /* bad signature */ { LOGERROR( "Bad Module signature for [%s] \n recompile and try again...\n", modname); my_exit(1); break; case 6: LOGERROR("Module '%s' is not a primary module.\n",modname); my_exit(1); break; } default: assert(0); } /* store the primary module into the modules array */ modules[0].flags = the_primary_module.flags; modules[0].byte_code = the_primary_module.byte_code; modules[0].starting_point = the_primary_module.starting_point; number_of_modules = 1; /* We have some initializations to perform depending on the flags in the module. */ #ifdef _WITH_SSL_ /* initialize SSL if needed */ if (the_primary_module.flags & mf_using_ssl) { c_rehash_all(); init_openssl(); } #endif #ifndef _WITH_GRAPHISM_ if (the_primary_module.flags & mf_using_graphism) { LOGERROR("Sorry, this version of 'anbexec' is not able to handle graphism,\n" " used by module '%s'.\n",modname); my_exit(1); } #endif #ifdef _WITH_FREETYPE_ if (the_primary_module.flags & mf_using_fonts) { if (FT_Init_FreeType(&freetype_library)) { LOGERROR("Cannot initialize FreeType font library.\n"); my_exit(1); } } #endif #ifdef _WITH_GRAPHISM_ /* initialize graphism if needed */ if (the_primary_module.flags & mf_using_graphism) { init_graphism(); } #endif profiling_start_time = system_time(); AnubisProcess * currentProcess; /* create a new Anubis Process, the first one. */ currentProcess = TheAnubisProcessList->CreateAnubisProcess((the_primary_module.byte_code)+8+(the_primary_module.starting_point), 255, /* priority of 'mother' process */ TheAnubisAllocator); if (currentProcess) { /* push a zero return address */ currentProcess->PushOnSP(0); /* mean 'end of computation' */ /* since version 1.13: push the function to be excuted (no need to count since the module is primary) */ currentProcess->PushOnSP((U32)(the_primary_module.byte_code+8+the_primary_module.starting_point)); /* push the list of arguments */ currentProcess->PushOnSP(args); } //printf("Stack ready.\n"); fflush(stdout); /* run the virtual machines */ schedul(); OutputProfiling(); #ifdef _WITH_GRAPHISM_ /* free graphical resources if needed */ if (the_primary_module.flags & mf_using_graphism) { host_free_graphical_resources(); } #endif /* check if we must restart */ if(must_restart_flag) { execvp(argv[0],argv); /* never returns */ } /* exit */ return 0; }