Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -522,6 +522,10 @@ HelpText<"Check for calls to blocking functions inside a critical section">, DescFile<"BlockInCriticalSectionChecker.cpp">; +def TimeChecker : Checker<"Time">, + HelpText<"Check time.h related functionality, especially, in the context of the 'Year 2038 (aka: Y2K38) Problem'">, + DescFile<"TimeChecker.cpp">; + } // end "alpha.unix" let ParentPackage = CString in { Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -87,6 +87,7 @@ StreamChecker.cpp TaintTesterChecker.cpp TestAfterDivZeroChecker.cpp + TimeChecker.cpp TraversalChecker.cpp TrustNonnullChecker.cpp UndefBranchChecker.cpp Index: lib/StaticAnalyzer/Checkers/TimeChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/TimeChecker.cpp @@ -0,0 +1,216 @@ +//===-- TimeChecker.cpp -------------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for proper definition/use of time.h related functionality. +// +// Reference used: +// +// ISO/IEC 9899:TC3 Committee Draft - September 7, 2007 WG14/N1256 +// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf +// Section 7.23 Date and time +// +// The intention for this work is to audit user code for identifying potential +// issues in the light of the "Year 2038" (aka: "Y2K38") problem. +// +// Checks implemented: +// +// 0. Warn if the time_t type is not defined to be a signed integer type, +// at-least 64-bits in size, having file scope. (When the size of the time_t +// type is less than 64-bits, report the actual size in the warning). +// +// 1. Warn when a time_t value is cast to: +// a. A non-integer type +// b. An integer type of lesser width +// c. An unsigned integer type +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class TimeVisitor : public RecursiveASTVisitor { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext *AC; + + bool hasTimeTypeSynonym(QualType T); + +public: + explicit TimeVisitor(BugReporter &B, const CheckerBase *Checker, + AnalysisDeclContext *A) + : BR(B), Checker(Checker), AC(A) {} + + bool VisitCastExpr(const CastExpr *CE); + bool VisitTypedefNameDecl(const TypedefNameDecl *TD); +}; +} // end anonymous namespace + +namespace { +class TimeChecker : public Checker> { +public: + void checkASTDecl(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + TimeVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); + Visitor.TraverseDecl(const_cast(D)); + } +}; +} // end anonymous namespace + +/// Peel through layers of typedefs, to check if there's a time_t type in there +/// somewhere. +bool TimeVisitor::hasTimeTypeSynonym(QualType T) { + + const TypedefType *TDPtr = nullptr; + + while (!T.isNull() && (TDPtr = dyn_cast(T)) && + TDPtr->isSugared()) { + + if (T.getBaseTypeIdentifier() && + (T.getBaseTypeIdentifier()->isStr("time_t"))) + return true; + else + T = TDPtr->desugar(); + } + + return false; +} + +/// Audit cast-expressions on a time_t value. +bool TimeVisitor::VisitCastExpr(const CastExpr *CE) { + + SmallString<128> Buffer; + llvm::raw_svector_ostream Output(Buffer); + + if (!CE) + return true; + + ASTContext &Ctx = AC->getASTContext(); + const Expr *E = CE->getSubExpr(); + + if (!E) + return true; + + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + QualType OrigTy = Ctx.getCanonicalType(E->getType()); + + if (OrigTy.isNull() || ToTy.isNull()) + return true; + + if (!OrigTy->isIntegerType()) + return true; + + if (!hasTimeTypeSynonym(E->getType())) + return true; + + if (ToTy->hasIntegerRepresentation()) { + + if (Ctx.getIntWidth(CE->getType()) < Ctx.getIntWidth(E->getType())) { + + Output << "Y2K38 Issue: "; + Output << (dyn_cast(CE) ? "Explicit" : "Implicit"); + Output << " cast of time_t value to a type of lesser width"; + + SourceRange Sr[1] = {CE->getSourceRange()}; + PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport( + AC->getDecl(), Checker, + "Y2K38 Issue: Cast of time_t value to a type of lesser width", + categories::LogicError, Output.str(), Loc, Sr); + } + + if (!ToTy->hasSignedIntegerRepresentation()) { + + Output << "Y2K38 Issue: "; + Output << (dyn_cast(CE) ? "Explicit" : "Implicit"); + Output << " cast of time_t value to an unsigned type"; + + SourceRange Sr[1] = {E->getSourceRange()}; + PathDiagnosticLocation Loc(E, BR.getSourceManager(), AC); + BR.EmitBasicReport( + AC->getDecl(), Checker, + "Y2K38 Issue: Cast of time_t value to an unsigned type", + categories::LogicError, Output.str(), Loc, Sr); + } + } else { + + Output << "Y2K38 Issue: "; + Output << (dyn_cast(CE) ? "Explicit" : "Implicit"); + Output << " cast of time_t value to a non-integer type"; + + SourceRange Sr[1] = {E->getSourceRange()}; + PathDiagnosticLocation Loc(E, BR.getSourceManager(), AC); + BR.EmitBasicReport( + AC->getDecl(), Checker, + "Y2K38 Issue: Cast of time_t value to a non-integer type", + categories::LogicError, Output.str(), Loc, Sr); + } + + return true; +} + +/// Audit typedef's on the time_t type. +bool TimeVisitor::VisitTypedefNameDecl(const TypedefNameDecl *TD) { + + if (!TD) + return true; + + ASTContext &Ctx = AC->getASTContext(); + + if (TD->getName().str() != "time_t") + return true; + + QualType T = TD->getUnderlyingType(); + if (!T->isIntegerType() || !T->hasSignedIntegerRepresentation() || + Ctx.getIntWidth(T) < 64 /* bits */ || + TD->isLexicallyWithinFunctionOrMethod()) { + SourceRange Sr[1] = {TD->getSourceRange()}; + PathDiagnosticLocation Loc(TD, BR.getSourceManager()); + BR.EmitBasicReport( + AC->getDecl(), Checker, "Y2K38 Issue: time_t incorrectly defined", + categories::LogicError, + "Y2K38 Issue: time_t expected to be a signed " + "integer type, at-least 64-bits in size, having file scope", + Loc, Sr); + } + if (TD->isLexicallyWithinFunctionOrMethod()) { + SourceRange Sr[1] = {TD->getSourceRange()}; + PathDiagnosticLocation Loc(TD, BR.getSourceManager()); + BR.EmitBasicReport( + AC->getDecl(), Checker, "Y2K38 Issue: time_t incorrectly defined", + categories::LogicError, + "Y2K38 Issue: time_t declaration is local to function", Loc, Sr); + } + if (Ctx.getIntWidth(T) < 64 /* bits */) { + + SmallString<128> Buffer; + llvm::raw_svector_ostream Output(Buffer); + + Output << "Y2K38 Issue: time_t size is " << Ctx.getIntWidth(T) << " bits"; + + SourceRange Sr[1] = {TD->getSourceRange()}; + PathDiagnosticLocation Loc(TD, BR.getSourceManager()); + BR.EmitBasicReport(AC->getDecl(), Checker, + "Y2K38 Issue: time_t incorrectly defined", + categories::LogicError, Output.str(), Loc, Sr); + } + + return true; +} + +void ento::registerTimeChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} Index: test/Analysis/TimeChecker-0.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-0.c @@ -0,0 +1,218 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef signed char int8_t; + +typedef long int __time_t; +typedef __time_t time_t; + +int8_t ImplicitCastOnVar(void) { + + time_t t = 0; + + return t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} +} + +int8_t ImplicitCastOnVal(void) { + + return ((time_t) 0); // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} +} + +_Bool Bad10E(void) { + + time_t t = 0; + + return (_Bool) t; + // expected-warning@24 {{Y2K38 Issue: Explicit cast of time_t value to a type of lesser width}} + // expected-warning@24 {{Y2K38 Issue: Explicit cast of time_t value to an unsigned type}} +} + +_Bool Bad10I(void) { + + time_t t = 0; + + return t; + // expected-warning@33 {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} + // expected-warning@33 {{Y2K38 Issue: Implicit cast of time_t value to an unsigned type}} +} + +struct tm { + char t; +}; + +extern time_t mktime(struct tm *tm); + +char *Bad11E(void) { + return (char *) mktime(0); // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} +} + +char *Bad11I(void) { + return mktime(0); + // expected-warning@49 {{incompatible integer to pointer conversion returning 'time_t' (aka 'long') from a function with result type 'char *'}} + // expected-warning@49 {{Y2K38 Issue: Implicit cast of time_t value to a non-integer type}} +} + +typedef time_t time_t_0; +typedef time_t_0 time_t_1; +typedef time_t_1 time_t_2; +typedef time_t_2 time_t_3; +typedef time_t_3 time_t_4; +typedef time_t_4 time_t_5; +typedef time_t_5 time_t_6; +typedef time_t_6 time_t_7; + +char Bad12E(void) { + + time_t_7 t = 0; + + return (char) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a type of lesser width}} +} + +char Bad12I(void) { + + time_t_7 t = 0; + + return t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} +} + +struct foo13 { + + time_t t; + +}; + +char *Bad13(void) { + + struct foo13 FOO; + + FOO.t = 0x0; + + return (char *) FOO.t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} +} + +char *Bad14E(void) { + + time_t t = 0; + + return (char *) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} +} + +char *Bad14I(void) { + + time_t t = 0; + + return t; + // expected-warning@103 {{incompatible integer to pointer conversion returning 'time_t' (aka 'long') from a function with result type 'char *'}} + // expected-warning@103 {{Y2K38 Issue: Implicit cast of time_t value to a non-integer type}} +} + +int Bad21E(void) { + + time_t t = 0; + int n = 0; + + n = n + (int) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a type of lesser width}} + + return n; +} + +int Bad21I(void) { + + time_t t = 0; + int n = 0; + + n = n + t; + + return n; +} + +extern int gorp(int x); + +int Bad22(void) { + + time_t t = 0; + + gorp(t); // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} + return 0; +} + +int8_t f4(void) { + + time_t t = 0; + int8_t c = 0; + + c = (int8_t) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a type of lesser width}} + + return c; +} + +typedef char A2C[2]; + +void Bad5(void) { + + time_t t = 0; + A2C a2c = { 0, 0 }; + + a2c[0] = t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} + a2c[1] = (char) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a type of lesser width}} +} + +float Bad6E(void) { + + time_t t = 0; + float f; + + f = (float) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} + + return f; +} + +float Bad6I(void) { + + time_t t = 0; + float f; + + f = t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a non-integer type}} + + return f; +} + +double Bad7E(void) { + + time_t t = 0; + double f; + + f = (double) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} + + return f; +} + +double Bad7I(void) { + + time_t t = 0; + double f; + + f = t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a non-integer type}} + + return f; +} + +unsigned long int Bad8E(void) { + + time_t t = 0; + + return (unsigned long int) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to an unsigned type}} +} + +unsigned long int Bad8I(void) { + + time_t t = 0; + + return t; // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to an unsigned type}} +} + +void Bad9(void) { + + time_t t = 0; + + return (void) t; // expected-warning {{Y2K38 Issue: Explicit cast of time_t value to a non-integer type}} +} Index: test/Analysis/TimeChecker-1.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-1.c @@ -0,0 +1,6 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef unsigned char __time_t; +typedef __time_t time_t; +// expected-warning@4 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} +// expected-warning@4 {{Y2K38 Issue: time_t size is 8 bits}} Index: test/Analysis/TimeChecker-10.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-10.c @@ -0,0 +1,11 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +int unwise(void) { + + typedef long int __time_t; + typedef __time_t time_t; + // expected-warning@6 {{Y2K38 Issue: time_t declaration is local to function}} + // expected-warning@6 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} + + return 0; +} Index: test/Analysis/TimeChecker-11.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-11.c @@ -0,0 +1,12 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef long int __time_t; +typedef __time_t time_t; +extern time_t time(time_t *t); + +extern int wrap(int n); + +int test(void) { + + return wrap(time((time_t *) 0)); // expected-warning {{Y2K38 Issue: Implicit cast of time_t value to a type of lesser width}} +} Index: test/Analysis/TimeChecker-12.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-12.c @@ -0,0 +1,3 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef int (*time_t)(int); // expected-warning {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} Index: test/Analysis/TimeChecker-2.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-2.c @@ -0,0 +1,6 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef signed char __time_t; +typedef __time_t time_t; +// expected-warning@4 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size}} +// expected-warning@4 {{Y2K38 Issue: time_t size is 8 bits}} Index: test/Analysis/TimeChecker-3.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-3.c @@ -0,0 +1,4 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef long int _time_t; +// expected-no-diagnostics Index: test/Analysis/TimeChecker-4.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-4.c @@ -0,0 +1,14 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef char T0; +typedef T0 T1; +typedef T1 T2; +typedef T2 T3; +typedef T3 T4; +typedef T4 T5; +typedef T5 T6; +typedef T6 T7; +typedef T7 time_t; +typedef time_t T8; +// expected-warning@11 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} +// expected-warning@11 {{Y2K38 Issue: time_t size is 8 bits}} Index: test/Analysis/TimeChecker-5.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-5.c @@ -0,0 +1,13 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef long int T0; +typedef T0 T1; +typedef T1 T2; +typedef T2 T3; +typedef T3 T4; +typedef T4 T5; +typedef T5 T6; +typedef T6 T7; +typedef T7 time_t; +typedef time_t T8; +// expected-no-diagnostics Index: test/Analysis/TimeChecker-6.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-6.c @@ -0,0 +1,7 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef signed char int8_t; +typedef struct time_t_ { + int8_t t[256]; +} time_t; +// expected-warning@6 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} Index: test/Analysis/TimeChecker-7.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-7.c @@ -0,0 +1,13 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef unsigned long int __time_t; +typedef __time_t time_t; +// expected-warning@4 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size, having file scope}} + +time_t Bad(void) { + + time_t t = 0; + + return t; +} +// expected-warning@11 {{Y2K38 Issue: Implicit cast of time_t value to an unsigned type}} Index: test/Analysis/TimeChecker-8.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-8.c @@ -0,0 +1,13 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef short __time_t; +typedef __time_t time_t; +// expected-warning@4 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size}} +// expected-warning@4 {{Y2K38 Issue: time_t size is 16 bits}} + +time_t Bad(void) { + + time_t t = 0; + + return t; +} Index: test/Analysis/TimeChecker-9.c =================================================================== --- /dev/null +++ test/Analysis/TimeChecker-9.c @@ -0,0 +1,7 @@ +// RUN: %clang_analyze_cc1 -analyze -analyzer-checker=alpha.unix.Time -verify %s + +typedef signed char int8_t; +typedef struct time_t_ { + int8_t t[8]; +} time_t; +// expected-warning@6 {{Y2K38 Issue: time_t expected to be a signed integer type, at-least 64-bits in size}} Index: www/analyzer/alpha_checks.html =================================================================== --- www/analyzer/alpha_checks.html +++ www/analyzer/alpha_checks.html @@ -1047,6 +1047,47 @@ } + + + +