From 83522282f1cc4919e2866104030364839fd482de Mon Sep 17 00:00:00 2001 From: Thilo Schulz Date: Thu, 16 Jun 2011 01:11:45 +0000 Subject: [PATCH] Various fixes to vm_interpreted.c: - Add opStack protection - Fix dataMask check for OP_BLOCK_COPY - Add instruction number check for conditional jumps - Make errors in VM_PrepareInterpreter nonfatal --- code/qcommon/vm.c | 22 +++ code/qcommon/vm_interpreted.c | 260 +++++++++++++++++----------------- code/qcommon/vm_local.h | 2 + code/qcommon/vm_x86.c | 24 +--- code/qcommon/vm_x86_64.c | 17 +-- 5 files changed, 156 insertions(+), 169 deletions(-) diff --git a/code/qcommon/vm.c b/code/qcommon/vm.c index 3ff5f707..292824d0 100644 --- a/code/qcommon/vm.c +++ b/code/qcommon/vm.c @@ -949,3 +949,25 @@ void VM_LogSyscalls( int *args ) { fprintf(f, "%i: %p (%i) = %i %i %i %i\n", callnum, (void*)(args - (int *)currentVM->dataBase), args[0], args[1], args[2], args[3], args[4] ); } + +/* +================= +VM_BlockCopy +Executes a block copy operation within currentVM data space +================= +*/ + +void VM_BlockCopy(unsigned int dest, unsigned int src, size_t n) +{ + unsigned int dataMask = currentVM->dataMask; + + if ((dest & dataMask) != dest + || (src & dataMask) != src + || ((dest + n) & dataMask) != dest + n + || ((src + n) & dataMask) != src + n) + { + Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!"); + } + + Com_Memcpy(currentVM->dataBase + dest, currentVM->dataBase + src, n); +} diff --git a/code/qcommon/vm_interpreted.c b/code/qcommon/vm_interpreted.c index abe24729..912ca719 100644 --- a/code/qcommon/vm_interpreted.c +++ b/code/qcommon/vm_interpreted.c @@ -192,9 +192,8 @@ void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) { op = (int)code[ byte_pc ]; codeBase[int_pc] = op; - if ( byte_pc > header->codeLength ) { - Com_Error( ERR_FATAL, "VM_PrepareInterpreter: pc > header->codeLength" ); - } + if(byte_pc > header->codeLength) + Com_Error(ERR_DROP, "VM_PrepareInterpreter: pc > header->codeLength"); byte_pc++; int_pc++; @@ -265,6 +264,9 @@ void VM_PrepareInterpreter( vm_t *vm, vmHeader_t *header ) { case OP_LEF: case OP_GTF: case OP_GEF: + if(codeBase[int_pc] < 0 || codeBase[int_pc] > vm->instructionCount) + Com_Error(ERR_DROP, "VM_PrepareInterpreter: Jump to invalid instruction number"); + // codeBase[pc] is the instruction index. Convert that into an offset into //the int-aligned codeBase[] by the lookup table. codeBase[int_pc] = vm->instructionPointers[codeBase[int_pc]]; @@ -312,11 +314,12 @@ locals from sp ============== */ -#define DEBUGSTR va("%s%i", VM_Indent(vm), opStack-stack ) +#define DEBUGSTR va("%s%i", VM_Indent(vm), opStackOfs) int VM_CallInterpreted( vm_t *vm, int *args ) { - int stack[OPSTACK_SIZE]; - int *opStack; + byte stack[OPSTACK_SIZE + 15]; + register int *opStack; + register uint8_t opStackOfs; int programCounter; int programStack; int stackOnEntry; @@ -345,10 +348,6 @@ int VM_CallInterpreted( vm_t *vm, int *args ) { codeImage = (int *)vm->codeBase; dataMask = vm->dataMask; - // leave a free spot at start of stack so - // that as long as opStack is valid, opStack-1 will - // not corrupt anything - opStack = stack; programCounter = 0; programStack -= 48; @@ -368,6 +367,13 @@ int VM_CallInterpreted( vm_t *vm, int *args ) { VM_Debug(0); + // leave a free spot at start of stack so + // that as long as opStack is valid, opStack-1 will + // not corrupt anything + opStack = PADP(stack, 16); + *opStack = 0xDEADBEEF; + opStackOfs = 0; + // vm_debugLevel=2; // main interpreter loop, will exit when a LEAVE instruction // grabs the -1 program counter @@ -379,27 +385,23 @@ int VM_CallInterpreted( vm_t *vm, int *args ) { // unsigned int r2; nextInstruction: - r0 = ((int *)opStack)[0]; - r1 = ((int *)opStack)[-1]; + r0 = opStack[opStackOfs]; + r1 = opStack[(uint8_t) (opStackOfs - 1)]; nextInstruction2: #ifdef DEBUG_VM if ( (unsigned)programCounter >= vm->codeLength ) { Com_Error( ERR_DROP, "VM pc out of range" ); - } - - if ( opStack < stack ) { - Com_Error( ERR_DROP, "VM opStack underflow" ); - } - if ( opStack >= stack+OPSTACK_SIZE ) { - Com_Error( ERR_DROP, "VM opStack overflow" ); + return 0; } if ( programStack <= vm->stackBottom ) { Com_Error( ERR_DROP, "VM stack overflow" ); + return 0; } if ( programStack & 3 ) { Com_Error( ERR_DROP, "VM program stack misaligned" ); + return 0; } if ( vm_debugLevel > 1 ) { @@ -413,79 +415,67 @@ nextInstruction2: #ifdef DEBUG_VM default: Com_Error( ERR_DROP, "Bad VM instruction" ); // this should be scanned on load! + return 0; #endif case OP_BREAK: vm->breakCount++; goto nextInstruction2; case OP_CONST: - opStack++; + opStackOfs++; r1 = r0; - r0 = *opStack = r2; + r0 = opStack[opStackOfs] = r2; programCounter += 1; goto nextInstruction2; case OP_LOCAL: - opStack++; + opStackOfs++; r1 = r0; - r0 = *opStack = r2+programStack; + r0 = opStack[opStackOfs] = r2+programStack; programCounter += 1; goto nextInstruction2; case OP_LOAD4: #ifdef DEBUG_VM - if ( *opStack & 3 ) { + if(opStack[opStackOfs] & 3) + { Com_Error( ERR_DROP, "OP_LOAD4 misaligned" ); + return 0; } #endif - r0 = *opStack = *(int *)&image[ r0&dataMask&~3 ]; + r0 = opStack[opStackOfs] = *(int *) &image[r0 & dataMask & ~3 ]; goto nextInstruction2; case OP_LOAD2: - r0 = *opStack = *(unsigned short *)&image[ r0&dataMask&~1 ]; + r0 = opStack[opStackOfs] = *(unsigned short *)&image[ r0&dataMask&~1 ]; goto nextInstruction2; case OP_LOAD1: - r0 = *opStack = image[ r0&dataMask ]; + r0 = opStack[opStackOfs] = image[ r0&dataMask ]; goto nextInstruction2; case OP_STORE4: *(int *)&image[ r1&(dataMask & ~3) ] = r0; - opStack -= 2; + opStackOfs -= 2; goto nextInstruction; case OP_STORE2: *(short *)&image[ r1&(dataMask & ~1) ] = r0; - opStack -= 2; + opStackOfs -= 2; goto nextInstruction; case OP_STORE1: image[ r1&dataMask ] = r0; - opStack -= 2; + opStackOfs -= 2; goto nextInstruction; case OP_ARG: // single byte offset from programStack *(int *)&image[ (codeImage[programCounter] + programStack)&dataMask&~3 ] = r0; - opStack--; + opStackOfs--; programCounter += 1; goto nextInstruction; case OP_BLOCK_COPY: - { - int *src, *dest; - int count, srci, desti; - - count = r2; - // MrE: copy range check - srci = r0 & dataMask; - desti = r1 & dataMask; - count = ((srci + count) & dataMask) - srci; - count = ((desti + count) & dataMask) - desti; - - src = (int *)&image[ srci ]; - dest = (int *)&image[ desti ]; - - memcpy(dest, src, count); - programCounter += 1; - opStack -= 2; - } + VM_BlockCopy(r1, r0, r2); + programCounter += 1; + opStackOfs -= 2; goto nextInstruction; case OP_CALL: @@ -494,7 +484,7 @@ nextInstruction2: // jump to the location on the stack programCounter = r0; - opStack--; + opStackOfs--; if ( programCounter < 0 ) { // system call int r; @@ -539,8 +529,8 @@ nextInstruction2: #endif // save return value - opStack++; - *opStack = r; + opStackOfs++; + opStack[opStackOfs] = r; programCounter = *(int *)&image[ programStack ]; // vm->callLevel = temp; #ifdef DEBUG_VM @@ -550,6 +540,7 @@ nextInstruction2: #endif } else if ( (unsigned)programCounter >= vm->instructionCount ) { Com_Error( ERR_DROP, "VM program counter out of range in OP_CALL" ); + return 0; } else { programCounter = vm->instructionPointers[ programCounter ]; } @@ -557,10 +548,10 @@ nextInstruction2: // push and pop are only needed for discarded or bad function return values case OP_PUSH: - opStack++; + opStackOfs++; goto nextInstruction; case OP_POP: - opStack--; + opStackOfs--; goto nextInstruction; case OP_ENTER: @@ -607,6 +598,7 @@ nextInstruction2: goto done; } else if ( (unsigned)programCounter >= vm->codeLength ) { Com_Error( ERR_DROP, "VM program counter out of range in OP_LEAVE" ); + return 0; } goto nextInstruction; @@ -618,15 +610,18 @@ nextInstruction2: case OP_JUMP: if ( (unsigned)r0 >= vm->instructionCount ) + { Com_Error( ERR_DROP, "VM program counter out of range in OP_JUMP" ); + return 0; + } programCounter = vm->instructionPointers[ r0 ]; - opStack--; + opStackOfs--; goto nextInstruction; case OP_EQ: - opStack -= 2; + opStackOfs -= 2; if ( r1 == r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -636,7 +631,7 @@ nextInstruction2: } case OP_NE: - opStack -= 2; + opStackOfs -= 2; if ( r1 != r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -646,7 +641,7 @@ nextInstruction2: } case OP_LTI: - opStack -= 2; + opStackOfs -= 2; if ( r1 < r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -656,7 +651,7 @@ nextInstruction2: } case OP_LEI: - opStack -= 2; + opStackOfs -= 2; if ( r1 <= r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -666,7 +661,7 @@ nextInstruction2: } case OP_GTI: - opStack -= 2; + opStackOfs -= 2; if ( r1 > r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -676,7 +671,7 @@ nextInstruction2: } case OP_GEI: - opStack -= 2; + opStackOfs -= 2; if ( r1 >= r0 ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -686,7 +681,7 @@ nextInstruction2: } case OP_LTU: - opStack -= 2; + opStackOfs -= 2; if ( ((unsigned)r1) < ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -696,7 +691,7 @@ nextInstruction2: } case OP_LEU: - opStack -= 2; + opStackOfs -= 2; if ( ((unsigned)r1) <= ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -706,7 +701,7 @@ nextInstruction2: } case OP_GTU: - opStack -= 2; + opStackOfs -= 2; if ( ((unsigned)r1) > ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -716,7 +711,7 @@ nextInstruction2: } case OP_GEU: - opStack -= 2; + opStackOfs -= 2; if ( ((unsigned)r1) >= ((unsigned)r0) ) { programCounter = r2; //vm->instructionPointers[r2]; goto nextInstruction; @@ -726,68 +721,74 @@ nextInstruction2: } case OP_EQF: - if ( ((float *)opStack)[-1] == *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] == ((float *) opStack)[(uint8_t) (opStackOfs + 2)]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } case OP_NEF: - if ( ((float *)opStack)[-1] != *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] != ((float *) opStack)[(uint8_t) (opStackOfs + 2)]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } case OP_LTF: - if ( ((float *)opStack)[-1] < *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] < ((float *) opStack)[(uint8_t) (opStackOfs + 2)]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } case OP_LEF: - if ( ((float *)opStack)[-1] <= *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) ((uint8_t) (opStackOfs + 1))] <= ((float *) opStack)[(uint8_t) ((uint8_t) (opStackOfs + 2))]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } case OP_GTF: - if ( ((float *)opStack)[-1] > *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] > ((float *) opStack)[(uint8_t) (opStackOfs + 2)]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } case OP_GEF: - if ( ((float *)opStack)[-1] >= *(float *)opStack ) { + opStackOfs -= 2; + + if(((float *) opStack)[(uint8_t) (opStackOfs + 1)] >= ((float *) opStack)[(uint8_t) (opStackOfs + 2)]) + { programCounter = r2; //vm->instructionPointers[r2]; - opStack -= 2; goto nextInstruction; } else { programCounter += 1; - opStack -= 2; goto nextInstruction; } @@ -795,101 +796,101 @@ nextInstruction2: //=================================================================== case OP_NEGI: - *opStack = -r0; + opStack[opStackOfs] = -r0; goto nextInstruction; case OP_ADD: - opStack[-1] = r1 + r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 + r0; goto nextInstruction; case OP_SUB: - opStack[-1] = r1 - r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 - r0; goto nextInstruction; case OP_DIVI: - opStack[-1] = r1 / r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 / r0; goto nextInstruction; case OP_DIVU: - opStack[-1] = ((unsigned)r1) / ((unsigned)r0); - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) / ((unsigned) r0); goto nextInstruction; case OP_MODI: - opStack[-1] = r1 % r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 % r0; goto nextInstruction; case OP_MODU: - opStack[-1] = ((unsigned)r1) % (unsigned)r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) % ((unsigned) r0); goto nextInstruction; case OP_MULI: - opStack[-1] = r1 * r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 * r0; goto nextInstruction; case OP_MULU: - opStack[-1] = ((unsigned)r1) * ((unsigned)r0); - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) * ((unsigned) r0); goto nextInstruction; case OP_BAND: - opStack[-1] = ((unsigned)r1) & ((unsigned)r0); - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) & ((unsigned) r0); goto nextInstruction; case OP_BOR: - opStack[-1] = ((unsigned)r1) | ((unsigned)r0); - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) | ((unsigned) r0); goto nextInstruction; case OP_BXOR: - opStack[-1] = ((unsigned)r1) ^ ((unsigned)r0); - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) ^ ((unsigned) r0); goto nextInstruction; case OP_BCOM: - *opStack = ~ ((unsigned)r0); + opStack[opStackOfs] = ~((unsigned) r0); goto nextInstruction; case OP_LSH: - opStack[-1] = r1 << r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 << r0; goto nextInstruction; case OP_RSHI: - opStack[-1] = r1 >> r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = r1 >> r0; goto nextInstruction; case OP_RSHU: - opStack[-1] = ((unsigned)r1) >> r0; - opStack--; + opStackOfs--; + opStack[opStackOfs] = ((unsigned) r1) >> r0; goto nextInstruction; case OP_NEGF: - *(float *)opStack = -*(float *)opStack; + ((float *) opStack)[opStackOfs] = -((float *) opStack)[opStackOfs]; goto nextInstruction; case OP_ADDF: - *(float *)(opStack-1) = *(float *)(opStack-1) + *(float *)opStack; - opStack--; + opStackOfs--; + ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] + ((float *) opStack)[(uint8_t) (opStackOfs + 1)]; goto nextInstruction; case OP_SUBF: - *(float *)(opStack-1) = *(float *)(opStack-1) - *(float *)opStack; - opStack--; + opStackOfs--; + ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] - ((float *) opStack)[(uint8_t) (opStackOfs + 1)]; goto nextInstruction; case OP_DIVF: - *(float *)(opStack-1) = *(float *)(opStack-1) / *(float *)opStack; - opStack--; + opStackOfs--; + ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] / ((float *) opStack)[(uint8_t) (opStackOfs + 1)]; goto nextInstruction; case OP_MULF: - *(float *)(opStack-1) = *(float *)(opStack-1) * *(float *)opStack; - opStack--; + opStackOfs--; + ((float *) opStack)[opStackOfs] = ((float *) opStack)[opStackOfs] * ((float *) opStack)[(uint8_t) (opStackOfs + 1)]; goto nextInstruction; case OP_CVIF: - *(float *)opStack = (float)*opStack; + ((float *) opStack)[opStackOfs] = (float) opStack[opStackOfs]; goto nextInstruction; case OP_CVFI: - *opStack = (int) *(float *)opStack; + opStack[opStackOfs] = (int) ((float *) opStack)[opStackOfs]; goto nextInstruction; case OP_SEX8: - *opStack = (signed char)*opStack; + opStack[opStackOfs] = (signed char) opStack[opStackOfs]; goto nextInstruction; case OP_SEX16: - *opStack = (short)*opStack; + opStack[opStackOfs] = (short) opStack[opStackOfs]; goto nextInstruction; } } @@ -897,12 +898,11 @@ nextInstruction2: done: vm->currentlyInterpreting = qfalse; - if ( opStack != &stack[1] ) { - Com_Error( ERR_DROP, "Interpreter error: opStack = %ld", (long int) (opStack - stack) ); - } + if (opStackOfs != 1 || *opStack != 0xDEADBEEF) + Com_Error(ERR_DROP, "Interpreter error: opStack[0] = %X, opStackOfs = %d", opStack[0], opStackOfs); vm->programStack = stackOnEntry; // return the result - return *opStack; + return opStack[opStackOfs]; } diff --git a/code/qcommon/vm_local.h b/code/qcommon/vm_local.h index 3066949e..aa14e672 100644 --- a/code/qcommon/vm_local.h +++ b/code/qcommon/vm_local.h @@ -190,3 +190,5 @@ vmSymbol_t *VM_ValueToFunctionSymbol( vm_t *vm, int value ); int VM_SymbolToValue( vm_t *vm, const char *symbol ); const char *VM_ValueToSymbol( vm_t *vm, int value ); void VM_LogSyscalls( int *args ); + +void VM_BlockCopy(unsigned int dest, unsigned int src, size_t n); diff --git a/code/qcommon/vm_x86.c b/code/qcommon/vm_x86.c index 8955be41..6449e5ce 100644 --- a/code/qcommon/vm_x86.c +++ b/code/qcommon/vm_x86.c @@ -386,28 +386,6 @@ static void ErrJump(void) exit(1); } -/* -================= -DoBlockCopy -Executes OP_BLOCK_COPY -================= -*/ - -void DoBlockCopy(unsigned int dest, unsigned int src, size_t n) -{ - unsigned int dataMask = currentVM->dataMask; - - if ((dest & dataMask) != dest - || (src & dataMask) != src - || ((dest + n) & dataMask) != dest + n - || ((src + n) & dataMask) != src + n) - { - Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!"); - } - - memcpy(currentVM->dataBase + dest, currentVM->dataBase + src, n); -} - /* ================= DoSyscall @@ -493,7 +471,7 @@ static void DoSyscall(void) if(opStackOfs < 1) Com_Error(ERR_DROP, "VM_BLOCK_COPY failed due to corrupted opStack"); - DoBlockCopy(opStackBase[opStackOfs - 1], opStackBase[opStackOfs], arg); + VM_BlockCopy(opStackBase[(opStackOfs - 1)], opStackBase[opStackOfs], arg); break; default: Com_Error(ERR_DROP, "Unknown VM operation %d", syscallNum); diff --git a/code/qcommon/vm_x86_64.c b/code/qcommon/vm_x86_64.c index 8de98ee9..9659265e 100644 --- a/code/qcommon/vm_x86_64.c +++ b/code/qcommon/vm_x86_64.c @@ -381,21 +381,6 @@ static void* getentrypoint(vm_t* vm) return vm->codeBase; } -static void CROSSCALL block_copy_vm(unsigned dest, unsigned src, unsigned count) -{ - unsigned dataMask = currentVM->dataMask; - - if ((dest & dataMask) != dest - || (src & dataMask) != src - || ((dest+count) & dataMask) != dest + count - || ((src+count) & dataMask) != src + count) - { - Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!"); - } - - memcpy(currentVM->dataBase+dest, currentVM->dataBase+src, count); -} - static void CROSSCALL eop(void) { Com_Error(ERR_DROP, "End of program reached without return!"); @@ -782,7 +767,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { emit("movl 4(%%r9, %%rbx, 4), %%edi"); // 1st argument dest emit("movl 8(%%r9, %%rbx, 4), %%rsi"); // 2nd argument src emit("movl $%d, %%edx", iarg); // 3rd argument count - emit("movq $%"PRIu64", %%rax", (intptr_t) block_copy_vm); + emit("movq $%"PRIu64", %%rax", (intptr_t) VM_BlockCopy); emit("callq *%%rax"); emit("pop %%rsi"); emit("addq %%rsi, %%rsp");