Static Checker: getting svals for a struct field value

I am new to building Clang static checkers and need some help. I am implementing a PreCall callback function that checks calls to a certain family of functions (from a local library) that takes a struct as an argument. I see how to get the SVal and Expr for the argument using CallEvent::getArgSVal() and CallEvent::getArgExpr(). What I’m struggling with is how to go from the SVal for the struct to an SVal for one of its fields. In particular, how do I get the memory region associated with the struct value? Once I have that, I think I know how to go from there, e.g.:

RecordType *structTy = structReg->getValueType()->getAsStructureType();

assert(structTy && “Value is not a structure”);

RecordDecl *structRD = structTy ->getDecl()->getDefinition();

assert(structRD && “structure has no definition”);

for (const auto *F : structRD->fields()) {

FieldRegion *FR = MRMgr.getFieldRegion(F, structReg);

if (FR->getDec()->getName() == “fieldName”)

size = StoreMgr.getBinding(store, loc::MemRegionVal(FR));

}

But how do I get the memory region structReg? And how do I get the Store store?

Thanks!

Hello Raymond,

1. You can get the Store with ProgramState::getStore() method().
2. To process an SVal representing a region, you may use SVal::getAsRegion() method. But I'm not sure what you really need here, could you explain more detailedly?
3. Your approach for FieldRegion will work, but it is better to search for a FieldDecl in your RecordDecl first and only then get its FieldRegion. FieldDecl has getName() method allowing doing this, so your code will look like:

for (const auto *F : structRD->fields()) {
   if (F->getName == "fieldName") {

     FieldRegion *FR = MRMgr.getFieldRegion(F, structReg);

     size = State->getSVal(FR);

}

where State is of ProgramStateRef type.

09.05.2016 21:55, McDowell, Raymond C. via cfe-dev пишет:

Thanks, Alexey, for your help.

Here’s a more detailed explanation of what I’m trying to do. In our project we’ve created a struct type containing a pointer to a buffer and the size of the buffer (in bytes), so that we can pass these around together and not lose track of the buffer size. I’m trying to write a checker that will make sure that any buffer access is in bounds by checking that 0 <= offset and offset + chunk_size < buffer_size, where chunk_size is the size of the data being read from / written to the buffer.

Your suggestions have helped a lot, but I’m still stuck getting from the Buffer (struct) SVal to its RecordType. You said to use SVal::getAsRegion() method to get the MemRegion. The next step would seem to be using the getValueType() and getAsStructureType() methods, but first I need to convert the MemRegion to a VarRegion or a TypedValueRegion. I tried each of these (using MemRegion::getAs), but both resulted in the checker crashing.

To follow up on my own question, in case others have it later… Here’s what I did, that seems to be working:

SVal BuffBase, BuffSize;

MemRegionManager &MRMgr = St->getStateManager().getRegionManager();

const TypedValueRegion *BuffReg = Buff.castAsnonloc::LazyCompoundVal().getCVData()->getRegion();

const RecordType *BuffRTy = BuffReg->getValueType()->getAsStructureType();

assert(BuffRTy && “Input buffer value is not a structure”);

const RecordDecl *BuffRD = BuffRTy->getDecl()->getDefinition();

assert(BuffRD && “Input buffer structure has no definition”);

for (const auto *F : BuffRD->fields()) {

if (F->getName() == “base”) {

BuffBase = St->getSVal(MRMgr.getFieldRegion(F, BuffReg));

}

else if (F->getName() == “size”) {

BuffSize = St->getSVal(MRMgr.getFieldRegion(F, BuffReg));

}

}

The above code sets BuffBase and BuffSize to be SVals for the field values in the structure SVal Buff. To assign values to the fields (instead of reading their current values), I used code like the following:

const VarRegion *BuffReg = Buff.getAsRegion()->getAs();

const RecordType *BuffRTy = BuffReg->getValueType()->getAsStructureType();

assert(BuffRTy && “Output buffer value is not a structure”);

const RecordDecl *BuffRD = BuffRTy->getDecl()->getDefinition();

assert(BuffRD && “Output buffer structure has no definition”);

for (const auto *F : BuffRD->fields()) {

if (F->getName() == “size”) {

const FieldRegion *SizeFR = St->getStateManager().getRegionManager().getFieldRegion(F, BuffReg);

St = St->bindLoc(loc::MemRegionVal(SizeFR), Size);

}

}

This changes the size field to have the value Size.

Hello,

I think there might be a subtle issue with your approach.

As far as I understand, 'Buff' is a lazy compound value. As such, it represents an r-value of some structure, which is a result of a deep copy of an object that was residing in 'BuffReg' (aka getCVData()->getRegion()) at the moment of copying. Contents of 'BuffReg' might have changed since the copy was obtained. And 'Buff' itself might have been written into a different region, or maybe into multiple different regions - after all, that's the whole point of copying objects. And in your code, you probably cannot obtain the "current" region for the lazy compound value, from which it was immediately read; even if you can, that's not the way to go.

If you're sure contents of the region have not changed since copy, then there's no problem. However, say, consider the following code:

   struct S { int x; ... };
   S bar();
   void baz(S);

   int foo() {
     S s1 = bar();
     // s1: LazyCompoundVal for CXXTempObjectRegion for bar() expression;

     S s2 = s1;
     // s1: LazyCompoundVal for CXXTempObjectRegion for bar() expression;
     // s2: LazyCompoundVal for CXXTempObjectRegion for bar() expression;

     s1.x = 42;
     // s1: LazyCompoundVal for VarRegion s2;
     // s2: LazyCompoundVal for CXXTempObjectRegion for bar() expression;

     // s2 doesn't contain 42, but you're likely to read it through your approach.
     baz(s2);
   }

If you're interested in contents of the LazyCompoundVal, rather than in current contents of its origin-region, you should be working with the second item in the CVData() - the Store thing. Store is a snapshot of the whole memory at the moment of copying (due to magic, such copies are very quick to obtain and lightweight to store, so it's a performance optimization in the analyzer, in a sense).

So the suggestion is to replace St->getSVal(MRMgr.getFieldRegion(F, BuffReg)) with something like St->getStateManager().getStoreManager().getBinding(Buff.castAs<nonloc::LazyCompoundVal>().getCVData()->getStore(), loc::MemRegionVal(MRMgr.getFieldRegion(F, BuffReg)), F->getType()). It should do the trick.

Thanks, Artem!

My approach worked fine until I tried analyzing code that accessed the buffer in a return statement. In that situation, for some reason I do not understand, the values of the buffer fields would disappear from the store. However, your approach still works then.