]> granicus.if.org Git - clang/commitdiff
Adding Static Analyzer checker for mempcpy().
authorLenny Maiorani <lenny@colorado.edu>
Thu, 31 Mar 2011 21:36:53 +0000 (21:36 +0000)
committerLenny Maiorani <lenny@colorado.edu>
Thu, 31 Mar 2011 21:36:53 +0000 (21:36 +0000)
Models mempcpy() so that if length is NULL the destination pointer is returned. Otherwise, the source and destination are confirmed not to be NULL and not overlapping. Finally the copy is validated to not cause a buffer overrun and the return value is bound to the address of the byte after the last byte copied.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@128677 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Checkers/CStringChecker.cpp
test/Analysis/bstring.c

index cbf322bf4efde4771b4530e3111542aef8db7c04..ccbc9343f36dead0cfc53de6b054411114e5a22e 100644 (file)
@@ -49,11 +49,14 @@ public:
                                           const CallExpr *) const;
 
   void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
+  void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
   void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
   void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
-  void evalCopyCommon(CheckerContext &C, const GRState *state,
+  void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
+                      const GRState *state,
                       const Expr *Size, const Expr *Source, const Expr *Dest,
-                      bool Restricted = false) const;
+                      bool Restricted = false,
+                      bool IsMempcpy = false) const;
 
   void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
 
@@ -655,9 +658,12 @@ bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx,
 // evaluation of individual function calls.
 //===----------------------------------------------------------------------===//
 
-void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
+void CStringChecker::evalCopyCommon(CheckerContext &C, 
+                                    const CallExpr *CE,
+                                    const GRState *state,
                                     const Expr *Size, const Expr *Dest,
-                                    const Expr *Source, bool Restricted) const {
+                                    const Expr *Source, bool Restricted,
+                                    bool IsMempcpy) const {
   // See if the size argument is zero.
   SVal sizeVal = state->getSVal(Size);
   QualType sizeTy = Size->getType();
@@ -665,12 +671,39 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
   const GRState *stateZeroSize, *stateNonZeroSize;
   llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy);
 
-  // If the size is zero, there won't be any actual memory access.
-  if (stateZeroSize)
+  // Get the value of the Dest.
+  SVal destVal = state->getSVal(Dest);
+
+  // If the size is zero, there won't be any actual memory access, so
+  // just bind the return value to the destination buffer and return.
+  if (stateZeroSize) {
     C.addTransition(stateZeroSize);
+    if (IsMempcpy)
+      state->BindExpr(CE, destVal);
+    else
+      state->BindExpr(CE, sizeVal);
+    return;
+  }
 
   // If the size can be nonzero, we have to check the other arguments.
   if (stateNonZeroSize) {
+
+    // Ensure the destination is not null. If it is NULL there will be a
+    // NULL pointer dereference.
+    state = checkNonNull(C, state, Dest, destVal);
+    if (!state)
+      return;
+
+    // Get the value of the Src.
+    SVal srcVal = state->getSVal(Source);
+    
+    // Ensure the source is not null. If it is NULL there will be a
+    // NULL pointer dereference.
+    state = checkNonNull(C, state, Source, srcVal);
+    if (!state)
+      return;
+
+    // Ensure the buffers do not overlap.
     state = stateNonZeroSize;
     state = CheckBufferAccess(C, state, Size, Dest, Source,
                               /* FirstIsDst = */ true);
@@ -678,6 +711,26 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state,
       state = CheckOverlap(C, state, Size, Dest, Source);
 
     if (state) {
+
+      // If this is mempcpy, get the byte after the last byte copied and 
+      // bind the expr.
+      if (IsMempcpy) {
+        loc::MemRegionVal *destRegVal = dyn_cast<loc::MemRegionVal>(&destVal);
+        
+        // Get the length to copy.
+        SVal lenVal = state->getSVal(Size);
+        NonLoc *lenValNonLoc = dyn_cast<NonLoc>(&lenVal);
+        
+        // Get the byte after the last byte copied.
+        SVal lastElement = C.getSValBuilder().evalBinOpLN(state, BO_Add, 
+                                                          *destRegVal,
+                                                          *lenValNonLoc, 
+                                                          Dest->getType());
+        
+        // The byte after the last byte copied is the return value.
+        state = state->BindExpr(CE, lastElement);
+      }
+
       // Invalidate the destination.
       // FIXME: Even if we can't perfectly model the copy, we should see if we
       // can use LazyCompoundVals to copy the source values into the destination.
@@ -696,7 +749,16 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
   const Expr *Dest = CE->getArg(0);
   const GRState *state = C.getState();
   state = state->BindExpr(CE, state->getSVal(Dest));
-  evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1), true);
+  evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true);
+}
+
+void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
+  // void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
+  // The return value is a pointer to the byte following the last written byte.
+  const Expr *Dest = CE->getArg(0);
+  const GRState *state = C.getState();
+  
+  evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
 }
 
 void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
