Skip to content
Open
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
79d6622
[clang-tidy] add `ctime` and `localtime` to `clang-tidy`
Dec 26, 2024
f90a091
[clang-tidy] add times to clang-tidy
Sep 28, 2024
d5d985a
[clang-tidy] add `ctime` and `localtime` to `clang-tidy`
Dec 26, 2024
3290834
[clang-tidy] add `ctime` and `localtime` to `clang-tidy`
Sep 30, 2024
cc3fa57
undo symbols, since these should be automatically generated
Sep 30, 2024
d372eaf
moved release notes for `ctime` and `localtime`
Dec 26, 2024
9eeca77
updated release notes for `ctime` and `localtime`
Dec 26, 2024
1d2e5ad
added `ctime_r` and `localtime_r` to documentation
Sep 30, 2024
668bcf7
updated release notes for `ctime` and `localtime`
Dec 26, 2024
2c2e3db
fix: release notes for `ctime` and `localtime`
Dec 26, 2024
bdf5f2e
release notes should be in alphabetical order
Dec 26, 2024
5600556
updated release notes
Sep 30, 2024
b33e2c9
fix: function for unsafe functions check
Sep 30, 2024
8afbbc4
add `ctime` and `localtime` to standard library checks
Sep 30, 2024
f8c70be
added `ctime` and `localtime` to bounds checking checks
Sep 30, 2024
da37620
added ctime and localtime to functions list
Sep 30, 2024
a120c01
format code with clang-format
Sep 30, 2024
e491609
fix: tests
Sep 30, 2024
8dada6d
fix: tests
Sep 30, 2024
bf8dd0c
fix: tests for `ctime`
Sep 30, 2024
dfad2fc
fix: message for localtime
Sep 30, 2024
c6b30f1
fix: tests for ctime and localtime
Sep 30, 2024
638e6ef
add ctime and localtime safe functions
Sep 30, 2024
7999af6
fix: localtime_r argument
Sep 30, 2024
d389e43
use `_r` functions, since `_s` functions are not implemented
Sep 30, 2024
8bbe896
safe functions should be used
Sep 30, 2024
1e7e3ba
use safe functions
Sep 30, 2024
244879c
fix: localtime argument
Sep 30, 2024
815a472
temporarily use `_r` functions
Sep 30, 2024
1d72f2b
use `_s` functions and add functions for `ctime` and `localtime`
Oct 1, 2024
0284ec7
fix: function names
Oct 1, 2024
59974db
fix: tests for clang-tidy
Oct 1, 2024
780fae6
temporarily remove localtime
Oct 1, 2024
412654e
add tests for ctime and localtime
Oct 1, 2024
1475515
update tests for ctime and localtime
Oct 4, 2024
fdf4245
format code with clang-format
Dec 26, 2024
45074ca
fix: document name
Dec 26, 2024
95342b9
fix: path for unsafe-functions
Dec 26, 2024
92cda2b
fix path
Dec 26, 2024
460ccf9
fix: release notes for clang-tidy
Dec 26, 2024
b70a529
fix: path for unsafe functions in release notes
Dec 26, 2024
b4d5208
fix path for unsafe functions in release notes
Dec 26, 2024
18e882e
fix: static analyzer for ctime_s
Dec 26, 2024
f1540c5
fix argument number
Dec 26, 2024
f28b90a
format code with clang-format
Dec 26, 2024
9d5fa73
include header file for size_t
Jan 4, 2025
2513ed9
fix clang static analyzer and functions checker
Jan 4, 2025
36f2968
combine `bugprone-unsafe-functions` release notes
zimirza Apr 30, 2025
7685ec0
fix: rebase from main branch
zimirza Apr 30, 2025
38a2135
fix: rebase from main branch
zimirza Apr 30, 2025
23d21cd
fix: rebase from main branch
zimirza Apr 30, 2025
428c299
update release notes for clang-tidy
zimirza Apr 30, 2025
4e66050
update release notes for clang-tidy
zimirza Apr 30, 2025
3326ab2
combine `bugprone-unsafe-functions` release notes
zimirza Apr 30, 2025
2aabbd7
fix: release notes for unsafe functions
zimirza Apr 30, 2025
cf1d22d
revert: identifier for bugprone-unsafe-functions
zimirza Apr 30, 2025
8cd9bd7
remove duplicate entry in release notes
zimirza May 2, 2025
6d61cac
removed ctime_s and localtime_s for this pull request. these will be
zimirza May 2, 2025
e27e233
format code with clang-format
zimirza May 2, 2025
26b2ab3
remove stddef.h
zimirza Aug 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions clang-tools-extra/clang-tidy/bugprone/UnsafeFunctionsCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ static StringRef getReplacementFor(StringRef FunctionName,
StringRef AnnexKReplacementFunction =
StringSwitch<StringRef>(FunctionName)
.Cases("asctime", "asctime_r", "asctime_s")
.Cases("ctime", "ctime_r", "ctime_s")
.Cases("localtime", "localtime_r", "localtime_s")
.Case("gets", "gets_s")
.Default({});
if (!AnnexKReplacementFunction.empty())
Expand All @@ -60,6 +62,8 @@ static StringRef getReplacementFor(StringRef FunctionName,
// should be matched and suggested.
return StringSwitch<StringRef>(FunctionName)
.Cases("asctime", "asctime_r", "strftime")
.Cases("ctime", "ctime_r", "ctime_s")
.Cases("localtime", "localtime_r", "localtime_s")
.Case("gets", "fgets")
.Case("rewind", "fseek")
.Case("setbuf", "setvbuf");
Expand Down Expand Up @@ -90,8 +94,8 @@ static StringRef getReplacementForAdditional(StringRef FunctionName,
/// safer alternative.
static StringRef getRationaleFor(StringRef FunctionName) {
return StringSwitch<StringRef>(FunctionName)
.Cases("asctime", "asctime_r", "ctime",
"is not bounds-checking and non-reentrant")
.Cases("asctime", "asctime_r", "ctime", "ctime_r", "localtime",
"localtime_r", "is not bounds-checking and non-reentrant")
.Cases("bcmp", "bcopy", "bzero", "is deprecated")
.Cases("fopen", "freopen", "has no exclusive access to the opened file")
.Case("gets", "is insecure, was deprecated and removed in C11 and C++14")
Expand Down Expand Up @@ -223,8 +227,9 @@ void UnsafeFunctionsCheck::registerMatchers(MatchFinder *Finder) {
}

// Matching functions with replacements without Annex K.
auto FunctionNamesMatcher =
hasAnyName("::asctime", "asctime_r", "::gets", "::rewind", "::setbuf");
auto FunctionNamesMatcher = hasAnyName(
"::asctime", "asctime_r", "::ctime", "ctime_r", "::localtime",
"localtime_r", "::gets", "::rewind", "::setbuf");
Finder->addMatcher(
declRefExpr(
to(functionDecl(FunctionNamesMatcher).bind(FunctionNamesId)))
Expand Down
6 changes: 5 additions & 1 deletion clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ New check aliases
Changes in existing checks
^^^^^^^^^^^^^^^^^^^^^^^^^^

- New unsafe functions checks :doc:`bugprone-unsafe-functions-check`
<clang-tidy/bugprone/UnsafeFunctionsCheck.cpp> were added to clang-tidy.

- Improved :doc:`bugprone-optional-value-conversion
<clang-tidy/checks/bugprone/optional-value-conversion>` check to detect
conversion in argument of ``std::make_optional``.
Expand All @@ -152,7 +155,8 @@ Changes in existing checks

- Improved :doc:`bugprone-unsafe-functions
<clang-tidy/checks/bugprone/unsafe-functions>` check to allow specifying
additional C++ member functions to match.
additional C++ member functions to match and by adding ``ctime`` and
``localtime`` to unsafe functions list.

- Improved :doc:`cert-err33-c
<clang-tidy/checks/cert/err33-c>` check by fixing false positives when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,15 @@ following functions:
``vsnprintf``, ``vsprintf``, ``vsscanf``, ``vswprintf``, ``vswscanf``,
``vwprintf``, ``vwscanf``, ``wcrtomb``, ``wcscat``, ``wcscpy``,
``wcslen``, ``wcsncat``, ``wcsncpy``, ``wcsrtombs``, ``wcstok``, ``wcstombs``,
``wctomb``, ``wmemcpy``, ``wmemmove``, ``wprintf``, ``wscanf``.
``wctomb``, ``wmemcpy``, ``wmemmove``, ``wprintf``, ``wscanf``. ``ctime_r``,
``localtime_r``

If *Annex K.* is not available, replacements are suggested only for the
following functions from the previous list:

- ``asctime``, ``asctime_r``, suggested replacement: ``strftime``
- ``ctime``, ``ctime_r``, suggested replacement: ``ctime_s``
- ``localtime``, ``localtime_r``, suggested replacement: ``localtime_s``
- ``gets``, suggested replacement: ``fgets``

The following functions are always checked, regardless of *Annex K* availability:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,25 @@ void fOptional() {
typedef int errno_t;
typedef size_t rsize_t;
errno_t asctime_s(char *S, rsize_t Maxsize, const struct tm *TimePtr);
errno_t ctime_s(char *S, rsize_t Maxsize, const time_t *Timep);
errno_t localtime_s(const time_t *Timep, rsize_t Maxsize, const struct tm *TimePtr);
errno_t strcat_s(char *S1, rsize_t S1Max, const char *S2);

void fUsingSafeFunctions(const struct tm *Time, FILE *F) {
void fUsingSafeFunctions(const struct tm *Time, FILE *F, time_t *Timep) {
char Buf[BUFSIZ] = {0};

// no-warning, safe function from annex K is used
if (asctime_s(Buf, BUFSIZ, Time) != 0)
return;

// no-warning, safe function from annex K is used
if (ctime_s(Buf, BUFSIZ, Time) != 0)
return;

// no-warning, safe function from annex K is used
if (localtime_s(Timep, BUFSIZ, Time) != 0)
return;

// no-warning, safe function from annex K is used
if (strcat_s(Buf, BUFSIZ, "something") != 0)
return;
Expand Down
39 changes: 31 additions & 8 deletions clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3533,10 +3533,19 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}),
Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0))));

// struct tm *localtime_r(const time_t *restrict timer,
// struct tm *localtime_r(const time_t *timer,
// struct tm *result);
addToFunctionSummaryMap("localtime_r",
Signature(ArgTypes{ConstTime_tPtrTy, StructTmPtrTy},
RetType{StructTmPtrTy}),
Summary(NoEvalCall)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));

// struct tm *localtime_s(const time_t *restrict timer,
// struct tm *restrict result);
addToFunctionSummaryMap(
"localtime_r",
"localtime_s",
Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
RetType{StructTmPtrTy}),
Summary(NoEvalCall)
Expand Down Expand Up @@ -3565,15 +3574,29 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
/*Buffer=*/ArgNo(1),
/*MinBufSize=*/BVF.getValue(26, IntTy))));

