This revision was automatically updated to reflect the committed changes.
Closed by commit rG94ca2beccc59: [clang][analyzer] Added partial wide character 
support to CStringChecker (authored by balazske).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D130091/new/

https://reviews.llvm.org/D130091

Files:
  clang/docs/analyzer/checkers.rst
  clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
  clang/test/Analysis/wstring.c

Index: clang/test/Analysis/wstring.c
===================================================================
--- /dev/null
+++ clang/test/Analysis/wstring.c
@@ -0,0 +1,389 @@
+// RUN: %clang_analyze_cc1 -verify %s \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.cstring \
+// RUN:   -analyzer-checker=alpha.unix.cstring \
+// RUN:   -analyzer-disable-checker=alpha.unix.cstring.UninitializedRead \
+// RUN:   -analyzer-checker=debug.ExprInspection \
+// RUN:   -analyzer-config eagerly-assume=false  
+//
+// RUN: %clang_analyze_cc1 -verify %s -DUSE_BUILTINS \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-checker=unix.cstring \
+// RUN:   -analyzer-checker=alpha.unix.cstring \
+// RUN:   -analyzer-disable-checker=alpha.unix.cstring.UninitializedRead \
+// RUN:   -analyzer-checker=debug.ExprInspection \
+// RUN:   -analyzer-config eagerly-assume=false
+
+//===----------------------------------------------------------------------===
+// Declarations
+//===----------------------------------------------------------------------===
+
+// Some functions are implemented as builtins. These should be #defined as
+// BUILTIN(f), which will prepend "__builtin_" if USE_BUILTINS is defined.
+
+#ifdef USE_BUILTINS
+# define BUILTIN(f) __builtin_ ## f
+#else /* USE_BUILTINS */
+# define BUILTIN(f) f
+#endif /* USE_BUILTINS */
+
+typedef __SIZE_TYPE__ size_t;
+typedef __WCHAR_TYPE__ wchar_t;
+
+void clang_analyzer_eval(int);
+
+//===----------------------------------------------------------------------===
+// wwmemcpy()
+//===----------------------------------------------------------------------===
+
+#define wmemcpy BUILTIN(wmemcpy)
+wchar_t *wmemcpy(wchar_t *restrict s1, const wchar_t *restrict s2, size_t n);
+
+void wmemcpy0 (void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[4] = {0};
+
+  wmemcpy(dst, src, 4); // no-warning
+
+  clang_analyzer_eval(wmemcpy(dst, src, 4) == dst); // expected-warning{{TRUE}}
+
+  // If we actually model the copy, we can make this known.
+  // The important thing for now is that the old value has been invalidated.
+  clang_analyzer_eval(dst[0] != 0); // expected-warning{{UNKNOWN}}
+}
+
+void wmemcpy1 (void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[10];
+
+  wmemcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+}
+
+void wmemcpy2 (void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[1];
+
+  wmemcpy(dst, src, 4); // expected-warning {{Memory copy function overflows the destination buffer}}
+}
+
+void wmemcpy3 (void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[3];
+
+  wmemcpy(dst+1, src+2, 2); // no-warning
+}
+
+void wmemcpy4 (void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[10];
+
+  wmemcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}}
+}
+
+void wmemcpy5(void) {
+  wchar_t src[] = {1, 2, 3, 4};
+  wchar_t dst[3];
+
+  wmemcpy(dst + 2, src + 2, 2); // expected-warning{{Memory copy function overflows the destination buffer}}
+}
+
+void wmemcpy6(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a, a, 2); // expected-warning{{overlapping}}
+}
+
+void wmemcpy7(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a+2, a+1, 2); // expected-warning{{overlapping}}
+}
+
+void wmemcpy8(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a+1, a+2, 2); // expected-warning{{overlapping}}
+}
+
+void wmemcpy9(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a+2, a+1, 1); // no-warning
+  wmemcpy(a+1, a+2, 1); // no-warning
+}
+
+void wmemcpy10(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(0, a, 1); // expected-warning{{Null pointer passed as 1st argument to memory copy function}}
+}
+
+void wmemcpy11(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a, 0, 1); // expected-warning{{Null pointer passed as 2nd argument to memory copy function}}
+}
+
+void wmemcpy12(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(0, a, 0); // no-warning
+}
+
+void wmemcpy13(void) {
+  wchar_t a[4] = {0};
+  wmemcpy(a, 0, 0); // no-warning
+}
+
+void wmemcpy_unknown_size (size_t n) {
+  wchar_t a[4], b[4] = {1};
+  clang_analyzer_eval(wmemcpy(a, b, n) == a); // expected-warning{{TRUE}}
+}
+
+void wmemcpy_unknown_size_warn (size_t n) {
+  wchar_t a[4];
+  void *result = wmemcpy(a, 0, n); // expected-warning{{Null pointer passed as 2nd argument to memory copy function}}
+  clang_analyzer_eval(result == a); // no-warning (above is fatal)
+}
+
+//===----------------------------------------------------------------------===
+// wcslen()
+//===----------------------------------------------------------------------===
+
+#define wcslen BUILTIN(wcslen)
+size_t wcslen(const wchar_t *s);
+
+void wcslen_constant0(void) {
+  clang_analyzer_eval(wcslen(L"123") == 3); // expected-warning{{TRUE}}
+}
+
+void wcslen_constant1(void) {
+  const wchar_t *a = L"123";
+  clang_analyzer_eval(wcslen(a) == 3); // expected-warning{{TRUE}}
+}
+
+void wcslen_constant2(wchar_t x) {
+  wchar_t a[] = L"123";
+  clang_analyzer_eval(wcslen(a) == 3); // expected-warning{{TRUE}}
+
+  a[0] = x;
+  clang_analyzer_eval(wcslen(a) == 3); // expected-warning{{UNKNOWN}}
+}
+
+size_t wcslen_null(void) {
+  return wcslen(0); // expected-warning{{Null pointer passed as 1st argument to string length function}}
+}
+
+size_t wcslen_fn(void) {
+  return wcslen((wchar_t*)&wcslen_fn); // expected-warning{{Argument to string length function is the address of the function 'wcslen_fn', which is not a null-terminated string}}
+}
+
+size_t wcslen_nonloc(void) {
+label:
+  return wcslen((wchar_t*)&&label); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}}
+}
+
+void wcslen_subregion(void) {
+  struct two_strings { wchar_t a[2], b[2]; };
+  extern void use_two_strings(struct two_strings *);
+
+  struct two_strings z;
+  use_two_strings(&z);
+
+  size_t a = wcslen(z.a);
+  z.b[0] = 5;
+  size_t b = wcslen(z.a);
+  if (a == 0)
+    clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
+
+  use_two_strings(&z);
+
+  size_t c = wcslen(z.a);
+  if (a == 0)
+    clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
+}
+
+extern void use_string(wchar_t *);
+void wcslen_argument(wchar_t *x) {
+  size_t a = wcslen(x);
+  size_t b = wcslen(x);
+  if (a == 0)
+    clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
+
+  use_string(x);
+
+  size_t c = wcslen(x);
+  if (a == 0)
+    clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
+}
+
+extern wchar_t global_str[];
+void wcslen_global(void) {
+  size_t a = wcslen(global_str);
+  size_t b = wcslen(global_str);
+  if (a == 0) {
+    clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
+    // Make sure clang_analyzer_eval does not invalidate globals.
+    clang_analyzer_eval(wcslen(global_str) == 0); // expected-warning{{TRUE}}
+  }
+
+  // Call a function with unknown effects, which should invalidate globals.
+  use_string(0);
+
+  size_t c = wcslen(global_str);
+  if (a == 0)
+    clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
+}
+
+void wcslen_indirect(wchar_t *x) {
+  size_t a = wcslen(x);
+  wchar_t *p = x;
+  wchar_t **p2 = &p;
+  size_t b = wcslen(x);
+  if (a == 0)
+    clang_analyzer_eval(b == 0); // expected-warning{{TRUE}}
+
+  extern void use_string_ptr(wchar_t*const*);
+  use_string_ptr(p2);
+
+  size_t c = wcslen(x);
+  if (a == 0)
+    clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
+}
+
+void wcslen_indirect2(wchar_t *x) {
+  size_t a = wcslen(x);
+  wchar_t *p = x;
+  wchar_t **p2 = &p;
+  extern void use_string_ptr2(wchar_t**);
+  use_string_ptr2(p2);
+
+  size_t c = wcslen(x);
+  if (a == 0)
+    clang_analyzer_eval(c == 0); // expected-warning{{UNKNOWN}}
+}
+
+void wcslen_liveness(const wchar_t *x) {
+  if (wcslen(x) < 5)
+    return;
+  clang_analyzer_eval(wcslen(x) < 5); // expected-warning{{FALSE}}
+}
+
+
+size_t wcslenWrapper(const wchar_t *str) {
+  return wcslen(str);
+}
+
+extern void invalidate(wchar_t *s);
+
+void testwcslenCallee(void) {
+  wchar_t str[42];
+  invalidate(str);
+  size_t lenBefore = wcslenWrapper(str);
+  invalidate(str);
+  size_t lenAfter = wcslenWrapper(str);
+  clang_analyzer_eval(lenBefore == lenAfter); // expected-warning{{UNKNOWN}}
+}
+
+//===----------------------------------------------------------------------===
+// wcsnlen()
+//===----------------------------------------------------------------------===
+
+size_t wcsnlen(const wchar_t *s, size_t maxlen);
+
+void wcsnlen_constant0(void) {
+  clang_analyzer_eval(wcsnlen(L"123", 10) == 3); // expected-warning{{TRUE}}
+}
+
+void wcsnlen_constant1(void) {
+  const wchar_t *a = L"123";
+  clang_analyzer_eval(wcsnlen(a, 10) == 3); // expected-warning{{TRUE}}
+}
+
+void wcsnlen_constant2(char x) {
+  wchar_t a[] = L"123";
+  clang_analyzer_eval(wcsnlen(a, 10) == 3); // expected-warning{{TRUE}}
+  a[0] = x;
+  clang_analyzer_eval(wcsnlen(a, 10) == 3); // expected-warning{{UNKNOWN}}
+}
+
+void wcsnlen_constant4(void) {
+  clang_analyzer_eval(wcsnlen(L"123456", 3) == 3); // expected-warning{{TRUE}}
+}
+
+void wcsnlen_constant5(void) {
+  const wchar_t *a = L"123456";
+  clang_analyzer_eval(wcsnlen(a, 3) == 3); // expected-warning{{TRUE}}
+}
+
+void wcsnlen_constant6(char x) {
+  wchar_t a[] = L"123456";
+  clang_analyzer_eval(wcsnlen(a, 3) == 3); // expected-warning{{TRUE}}
+  a[0] = x;
+  clang_analyzer_eval(wcsnlen(a, 3) == 3); // expected-warning{{UNKNOWN}}
+}
+
+size_t wcsnlen_null(void) {
+  return wcsnlen(0, 3); // expected-warning{{Null pointer passed as 1st argument to string length function}}
+}
+
+size_t wcsnlen_fn(void) {
+  return wcsnlen((wchar_t*)&wcsnlen_fn, 3); // expected-warning{{Argument to string length function is the address of the function 'wcsnlen_fn', which is not a null-terminated string}}
+}
+
+size_t wcsnlen_nonloc(void) {
+label:
+  return wcsnlen((wchar_t*)&&label, 3); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}}
+}
+
+void wcsnlen_zero(void) {
+  clang_analyzer_eval(wcsnlen(L"abc", 0) == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(wcsnlen(0, 0) == 0); // expected-warning{{TRUE}}
+}
+
+size_t wcsnlen_compound_literal(void) {
+  // This used to crash because we don't model the string lengths of
+  // compound literals.
+  return wcsnlen((wchar_t[]) { 'a', 'b', 0 }, 1);
+}
+
+size_t wcsnlen_unknown_limit(float f) {
+  // This used to crash because we don't model the integer values of floats.
+  return wcsnlen(L"abc", (int)f);
+}
+
+void wcsnlen_is_not_wcslen(wchar_t *x) {
+  clang_analyzer_eval(wcsnlen(x, 10) == wcslen(x)); // expected-warning{{UNKNOWN}}
+}
+
+void wcsnlen_at_limit(wchar_t *x) {
+  size_t len = wcsnlen(x, 10);
+  clang_analyzer_eval(len <= 10); // expected-warning{{TRUE}}
+  clang_analyzer_eval(len == 10); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(len < 10); // expected-warning{{UNKNOWN}}
+}
+
+void wcsnlen_at_actual(size_t limit) {
+  size_t len = wcsnlen(L"abc", limit);
+  clang_analyzer_eval(len <= 3); // expected-warning{{TRUE}}
+  // This is due to eager assertion in wcsnlen.
+  if (limit == 0) {
+    clang_analyzer_eval(len == 0); // expected-warning{{TRUE}}
+  } else {
+    clang_analyzer_eval(len == 3); // expected-warning{{UNKNOWN}}
+    clang_analyzer_eval(len < 3); // expected-warning{{UNKNOWN}}
+  }
+}
+
+//===----------------------------------------------------------------------===
+// other tests
+//===----------------------------------------------------------------------===
+
+static const wchar_t w_str[] = L"Hello world";
+
+void wmemcpy_sizeof(void) {
+  wchar_t a[32];
+  wmemcpy(a, w_str, sizeof(w_str) / sizeof(w_str[0]));
+  wmemcpy(a, w_str, (sizeof(w_str) / sizeof(w_str[0])) + 1); // expected-warning {{Memory copy function accesses out-of-bound array element}}
+}
+
+void wmemcpy_wcslen(void) {
+  wchar_t a[32];
+  // FIXME: This should work with 'w_str' instead of 'w_str1'
+  const wchar_t w_str1[] = L"Hello world";
+  wmemcpy(a, w_str1, wcslen(w_str1) + 1);
+  wmemcpy(a, w_str1, wcslen(w_str1) + 2); // expected-warning {{Memory copy function accesses out-of-bound array element}}
+}
Index: clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -26,9 +26,11 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
+#include <functional>
 
 using namespace clang;
 using namespace ento;
+using namespace std::placeholders;
 
 namespace {
 struct AnyArgExpr {
@@ -118,10 +120,14 @@
                        const LocationContext *LCtx,
                        const CallEvent *Call) const;
 
-  typedef void (CStringChecker::*FnCheck)(CheckerContext &,
-                                          const CallExpr *) const;
+  using FnCheck = std::function<void(const CStringChecker *, CheckerContext &,
+                                     const CallExpr *)>;
+
   CallDescriptionMap<FnCheck> Callbacks = {
-      {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy},
+      {{CDF_MaybeBuiltin, "memcpy", 3},
+       std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, false)},
+      {{CDF_MaybeBuiltin, "wmemcpy", 3},
+       std::bind(&CStringChecker::evalMemcpy, _1, _2, _3, true)},
       {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
       {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
       {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
@@ -135,7 +141,9 @@
       {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
       {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
       {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
+      {{CDF_MaybeBuiltin, "wcslen", 1}, &CStringChecker::evalstrLength},
       {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
+      {{CDF_MaybeBuiltin, "wcsnlen", 2}, &CStringChecker::evalstrnLength},
       {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
       {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
       {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
@@ -152,14 +160,14 @@
       StdCopyBackward{{"std", "copy_backward"}, 3};
 
   FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) const;
-  void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
+  void evalMemcpy(CheckerContext &C, const CallExpr *CE, bool IsWide) 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 CallExpr *CE,
                       ProgramStateRef state, SizeArgExpr Size,
                       DestinationArgExpr Dest, SourceArgExpr Source,
-                      bool Restricted, bool IsMempcpy) const;
+                      bool Restricted, bool IsMempcpy, bool IsWide) const;
 
   void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
 
@@ -240,13 +248,14 @@
                                AnyArgExpr Arg, SVal l) const;
   ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state,
                                 AnyArgExpr Buffer, SVal Element,
-                                AccessKind Access) const;
+                                AccessKind Access, bool IsWide = false) const;
   ProgramStateRef CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
                                     AnyArgExpr Buffer, SizeArgExpr Size,
-                                    AccessKind Access) const;
+                                    AccessKind Access,
+                                    bool IsWide = false) const;
   ProgramStateRef CheckOverlap(CheckerContext &C, ProgramStateRef state,
                                SizeArgExpr Size, AnyArgExpr First,
-                               AnyArgExpr Second) const;
+                               AnyArgExpr Second, bool IsWide = false) const;
   void emitOverlapBug(CheckerContext &C,
                       ProgramStateRef state,
                       const Stmt *First,
@@ -329,7 +338,8 @@
 ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
                                               ProgramStateRef state,
                                               AnyArgExpr Buffer, SVal Element,
-                                              AccessKind Access) const {
+                                              AccessKind Access,
+                                              bool IsWide) const {
 
   // If a previous check has failed, propagate the failure.
   if (!state)
@@ -344,17 +354,36 @@
   if (!ER)
     return state;
 
-  if (ER->getValueType() != C.getASTContext().CharTy)
-    return state;
+  SValBuilder &svalBuilder = C.getSValBuilder();
+  ASTContext &Ctx = svalBuilder.getContext();
+
+  // Get the index of the accessed element.
+  NonLoc Idx = ER->getIndex();
+
+  if (!IsWide) {
+    if (ER->getValueType() != Ctx.CharTy)
+      return state;
+  } else {
+    if (ER->getValueType() != Ctx.WideCharTy)
+      return state;
+
+    QualType SizeTy = Ctx.getSizeType();
+    NonLoc WideSize =
+        svalBuilder
+            .makeIntVal(Ctx.getTypeSizeInChars(Ctx.WideCharTy).getQuantity(),
+                        SizeTy)
+            .castAs<NonLoc>();
+    SVal Offset = svalBuilder.evalBinOpNN(state, BO_Mul, Idx, WideSize, SizeTy);
+    if (Offset.isUnknown())
+      return state;
+    Idx = Offset.castAs<NonLoc>();
+  }
 
   // Get the size of the array.
   const auto *superReg = cast<SubRegion>(ER->getSuperRegion());
   DefinedOrUnknownSVal Size =
       getDynamicExtent(state, superReg, C.getSValBuilder());
 
-  // Get the index of the accessed element.
-  DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
-
   ProgramStateRef StInBound, StOutBound;
   std::tie(StInBound, StOutBound) = state->assumeInBoundDual(Idx, Size);
   if (StOutBound && !StInBound) {
@@ -385,11 +414,10 @@
   return StInBound;
 }
 
-ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
-                                                  ProgramStateRef State,
-                                                  AnyArgExpr Buffer,
-                                                  SizeArgExpr Size,
-                                                  AccessKind Access) const {
+ProgramStateRef
+CStringChecker::CheckBufferAccess(CheckerContext &C, ProgramStateRef State,
+                                  AnyArgExpr Buffer, SizeArgExpr Size,
+                                  AccessKind Access, bool IsWide) const {
   // If a previous check has failed, propagate the failure.
   if (!State)
     return nullptr;
@@ -398,7 +426,7 @@
   ASTContext &Ctx = svalBuilder.getContext();
 
   QualType SizeTy = Size.Expression->getType();
-  QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+  QualType PtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
 
   // Check that the first buffer is non-null.
   SVal BufVal = C.getSVal(Buffer.Expression);
@@ -432,7 +460,7 @@
 
     SVal BufEnd =
         svalBuilder.evalBinOpLN(State, BO_Add, *BufLoc, LastOffset, PtrTy);
-    State = CheckLocation(C, State, Buffer, BufEnd, Access);
+    State = CheckLocation(C, State, Buffer, BufEnd, Access, IsWide);
 
     // If the buffer isn't large enough, abort.
     if (!State)
@@ -446,7 +474,8 @@
 ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
                                              ProgramStateRef state,
                                              SizeArgExpr Size, AnyArgExpr First,
-                                             AnyArgExpr Second) const {
+                                             AnyArgExpr Second,
+                                             bool IsWide) const {
   if (!Filter.CheckCStringBufferOverlap)
     return state;
 
@@ -525,7 +554,7 @@
   // Convert the first buffer's start address to char*.
   // Bail out if the cast fails.
   ASTContext &Ctx = svalBuilder.getContext();
-  QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+  QualType CharPtrTy = Ctx.getPointerType(IsWide ? Ctx.WideCharTy : Ctx.CharTy);
   SVal FirstStart =
       svalBuilder.evalCast(*firstLoc, CharPtrTy, First.Expression->getType());
   Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
@@ -1161,7 +1190,7 @@
                                     ProgramStateRef state, SizeArgExpr Size,
                                     DestinationArgExpr Dest,
                                     SourceArgExpr Source, bool Restricted,
-                                    bool IsMempcpy) const {
+                                    bool IsMempcpy, bool IsWide) const {
   CurrentFunctionDescription = "memory copy function";
 
   // See if the size argument is zero.
@@ -1204,11 +1233,11 @@
       return;
 
     // Ensure the accesses are valid and that the buffers do not overlap.
-    state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write);
-    state = CheckBufferAccess(C, state, Source, Size, AccessKind::read);
+    state = CheckBufferAccess(C, state, Dest, Size, AccessKind::write, IsWide);
+    state = CheckBufferAccess(C, state, Source, Size, AccessKind::read, IsWide);
 
     if (Restricted)
-      state = CheckOverlap(C, state, Size, Dest, Source);
+      state = CheckOverlap(C, state, Size, Dest, Source, IsWide);
 
     if (!state)
       return;
@@ -1258,7 +1287,8 @@
   }
 }
 
-void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
+void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE,
+                                bool IsWide) const {
   // void *memcpy(void *restrict dst, const void *restrict src, size_t n);
   // The return value is the address of the destination buffer.
   DestinationArgExpr Dest = {CE->getArg(0), 0};
@@ -1269,7 +1299,8 @@
 
   constexpr bool IsRestricted = true;
   constexpr bool IsMempcpy = false;
-  evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy);
+  evalCopyCommon(C, CE, State, Size, Dest, Src, IsRestricted, IsMempcpy,
+                 IsWide);
 }
 
 void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
