// Tries to find a static array within structVar whose address is within // range of targetAddr. The struct's base addr is structVarBaseAddr. // The return value is the static array variable. // Remember to recurse on non-pointer struct variables within structVar // and repeat this same process because they themselves might contain // static arrays // *baseAddr = base address of the array variable // Pre: VAR_IS_BASE_STRUCT(structVar) static VariableEntry* searchForArrayWithinStruct(VariableEntry* structVar, Addr structVarBaseAddr, Addr targetAddr, Addr* baseAddr) { VarNode* v = 0; tl_assert(structVar->varType->aggType); FJALAR_DPRINTF("searchForArrayWithinStruct: %s, structVarBaseAddr: %p, targetAddr: %p, baseAddr: %p\n", structVar->name, (void*)structVarBaseAddr, (void*)targetAddr, (void*)baseAddr); // FJALAR_DPRINTF("aggType: %p, memberVarList: %p\n", // structVar->varType->aggType, // (structVar->varType->aggType ? structVar->varType->aggType->memberVarList : 0)); if (structVar->varType->aggType->memberVarList) { for (v = structVar->varType->aggType->memberVarList->first; v != 0; v = v->next) { VariableEntry* potentialVar; Addr potentialVarBaseAddr; potentialVar = v->var; tl_assert(IS_MEMBER_VAR(potentialVar)); FJALAR_DPRINTF("examining: %s, offset: %ld\n", potentialVar->name, potentialVar->memberVar->data_member_location); // FJALAR_DPRINTF("staticArr: %p, ptrLevels: %d, varType: %d\n", // potentialVar->staticArr, potentialVar->ptrLevels, // (potentialVar->varType ? potentialVar->varType->decType : 0)); potentialVarBaseAddr = structVarBaseAddr + potentialVar->memberVar->data_member_location; if (IS_STATIC_ARRAY_VAR(potentialVar) && (potentialVarBaseAddr <= targetAddr) && (targetAddr < (potentialVarBaseAddr + (potentialVar->staticArr->upperBounds[0] * getBytesBetweenElts(potentialVar))))) { *baseAddr = potentialVarBaseAddr; FJALAR_DPRINTF("Wins: %s\n", potentialVar->name); return potentialVar; } // Recursive step (be careful to avoid infinite recursion) else if VAR_IS_BASE_STRUCT(potentialVar) { VariableEntry* targetVar = searchForArrayWithinStruct(potentialVar, potentialVarBaseAddr, targetAddr, baseAddr); if (targetVar) { FJALAR_DPRINTF("wins: %s\n", potentialVar->name); return targetVar; } } } } *baseAddr = 0; return 0; }
// Prints a .dtrace entry for a sequence of variable values denoted by // pValueArray (size numElts). Returns 1 if variable successfully // observed and printed, and 0 otherwise. // // Upon exit, if pFirstInitElt, then *pFirstInitElt contains the // pointer to the first initialized element in the sequence, or 0 if // there are no initialized elements in the sequence. This is useful // for DynComp to determine which memory location to use as the // canonical one for the entire sequence in terms of getting tags. static char printDtraceSequence(VariableEntry* var, Addr* pValueArray, Addr* pValueArrayGuest, UInt numElts, VariableOrigin varOrigin, char isHashcode, DisambigOverride disambigOverride, Addr* pFirstInitElt) { UInt i; char someEltNonZero = 0; char someEltInit = 0; char firstInitEltFound = 0; Addr firstInitElt = 0; DPRINTF("pValueArray: %p - pValueArrayGuest: %p\nnumElts: %d\n", (void *)pValueArray, (void *)pValueArrayGuest, numElts); /* if(numElts > 10) { */ /* VG_(printf)("%s - numElts: %d\n", var->name, numElts); */ /* } */ if (pFirstInitElt) { *pFirstInitElt = 0; } tl_assert(var); // a pValueArray of 0 or numElts of 0 means nonsensical because // there is no content to dereference: if (!pValueArray || !numElts) { DPRINTF("Pointer null or 0 elements\n"); DTRACE_PRINTF("%s\n%d\n", NONSENSICAL, mapInitToModbit(0)); return 0; } // If all elements of pValueArray are 0, then this also means // nonsensical because there is no content to dereference: for (i = 0; i < numElts; i++) { if (pValueArray[i]) { someEltNonZero = 1; break; } } if (!someEltNonZero) { DPRINTF("All elements 0\n"); DTRACE_PRINTF("%s\n%d\n", NONSENSICAL, mapInitToModbit(0)); return 0; } // If all elements in pValueArray are uninit, then print out UNINIT // and return 0. (be conservative and only check the first byte so that // we don't mistakenly mark an array of shorts as uninitialized) for (i = 0; i < numElts; i++) { Addr pCurValue = pValueArray[i]; char eltInit = addressIsInitialized(pCurValue, sizeof(char)); if (eltInit) { someEltInit = 1; break; } } if (!someEltInit) { DPRINTF("All elements uninit\n"); DTRACE_PRINTF("%s\n%d\n", UNINIT, mapInitToModbit(0)); return 0; } // Pointer (put this check first before the IS_STRING(var) check so // that it will work even for pointers to strings): if (isHashcode) { int ind; int limit = numElts; DPRINTF("hashcode\n"); if (fjalar_array_length_limit != -1) { limit = min(limit, fjalar_array_length_limit); } DTRACE_PRINTF( "[ "); for (ind = 0; ind < limit; ind++) { Addr pCurValue = pValueArray[ind]; Addr pCurValueGuest = pValueArrayGuest[ind]; char eltInit = addressIsInitialized(pCurValue, sizeof(void*)); if(ind == 0) { // Lets print out the first element for debugging DPRINTF("First element is: %x(GUEST) ", (UInt)pCurValueGuest); DPRINTF("First element is: %x(ACTUAL) ", (UInt)(*((Addr*)pCurValue))); } if (eltInit) { if (!firstInitEltFound) { firstInitElt = pCurValue; firstInitEltFound = 1; } DTRACE_PRINTF("%u ", IS_STATIC_ARRAY_VAR(var) ? (UInt)pCurValueGuest : (UInt)(*((Addr*)pCurValue))); // Merge the tags of the 4-bytes of the observed pointer as // well as the tags of the first initialized address and the // current address because we are observing everything as a // sequence // TODO: This may cause unnecessarily large comparability // sets - watch out! if (kvasir_with_dyncomp && firstInitElt) { val_uf_union_tags_in_range((Addr)pCurValue, sizeof(void*)); val_uf_union_tags_at_addr((Addr)firstInitElt, (Addr)pCurValue); } } else { // Daikon currently only supports 'nonsensical' values // inside of sequences, not 'uninit' value. if (!kvasir_repair_format) { DTRACE_PRINTF(NONSENSICAL); DTRACE_PRINTF(" "); } } } DTRACE_PRINTF( "]\n%d\n", mapInitToModbit(1)); } // String (not pointer to string) else if (IS_STRING(var)) { printDtraceStringSequence(var, pValueArray, numElts, disambigOverride, &firstInitElt); } // Base (non-hashcode) struct or union type // Simply print out its hashcode location else if (IS_AGGREGATE_TYPE(var->varType)) { int ind; int limit = numElts; if (fjalar_array_length_limit != -1) { limit = min(limit, fjalar_array_length_limit); } DTRACE_PRINTF( "[ "); for (ind = 0; ind < limit; ind++) { Addr pCurValueGuest = pValueArray[ind]; DTRACE_PRINTF("%u ", (UInt)pCurValueGuest); } DTRACE_PRINTF( "]\n%d\n", mapInitToModbit(1)); } // Base type else { DeclaredType decType = var->varType->decType; // override float as double when printing // out function return variables because // return variables stored in registers are always doubles char overrideFloatAsDouble = (varOrigin == FUNCTION_RETURN_VAR); if (overrideFloatAsDouble && (decType == D_FLOAT)) { decType = D_DOUBLE; } printDtraceBaseValueSequence(decType, pValueArray, numElts, disambigOverride, &firstInitElt); } if (pFirstInitElt) { *pFirstInitElt = firstInitElt; } // Default return value: return 1; }
// Prints a .dtrace entry for a single variable value denoted by // pValue. Returns 1 if variable successfully observed and printed, // and 0 otherwise. static char printDtraceSingleVar(VariableEntry* var, Addr pValue, Addr pValueGuest, VariableOrigin varOrigin, char isHashcode, char overrideIsInit, DisambigOverride disambigOverride) { char allocated = 0; char initialized = 0; tl_assert(var); DPRINTF(" printDtraceSingleVar(): %p(guest %p) overrideisInit: %d\n", (void *)pValue, (void *)pValueGuest, overrideIsInit); // a pValue of 0 means nonsensical because there is no content to // dereference: if (!pValue) { DPRINTF("no address\n"); DTRACE_PRINTF("%s\n%d\n", NONSENSICAL, mapInitToModbit(0)); return 0; } // At minimum, check whether the first byte is allocated and/or // initialized allocated = (overrideIsInit ? 1 : addressIsAllocated((Addr)pValue, sizeof(char))); if (!allocated) { DPRINTF("unallocated\n"); DTRACE_PRINTF("%s\n%d\n", NONSENSICAL, mapInitToModbit(0)); return 0; } initialized = (overrideIsInit ? 1 : addressIsInitialized((Addr)pValue, sizeof(char))); if (!initialized) { DPRINTF("uninit\n"); DTRACE_PRINTF("%s\n%d\n", UNINIT, mapInitToModbit(0)); return 0; } // From this point onwards we know that pValue is safe to // dereference because it has been both allocated and initialized // Pointer (put this check first before the IS_STRING(var) check so // that it will work even for pointers to strings): if (isHashcode) { // Be careful of what to print depending on whether the // variable is a static array: // TODO: What about a pointer to a static array? // var->isStaticArray says that the base variable is a // static array after all dereferences are done. DTRACE_PRINTF("%u\n%d\n", IS_STATIC_ARRAY_VAR(var) ? (UInt)pValueGuest : (UInt)(*((Addr*)pValue)), mapInitToModbit(1)); // Since we observed all of these bytes as one value, we will // merge all of their tags in memory if (kvasir_with_dyncomp) { DYNCOMP_DPRINTF("dtrace call val_uf_union_tags_in_range(%p, %d) (pointer)\n", (void *)pValue, sizeof(void*)); val_uf_union_tags_in_range((Addr)pValue, sizeof(void*)); } } // String (not pointer to string) else if (IS_STRING(var)) { char stringReadable = 0; // Depends on whether the variable is a static array or not: char * actualString = (IS_STATIC_ARRAY_VAR(var) ? (char *)pValue : *((char **)pValue)); // If this address hasn't been initialized to anything valid, // then we shouldn't try to do anything further with it because // it's garbage!!! stringReadable = checkStringReadable(actualString); if (stringReadable) { printDtraceSingleString(actualString, disambigOverride); } else { DTRACE_PRINTF("%s\n%d\n", UNINIT, mapInitToModbit(0)); return 0; } } // Base (non-hashcode) struct or union type // Simply print out its hashcode location else if (IS_AGGREGATE_TYPE(var->varType)) { DTRACE_PRINTF("%u\n%d\n", ((UInt)pValue), mapInitToModbit(1)); } // Base type else { DeclaredType decType = var->varType->decType; // override float as double when printing // out function return variables because // return variables stored in registers are always doubles char overrideFloatAsDouble = (varOrigin == FUNCTION_RETURN_VAR); if (overrideFloatAsDouble && (decType == D_FLOAT)) { decType = D_DOUBLE; } return printDtraceSingleBaseValue(pValue, decType, overrideIsInit, disambigOverride); } // Default return value: return 1; }
// Takes a location and a VariableEntry and tries to determine // the UPPER BOUND of the array which the pointer refers to. // CAUTION: This function is still fairly primitive and untested // // This now uses a two-pass scheme which first searches to the end of the // array and then goes backwards until it finds the first byte whose V-bit // is valid so that it can avoid printing out tons of garbage values and // cluttering up the .dtrace file. // // This also now has support to find statically-sized arrays within structs // declared as global and local variables as well as statically-sized arrays // which are themselves global and local variables int returnArrayUpperBoundFromPtr(VariableEntry* var, Addr varLocation) { VariableEntry* targetVar = 0; Addr baseAddr = 0; char foundGlobalArrayVariable = 0; FJALAR_DPRINTF("Checking for upper bound of %p\n", (void *)varLocation); // 1. Search if varLocation is within a global variable if (addressIsGlobal(varLocation)) { targetVar = returnArrayVariableWithAddr(&globalVars, varLocation, 1, 0, &baseAddr); if (targetVar) { foundGlobalArrayVariable = 1; } else { // UNCONDITIONALLY RETURN 0 IF WE CANNOT FIND A GLOBAL ARRAY // VARIABLE. WE DO NOT WANT TO PROBE IN THE GLOBAL SPACE // BECAUSE ALL OF IT MAY POSSIBLY BE INITIALIZED. // targetVar = returnGlobalSingletonWithAddress(varLocation); // if (targetVar) { return 0; // } } } // 2. If not found, then search if varLocation is within the stack // frame of a function currently on the stack if (!targetVar) { FunctionExecutionState* e; FJALAR_DPRINTF("Not found in globals area, checking on stack\n"); e = returnFunctionExecutionStateWithAddress(varLocation); FJALAR_DPRINTF("Found function entry %p\n", e); if (e) { VarList* localArrayAndStructVars = &(e->func->localArrayAndStructVars); FJALAR_DPRINTF(" e->FP is %p\n", (void *)e->FP); FJALAR_DPRINTF(" localArrayAndSTructVars: %p, numVars: %d\n", localArrayAndStructVars, localArrayAndStructVars->numVars); tl_assert(!localArrayAndStructVars || (Addr)localArrayAndStructVars > 0x100); if (localArrayAndStructVars && // hopefully ensures that it's not totally bogus ((Addr)localArrayAndStructVars > 0x100) && (localArrayAndStructVars->numVars > 0)) { targetVar = returnArrayVariableWithAddr(localArrayAndStructVars, varLocation, 0, e, &baseAddr); } } } // 3. If still not found, then search the heap for varLocation // if it is lower than the current frame pointer // This is a last-ditch desperation attempt and won't yield valid-looking // results in cases like when you have a pointer to an int which is located // within a struct malloc'ed on the heap. if (!targetVar) { FJALAR_DPRINTF("Not found on stack, checking in heap\n"); tl_assert(curFunctionExecutionStatePtr); FJALAR_DPRINTF("Checking if the variable is on the stack:\n"); FJALAR_DPRINTF("\tCurrent Stackframe: [%p - %p]\n", (void*)curFunctionExecutionStatePtr->FP, (void*)curFunctionExecutionStatePtr->lowestSP); // Make sure the address is not in the stack or global region // before probing so that we don't accidentally make a mistake // where we erroneously conclude that the array size is HUGE // since all areas on the stack and global regions are ALLOCATED // so probing won't do us much good if ((varLocation < curFunctionExecutionStatePtr->lowestSP) && !addressIsGlobal(varLocation)) { int size; FJALAR_DPRINTF("Location looks reasonable, probing at %p\n", (void *)varLocation); size = probeAheadDiscoverHeapArraySize(varLocation, getBytesBetweenElts(var)); FJALAR_DPRINTF("Size is %d\n", size); // We want an upper-bound on the array, not the actual size if (size > 0) return (size - 1); else return 0; } } // This is a less strict match which only compares rep. types // ... we will do more checking later to really determine the relative sizes. // This leniency allows an int* to reference a char[] and so forth ... // see below for translation // else if (baseAddr && // (targetVar->varType->repType == var->varType->repType)) { // (comment added 2005) // TODO: Hmmmm, what are we gonna do without repTypes??? I need to // investigate this 'if' condition more carefully later: else if (baseAddr) { int targetVarSize = 0; int targetVarBytesBetweenElts = getBytesBetweenElts(targetVar); int varBytesBetweenElts = getBytesBetweenElts(var); Addr highestAddr; tl_assert(IS_STATIC_ARRAY_VAR(targetVar)); FJALAR_DPRINTF("varLocation: %p\n", (void *)varLocation); highestAddr = baseAddr + ((targetVar->staticArr->upperBounds[0]) * targetVarBytesBetweenElts); FJALAR_DPRINTF("baseAddr is: %p, highestAddr is %p\n", (void *)baseAddr, (void *)highestAddr); // NEW!: Probe backwards until you find the first address whose V-bit is SET: // but ONLY do this for globals and NOT for stuff on the stack because // V-bits for stack variables are FLAKY!!! During function exit, all the V-bits // are wiped out :( if (foundGlobalArrayVariable) { while ((highestAddr > varLocation) && (MC_Ok != mc_check_readable(highestAddr, targetVarBytesBetweenElts, 0))) { highestAddr -= targetVarBytesBetweenElts; } } // This is IMPORTANT that we subtract from varLocation RATHER than baseAddr // because of the fact that varLocation can point to the MIDDLE of an array targetVarSize = (highestAddr - varLocation) / targetVarBytesBetweenElts; FJALAR_DPRINTF("targetVarBytesBetweenElts is %d, varBytesBetweenElts is %d, targetVarSize is %d\n", targetVarBytesBetweenElts, varBytesBetweenElts, targetVarSize); FJALAR_DPRINTF("Target : [%s - %d] Source : [%s - %d]\n", targetVar->varType->typeName, targetVarBytesBetweenElts, var->varType->typeName, varBytesBetweenElts); if (targetVarBytesBetweenElts == varBytesBetweenElts) { return targetVarSize; } else { return (targetVarSize * targetVarBytesBetweenElts) / varBytesBetweenElts; } } return 0; }
// Returns an array or struct variable within varList // that encompasses the address provided by "a". // Properties for return value r = &(returnNode.var): // location(r) <= "a" < location(r) + (r->upperBounds[0] * getBytesBetweenElts(r)) // [if array] // location(r) <= "a" < location(r) + (getBytesBetweenElts(r)) // [if struct] // where location(.) is the global location if isGlobal and stack location // based on FP or SP if !isGlobal // *baseAddr = the base address of the variable returned static VariableEntry* returnArrayVariableWithAddr(VarList* varList, Addr a, char isGlobal, FunctionExecutionState* e, Addr* baseAddr) { VarNode* cur_node = 0; ThreadId tid = VG_(get_running_tid)(); Addr var_loc = 0; FJALAR_DPRINTF("[returnArrayVariableWithAddr] varList: %p, Addr: %p, %s\n", varList, (void *)a, (isGlobal)?"Global":"NonGlobal"); if (!isGlobal) { FJALAR_DPRINTF("frame_ptr: %p, stack_ptr: %p\n", (void *)e->FP, (void *)e->lowSP); } for (cur_node = varList->first; cur_node != 0; cur_node = cur_node->next) { VariableEntry* potentialVar = cur_node->var; Addr potentialVarBaseAddr = 0; if (!potentialVar) continue; FJALAR_DPRINTF("Examining potential var: %s, offset: 0x%x, locType: 0x%x\n", potentialVar->name, potentialVar->byteOffset, potentialVar->locationType); if (isGlobal) { tl_assert(IS_GLOBAL_VAR(potentialVar)); potentialVarBaseAddr = potentialVar->globalVar->globalLocation; FJALAR_DPRINTF("Examining potential var address: %p\n",(void *) potentialVarBaseAddr); } else { if (potentialVar->locationType == FP_OFFSET_LOCATION) { potentialVarBaseAddr = e->FP + potentialVar->byteOffset; } else { // (comment added 2014) // Potential bug! We are ignoring other locationTypes // and just assuming it is ESP. This is the only case // we've seen (i386 only) so far. (markro) potentialVarBaseAddr = e->lowSP + potentialVar->byteOffset; } } if (potentialVar->location_expression_size) { unsigned int i = 0; for(i = 0; i < potentialVar->location_expression_size; i++ ) { dwarf_location *dloc = &(potentialVar->location_expression[i]); unsigned int op = dloc->atom; int reg_val; if(op == DW_OP_addr) { // DWARF supplied address var_loc = dloc->atom_offset; } else if(op == DW_OP_deref) { // Dereference result of last DWARF operation tl_assert(var_loc); var_loc = *(Addr *)var_loc; } else if((op >= DW_OP_const1u) && (op <= DW_OP_consts)) { // DWARF supplied constant var_loc = dloc->atom_offset; } else if((op >= DW_OP_plus) && (op <= DW_OP_plus_uconst)) { // Add DWARF supplied constant to value to result of last DWARF operation var_loc += dloc->atom_offset; } else if((op >= DW_OP_reg0) && (op <= DW_OP_reg31)) { // Get value located in architectural register reg_val = (*get_reg[dloc->atom - DW_OP_reg0])(tid); FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %x\n", dwarf_reg_string[dloc->atom - DW_OP_reg0], reg_val); var_loc = (Addr)®_val; } else if((op >= DW_OP_breg0) && (op <= DW_OP_breg31)) { // Get value pointed to by architectural register reg_val = (*get_reg[dloc->atom - DW_OP_breg0])(tid); FJALAR_DPRINTF("\tObtaining register value: [%%%s]: %x\n", dwarf_reg_string[dloc->atom - DW_OP_breg0], reg_val); var_loc = reg_val + dloc->atom_offset; FJALAR_DPRINTF("\tAdding %lld to the register value for %p\n", dloc->atom_offset, (void *)var_loc); tl_assert(var_loc); } else if(op == DW_OP_fbreg) { // Get value located at an offset from the FRAME_BASE. FJALAR_DPRINTF("atom offset: %lld vs. byteOffset: %d\n", dloc->atom_offset, potentialVar->byteOffset); var_loc = e->FP + dloc->atom_offset; } else { // There's a fair number of DWARF operations still unsupported. There is a full list // in fjalar_debug.h FJALAR_DPRINTF("\tUnsupported DWARF stack OP: %s\n", location_expression_to_string(op)); tl_assert(0); } FJALAR_DPRINTF("\tApplying DWARF Stack Operation %s - %p\n",location_expression_to_string(op), (void *)var_loc); } } FJALAR_DPRINTF("addr: %p, potential var_loc: %p, staticArr: %p, ptrLevels: %d, varType: %d\n", (void*)a, (void*)potentialVarBaseAddr, potentialVar->staticArr, potentialVar->ptrLevels, (potentialVar->varType ? potentialVar->varType->decType : 0)); // array if (IS_STATIC_ARRAY_VAR(potentialVar) && (potentialVarBaseAddr <= a) && (a < (potentialVarBaseAddr + (potentialVar->staticArr->upperBounds[0] * getBytesBetweenElts(potentialVar))))) { FJALAR_DPRINTF("returnArrayVar: found matching array with upperBounds[0]: %d\n", potentialVar->staticArr->upperBounds[0]); *baseAddr = potentialVarBaseAddr; return potentialVar; } // struct else if (VAR_IS_BASE_STRUCT(potentialVar) && (potentialVarBaseAddr <= a) && (a < (potentialVarBaseAddr + getBytesBetweenElts(potentialVar)))) { return searchForArrayWithinStruct(potentialVar, potentialVarBaseAddr, a, baseAddr); } } *baseAddr = 0; return 0; }