// struct tm *gmtime_r(const time_t *restrict timer,
// struct tm *restrict result);
// char *ctime_s(char *buf, rsize_t buf_size, const time_t *timep);
addToFunctionSummaryMap(
"gmtime_r",
Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
RetType{StructTmPtrTy}),
"ctime_s",
Signature(ArgTypes{CharPtrTy,
BufferSize(ArgNo(0), BVF.getValue(26, IntTy)),
ConstTime_tPtrTy},
RetType{CharPtrTy}),
Summary(NoEvalCall)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));
.ArgConstraint(BufferSize(
/*Buffer=*/ArgNo(0),
/*MinBufSize=*/BVF.getValue(26, IntTy))));
.ArgConstraint(NotNull(ArgNo(2)))

// struct tm *gmtime_r(const time_t *restrict timer,
// struct tm *restrict result);
addToFunctionSummaryMap(
"gmtime_r",
Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
RetType{StructTmPtrTy}),
Summary(NoEvalCall)
.ArgConstraint(NotNull(ArgNo(0)))
.ArgConstraint(NotNull(ArgNo(1))));

// struct tm * gmtime(const time_t *tp);
addToFunctionSummaryMap(
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ class InvalidPtrChecker
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{CDM::CLibrary, {"asctime"}, 1},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{CDM::CLibrary, {"ctime"}, 1},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
{{CDM::CLibrary, {"localtime"}, 1},
&InvalidPtrChecker::postPreviousReturnInvalidatingCall},
};