@@ -1281,7 +1312,8 @@
 
   constexpr bool IsRestricted = true;
   constexpr bool IsMempcpy = true;
-  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+                 false);
 }
 
 void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
@@ -1293,7 +1325,8 @@
 
   constexpr bool IsRestricted = false;
   constexpr bool IsMempcpy = false;
-  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+                 false);
 }
 
 void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
@@ -1304,7 +1337,8 @@
 
   constexpr bool IsRestricted = false;
   constexpr bool IsMempcpy = false;
-  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy);
+  evalCopyCommon(C, CE, C.getState(), Size, Dest, Src, IsRestricted, IsMempcpy,
+                 false);
 }
 
 void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
@@ -2336,7 +2370,7 @@
 
   // Check and evaluate the call.
   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
-  (this->*Callback)(C, CE);
+  Callback(this, C, CE);
 
   // If the evaluate call resulted in no change, chain to the next eval call
   // handler.
Index: clang/docs/analyzer/checkers.rst
===================================================================
--- clang/docs/analyzer/checkers.rst
+++ clang/docs/analyzer/checkers.rst
@@ -952,7 +952,7 @@
 unix.cstring.NullArg (C)
 """""""""""""""""""""""""
 Check for null pointers being passed as arguments to C string functions:
-``strlen, strnlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcasecmp, strncasecmp``.
+``strlen, strnlen, strcpy, strncpy, strcat, strncat, strcmp, strncmp, strcasecmp, strncasecmp, wcslen, wcsnlen``.
 
 .. code-block:: c
 
