On 04/08/2011 10:28 AM, Lenny Maiorani wrote:
This patch adds modeling of strcmp() to the CString checker. Validates inputs are not NULL and are real C strings, then does the comparison and binds the proper return value. Unit tests included.

-Lenny


Oops. Empty patch. Thanks Joerg.

-Lenny

diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index ccbc934..255be05 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -71,6 +71,8 @@ public:
   void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd,
                         bool isStrncpy) const;
 
+  void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
+
   // Utility methods
   std::pair<const GRState*, const GRState*>
   static assumeZero(CheckerContext &C,
@@ -84,6 +86,11 @@ public:
   SVal getCStringLength(CheckerContext &C, const GRState *&state,
                         const Expr *Ex, SVal Buf) const;
 
+  const StringLiteral *getCStringLiteral(CheckerContext &C, 
+                                         const GRState *&state,
+                                         const Expr *expr,  
+                                         SVal val) const;
+
   static const GRState *InvalidateBuffer(CheckerContext &C,
                                          const GRState *state,
                                          const Expr *Ex, SVal V);
@@ -584,6 +591,26 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state,
   }
 }
 
+const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
+  const GRState *&state, const Expr *expr, SVal val) const {
+
+  // Get the memory region pointed to by the val.
+  const MemRegion *bufRegion = val.getAsRegion();
+  if (!bufRegion)
+    return NULL; 
+
+  // Strip casts off the memory region.
+  bufRegion = bufRegion->StripCasts();
+
+  // Cast the memory region to a string region.
+  const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);
+  if (!strRegion)
+    return NULL; 
+
+  // Return the actual string in the string region.
+  return strRegion->getStringLiteral();
+}
+
 const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C,
                                                 const GRState *state,
                                                 const Expr *E, SVal V) {
@@ -1021,6 +1048,61 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
   C.addTransition(state);
 }
 
+void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
+  //int strcmp(const char *restrict s1, const char *restrict s2);
+
+  const GRState *state = C.getState();
+
+  // Check that the first string is non-null
+  const Expr *s1 = CE->getArg(0);
+  SVal s1Val = state->getSVal(s1);
+  state = checkNonNull(C, state, s1, s1Val);
+  if (!state)
+    return;
+
+  // Check that the second string is non-null.
+  const Expr *s2 = CE->getArg(1);
+  SVal s2Val = state->getSVal(s2);
+  state = checkNonNull(C, state, s2, s2Val);
+  if (!state)
+    return;
+
+  // Get the string length of the first string or give up.
+  SVal s1Length = getCStringLength(C, state, s1, s1Val);
+  if (s1Length.isUndef())
+    return;
+
+  // Get the string length of the second string or give up.
+  SVal s2Length = getCStringLength(C, state, s2, s2Val);
+  if (s2Length.isUndef())
+    return;
+
+  // Get the string literal of the first string.
+  const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val);
+  if (!s1StrLiteral)
+    return;
+  llvm::StringRef s1StrRef = s1StrLiteral->getString();
+
+  // Get the string literal of the second string.
+  const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val);
+  if (!s2StrLiteral)
+    return;
+  llvm::StringRef s2StrRef = s2StrLiteral->getString();
+
+  // Compare string 1 to string 2 the same way strcmp() does.
+  int result = s1StrRef.compare(s2StrRef);
+  
+  // Build the SVal of the comparison to bind the return value.
+  SValBuilder &svalBuilder = C.getSValBuilder();
+  QualType intTy = svalBuilder.getContext().IntTy;
+  SVal resultVal = svalBuilder.makeIntVal(result, intTy);
+
+  // Bind the return value of the expression.
+  // Set the return value.
+  state = state->BindExpr(CE, resultVal);
+  C.addTransition(state);
+}
+
 //===----------------------------------------------------------------------===//
 // The driver method, and other Checker callbacks.
 //===----------------------------------------------------------------------===//
@@ -1053,6 +1135,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
     .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy)
     .Case("strlen", &CStringChecker::evalstrLength)
     .Case("strnlen", &CStringChecker::evalstrnLength)
+    .Case("strcmp", &CStringChecker::evalStrcmp)
     .Case("bcopy", &CStringChecker::evalBcopy)
     .Default(NULL);
 
diff --git a/test/Analysis/string.c b/test/Analysis/string.c
index 430145f..25a8061 100644
--- a/test/Analysis/string.c
+++ b/test/Analysis/string.c
@@ -438,3 +438,88 @@ void stpcpy_no_overflow(char *y) {
   if (strlen(y) == 3)
     stpcpy(x, y); // no-warning
 }
+
+//===----------------------------------------------------------------------===
+// strcmp()
+//===----------------------------------------------------------------------===
+
+#define strcmp BUILTIN(strcmp)
+int strcmp(const char *restrict s1, const char *restrict s2);
+
+void strcmp_constant0() {
+  if (strcmp("123", "123") != 0)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_constant_and_var_0() {
+  char *x = "123";
+  if (strcmp(x, "123") != 0)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_constant_and_var_1() {
+  char *x = "123";
+    if (strcmp("123", x) != 0)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_0() {
+  char *x = "123";
+  char *y = "123";
+  if (strcmp(x, y) != 0)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_1() {
+  char *x = "234";
+  char *y = "123";
+  if (strcmp(x, y) != 1)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_2() {
+  char *x = "123";
+  char *y = "234";
+  if (strcmp(x, y) != -1)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_null_0() {
+  char *x = NULL;
+  char *y = "123";
+  strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strcmp_null_1() {
+  char *x = "123";
+  char *y = NULL;
+  strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}}
+}
+
+void strcmp_diff_length_0() {
+  char *x = "12345";
+  char *y = "234";
+  if (strcmp(x, y) != -1)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_diff_length_1() {
+  char *x = "123";
+  char *y = "23456";
+  if (strcmp(x, y) != -1)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_diff_length_2() {
+  char *x = "12345";
+  char *y = "123";
+  if (strcmp(x, y) != 1)
+    (void)*(char*)0; // no-warning
+}
+
+void strcmp_diff_length_3() {
+  char *x = "123";
+  char *y = "12345";
+  if (strcmp(x, y) != -1)
+    (void)*(char*)0; // no-warning
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to