// The private members of this checker corresponding to commandline options
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Tooling/Inclusions/Stdlib/StdSymbolMap.inc
Original file line number Diff line number Diff line change
Expand Up @@ -2032,8 +2032,8 @@ SYMBOL(locale, std::, <locale>)
SYMBOL(localeconv, std::, <clocale>)
SYMBOL(localeconv, None, <clocale>)
SYMBOL(localeconv, None, <locale.h>)
SYMBOL(localtime, std::, <ctime>)
SYMBOL(localtime, None, <ctime>)
SYMBOL(localtime, std::, <localtime>)
SYMBOL(localtime, None, <localtime>)
SYMBOL(localtime, None, <time.h>)
SYMBOL(lock, std::, <mutex>)
SYMBOL(lock_guard, std::, <mutex>)
Expand Down
4 changes: 3 additions & 1 deletion clang/test/Analysis/Inputs/std-c-library-functions-POSIX.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,11 @@ int utimensat(int dirfd, const char *pathname, const struct timespec times[2], i
int utimes(const char *filename, const struct timeval times[2]);
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
struct tm *localtime(const time_t *tp);
struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result);
struct tm *localtime_r(const time_t *timer, struct tm *result);
struct tm *localtime_s(const time_t *restrict timer, struct tm *restrict result);
char *asctime_r(const struct tm *restrict tm, char *restrict buf);
char *ctime_r(const time_t *timep, char *buf);
char *ctime_s(char *buf, rsize_t buf_size, const time_t *timep);
struct tm *gmtime_r(const time_t *restrict timer, struct tm *restrict result);
struct tm *gmtime(const time_t *tp);
int clock_gettime(clockid_t clock_id, struct timespec *tp);
Expand Down
30 changes: 30 additions & 0 deletions clang/test/Analysis/cert/env34-c.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ lconv *localeconv(void);
typedef struct {
} tm;
char *asctime(const tm *timeptr);
char *ctime(const time_t *time);
struct tm *localtime(const time_t *time);