@@ -2699,7 +2699,7 @@
 
 alpha.unix.cstring.BufferOverlap (C)
 """"""""""""""""""""""""""""""""""""
-Checks for overlap in two buffer arguments. Applies to:  ``memcpy, mempcpy``.
+Checks for overlap in two buffer arguments. Applies to:  ``memcpy, mempcpy, wmemcpy``.
 
 .. code-block:: c
 
@@ -2712,7 +2712,7 @@
 
 alpha.unix.cstring.NotNullTerminated (C)
 """"""""""""""""""""""""""""""""""""""""
-Check for arguments which are not null-terminated strings; applies to: ``strlen, strnlen, strcpy, strncpy, strcat, strncat``.
+Check for arguments which are not null-terminated strings; applies to: ``strlen, strnlen, strcpy, strncpy, strcat, strncat, wcslen, wcsnlen``.
 
 .. code-block:: c
 
@@ -2724,15 +2724,24 @@
 
 alpha.unix.cstring.OutOfBounds (C)
 """"""""""""""""""""""""""""""""""
-Check for out-of-bounds access in string functions; applies to:`` strncopy, strncat``.
+Check for out-of-bounds access in string functions, such as:
+``memcpy, bcopy, strcpy, strncpy, strcat, strncat, memmove, memcmp, memset`` and more.
 
-This check also applies to string literals, except there is a known bug in that
-the analyzer cannot detect embedded NULL characters.
+This check also works with string literals, except there is a known bug in that
+the analyzer cannot detect embedded NULL characters when determining the string length.
 
 .. code-block:: c
 
- void test() {
-   int y = strlen((char *)&test); // warn
+ void test1() {
+   const char str[] = "Hello world";
+   char buffer[] = "Hello world";
+   memcpy(buffer, str, sizeof(str) + 1); // warn
+ }
+
+ void test2() {
+   const char str[] = "Hello world";
+   char buffer[] = "Helloworld";
+   memcpy(buffer, str, sizeof(str)); // warn
  }
 
 .. _alpha-unix-cstring-UninitializedRead:
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to