Index: include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -244,6 +244,11 @@ ProgramStateRef bindDefault(SVal loc, SVal V, const LocationContext *LCtx) const; + // Create a new state by invalidating the given region and binging the new + // value with default binding. + ProgramStateRef overwriteRegion(SVal Loc, SVal V, + const LocationContext *LCtx) const; + ProgramStateRef killBinding(Loc LV) const; /// \brief Returns the state with bindings for the given regions Index: include/clang/StaticAnalyzer/Core/PathSensitive/Store.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/Store.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/Store.h @@ -109,6 +109,10 @@ virtual StoreRef BindDefault(Store store, const MemRegion *R, SVal V); + // Clears out the specified region from the store and bind a new value with + // default binding. + virtual StoreRef OverwriteRegion(Store store, const MemRegion *R, SVal V); + /// \brief Create a new store with the specified binding removed. /// \param ST the original store, that is the basis for the new store. /// \param L the location whose binding should be removed. Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -2013,8 +2013,13 @@ CurrentFunctionDescription = "memory set function"; const Expr *Mem = CE->getArg(0); + const Expr *CharE = CE->getArg(1); const Expr *Size = CE->getArg(2); + ProgramStateRef State = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); + QualType CmpType = svalBuilder.getConditionType(); // See if the size argument is zero. const LocationContext *LCtx = C.getLocationContext(); @@ -2023,11 +2028,14 @@ ProgramStateRef StateZeroSize, StateNonZeroSize; std::tie(StateZeroSize, StateNonZeroSize) = - assumeZero(C, State, SizeVal, SizeTy); + assumeZero(C, State, SizeVal, SizeTy); // Get the value of the memory area. SVal MemVal = State->getSVal(Mem, LCtx); + // Get the value for writing. + SVal CharVal = State->getSVal(CharE, LCtx); + // If the size is zero, there won't be any actual memory access, so // just bind the return value to the Mem buffer and return. if (StateZeroSize && !StateNonZeroSize) { @@ -2045,11 +2053,79 @@ State = CheckBufferAccess(C, State, Size, Mem); if (!State) return; - State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), - /*IsSourceBuffer*/false, Size); - if (!State) + + const MemRegion *MR = MemVal.getAsRegion(); + if (!MR) return; + // Get the offset and the base region to figure out whether the offset of + // buffer is 0. + RegionOffset Offset = MR->getAsOffset(); + const MemRegion *BR = Offset.getRegion(); + + Optional SizeNL = SizeVal.getAs(); + + // void *memset(void *dest, int ch, size_t count); + // For now we can only handle the case of offset is 0 and concrete char value. + if (Offset.isValid() && !Offset.hasSymbolicOffset() && + Offset.getOffset() == 0 && CharVal.isConstant() && SizeNL) { + // Get the base region's extent. + auto *SubReg = cast(BR); + DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder); + + ProgramStateRef StateWholeReg, StateNotWholeReg; + std::tie(StateWholeReg, StateNotWholeReg) = + State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL)); + + if (StateWholeReg && !StateNotWholeReg) { + // If the 'memset()' acts on the whole region of destination buffer, + // bind the second argument's value to the destination buffer with + // 'default binding'. + State = State->overwriteRegion(svalBuilder.makeLoc(BR), CharVal, + C.getLocationContext()); + } else { + // If the destination buffer's extent is not equal to the value of + // third argument, just invalidate buffer. + // + // FIXME: This is a hack, 'InvalidateBuffer()' calls + // 'invalidateRegions()', which will remove the pair + // in CStringLength map. So calls 'InvalidateBuffer()' after getting + // old string length and before setting the new string length. + State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), + /*IsSourceBuffer*/ false, Size); + } + + ProgramStateRef StateNullChar, StateNonNullChar; + std::tie(StateNullChar, StateNonNullChar) = + assumeZero(C, State, CharVal, Ctx.UnsignedCharTy); + + if (StateNullChar && !StateNonNullChar) { + // If the value of the second arguement of 'memset()' is zero, set the + // string length of destination buffer to 0 directly. + State = setCStringLength(StateNullChar, MR, + svalBuilder.makeZeroVal(Ctx.getSizeType())); + } else if (!StateNullChar && StateNonNullChar) { + SVal NewStrLen = svalBuilder.getMetadataSymbolVal( + CStringChecker::getTag(), MR, CE, Ctx.getSizeType(), + C.getLocationContext(), C.blockCount()); + + // If the value of second argument is not zero, then the string length + // is at least the size argument. + SVal NewStrLenGESize = svalBuilder.evalBinOp(StateNonNullChar, BO_GE, + NewStrLen, SizeVal, CmpType); + + State = setCStringLength( + StateNonNullChar->assume( + NewStrLenGESize.castAs(), true), + MR, NewStrLen); + } + } else { + // If the offset is not zero and char value is not concrete, we can do + // nothing but invalidate the buffer. + State = InvalidateBuffer(C, State, Mem, C.getSVal(Mem), + /*IsSourceBuffer*/ false, Size); + } + State = State->BindExpr(CE, LCtx, MemVal); C.addTransition(State); } Index: lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- lib/StaticAnalyzer/Core/ProgramState.cpp +++ lib/StaticAnalyzer/Core/ProgramState.cpp @@ -138,6 +138,18 @@ new_state; } +ProgramStateRef +ProgramState::overwriteRegion(SVal Loc, SVal V, + const LocationContext *LCtx) const { + ProgramStateManager &Mgr = getStateManager(); + const MemRegion *R = Loc.castAs().getRegion(); + const StoreRef &NewStore = Mgr.StoreMgr->OverwriteRegion(getStore(), R, V); + ProgramStateRef NewState = makeWithStore(NewStore); + return Mgr.getOwningEngine() + ? Mgr.getOwningEngine()->processRegionChange(NewState, R, LCtx) + : NewState; +} + typedef ArrayRef RegionList; typedef ArrayRef ValueList; Index: lib/StaticAnalyzer/Core/RegionStore.cpp =================================================================== --- lib/StaticAnalyzer/Core/RegionStore.cpp +++ lib/StaticAnalyzer/Core/RegionStore.cpp @@ -441,6 +441,16 @@ return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); } + // Clears out the specified region from the store and bind a new value with + // default binding. + StoreRef OverwriteRegion(Store store, const MemRegion *R, SVal V) override { + RegionBindingsRef B = getRegionBindings(store); + B = removeSubRegionBindings(B, cast(R)) + .addBinding(BindingKey::Make(R, BindingKey::Default), V); + + return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); + } + /// Attempt to extract the fields of \p LCV and bind them to the struct region /// \p R. /// @@ -1705,7 +1715,7 @@ val.getAs()) return val; - llvm_unreachable("Unknown default value"); + return val; } return None; Index: lib/StaticAnalyzer/Core/Store.cpp =================================================================== --- lib/StaticAnalyzer/Core/Store.cpp +++ lib/StaticAnalyzer/Core/Store.cpp @@ -69,6 +69,10 @@ return StoreRef(store, *this); } +StoreRef StoreManager::OverwriteRegion(Store store, const MemRegion *R, SVal V) { + return StoreRef(store, *this); +} + const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, QualType T) { NonLoc idx = svalBuilder.makeZeroArrayIndex(); Index: test/Analysis/bstring.cpp =================================================================== --- test/Analysis/bstring.cpp +++ test/Analysis/bstring.cpp @@ -1,7 +1,8 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s -// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s -// RUN: %clang_analyze_cc1 -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s -// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,unix.Malloc,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,unix.Malloc,debug.ExprInspection -analyzer-store=region -verify %s +// RUN: %clang_analyze_cc1 -DSUPPRESS_OUT_OF_BOUND -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring.BufferOverlap,alpha.unix.cstring.NotNullTerminated,debug.ExprInspection -analyzer-store=region -verify %s #include "Inputs/system-header-simulator-cxx.h" #include "Inputs/system-header-simulator-for-malloc.h" @@ -77,3 +78,118 @@ unsigned *f; }; } + +void *memset(void *dest, int ch, std::size_t count); +namespace memset_non_pod { +class Base { +public: + int b_mem; + Base() : b_mem(1) {} +}; + +class Derived : public Base { +public: + int d_mem; + Derived() : d_mem(2) {} +}; + +void memset1_inheritance() { + Derived d; + memset(&d, 0, sizeof(Derived)); + clang_analyzer_eval(d.b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(d.d_mem == 0); // expected-warning{{TRUE}} +} + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset2_inheritance_field() { + Derived d; + memset(&d.d_mem, 0, sizeof(Derived)); + clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}} +} + +void memset3_inheritance_field() { + Derived d; + memset(&d.b_mem, 0, sizeof(Derived)); + clang_analyzer_eval(d.b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(d.d_mem == 0); // expected-warning{{TRUE}} +} +#endif + +void memset4_array_nonpod_object() { + Derived array[10]; + clang_analyzer_eval(array[1].b_mem == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(array[1].d_mem == 2); // expected-warning{{UNKNOWN}} + memset(&array[1], 0, sizeof(Derived)); + clang_analyzer_eval(array[1].b_mem == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(array[1].d_mem == 0); // expected-warning{{UNKNOWN}} +} + +void memset5_array_nonpod_object() { + Derived array[10]; + clang_analyzer_eval(array[1].b_mem == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(array[1].d_mem == 2); // expected-warning{{UNKNOWN}} + memset(array, 0, sizeof(array)); + clang_analyzer_eval(array[1].b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(array[1].d_mem == 0); // expected-warning{{TRUE}} +} + +void memset6_new_array_nonpod_object() { + Derived *array = new Derived[10]; + clang_analyzer_eval(array[2].b_mem == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(array[2].d_mem == 2); // expected-warning{{UNKNOWN}} + memset(array, 0, 10 * sizeof(Derived)); + clang_analyzer_eval(array[2].b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(array[2].d_mem == 0); // expected-warning{{TRUE}} + delete [] array; +} + +void memset7_placement_new() { + Derived *d = new Derived(); + clang_analyzer_eval(d->b_mem == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(d->d_mem == 2); // expected-warning{{TRUE}} + + memset(d, 0, sizeof(Derived)); + clang_analyzer_eval(d->b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(d->d_mem == 0); // expected-warning{{TRUE}} + + Derived *d1 = new (d) Derived(); + clang_analyzer_eval(d1->b_mem == 1); // expected-warning{{TRUE}} + clang_analyzer_eval(d1->d_mem == 2); // expected-warning{{TRUE}} + + memset(d1, 0, sizeof(Derived)); + clang_analyzer_eval(d->b_mem == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(d->d_mem == 0); // expected-warning{{TRUE}} +} + +class BaseVirtual { +public: + int b_mem; + virtual int get() { return 1; } +}; + +class DerivedVirtual : public BaseVirtual { +public: + int d_mem; +}; + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset8_virtual_inheritance_field() { + DerivedVirtual d; + memset(&d.b_mem, 0, sizeof(Derived)); + clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}} +} +#endif +} // namespace memset_non_pod + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset1_new_array() { + int *array = new int[10]; + memset(array, 0, 10 * sizeof(int)); + clang_analyzer_eval(array[2] == 0); // expected-warning{{TRUE}} + memset(array + 1, 'a', 10 * sizeof(9)); + clang_analyzer_eval(array[2] == 0); // expected-warning{{UNKNOWN}} + delete [] array; +} +#endif Index: test/Analysis/null-deref-ps-region.c =================================================================== --- test/Analysis/null-deref-ps-region.c +++ test/Analysis/null-deref-ps-region.c @@ -22,7 +22,7 @@ void foo() { int *x = malloc(sizeof(int)); memset(x, 0, sizeof(int)); - int n = 1 / *x; // FIXME: no-warning + int n = 1 / *x; // expected-warning {{Division by zero}} free(x); } Index: test/Analysis/string.c =================================================================== --- test/Analysis/string.c +++ test/Analysis/string.c @@ -1,7 +1,8 @@ -// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_analyze_cc1 -DVARIANT -analyzer-checker=core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -DVARIANT -analyzer-checker=alpha.security.taint,core,unix.cstring,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_analyze_cc1 -DVARIANT -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_analyze_cc1 -DUSE_BUILTINS -DVARIANT -analyzer-checker=alpha.security.taint,core,unix.cstring,unix.Malloc,alpha.unix.cstring,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_analyze_cc1 -DSUPPRESS_OUT_OF_BOUND -analyzer-checker=core,unix.cstring,unix.Malloc,alpha.unix.cstring.BufferOverlap,alpha.unix.cstring.NotNullTerminated,debug.ExprInspection -analyzer-store=region -Wno-null-dereference -verify %s //===----------------------------------------------------------------------=== // Declarations @@ -1159,6 +1160,248 @@ clang_analyzer_eval(str[1] == 'b'); // expected-warning{{UNKNOWN}} } +//===----------------------------------------------------------------------=== +// memset() +//===----------------------------------------------------------------------=== + +void *memset( void *dest, int ch, size_t count ); + +void* malloc(size_t size); +void free(void*); + +void memset1_char_array_null() { + char str[] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + memset(str, '\0', 2); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} +} + +void memset2_char_array_null() { + char str[] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + memset(str, '\0', strlen(str) + 1); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(str[2] == 0); // expected-warning{{TRUE}} +} + +void memset3_char_malloc_null() { + char *str = (char *)malloc(10 * sizeof(char)); + memset(str + 1, '\0', 8); + clang_analyzer_eval(str[1] == 0); // expected-warning{{UNKNOWN}} + free(str); +} + +void memset4_char_malloc_null() { + char *str = (char *)malloc(10 * sizeof(char)); + //void *str = malloc(10 * sizeof(char)); + memset(str, '\0', 10); + clang_analyzer_eval(str[1] == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} + free(str); +} + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset5_char_malloc_overflow_null() { + char *str = (char *)malloc(10 * sizeof(char)); + memset(str, '\0', 12); + clang_analyzer_eval(str[1] == 0); // expected-warning{{UNKNOWN}} + free(str); +} +#endif + +void memset6_char_array_nonnull() { + char str[] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + memset(str, '0', 2); + clang_analyzer_eval(str[0] == 'a'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{UNKNOWN}} +} + +void memset7_char_array_nonnull() { + char str[5] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + memset(str, '0', 5); + clang_analyzer_eval(str[0] == '0'); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) >= 5); // expected-warning{{TRUE}} +} + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset8_char_array_nonnull() { + char str[5] = "abcd"; + clang_analyzer_eval(strlen(str) == 4); // expected-warning{{TRUE}} + memset(str, '0', 10); + clang_analyzer_eval(str[0] != '0'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(strlen(str) >= 10); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) < 10); // expected-warning{{FALSE}} +} +#endif + +struct POD_memset { + int num; + char c; +}; + +void memset10_struct() { + struct POD_memset pod; + char *str = (char *)&pod; + pod.num = 1; + pod.c = 1; + clang_analyzer_eval(pod.num == 0); // expected-warning{{FALSE}} + memset(str, 0, sizeof(struct POD_memset)); + clang_analyzer_eval(pod.num == 0); // expected-warning{{TRUE}} +} + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset11_struct_field() { + struct POD_memset pod; + pod.num = 1; + pod.c = '1'; + memset(&pod.num, 0, sizeof(struct POD_memset)); + + clang_analyzer_eval(pod.num == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(pod.c == '\0'); // expected-warning{{TRUE}} +} + +void memset12_struct_field() { + struct POD_memset pod; + pod.num = 1; + pod.c = '1'; + memset(&pod.c, 0, sizeof(struct POD_memset)); + clang_analyzer_eval(pod.num == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(pod.c == 0); // expected-warning{{UNKNOWN}} +} + +union U_memset { + int i; + double d; + char c; +}; + +void memset13_union_field() { + union U_memset u; + u.i = 5; + memset(&u.i, '\0', sizeof(union U_memset)); + // Note: This should be TRUE, analyzer can't handle union perfectly now. + clang_analyzer_eval(u.d == 0); // expected-warning{{UNKNOWN}} +} +#endif + +void memset14_region_cast() { + char *str = (char*)malloc(10 * sizeof(int)); + int *array = (int*)str; + memset(array, 0, 10 * sizeof(int)); + clang_analyzer_eval(str[10] == '\0'); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen((char*)array) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} + free(str); +} + +void memset15_region_cast() { + char *str = (char*)malloc(10 * sizeof(int)); + int *array = (int*)str; + memset(array, 0, 5 * sizeof(int)); + clang_analyzer_eval(str[10] == '\0'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(strlen((char*)array) == 0); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{TRUE}} + free(str); +} + + +void memset16_region_cast() { + char *str = (char*)malloc(10 * sizeof(int)); + int *array = (int*)str; + memset(array, '0', 10 * sizeof(int)); + clang_analyzer_eval(str[10] == '0'); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen((char*)array) >= 10 * sizeof(int)); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) >= 10 * sizeof(int)); // expected-warning{{TRUE}} + free(str); +} + +#ifdef SUPPRESS_OUT_OF_BOUND +void memset17_region_cast() { + char *str = (char*)malloc(10 * sizeof(int)); + int *array = (int*)str; + memset(array, '0', 12 * sizeof(int)); + clang_analyzer_eval(str[10] == '0'); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(strlen((char*)array) >= 12 * sizeof(int)); // expected-warning{{TRUE}} + clang_analyzer_eval(strlen(str) >= 12 * sizeof(int)); // expected-warning{{TRUE}} + free(str); +} + +void memset18_memset_multiple_times() { + char *str = (char *)malloc(10 * sizeof(char)); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{UNKNOWN}} + + memset(str + 2, '\0', 10 * sizeof(char)); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(str[1] == '\0'); // expected-warning{{UNKNOWN}} + + memset(str, '0', 10 * sizeof(char)); + clang_analyzer_eval(strlen(str) >= 10); // expected-warning{{TRUE}} + clang_analyzer_eval(str[1] == '0'); // expected-warning{{TRUE}} + + free(str); +} + +void memset19_memset_multiple_times() { + char *str = (char *)malloc(10 * sizeof(char)); + clang_analyzer_eval(strlen(str) == 0); // expected-warning{{UNKNOWN}} + + memset(str, '0', 10 * sizeof(char)); + clang_analyzer_eval(strlen(str) >= 10); // expected-warning{{TRUE}} + clang_analyzer_eval(str[1] == '0'); // expected-warning{{TRUE}} + + memset(str + 2, '\0', 10 * sizeof(char)); + clang_analyzer_eval(strlen(str) >= 10); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(str[1] == '0'); // expected-warning{{UNKNOWN}} + + free(str); +} +#endif + +int memset20_scalar() { + int *x = malloc(sizeof(int)); + *x = 10; + memset(x, 0, sizeof(int)); + int num = 1 / *x; // expected-warning{{Division by zero}} + free(x); + return num; +} + +int memset21_scalar() { + int *x = malloc(sizeof(int)); + memset(x, 0, 1); + int num = 1 / *x; + free(x); + return num; +} + +void memset22_array() { + int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + clang_analyzer_eval(array[1] == 2); // expected-warning{{TRUE}} + memset(array, 0, sizeof(array)); + clang_analyzer_eval(array[1] == 0); // expected-warning{{TRUE}} +} + + +void memset23_array_pod_object() { + struct POD_memset array[10]; + array[1].num = 10; + array[1].c = 'c'; + clang_analyzer_eval(array[1].num == 10); // expected-warning{{TRUE}} + memset(&array[1], 0, sizeof(struct POD_memset)); + clang_analyzer_eval(array[1].num == 0); // expected-warning{{UNKNOWN}} +} + +void memset24_array_pod_object() { + struct POD_memset array[10]; + array[1].num = 10; + array[1].c = 'c'; + clang_analyzer_eval(array[1].num == 10); // expected-warning{{TRUE}} + memset(array, 0, sizeof(array)); + clang_analyzer_eval(array[1].num == 0); // expected-warning{{TRUE}} +} + //===----------------------------------------------------------------------=== // FIXMEs //===----------------------------------------------------------------------=== @@ -1186,3 +1429,15 @@ // This time, we know that y fits in x anyway. clang_analyzer_eval(strlen(x) <= 3); // expected-warning{{UNKNOWN}} } + +// The analyzer does not support binding a symbol with default binding. +void memset25_symbol(char c) { + char array[10] = {0}; + if (c > 10) + return; + + memset(array, c, 10); + + // This should be TRUE. + clang_analyzer_eval(array[4] <= 10); // expected-warning{{UNKNOWN}} +}