int strcmp(const char*, const char*);
extern void foo(char *e);
Expand Down Expand Up @@ -313,6 +315,34 @@ void asctime_test(void) {
// expected-note@-2{{dereferencing an invalid pointer}}
}

void ctime_test(void) {
const time_t *t;
const time_t *tt;

char* p = ctime(t);
// expected-note@-1{{previous function call was here}}
char* pp = ctime(tt);
// expected-note@-1{{'ctime' call may invalidate the result of the previous 'ctime'}}

*p;
// expected-warning@-1{{dereferencing an invalid pointer}}
// expected-note@-2{{dereferencing an invalid pointer}}
}

void localtime_test(void) {
const time_t *t;
const time_t *tt;

struct tm* p = localtime(t);
// expected-note@-1{{previous function call was here}}
struct tm* pp = localtime(tt);
// expected-note@-1{{'localtime' call may invalidate the result of the previous 'localtime'}}

*p;
// expected-warning@-1{{dereferencing an invalid pointer}}
// expected-note@-2{{dereferencing an invalid pointer}}
}

void localeconv_test1(void) {
lconv *lc1 = localeconv();
// expected-note@-1{{previous function call was here}}
Expand Down
4 changes: 3 additions & 1 deletion clang/test/Analysis/std-c-library-functions-POSIX.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,11 @@
// CHECK: Loaded summary for: int utimes(const char *filename, const struct timeval times[2])
// CHECK: Loaded summary for: int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
// CHECK: Loaded summary for: struct tm *localtime(const time_t *tp)
// CHECK: Loaded summary for: struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result)
// CHECK: Loaded summary for: struct tm *localtime_r(const time_t *timer, struct tm *result)
// CHECK: Loaded summary for: struct tm *localtime_s(const time_t *restrict timer, struct tm *restrict result)
// CHECK: Loaded summary for: char *asctime_r(const struct tm *restrict tm, char *restrict buf)
// CHECK: Loaded summary for: char *ctime_r(const time_t *timep, char *buf)
// CHECK: Loaded summary for: char *ctime_s(char *buf, rsize_t buf_size, const time_t *timep)
// CHECK: Loaded summary for: struct tm *gmtime_r(const time_t *restrict timer, struct tm *restrict result)
// CHECK: Loaded summary for: struct tm *gmtime(const time_t *tp)
// CHECK: Loaded summary for: int clock_gettime(clockid_t clock_id, struct timespec *tp)
Expand Down
2 changes: 2 additions & 0 deletions compiler-rt/lib/dfsan/libc_ubuntu1404_abilist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ fun:ctanl=uninstrumented
fun:ctermid=uninstrumented
fun:ctime=uninstrumented
fun:ctime_r=uninstrumented
fun:ctime_s=uninstrumented
fun:cuserid=uninstrumented
fun:daemon=uninstrumented
fun:dcgettext=uninstrumented
Expand Down Expand Up @@ -2205,6 +2206,7 @@ fun:llseek=uninstrumented
fun:localeconv=uninstrumented
fun:localtime=uninstrumented
fun:localtime_r=uninstrumented
fun:localtime_s=uninstrumented
fun:lockf=uninstrumented
fun:lockf64=uninstrumented
fun:log=uninstrumented
Expand Down
25 changes: 25 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
//===----------------------------------------------------------------------===//

#include <stdarg.h>
#include <stddef.h>

#include "interception/interception.h"
#include "sanitizer_addrhashmap.h"
Expand Down Expand Up @@ -1375,6 +1376,16 @@ INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) {
}
return res;
}
INTERCEPTOR(__sanitizer_tm *, localtime_s, unsigned long *timep, void *result) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result);
__sanitizer_tm *res = REAL(localtime_r)(timep, result);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
unpoison_tm(ctx, res);
}
return res;
}
INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep);
Expand Down Expand Up @@ -1421,6 +1432,20 @@ INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) {
}
return res;
}
INTERCEPTOR(char *, ctime_s, char *result, size_t result_size,
unsigned long *timep) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, ctime_s, result, result_size, timep);
// FIXME: under ASan the call below may write to freed memory and corrupt
// its metadata. See
// https://github.com/google/sanitizers/issues/321.
char *res = REAL(ctime_s)(result, result_size, timep);
if (res) {
COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep));
COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, internal_strlen(res) + 1);
}
return res;
}
INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) {
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm);
Expand Down
Loading