[PATCH] Fix bug 14526 - Make malloc() static analysis M_ZERO-aware

Meyer, Conrad conrad.meyer at isilon.com
Thu Mar 13 23:35:57 PDT 2014


From: Jordan Rose [jordan_rose at apple.com]
Sent: Thursday, March 13, 2014 8:46 PM
To: Meyer, Conrad
Cc: cfe-commits at cs.uiuc.edu
Subject: Re: [PATCH] Fix bug 14526 - Make malloc() static analysis M_ZERO-aware

> Hi, Conrad. Thanks for picking this up. I have a few comments on the patch, and of course you'll need to include a regression test too before it can be committed. You can see how these work in test/Analysis/malloc.c; feel free to make a new test file so that you can declare the kernel version of malloc without conflicting with the normal one.

Cool, will do.

>  On Mar 13, 2014, at 10:28 , Meyer, Conrad <conrad.meyer at isilon.com> wrote:
> > @@ -586,7 +588,55 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
> >     initIdentifierInfo(C.getASTContext());
> >     IdentifierInfo *FunI = FD->getIdentifier();
> >
> > -    if (FunI == II_malloc || FunI == II_valloc) {
> > +    if (FunI == II_malloc) {
> > +      if (CE->getNumArgs() < 1)
> > +        return;
> > +      if (CE->getNumArgs() < 2) {
> > +        State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
> > +      } else if (CE->getNumArgs() == 3) {
> > +        // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some
> > +        // code could be shared.

> Most people have probably never seen a three-argument malloc. Mentioning that it's from the FreeBSD kernel (and possibly other kernels?) and possibly including the declaration in a comment would go a long way towards explaining why this code is here at all.

Can do. Looks like NetBSD also has it, although there it is 0x0002. And on OpenBSD it is 0x0008.

> > +
> > +        if (!Val_M_ZERO.hasValue()) {
> > +          if (C.getASTContext().getTargetInfo().getTriple().getOS()
> > +                                                            == llvm::Triple::FreeBSD)
> > +            Val_M_ZERO = 0x00100;
> > +          else
> > +            // FIXME: We need a more general way of getting the M_ZERO value.
> > +            // See also: O_CREAT in UnixAPIChecker.cpp.
> > +            return;
> > +        }

> I don't think "return" is appropriate here. Isn't it better to fall back to regular malloc() if we don't know what M_ZERO is on this platform? (In the O_CREAT case, the warning was only about misusing O_CREAT, so it didn't matter that we were bailing out early.)

Sure, that seems reasonable.

> > +
> > +        ProgramStateRef state = C.getState();
> > +        const Expr *flagsEx = CE->getArg(2);

> Please follow the LLVM naming conventions: capital letters for all variables as well as types. (This applies further down as well.)

Okay, will fix.

> > +        const SVal V = state->getSVal(flagsEx, C.getLocationContext());
> > +        if (!V.getAs<NonLoc>()) {
> > +          // The case where 'V' can be a location can only be due to a bad header,
> > +          // so in this case bail out.
> > +          return;
> > +        }
> > +
> > +        NonLoc flags = V.castAs<NonLoc>();
> > +        NonLoc zeroFlag = C.getSValBuilder()
> > +            .makeIntVal(Val_M_ZERO.getValue(), flagsEx->getType()).castAs<NonLoc>();
> > +        SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
> > +                                                            flags, zeroFlag,
> > +                                                            flagsEx->getType());
> > +        if (maskedFlagsUC.isUnknownOrUndef())
> > +          return;
> > +        DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
> > +
> > +        // Check if maskedFlags is non-zero.
> > +        ProgramStateRef trueState, falseState;
> > +        std::tie(trueState, falseState) = state->assume(maskedFlags);
> > +
> > +        // If M_ZERO is set, treat this like calloc (initialized).
> > +        if (!(trueState && !falseState))
> > +          State = CallocMem(C, CE);
> > +        else
> > +          State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);

> You need to be a bit more careful here...the if-check is exactly backwards from what you want. In more detail, there are three possible cases:
> 
> - M_ZERO is known to be set: trueState exists, but falseState doesn't.
> - M_ZERO is known not to be set: falseState exists, but trueState doesn't
> - The analyzer isn't sure: both states exist

Ah, you're right.

> In the latter two states, we should probably assume the existing behavior (malloc returns undefined).

Agreed.

> Thanks for working on this!
> Jordan

Thanks for reviewing. I'll try and clean this up and get a fresh patch submitted tomorrow.

Conrad





More information about the cfe-commits mailing list