@@ -705,12 +767,13 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
   const Expr *Dest = CE->getArg(0);
   const GRState *state = C.getState();
   state = state->BindExpr(CE, state->getSVal(Dest));
-  evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1));
+  evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1));
 }
 
 void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
   // void bcopy(const void *src, void *dst, size_t n);
-  evalCopyCommon(C, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0));
+  evalCopyCommon(C, CE, C.getState(), 
+                 CE->getArg(2), CE->getArg(1), CE->getArg(0));
 }
 
 void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
@@ -982,6 +1045,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
 
   FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
     .Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy)
+    .Case("mempcpy", &CStringChecker::evalMempcpy)
     .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp)
     .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove)
     .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy)
index a5d66058215b18801a28559d73a1b52d42c63df5..08b93c64a1165334c95f682329a3ce3350a9d478 100644 (file)
@@ -129,9 +129,110 @@ void memcpy11() {
 void memcpy12() {
   char a[4] = {0};
   memcpy(0, a, 0); // no-warning
+}
+
+void memcpy13() {
+  char a[4] = {0};
   memcpy(a, 0, 0); // no-warning
 }
 
+//===----------------------------------------------------------------------===
+// mempcpy()
+//===----------------------------------------------------------------------===
+
+#define mempcpy BUILTIN(mempcpy)
+void *mempcpy(void *restrict s1, const void *restrict s2, size_t n);
+
+void mempcpy0 () {
+  char src[] = {1, 2, 3, 4};
+  char dst[5] = {0};
+
+  mempcpy(dst, src, 4); // no-warning
+
+  if (mempcpy(dst, src, 4) != &dst[4]) {
+    (void)*(char*)0; // no-warning
+  }
+
+  if (dst[0] != 0)
+    (void)*(char*)0; // expected-warning{{null}}
+}
+
+void mempcpy1 () {
+  char src[] = {1, 2, 3, 4};
+  char dst[10];
+
+  mempcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}}
+}
+
+void mempcpy2 () {
+  char src[] = {1, 2, 3, 4};
+  char dst[1];
+
+  mempcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void mempcpy3 () {
+  char src[] = {1, 2, 3, 4};
+  char dst[3];
+
+  mempcpy(dst+1, src+2, 2); // no-warning
+}
+
+void mempcpy4 () {
+  char src[] = {1, 2, 3, 4};
+  char dst[10];
+
+  mempcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}}
+}
+
+void mempcpy5() {
+  char src[] = {1, 2, 3, 4};
+  char dst[3];
+
+  mempcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}}
+}
+
+void mempcpy6() {
+  int a[4] = {0};
+  mempcpy(a, a, 8); // expected-warning{{overlapping}}  
+}
+
+void mempcpy7() {
+  int a[4] = {0};
+  mempcpy(a+2, a+1, 8); // expected-warning{{overlapping}}
+}
+
+void mempcpy8() {
+  int a[4] = {0};
+  mempcpy(a+1, a+2, 8); // expected-warning{{overlapping}}
+}
+
+void mempcpy9() {
+  int a[4] = {0};
+  mempcpy(a+2, a+1, 4); // no-warning
+  mempcpy(a+1, a+2, 4); // no-warning
+}
+
+void mempcpy10() {
+  char a[4] = {0};
+  mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void mempcpy11() {
+  char a[4] = {0};
+  mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void mempcpy12() {
+  char a[4] = {0};
+  mempcpy(0, a, 0); // no-warning
+}
+
+void mempcpy13() {
+  char a[4] = {0};
+  mempcpy(a, 0, 0); // no-warning
+}
+
 //===----------------------------------------------------------------------===
 // memmove()
 //===----------------------------------------------------------------------===