[clang] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker (PR #71394)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/71394 None >From 965c109cc19187329d5ab2ae324665dfbd7c17ee Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 6 Nov 2023 21:49:36 +0800 Subject: [PATCH] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 35 ++- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..e35929c4dcd28e4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -342,6 +342,11 @@ class StreamChecker : public CheckerStreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; + State = ensureNoFilePositionIndeterminate(StreamVal, C, State); if (!State) return; @@ -749,13 +750,9 @@ void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); - State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; + State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); if (!State) return; @@ -1002,15 +999,19 @@ void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); + if (basicCheck(Desc, Call, C, State, StreamVal)) +C.addTransition(State); +} + +bool StreamChecker::basicCheck(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, ProgramStateRef &State, + SVal &StreamVal) const { State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, State); if (!State) -return; +return false; State = ensureStreamOpened(StreamVal, C, State); - if (!State) -return; - - C.addTransition(State); + return State != nullptr; } void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker (PR #71394)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71394 >From abbca31776cf4223392726d64aadfa5c79b57a69 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 6 Nov 2023 21:49:36 +0800 Subject: [PATCH] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 34 +-- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..c5c33979b202154 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -342,6 +342,11 @@ class StreamChecker : public CheckerStreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; + State = ensureNoFilePositionIndeterminate(StreamVal, C, State); if (!State) return; @@ -749,12 +750,7 @@ void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); - State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); if (!State) @@ -1002,15 +998,19 @@ void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); + if (basicCheck(Desc, Call, C, State, StreamVal)) +C.addTransition(State); +} + +bool StreamChecker::basicCheck(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, ProgramStateRef &State, + SVal &StreamVal) const { State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, State); if (!State) -return; +return false; State = ensureStreamOpened(StreamVal, C, State); - if (!State) -return; - - C.addTransition(State); + return State != nullptr; } void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker (PR #71394)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71394 >From 56d5604cf0442919d62def08c233e8d48b654885 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 6 Nov 2023 21:49:36 +0800 Subject: [PATCH] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 33 +-- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..877e5d2a09f398c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -342,6 +342,11 @@ class StreamChecker : public CheckerStreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; State = ensureNoFilePositionIndeterminate(StreamVal, C, State); if (!State) @@ -749,12 +749,7 @@ void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); - State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, - State); - if (!State) -return; - State = ensureStreamOpened(StreamVal, C, State); - if (!State) + if (!basicCheck(Desc, Call, C, State, StreamVal)) return; State = ensureFseekWhenceCorrect(Call.getArgSVal(2), C, State); if (!State) @@ -1002,15 +997,19 @@ void StreamChecker::preDefault(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); SVal StreamVal = getStreamArg(Desc, Call); + if (basicCheck(Desc, Call, C, State, StreamVal)) +C.addTransition(State); +} + +bool StreamChecker::basicCheck(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, ProgramStateRef &State, + SVal &StreamVal) const { State = ensureStreamNonNull(StreamVal, Call.getArgExpr(Desc->StreamArgNo), C, State); if (!State) -return; +return false; State = ensureStreamOpened(StreamVal, C, State); - if (!State) -return; - - C.addTransition(State); + return State != nullptr; } void StreamChecker::evalSetFeofFerror(const FnDescription *Desc, ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker (PR #71394)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71394 >From 3be57e39926bda29b273d5e9e01bff5ec8b2302e Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 14:11:37 +0800 Subject: [PATCH] [clang][analyzer][NFC] Remove redundant code in StreamChecker --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..69610029beb0a9e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1093,7 +1093,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, N)); return nullptr; } -return State; } return State; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/71394 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
benshi001 wrote: It seems my previous change make little sense, so I reuse this PR for another change. https://github.com/llvm/llvm-project/pull/71394 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71394 >From 095c386c2a9a4be6bf87d36ed0081d3756c9624b Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 14:11:37 +0800 Subject: [PATCH] [clang][analyzer][NFC] Remove redundant code in StreamChecker --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..9e3fc49ee0154ca 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1074,8 +1074,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, "Stream might be already closed. Causes undefined behaviour.", N)); return nullptr; } - -return State; } if (SS->isOpenFailed()) { @@ -1093,7 +1091,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, N)); return nullptr; } -return State; } return State; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71394 >From f4115e95349fdc6970e40176aafd89ef12d3f14b Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 14:11:37 +0800 Subject: [PATCH] [clang][analyzer][NFC] Remove redundant code in StreamChecker --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..69610029beb0a9e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1093,7 +1093,6 @@ ProgramStateRef StreamChecker::ensureStreamOpened(SVal StreamVal, N)); return nullptr; } -return State; } return State; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
benshi001 wrote: How about only remove the second `return State` ? The removal of the first one may be less readable. But for the second one, it is very near to the final `return State`. It may looks strange for very near `return State`s. ``` if () { return State; } return State; ``` https://github.com/llvm/llvm-project/pull/71394 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/71518 None >From c2411fe31a465eaafc8606b5f0ef95d94781a25b Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 74 +-- clang/test/Analysis/stream-error.c| 46 clang/test/Analysis/stream.c | 14 3 files changed, 126 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..43ef9a2d377c512 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,58 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFputc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = SVB.evalBinOp(State, BO_EQ, RetVal, +SVB.makeIntVal(*EofVal, C.getASTContext().IntTy), +SVB.getConditionType()) + .getAs(); + if (!Cond) +return; + StateFailed = StateFailed->assume(*Cond, true); + if (!StateFailed) +return; + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..7e895f3fe947158 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -241,6 +241,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +285,32 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + +void error_indeterminate_feof4(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Y', F) == EOF) { +fputc('W', F); // expected-warning {{might be 'indeterminate'}} + } else { +fputc('H', F); // no warning + } + fclose(F); +} diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..a2eaaa2bd0142c5 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void check_fwrite(void) { fclose(fp); } +v
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
benshi001 wrote: `fgetc` should also be supported, and I will do that next week. https://github.com/llvm/llvm-project/pull/71518 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Remove redundant code in StreamChecker (PR #71394)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/71394 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 reopened https://github.com/llvm/llvm-project/pull/71518 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From eb4da0df114586c4099a495e5114ab0c1c9f9e27 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 75 +-- clang/test/Analysis/stream-error.c| 46 clang/test/Analysis/stream.c | 14 3 files changed, 127 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..a774b61cc820df1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,59 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFgetcFputc(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = SVB.evalBinOp(State, BO_EQ, RetVal, +SVB.makeIntVal(*EofVal, C.getASTContext().IntTy), +SVB.getConditionType()) + .getAs(); + if (!Cond) +return; + StateFailed = StateFailed->assume(*Cond, true); + if (!StateFailed) +return; + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..7e895f3fe947158 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -241,6 +241,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +285,32 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + +void error_indeterminate_feof4(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Y', F) == EOF) { +fputc('W', F); // expected-warning {{might be 'indeterminate'}} + } else { +fputc('H', F); // no warning + } + fclose(F); +} diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..a2eaaa2bd0142c5 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void che
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From 3a51a9067e2581e1cb7091e24725439a991fdc78 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 75 +-- clang/test/Analysis/stream-error.c| 55 ++ clang/test/Analysis/stream.c | 14 3 files changed, 136 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..a774b61cc820df1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,59 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFgetcFputc(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + SValBuilder &SVB = C.getSValBuilder(); + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = SVB.evalBinOp(State, BO_EQ, RetVal, +SVB.makeIntVal(*EofVal, C.getASTContext().IntTy), +SVB.getConditionType()) + .getAs(); + if (!Cond) +return; + StateFailed = StateFailed->assume(*Cond, true); + if (!StateFailed) +return; + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..5134be492696813 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -241,6 +241,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +285,41 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + +void error_indeterminate_feof4(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Y', F) == EOF) { +fputc('W', F); // expected-warning {{might be 'indeterminate'}} + } else { +fputc('H', F); // no warning + } + fclose(F); +} + +void determinate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Q', F) == 'Q') +fputc('X', F); // no warning + fclose(F); +} diff --git a/clang/test/Analysis/stream.c
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From b12b8ef5b9cc65cabf14eeeb3dacd6f41e5bf54e Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 62 --- clang/test/Analysis/stream-error.c| 55 clang/test/Analysis/stream.c | 14 + 3 files changed, 123 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..dd54d5d33a3faa4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,46 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFgetcFputc(const FnDescription *Desc, + const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..5134be492696813 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -241,6 +241,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +285,41 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + +void error_indeterminate_feof4(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Y', F) == EOF) { +fputc('W', F); // expected-warning {{might be 'indeterminate'}} + } else { +fputc('H', F); // no warning + } + fclose(F); +} + +void determinate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + if (fputc('Q', F) == 'Q') +fputc('X', F); // no warning + fclose(F); +} diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..a2eaaa2bd0142c5 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void check_fwrite(void) { fclose(fp); } +void check_fgetc(void) { + FILE *fp = tmpfile(); + fputc('A', fp); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_fseek(void) { FILE *fp = tmpfile(); fseek(fp, 0, 0); // expected-wa
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From 7918014a23d4e4e996896164139dafe3c98aa8a5 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 --- clang/test/Analysis/stream-error.c| 51 clang/test/Analysis/stream.c | 14 + 3 files changed, 118 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..a4b367a5599f58f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,45 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFputc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..45bebac02e49f85 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,23 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fputc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fputc('X', F); + if (Ret == EOF) { +clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} +fputc('Y', F); // expected-warning {{might be 'indeterminate'}} + } else { +clang_analyzer_eval(Ret == 'X'); // expected-warning {{TRUE}} +clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} +fputc('Y', F); // no-warning + } + fclose(F); + fputc('A', F); // expected-warning {{Stream might be already closed}} +} + void freadwrite_zerosize(FILE *F) { size_t Ret; Ret = fwrite(0, 1, 0, F); @@ -241,6 +258,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +302,20 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..c4e00ed7ffadb64 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void c
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From 2ebbb60c04968f9c953790daa5c791f072d975b8 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 --- clang/test/Analysis/stream-error.c| 51 clang/test/Analysis/stream.c | 6 ++ 3 files changed, 110 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 898906977ba9bb6..a4b367a5599f58f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,45 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFputc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..45bebac02e49f85 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,23 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fputc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fputc('X', F); + if (Ret == EOF) { +clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} +fputc('Y', F); // expected-warning {{might be 'indeterminate'}} + } else { +clang_analyzer_eval(Ret == 'X'); // expected-warning {{TRUE}} +clang_analyzer_eval(ferror(F)); // expected-warning {{FALSE}} +fputc('Y', F); // no-warning + } + fclose(F); + fputc('A', F); // expected-warning {{Stream might be already closed}} +} + void freadwrite_zerosize(FILE *F) { size_t Ret; Ret = fwrite(0, 1, 0, F); @@ -241,6 +258,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) @@ -268,3 +302,20 @@ void error_indeterminate_feof2(void) { } fclose(F); } + +void error_indeterminate_feof3(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + char Buf[10]; + if (fread(Buf, 1, 10, F) < 10) { +if (feof(F)) { + // error is feof, should be non-indeterminate + fputc(';', F); // no warning +} +if (ferror(F)) { + fputc('=', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..9b6a31738d6e0b8 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void chec
[clang] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker (PR #72016)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/72016 None >From 327374741e5b73943a78879c0425eaf1abac1273 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 11 Nov 2023 11:31:57 +0800 Subject: [PATCH] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker --- .../Checkers/SimpleStreamChecker.cpp | 50 --- .../test/Analysis/stream-non-posix-function.c | 1 + 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 32d95e944195390..d78761b0ea4553b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -1,4 +1,4 @@ -//===-- SimpleStreamChecker.cpp -*- C++ -*--// +//===-- SimpleStreamChecker.cpp ---*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -31,7 +31,7 @@ typedef SmallVector SymbolVector; struct StreamState { private: enum Kind { Opened, Closed } K; - StreamState(Kind InK) : K(InK) { } + StreamState(Kind InK) : K(InK) {} public: bool isOpened() const { return K == Opened; } @@ -40,25 +40,19 @@ struct StreamState { static StreamState getOpened() { return StreamState(Opened); } static StreamState getClosed() { return StreamState(Closed); } - bool operator==(const StreamState &X) const { -return K == X.K; - } - void Profile(llvm::FoldingSetNodeID &ID) const { -ID.AddInteger(K); - } + bool operator==(const StreamState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } }; -class SimpleStreamChecker : public Checker { +class SimpleStreamChecker +: public Checker { CallDescription OpenFn, CloseFn; std::unique_ptr DoubleCloseBugType; std::unique_ptr LeakBugType; - void reportDoubleClose(SymbolRef FileDescSym, - const CallEvent &Call, + void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; void reportLeaks(ArrayRef LeakedStreams, CheckerContext &C, @@ -78,9 +72,9 @@ class SimpleStreamChecker : public Checker LeakedStreams, } } -bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ +bool SimpleStreamChecker::guaranteedNotToCloseFile( +const CallEvent &Call) const { // If it's not in a system header, assume it might close a file. if (!Call.isInSystemHeader()) return false; @@ -229,11 +223,9 @@ bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // If the pointer we are tracking escaped, do not track the symbol as // we cannot reason about it anymore. -ProgramStateRef -SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, -const InvalidatedSymbols &Escaped, -const CallEvent *Call, -PointerEscapeKind Kind) const { +ProgramStateRef SimpleStreamChecker::checkPointerEscape( +ProgramStateRef State, const InvalidatedSymbols &Escaped, +const CallEvent *Call, PointerEscapeKind Kind) const { // If we know that the call cannot close a file, there is nothing to do. if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { return State; diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c index 70b3ab25d026532..ab7c60a2c6c76e6 100644 --- a/clang/test/Analysis/stream-non-posix-function.c +++ b/clang/test/Analysis/stream-non-posix-function.c @@ -1,4 +1,5 @@ // RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.SimpleStream -verify %s // expected-no-diagnostics typedef struct _FILE FILE; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Supplement comments in `evalFtell` of StreamChecker (PR #74291)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/74291 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From 65ce18117f99056aafcf58151b64f4243f4d5e26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/2] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e35543..2c725c01dc285 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From dcb766b468a2f29df30451f8f196d7a2371fd038 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/2] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285..a368619fd37d2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773..94787874cf839 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush(void) {
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
benshi001 wrote: > For now only the pre-condition is added, the `evalFflush` function is missing. I have also added `evalFflush`. https://github.com/llvm/llvm-project/pull/74296 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From 65ce18117f99056aafcf58151b64f4243f4d5e26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e35543..2c725c01dc285 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From dcb766b468a2f29df30451f8f196d7a2371fd038 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285..a368619fd37d2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773..94787874cf839 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush(void) {
[clang] [llvm] [clang][RISCV] Change default abi with f extension but without d extension (PR #73489)
benshi001 wrote: I think this is reasonable, even I myself made such a change months ago in Phabracator, but finally not approved. https://github.com/llvm/llvm-project/pull/73489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [llvm] [clang][RISCV] Change default abi with f extension but without d extension (PR #73489)
https://github.com/benshi001 approved this pull request. https://github.com/llvm/llvm-project/pull/73489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From 65ce18117f99056aafcf58151b64f4243f4d5e26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e35543..2c725c01dc285 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From dcb766b468a2f29df30451f8f196d7a2371fd038 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285..a368619fd37d2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773..94787874cf839 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush(void) {
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From 65ce18117f99056aafcf58151b64f4243f4d5e26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e35543..2c725c01dc285 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From dcb766b468a2f29df30451f8f196d7a2371fd038 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285..a368619fd37d2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773..94787874cf839 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush(void) {
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
benshi001 wrote: I can not understand why the new test `error_fflush_1` failed on windows, it did succeed on my local linux. So shall we 1. Add a `#if _win32` in the test? 2. Or only commit `fflush(not_null_stream) == 0` part, and leave `fflush(NULL) == 0` in the next patch? I hope the second one, at least we can break down this change. https://github.com/llvm/llvm-project/pull/74296 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From 65ce18117f99056aafcf58151b64f4243f4d5e26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e355431..2c725c01dc285b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d98..409a969a0d4cce 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca7..aa5b6be851773a 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From dcb766b468a2f29df30451f8f196d7a2371fd038 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/3] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285b..a368619fd37d22 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773a..94787874cf8396 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush
[clang] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker (PR #72016)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/72016 >From dfcae6556ea05d72f871f13cc76984a0745fff26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 15 Nov 2023 14:02:46 +0800 Subject: [PATCH] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker --- clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp | 2 +- clang/test/Analysis/stream-non-posix-function.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 32d95e944195390..2ac9f65c9793f45 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -90,7 +90,7 @@ class SimpleStreamChecker : public Checkerhttps://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker (PR #72016)
benshi001 wrote: > The functional change looks good but the reformatting should be put into a > separate change. Done. Thanks. https://github.com/llvm/llvm-project/pull/72016 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/71518 >From 15367bae9f129b20885f13e5ca5ae816271d7214 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 7 Nov 2023 16:44:05 +0800 Subject: [PATCH] [clang][analyzer] Support `fputc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 --- clang/test/Analysis/stream-error.c| 35 +++ clang/test/Analysis/stream.c | 6 ++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 69610029beb0a9e..df0865e85eaefda 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -245,11 +245,14 @@ class StreamChecker : public CheckerStreamArgNo), C, @@ -650,7 +656,7 @@ void StreamChecker::preFreadFwrite(const FnDescription *Desc, if (!State) return; - if (!IsFread) { + if (!IsRead) { C.addTransition(State); return; } @@ -745,6 +751,45 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFputc(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + + // Generate a transition for the success state. + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = + StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); + + // Add transition for the failed state. + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened( + Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 3c00e59bb6bd19d..5ebdc32bb1b92ff 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,24 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fputc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fputc('X', F); + if (Ret == EOF) { +clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} +clang_analyzer_eval(feof(F)); // expected-warning {{FALSE}} +fputc('Y', F); // expected-warning {{might be 'indeterminate'}} + } else { +clang_analyzer_eval(Ret == 'X'); // expected-warning {{TRUE}} +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fputc('Y', F); // no-warning + } + fclose(F); + fputc('A', F); // expected-warning {{Stream might be already closed}} +} + void freadwrite_zerosize(FILE *F) { size_t Ret; Ret = fwrite(0, 1, 0, F); @@ -241,6 +259,23 @@ void error_indeterminate_clearerr(void) { fclose(F); } +void error_indeterminate_fputc(void) { + FILE *F = fopen("file", "r+"); + if (!F) +return; + int rc = fseek(F, 0, SEEK_SET); + if (rc) { +if (feof(F)) { + fputc('X', F); // no warning +} else if (ferror(F)) { + fputc('C', F); // expected-warning {{might be 'indeterminate'}} +} else { + fputc('E', F); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); +} + void error_indeterminate_feof1(void) { FILE *F = fopen("file", "r+"); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a01310cfef5dd8a..9b6a31738d6e0b8 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -14,6 +14,12 @@ void check_fwrite(void) { fclose(fp); } +void check_fputc(void) { + FILE *fp = tmpfile(); + fputc('A', fp); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_fseek(void) { FILE *fp = tmpfile(); fseek(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}} ___ cfe-commits mailing list cfe-commits@lists.llvm.
[clang] [clang][Analyzer][NFC] Use condition type for comparison in several checkers (PR #72358)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/72358 None >From b2abb3ff72a2a9f57a3fc4b71d19daefd436410b Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 15 Nov 2023 15:16:18 +0800 Subject: [PATCH] [clang][Analyzer][NFC] Use condition type for comparison in several checkers --- clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 4 ++-- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 4 ++-- clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index b1bc98e93a27995..31f5b03dcdeba80 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -881,8 +881,8 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt, fourInt); NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt); - SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, -maxLength, sizeTy); + SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, maxLength, + svalBuilder.getConditionType()); state = state->assume(evalLength.castAs(), true); } state = state->set(MR, strLength); diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 69610029beb0a9e..2b5c1dc4c99affa 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -719,9 +719,9 @@ void StreamChecker::evalFreadFwrite(const FnDescription *Desc, NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); auto Cond = - C.getSValBuilder() - .evalBinOpNN(State, BO_LT, RetVal, *NMembVal, C.getASTContext().IntTy) + SVB.evalBinOpNN(State, BO_LT, RetVal, *NMembVal, SVB.getConditionType()) .getAs(); if (!Cond) return; diff --git a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index b195d912cadfe9b..1d03d1656b3cb14 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -188,7 +188,8 @@ ProgramStateRef VLASizeChecker::checkVLAIndexSize(CheckerContext &C, QualType SizeTy = SizeE->getType(); DefinedOrUnknownSVal Zero = SVB.makeZeroVal(SizeTy); - SVal LessThanZeroVal = SVB.evalBinOp(State, BO_LT, SizeD, Zero, SizeTy); + SVal LessThanZeroVal = + SVB.evalBinOp(State, BO_LT, SizeD, Zero, SVB.getConditionType()); if (std::optional LessThanZeroDVal = LessThanZeroVal.getAs()) { ConstraintManager &CM = C.getConstraintManager(); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' modeling to POSIX versions in SimpleStreamChecker (PR #72016)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/72016 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputc` in StreamChecker (PR #71518)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/71518 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][Analyzer][NFC] Use condition type for comparison in several checkers (PR #72358)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/72358 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/72627 None >From 87563994d96519bef69cb14c284a9a1273ab343c Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 17 Nov 2023 17:22:10 +0800 Subject: [PATCH] [clang][analyzer] Support `fgetc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 70 - .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 75 ++- clang/test/Analysis/stream.c | 6 ++ 4 files changed, 131 insertions(+), 21 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..3acf23f090cb397 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -250,9 +250,12 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); - if (!PutVal) -return; - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), *PutVal); - StateNotFailed = - StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); + // Generate a transition for the success state of fputc. + if (!IsRead) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generate a transition for the success state of fgetc. + // If we know the state to be FEOF at fgetc, do not add a success state. + else if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, + SVB.makeZeroVal(C.getASTContext().IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); - StreamState NewSS = StreamState::getOpened( - Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StreamErrorState NewES; + if (IsRead) +NewES = +OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + else +NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - C.addTransition(StateFailed); + if (IsRead && OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); } void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 8924103f5046ea2..fc57e8bdc3d30c3 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,6 +48,7 @@ FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *re int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); +int fgetc(FILE *stream); int fputc(int ch, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 5ebdc32bb1b92ff..521bdb15f706bc3 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,30 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fgetc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fgetc(F); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} + } else { +clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}} +if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fgetc(F); // expected-warning {{Read function called when stream is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} + clang_analyze
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/72627 >From d3ce61fecbe922f09ad1228a3305f484e8e1dcfc Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 17 Nov 2023 17:22:10 +0800 Subject: [PATCH] [clang][analyzer] Support `fgetc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 70 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 71 ++- clang/test/Analysis/stream.c | 6 ++ 4 files changed, 129 insertions(+), 19 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..3acf23f090cb397 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -250,9 +250,12 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); - if (!PutVal) -return; - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), *PutVal); - StateNotFailed = - StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); + // Generate a transition for the success state of fputc. + if (!IsRead) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generate a transition for the success state of fgetc. + // If we know the state to be FEOF at fgetc, do not add a success state. + else if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, + SVB.makeZeroVal(C.getASTContext().IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); - StreamState NewSS = StreamState::getOpened( - Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StreamErrorState NewES; + if (IsRead) +NewES = +OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + else +NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - C.addTransition(StateFailed); + if (IsRead && OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); } void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 8924103f5046ea2..fc57e8bdc3d30c3 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,6 +48,7 @@ FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *re int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); +int fgetc(FILE *stream); int fputc(int ch, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 5ebdc32bb1b92ff..c86e2bb71172692 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,30 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fgetc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fgetc(F); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} + } else { +clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}} +if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fgetc(F); // expected-warning {{Read function called when stream is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} + clang_analyzer_eva
[clang] [clang][analyzer][NFC] Use `*EofVal` instead of constant `-1` (PR #73072)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/73072 None >From 1079cdb578a434344ac525e32d9931325e6f3f6c Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 22 Nov 2023 11:00:50 +0800 Subject: [PATCH] [clang][analyzer][NFC] Use '*EofVal' instead of constant '-1' --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..3d6f54c1b606ac0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -952,8 +952,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, if (!StateNotFailed) return; - ProgramStateRef StateFailed = State->BindExpr( - CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + ProgramStateRef StateFailed = + State->BindExpr(CE, C.getLocationContext(), + SVB.makeIntVal(*EofVal, C.getASTContext().LongTy)); C.addTransition(StateNotFailed); C.addTransition(StateFailed); ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Use `*EofVal` instead of constant `-1` (PR #73072)
benshi001 wrote: According to line 442 of this file, EOF might be a different value than -1, although it is unusual. https://github.com/llvm/llvm-project/pull/73072 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Use `*EofVal` instead of constant `-1` (PR #73072)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/73072 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/72627 >From 3032cafc2ad43baeeea14de318cd82026b96d035 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 17 Nov 2023 17:22:10 +0800 Subject: [PATCH] [clang][analyzer] Support `fgetc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 79 ++- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 71 - clang/test/Analysis/stream.c | 6 ++ 4 files changed, 138 insertions(+), 19 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..d6651d6f0cd3a31 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -250,9 +250,12 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); - if (!PutVal) -return; - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), *PutVal); - StateNotFailed = - StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); + // Generate a transition for the success state of fputc. + if (!IsRead) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generate a transition for the success state of fgetc. + // If we know the state to be FEOF at fgetc, do not add a success state. + else if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +// The returned 'unsigned char' of `fgetc` is converted to 'int', +// so we need to check if it is in range [0, 255]. +auto CondLow = SVB.evalBinOp(State, BO_GE, RetVal, + SVB.makeZeroVal(C.getASTContext().IntTy), + SVB.getConditionType()) + .getAs(); +auto CondHigh = SVB.evalBinOp(State, BO_LE, RetVal, + SVB.makeIntVal(255, C.getASTContext().IntTy), + SVB.getConditionType()) +.getAs(); +if (!CondLow || !CondHigh) + return; +StateNotFailed = StateNotFailed->assume(*CondLow, true); +if (!StateNotFailed) + return; +StateNotFailed = StateNotFailed->assume(*CondHigh, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); - StreamState NewSS = StreamState::getOpened( - Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StreamErrorState NewES; + if (IsRead) +NewES = +OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + else +NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - C.addTransition(StateFailed); + if (IsRead && OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); } void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 8924103f5046ea2..fc57e8bdc3d30c3 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,6 +48,7 @@ FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *re int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); +int fgetc(FILE *stream); int fputc(int ch, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 5ebdc32bb1b92ff..8bdd483da7c7c43 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,30 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-warning {{Stream might be already closed}} } +void error_fgetc(void) { + FILE *F = tmpfile(); + if (!F) +return; + int
[clang] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker (PR #70539)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/70539 None >From 10bd2b51df19b6a2a76ad326462528d7aebfc548 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 28 Oct 2023 14:41:47 +0800 Subject: [PATCH] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker --- .../Checkers/SimpleStreamChecker.cpp | 64 ++- ...ystem-header-simulator-for-simple-stream.h | 1 + clang/test/Analysis/simple-stream-checks.c| 24 +++ 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index 32d95e944195390..178ebd00ac6377c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -1,4 +1,4 @@ -//===-- SimpleStreamChecker.cpp -*- C++ -*--// +//===-- SimpleStreamChecker.cpp ---*- C++ -*--// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -20,6 +20,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include #include using namespace clang; @@ -31,7 +32,7 @@ typedef SmallVector SymbolVector; struct StreamState { private: enum Kind { Opened, Closed } K; - StreamState(Kind InK) : K(InK) { } + StreamState(Kind InK) : K(InK) {} public: bool isOpened() const { return K == Opened; } @@ -40,25 +41,20 @@ struct StreamState { static StreamState getOpened() { return StreamState(Opened); } static StreamState getClosed() { return StreamState(Closed); } - bool operator==(const StreamState &X) const { -return K == X.K; - } - void Profile(llvm::FoldingSetNodeID &ID) const { -ID.AddInteger(K); - } + bool operator==(const StreamState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } }; -class SimpleStreamChecker : public Checker { - CallDescription OpenFn, CloseFn; +class SimpleStreamChecker +: public Checker { + std::array OpenFns; + CallDescription CloseFn; std::unique_ptr DoubleCloseBugType; std::unique_ptr LeakBugType; - void reportDoubleClose(SymbolRef FileDescSym, - const CallEvent &Call, + void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, CheckerContext &C) const; void reportLeaks(ArrayRef LeakedStreams, CheckerContext &C, @@ -78,9 +74,9 @@ class SimpleStreamChecker : public Checker LeakedStreams, } } -bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ +bool SimpleStreamChecker::guaranteedNotToCloseFile( +const CallEvent &Call) const { // If it's not in a system header, assume it might close a file. if (!Call.isInSystemHeader()) return false; @@ -229,11 +233,9 @@ bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ // If the pointer we are tracking escaped, do not track the symbol as // we cannot reason about it anymore. -ProgramStateRef -SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, -const InvalidatedSymbols &Escaped, -const CallEvent *Call, -PointerEscapeKind Kind) const { +ProgramStateRef SimpleStreamChecker::checkPointerEscape( +ProgramStateRef State, const InvalidatedSymbols &Escaped, +const CallEvent *Call, PointerEscapeKind Kind) const { // If we know that the call cannot close a file, there is nothing to do. if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { return State; diff --git a/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h b/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h index b65b7a6b0e7b020..194a8a269e722ee 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h +++ b/clang/test/Analysis/Inputs/system-header-simulator-for-simple-stream.h @@ -13,6 +13,7 @@ int fputc(int, FILE *); int fputs(const char * restrict, FILE * restrict) __asm("_" "fputs" ); int fclose(FILE *); void exit(int); +FILE *tmpfile(void); // The following is a fake system header function typedef struct __FileStruct { diff --git a/clang/test/Analysis/simple-stream-checks.c b/clang/test/Analysis/simple-stream-checks.c index 767ffaf53fcbfd1..705dde84b893fed 100644 --- a/clang/test/Analysis/simple-stream-checks.c +++ b/clang/test/Analysis/simple-stream-checks.c @@ -94,3 +94,27 @@ void testOverwrite(void) { FILE *fp = fopen("myfile.txt", "w"); fp = 0; } // expected-warning {{Opened file is never closed;
[clang] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker (PR #70539)
benshi001 wrote: There are also many format changes in this PR, since I `clang-format -i` it. https://github.com/llvm/llvm-project/pull/70539 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more information to CallDescriptions in StreamChecker (PR #70540)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/70540 'tmpfile' has only one form that it has no argument. >From 043fbf02975dcc1adb894ae87f6c21dfd5f8479d Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Sat, 28 Oct 2023 14:57:44 +0800 Subject: [PATCH] [clang][analyzer][NFC] Add more information to CallDescriptions in StreamChecker 'tmpfile' has only one form that it has no argument. --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..34df62f073e9a8c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -241,7 +241,7 @@ class StreamChecker : public Checkerhttps://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker (PR #70539)
benshi001 wrote: > If I remember correctly there is somewhere in the clang documentation a file > that refers to this checker, and the purpose of this checker is mostly > documentation. I do not know if it is good to change the code or to extend > this checker, because there is `StreamChecker` that should do the same checks > but in more advanced way. Maybe @haoNoQ can tell more about this. But the `StreamChecker` can not detect `DoubleCloseBugType`, while `SimpleStreamChecker` can. https://github.com/llvm/llvm-project/pull/70539 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more information to CallDescriptions in StreamChecker (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From 323da016fa4fae1254c7c3893c77a60f7d5172aa Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' --- clang/test/Analysis/stream.cpp | 6 ++ 1 file changed, 6 insertions(+) diff --git a/clang/test/Analysis/stream.cpp b/clang/test/Analysis/stream.cpp index 7eca505bcaf5d93..0e0fcbf4ee23128 100644 --- a/clang/test/Analysis/stream.cpp +++ b/clang/test/Analysis/stream.cpp @@ -20,3 +20,9 @@ void f1() { void f2() { FILE *f = fopen("file", "r"); } // expected-warning {{Opened stream never closed. Potential resource leak}} + +extern FILE *tmpfile(const char *path); + +void f3(void) { + FILE *fp = tmpfile("file"); +} // expected-warning {{Opened stream never closed. Potential resource leak}} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
benshi001 wrote: > The change is good but the title is too general, better is for example > "Update CallDescription in StreamChecker for `tmpfile`", and this is not a > NFC (it fixes a problem). Thanks for your reply. I changed this PR to a pure test supplement. And I will create another PR for my change. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add 'tmpfile' as an open function to SimpleStreamChecker (PR #70539)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/70539 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
benshi001 wrote: > I would like to have the new test in `stream.c` (not `stream.cpp`) because > the C++ test file contains only C++ related StreamChecker tests (`tmpfile` > belongs not here). And a "FIXME" could be added to the test to indicate that > this is a faulty behavior (in the current case). The test and code change can > be in a single PR, this is a bugfix and addition of a related test, these are > done usually together. Unfortunately I can not add `extern FILE *tmpfile(char *)` in `stream.c`, since pure C program does not support function overload. So I add it to `stream.cpp`. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From 73d6ce1980fcbb1869b1be522adda6ab5af8a385 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer] Update CallDescription of 'tmpfile' in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 2 +- clang/test/Analysis/stream-wild-function.c| 22 +++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 clang/test/Analysis/stream-wild-function.c diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..34df62f073e9a8c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -241,7 +241,7 @@ class StreamChecker : public Checkerhttps://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
benshi001 wrote: > I would like to have the new test in `stream.c` (not `stream.cpp`) because > the C++ test file contains only C++ related StreamChecker tests (`tmpfile` > belongs not here). And a "FIXME" could be added to the test to indicate that > this is a faulty behavior (in the current case). The test and code change can > be in a single PR, this is a bugfix and addition of a related test, these are > done usually together. I have created a new file `stream-wild-function.c`, other than adding to `stream.c`, since pure C does not support function overload, there could be a conflict between standard `tmpfile` and non standard one. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Add more tests of 'StreamChecker' about 'tmpfile' (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From e89412b0a67927145f49d9cc53562eed77989f99 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer] Update CallDescription of 'tmpfile' & 'fopen' in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 4 ++-- clang/test/Analysis/stream-wild-function.c | 16 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/stream-wild-function.c diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..7e8031c7545f691 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -238,10 +238,10 @@ class StreamChecker : public Checker FnDescriptions = { - {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, {{{"fread"}, 4}, diff --git a/clang/test/Analysis/stream-wild-function.c b/clang/test/Analysis/stream-wild-function.c new file mode 100644 index 000..b059816249de77c --- /dev/null +++ b/clang/test/Analysis/stream-wild-function.c @@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); +FILE *fopen(const char *restrict path); + +void test_fopen(void) { + FILE *fp = fopen("file"); +} + +void test_tmpfile(void) { + FILE *fp = tmpfile("file"); +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Update CallDescription of 'tmpfile' & 'fopen' in StreamChecker (PR #70540)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Update CallDescription of 'tmpfile' & 'fopen' in StreamChecker (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From e7f49e4c01de58c61337c577854b33793da46e8a Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 4 ++-- clang/test/Analysis/stream-non-posix-function.c | 16 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/stream-non-posix-function.c diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..7e8031c7545f691 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -238,10 +238,10 @@ class StreamChecker : public Checker FnDescriptions = { - {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, {{{"fread"}, 4}, diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c new file mode 100644 index 000..2d34d10b239bb4a --- /dev/null +++ b/clang/test/Analysis/stream-non-posix-function.c @@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters. +FILE *fopen(const char *restrict path); // Real 'fopen' should have exactly 2 formal parameters. + +void test_fopen_non_posix(void) { + FILE *fp = fopen("file"); // no-leak: this isn't the standard POSIX fopen, we don't the semantics of this call. +} + +void test_tmpfile_non_posix(void) { + FILE *fp = tmpfile("file"); // no-leak: this isn't the standard POSIX tmpfile, we don't the semantics of this call. +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Update CallDescription of 'tmpfile' & 'fopen' in StreamChecker (PR #70540)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); +FILE *fopen(const char *restrict path); benshi001 wrote: Done. Thanks. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Update CallDescription of 'tmpfile' & 'fopen' in StreamChecker (PR #70540)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); +FILE *fopen(const char *restrict path); + +void test_fopen(void) { + FILE *fp = fopen("file"); +} + +void test_tmpfile(void) { + FILE *fp = tmpfile("file"); +} benshi001 wrote: Done. Thanks. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
benshi001 wrote: @balazske Do you have any further concern? https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Simplify method 'ensureStreamNonNull' of StreamChecker (PR #70927)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/70927 The passed in parameter 'State' is always identical to 'C.getState()'. >From 48dc5f677a5d4eb5e2bc012665f69cc038a20407 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 1 Nov 2023 17:32:10 +0800 Subject: [PATCH] [clang][analyzer] Simplify method 'ensureStreamNonNull' of StreamChecker The passed in parameter 'State' is always identical to 'C.getState()'. --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..a5f8d855f8e06e1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1037,7 +1037,7 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StateNotNull, StateNull; - std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); + std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); if (!StateNotNull && StateNull) { if (ExplodedNode *N = C.generateErrorNode(StateNull)) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Simplify method 'ensureStreamNonNull' of StreamChecker (PR #70927)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70927 >From 1cb32d9bb1b48af8282799d9879b91ae45bf035e Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 1 Nov 2023 17:32:10 +0800 Subject: [PATCH] [clang][analyzer][NFC] Simplify method 'ensureStreamNonNull' of StreamChecker The passed in parameter 'State' is always identical to 'C.getState()'. --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..a5f8d855f8e06e1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1037,7 +1037,7 @@ StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, ConstraintManager &CM = C.getConstraintManager(); ProgramStateRef StateNotNull, StateNull; - std::tie(StateNotNull, StateNull) = CM.assumeDual(C.getState(), *Stream); + std::tie(StateNotNull, StateNull) = CM.assumeDual(State, *Stream); if (!StateNotNull && StateNull) { if (ExplodedNode *N = C.generateErrorNode(StateNull)) { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer]][NFC] Simplify method 'ensureStreamNonNull' of StreamChecker (PR #70927)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/70927 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer]][NFC] Simplify method 'ensureStreamNonNull' of StreamChecker (PR #70927)
benshi001 wrote: > much, and I wonder if you have further ideas refactoring the checker; if so > we could probably bundle up similar changes into this one. Thanks for your reply. Actually I have no plan about improving this checker. Currently I am just reading its code and trying to understand its details. https://github.com/llvm/llvm-project/pull/70927 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer]][NFC] Simplify method 'ensureStreamNonNull' of StreamChecker (PR #70927)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/70927 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters. +FILE *fopen(const char *restrict path); // Real 'fopen' should have exactly 2 formal parameters. + +void test_fopen_non_posix(void) { + FILE *fp = fopen("file"); // no-leak: this isn't the standard POSIX fopen, we don't the semantics of this call. +} + +void test_tmpfile_non_posix(void) { + FILE *fp = tmpfile("file"); // no-leak: this isn't the standard POSIX tmpfile, we don't the semantics of this call. benshi001 wrote: I will supplement more, thanks. https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From bf36469281f52fe34866e6df5eeafdc51d28819c Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 4 ++-- .../test/Analysis/stream-non-posix-function.c | 20 +++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/stream-non-posix-function.c diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..7e8031c7545f691 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -238,10 +238,10 @@ class StreamChecker : public Checker FnDescriptions = { - {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, {{{"fread"}, 4}, diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c new file mode 100644 index 000..3caabb4b097384a --- /dev/null +++ b/clang/test/Analysis/stream-non-posix-function.c @@ -0,0 +1,20 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters. +FILE *fopen(const char *restrict path); // Real 'fopen' should have exactly 2 formal parameters. + +void test_fopen_non_posix(void) { + FILE *fp = fopen("file"); + // no-leak: this isn't the standard POSIX fopen, so we do not treat + // `fp` as a new opened local file stream. +} + +void test_tmpfile_non_posix(void) { + FILE *fp = tmpfile("file"); + // no-leak: this isn't the standard POSIX tmpfile, so we do not treat + // `fp` as a new opened local file stream. +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
@@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters. +FILE *fopen(const char *restrict path); // Real 'fopen' should have exactly 2 formal parameters. + +void test_fopen_non_posix(void) { + FILE *fp = fopen("file"); // no-leak: this isn't the standard POSIX fopen, we don't the semantics of this call. +} + +void test_tmpfile_non_posix(void) { + FILE *fp = tmpfile("file"); // no-leak: this isn't the standard POSIX tmpfile, we don't the semantics of this call. benshi001 wrote: sorry, English is not my native language :) https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/70540 >From bbdb534d7c1300c4b18128fc0ccdd3476c3f6d7f Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 31 Oct 2023 13:05:19 +0800 Subject: [PATCH] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 4 ++-- clang/test/Analysis/stream-non-posix-function.c | 16 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 clang/test/Analysis/stream-non-posix-function.c diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 4b7103c20557cc4..7e8031c7545f691 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -238,10 +238,10 @@ class StreamChecker : public Checker FnDescriptions = { - {{{"fopen"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"fopen"}, 2}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"freopen"}, 3}, {&StreamChecker::preFreopen, &StreamChecker::evalFreopen, 2}}, - {{{"tmpfile"}}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, + {{{"tmpfile"}, 0}, {nullptr, &StreamChecker::evalFopen, ArgNone}}, {{{"fclose"}, 1}, {&StreamChecker::preDefault, &StreamChecker::evalFclose, 0}}, {{{"fread"}, 4}, diff --git a/clang/test/Analysis/stream-non-posix-function.c b/clang/test/Analysis/stream-non-posix-function.c new file mode 100644 index 000..70b3ab25d026532 --- /dev/null +++ b/clang/test/Analysis/stream-non-posix-function.c @@ -0,0 +1,16 @@ +// RUN: %clang_analyze_cc1 -fno-builtin -analyzer-checker=core,alpha.unix.Stream -verify %s +// expected-no-diagnostics + +typedef struct _FILE FILE; + +// These functions are not standard C library functions. +FILE *tmpfile(const char *restrict path); // Real 'tmpfile' should have exactly 0 formal parameters. +FILE *fopen(const char *restrict path); // Real 'fopen' should have exactly 2 formal parameters. + +void test_fopen_non_posix(void) { + FILE *fp = fopen("file"); // no-leak: This isn't the standard POSIX `fopen`, we don't know the semantics of this call. +} + +void test_tmpfile_non_posix(void) { + FILE *fp = tmpfile("file"); // // no-leak: This isn't the standard POSIX `tmpfile`, we don't know the semantics of this call. +} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Restrict 'fopen' & 'tmpfile' modeling to POSIX versions in StreamChecker (PR #70540)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/70540 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From fbfe3492b66492948c9b0220af38d59345c5a793 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/4] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e355431..2c725c01dc285b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d98..409a969a0d4cce 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca7..aa5b6be851773a 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From fab80da9cf06bd0fa73dfdca1d4f2ed23ad060ba Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/4] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285b..a368619fd37d22 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773a..94787874cf8396 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/72627 >From 81973987254f037f1682b7f0cd7a970177051f04 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 17 Nov 2023 17:22:10 +0800 Subject: [PATCH] [clang][analyzer] Support `fgetc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 84 +++ .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 40 + clang/test/Analysis/stream.c | 6 ++ 4 files changed, 96 insertions(+), 35 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..8eca989d7bcdea4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -250,9 +250,12 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); - if (!PutVal) -return; - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), *PutVal); - StateNotFailed = - StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); + // Generate a transition for the success state of fputc. + if (!IsRead) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generate a transition for the success state of fgetc. + // If we know the state to be FEOF at fgetc, do not add a success state. + else if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +// The returned 'unsigned char' of `fgetc` is converted to 'int', +// so we need to check if it is in range [0, 255]. +auto CondLow = +SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +auto CondHigh = +SVB.evalBinOp(State, BO_LE, RetVal, + SVB.makeIntVal(SVB.getBasicValueFactory() + .getMaxValue(ASTC.UnsignedCharTy) + .getLimitedValue(), + ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!CondLow || !CondHigh) + return; +StateNotFailed = StateNotFailed->assume(*CondLow, true); +if (!StateNotFailed) + return; +StateNotFailed = StateNotFailed->assume(*CondHigh, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); - StreamState NewSS = StreamState::getOpened( - Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StreamErrorState NewES; + if (IsRead) +NewES = +OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + else +NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - C.addTransition(StateFailed); + if (IsRead && OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); } void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 8924103f5046ea2..fc57e8bdc3d30c3 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,6 +48,7 @@ FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *re int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); +int fgetc(FILE *stream); int fputc(int ch, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 5ebdc32bb1b92ff..51bb600c6d024e3 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,29 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-war
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/72627 >From 5c3cb2cfbb91ac462633aa223ebeecc1d7790138 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 17 Nov 2023 17:22:10 +0800 Subject: [PATCH] [clang][analyzer] Support `fgetc` in StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 84 +++ .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 39 + clang/test/Analysis/stream.c | 6 ++ 4 files changed, 95 insertions(+), 35 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1d53e59ca067c27..8eca989d7bcdea4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -250,9 +250,12 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); - if (!PutVal) -return; - ProgramStateRef StateNotFailed = - State->BindExpr(CE, C.getLocationContext(), *PutVal); - StateNotFailed = - StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); - C.addTransition(StateNotFailed); + // Generate a transition for the success state of fputc. + if (!IsRead) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generate a transition for the success state of fgetc. + // If we know the state to be FEOF at fgetc, do not add a success state. + else if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +// The returned 'unsigned char' of `fgetc` is converted to 'int', +// so we need to check if it is in range [0, 255]. +auto CondLow = +SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +auto CondHigh = +SVB.evalBinOp(State, BO_LE, RetVal, + SVB.makeIntVal(SVB.getBasicValueFactory() + .getMaxValue(ASTC.UnsignedCharTy) + .getLimitedValue(), + ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!CondLow || !CondHigh) + return; +StateNotFailed = StateNotFailed->assume(*CondLow, true); +if (!StateNotFailed) + return; +StateNotFailed = StateNotFailed->assume(*CondHigh, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); - StreamState NewSS = StreamState::getOpened( - Desc, ErrorFError, /*IsFilePositionIndeterminate*/ true); + StreamErrorState NewES; + if (IsRead) +NewES = +OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; + else +NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - C.addTransition(StateFailed); + if (IsRead && OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); } void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 8924103f5046ea2..fc57e8bdc3d30c3 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -48,6 +48,7 @@ FILE *freopen(const char *restrict pathname, const char *restrict mode, FILE *re int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); +int fgetc(FILE *stream); int fputc(int ch, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 5ebdc32bb1b92ff..38e6b77b9bb5053 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -101,6 +101,28 @@ void error_fwrite(void) { Ret = fwrite(0, 1, 10, F); // expected-war
[clang] [clang][analyzer] Support `fprintf` in the SecuritySyntaxChecker (PR #73247)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/73247 None >From 150d3374346ef2f09e4c656614c283771083 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Thu, 23 Nov 2023 18:22:14 +0800 Subject: [PATCH] [clang][analyzer] Support `fprintf` in the SecuritySyntaxChecker --- .../Checkers/CheckSecuritySyntaxOnly.cpp | 13 +++-- clang/test/Analysis/security-syntax-checks.c | 15 --- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 1a88615f01697cb..e96f8f131e0034b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -162,7 +162,7 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) -.Cases("strncpy", "strncat", "memset", +.Cases("strncpy", "strncat", "memset", "fprintf", &WalkAST::checkDeprecatedOrUnsafeBufferHandling) .Case("drand48", &WalkAST::checkCall_rand) .Case("erand48", &WalkAST::checkCall_rand) @@ -737,10 +737,10 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', //'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', //'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', -//'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset' -//is deprecated since C11. +//'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset', +//'fprintf' is deprecated since C11. // -//Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf', +//Use of 'sprintf', 'fprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', //'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', //'swscanf', 'vsscanf', 'vswscanf' without buffer limitations //is insecure. @@ -768,8 +768,9 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE, int ArgIndex = llvm::StringSwitch(Name) .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0) - .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf", - "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1) + .Cases("fscanf", "fwscanf", "vfscanf", "vfwscanf", "sscanf", + "swscanf", "vsscanf", "vswscanf", 1) + .Cases("sprintf", "vsprintf", "fprintf", 1) .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", "memset", "strncpy", "strncat", DEPR_ONLY) .Default(UNKNOWN_CALL); diff --git a/clang/test/Analysis/security-syntax-checks.c b/clang/test/Analysis/security-syntax-checks.c index 392a65ff5f167ce..2c904f41623fcd1 100644 --- a/clang/test/Analysis/security-syntax-checks.c +++ b/clang/test/Analysis/security-syntax-checks.c @@ -5,15 +5,24 @@ // RUN: %clang_analyze_cc1 %s -verify -std=gnu99 \ // RUN: -analyzer-checker=security.insecureAPI +#include "Inputs/system-header-simulator.h" + +extern FILE *fp; +extern char buf[128]; + void builtin_function_call_crash_fixes(char *c) { __builtin_strncpy(c, "", 6); __builtin_memset(c, '\0', (0)); __builtin_memcpy(c, c, 0); + __builtin_sprintf(buf, "%s", c); + __builtin_fprintf(fp, "%s", c); #if __STDC_VERSION__ > 199901 - // expected-warning@-5{{Call to function 'strncpy' is insecure as it does not provide security checks introduced in the C11 standard.}} - // expected-warning@-5{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard.}} - // expected-warning@-5{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard.}} + // expected-warning@-7{{Call to function 'strncpy' is insecure as it does not provide security checks introduced in the C11 standard.}} + // expected-warning@-7{{Call to function 'memset' is insecure as it does not provide security checks introduced in the C11 standard.}} + // expected-warning@-7{{Call to function 'memcpy' is insecure as it does not provide security checks introduced in the C11 standard.}} + // expected-warning@-7{{Call to function 'sprintf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard.}} + // expected-warning@-7{{Call to function 'fprintf' is insecure as it does not provide bounding of the memory buffer or security checks introduced in the C11 standard.}} #else // expected-no-diagnostics #endif ___ cfe-commits mailing list cfe-commits@lists.llv
[clang] [clang][analyzer] Support `fprintf` in the SecuritySyntaxChecker (PR #73247)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73247 >From 34603a920d5b3ce462c85980c4abff912c1bb22a Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Thu, 23 Nov 2023 18:22:14 +0800 Subject: [PATCH] [clang][analyzer] Support `fprintf` in the SecuritySyntaxChecker --- .../Checkers/CheckSecuritySyntaxOnly.cpp | 76 ++- clang/test/Analysis/security-syntax-checks.c | 15 +++- 2 files changed, 51 insertions(+), 40 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 1a88615f01697cb..dbba12bb4355c3a 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -144,38 +144,39 @@ void WalkAST::VisitCallExpr(CallExpr *CE) { Name = Name.substr(10); // Set the evaluation function by switching on the callee name. - FnCheck evalFunction = llvm::StringSwitch(Name) -.Case("bcmp", &WalkAST::checkCall_bcmp) -.Case("bcopy", &WalkAST::checkCall_bcopy) -.Case("bzero", &WalkAST::checkCall_bzero) -.Case("gets", &WalkAST::checkCall_gets) -.Case("getpw", &WalkAST::checkCall_getpw) -.Case("mktemp", &WalkAST::checkCall_mktemp) -.Case("mkstemp", &WalkAST::checkCall_mkstemp) -.Case("mkdtemp", &WalkAST::checkCall_mkstemp) -.Case("mkstemps", &WalkAST::checkCall_mkstemp) -.Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) -.Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) -.Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", - "vscanf", "vwscanf", "vfscanf", "vfwscanf", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) -.Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", - "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) -.Cases("strncpy", "strncat", "memset", - &WalkAST::checkDeprecatedOrUnsafeBufferHandling) -.Case("drand48", &WalkAST::checkCall_rand) -.Case("erand48", &WalkAST::checkCall_rand) -.Case("jrand48", &WalkAST::checkCall_rand) -.Case("lrand48", &WalkAST::checkCall_rand) -.Case("mrand48", &WalkAST::checkCall_rand) -.Case("nrand48", &WalkAST::checkCall_rand) -.Case("lcong48", &WalkAST::checkCall_rand) -.Case("rand", &WalkAST::checkCall_rand) -.Case("rand_r", &WalkAST::checkCall_rand) -.Case("random", &WalkAST::checkCall_random) -.Case("vfork", &WalkAST::checkCall_vfork) -.Default(nullptr); + FnCheck evalFunction = + llvm::StringSwitch(Name) + .Case("bcmp", &WalkAST::checkCall_bcmp) + .Case("bcopy", &WalkAST::checkCall_bcopy) + .Case("bzero", &WalkAST::checkCall_bzero) + .Case("gets", &WalkAST::checkCall_gets) + .Case("getpw", &WalkAST::checkCall_getpw) + .Case("mktemp", &WalkAST::checkCall_mktemp) + .Case("mkstemp", &WalkAST::checkCall_mkstemp) + .Case("mkdtemp", &WalkAST::checkCall_mkstemp) + .Case("mkstemps", &WalkAST::checkCall_mkstemp) + .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) + .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) + .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf", + "vscanf", "vwscanf", "vfscanf", "vfwscanf", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf", + "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Cases("strncpy", "strncat", "memset", "fprintf", + &WalkAST::checkDeprecatedOrUnsafeBufferHandling) + .Case("drand48", &WalkAST::checkCall_rand) + .Case("erand48", &WalkAST::checkCall_rand) + .Case("jrand48", &WalkAST::checkCall_rand) + .Case("lrand48", &WalkAST::checkCall_rand) + .Case("mrand48", &WalkAST::checkCall_rand) + .Case("nrand48", &WalkAST::checkCall_rand) + .Case("lcong48", &WalkAST::checkCall_rand) + .Case("rand", &WalkAST::checkCall_rand) + .Case("rand_r", &WalkAST::checkCall_rand) + .Case("random", &WalkAST::checkCall_random) + .Case("vfork", &WalkAST::checkCall_vfork) + .Default(nullptr); // If the callee isn't defined, it is not of security concern. // Check and evaluate the call. @@ -737,10 +738,10 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { // Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf', //'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf', //'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf', -//'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat',
[clang] [clang][analyzer] Support `fgetc` in StreamChecker (PR #72627)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/72627 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fprintf` in the SecuritySyntaxChecker (PR #73247)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/73247 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/73335 None >From c59788f4b918c5335efe7444b8d588878565cb26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 +-- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 ++ clang/test/Analysis/stream.c | 6 ++ 4 files changed, 68 insertions(+), 18 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..0e85f1fd0655143 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,16 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); +// Generddate a transition for the success state of `fputc`. +if (SingleChar) { + std::optional PutVal = Call.getArgSVal(0).getAs(); + if (!PutVal) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *PutVal); + StateNotFailed = StateNotFailed->set( + StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); +} +// Generddate a transition for the success state of `fputs`. +else { + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + auto Cond = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), +SVB.getConditionType()) + .getAs(); + if (!Cond) +return; + StateNotFailed = StateNotFailed->assume(*Cond, true); + if (!StateNotFailed) +return; + C.addTransition(StateNotFailed); +} } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. else if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index fc57e8bdc3d30c3..351de8c132fecc7 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -50,6 +50,7 @@ size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); int fgetc(FILE *stream); int fputc(int ch, FILE *stream); +int fputs(const char *str, FILE *stream); int fseek(FILE *__stream, long int __off, int __whence); long int ftell(FILE *__stream); void rewind(FILE *__stream); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 38e6b77b9bb5053..04e6b834fe9d4b5 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -141,6 +141,24 @@ void error_fputc(void) { fputc('A', F); // expected-warning {{Stream might be already closed}} } +void error_fputs(void) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fputs("XYZ", F); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fputs("QWD", F); // no-warning + } else { +clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}} +clang_analyzer_eval(ferror(F)); // expected-warning {{TRUE}} +clang_analyzer_eval(feof(F));// expected-warning {{FALSE}} +fputs("QWD", F); // expected-warning {{might be 'indeterminate'}} + } + fclose(F); + fputs("ABC", F); // expected-warning {{Stream might be already closed}} +} + void freadwrite_zerosize(FILE *F) { size_t Ret; Ret = fwrite(0, 1, 0, F); diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index a7ee9982478bb96..b0f39af8eb2f3a3 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -26,6 +26,12 @@ void check_fputc(void) { fclose(fp); } +void check_fputs(void) { + FILE *fp = tmpfile(); + fputs("ABC", fp); // expected-warning {{Stream
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From c015c1aa18e1048071dc59e637b7e20707b9ae83 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 109 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..b5101f72f316a0e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + SValBuilder &SVB = C.getSValBuilder(); + auto &ASTC = C.getASTContext(); + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + SValBuilder &SVB = C.getSValBuilder(); + auto &ASTC = C.getASTContext(); + // Generddate a transition for the success state of `fputc`. + if (SingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From caa4a548adbab8532b6a3906e574eb9584914631 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 109 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..5e87f824cd780fa 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + SValBuilder &SVB = C.getSValBuilder(); + auto &ASTC = C.getASTContext(); + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + SValBuilder &SVB = C.getSValBuilder(); + auto &ASTC = C.getASTContext(); + // Generddate a transition for the success state of `fputc`. + if (SingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From efb399b878830899cdabc2fe0ee9d6fd398df259 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 109 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..6c0e675173bef55 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + // Generddate a transition for the success state of `fputc`. + if (SingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFsee
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From 9a11834bb5a8d2c0aa97311541ca5a2b9848c859 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 109 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 104 insertions(+), 30 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..b07709c4572ba0e 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); auto CondLow = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), SVB.getConditionType()) @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + // Generddate a transition for the success state of `fputc`. + if (SingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof())
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From 2759d206be48d68dc93ed34efa782daf1097bb26 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 102 insertions(+), 28 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..919e620a58743e1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool SingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + // Generddate a transition for the success state of `fputc`. + if (SingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index fc57e8bdc3d30c3..7ef5f29fbf42cb
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From 881d627cde823edded1c0fd1a72d9ffc2f4ee186 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 18 +++ clang/test/Analysis/stream.c | 6 + 4 files changed, 102 insertions(+), 28 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..17e742e1178df1d 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); @@ -824,20 +817,76 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + // Generddate a transition for the success state of `fputc`. + if (IsSingleChar) { +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + // Generddate a transition for the success state of `fputs`. + else { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // If a (non-EOF) error occurs, the resulting value of the file position + // indicator for the stream is indeterminate. + StreamErrorState NewES = ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *Desc, const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index fc57e8bdc3d30c3..7ef5f29fbf
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From d2a50ecca1d4a3d7c3db5331486488effe2d120c Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 107 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 30 - clang/test/Analysis/stream.c | 6 + 4 files changed, 111 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..f3686d173859c16 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); +ASTContext &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -816,6 +809,8 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, StateNotFailed = StateNotFailed->assume(*CondHigh, true); if (!StateNotFailed) return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); C.addTransition(StateNotFailed); } @@ -824,20 +819,74 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { +// Generate a transition for the success state of `fputc`. +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } else { +// Generate a transition for the success state of `fputs`. +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // positi
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From a335821f73529a0460a848fb12820fb60dc91c09 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 30 - clang/test/Analysis/stream.c | 6 + 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..eccb2063fad10d0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); +ASTContext &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,74 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { +// Generate a transition for the success state of `fputc`. +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } else { +// Generate a transition for the success state of `fputs`. +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription *D
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From ff1ab48c5760302dc55ff889b5b3dfd0b884aa33 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH 1/2] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 30 - clang/test/Analysis/stream.c | 6 + 4 files changed, 109 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..eccb2063fad10d0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); +ASTContext &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,74 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { +// Generate a transition for the success state of `fputc`. +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } else { +// Generate a transition for the success state of `fputs`. +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescriptio
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
benshi001 wrote: > I prefer to have the new test code with > `StreamTesterChecker_make_feof_stream` in a new test function (with > appropriate name like `write_after_eof_is_allowed`), to improve code > maintainability (`fputc` and `fputs` can be in the same function). > > In github PR's usually force push is not the good way to update the code (it > makes difficult to follow what was updated compared to the previous code). It > is better to add new commits for the changes. The final "squash and merge" > will merge all into one commit. I have added a new test `write_after_eof_is_allowed` as you mentioned. Thanks. https://github.com/llvm/llvm-project/pull/73335 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73335 >From b520a4aee77f36622e82b12a32fee54ed20b07d0 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 42 ++- clang/test/Analysis/stream.c | 6 + 4 files changed, 121 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..eccb2063fad10d0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); +ASTContext &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,74 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { +// Generate a transition for the success state of `fputc`. +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } else { +// Generate a transition for the success state of `fputs`. +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened(Desc, ErrorFError, true); + StateFailed = StateFailed->set(StreamSym, NewSS); + C.addTransition(StateFailed); +} + void StreamChecker::preFseek(const FnDescription
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
benshi001 wrote: The CI test seems being pending, so I do a new `push` to refresh it. https://github.com/llvm/llvm-project/pull/73335 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fgets` in the SteamChecker (PR #73638)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/73638 This PR contains two commits, the first one is identical to https://github.com/llvm/llvm-project/pull/73335, please just review the second one about `fgets`. >From 31d7b6c5ddbbe37d59819fd7728e817102a9d24b Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Fri, 24 Nov 2023 22:51:27 +0800 Subject: [PATCH 1/2] [clang][analyzer] Support `fputs` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 105 +- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 42 ++- clang/test/Analysis/stream.c | 6 + 4 files changed, 121 insertions(+), 33 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 8eca989d7bcdea4..eccb2063fad10d0 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,10 +252,13 @@ class StreamChecker : public Checker PutVal = Call.getArgSVal(0).getAs(); -if (!PutVal) - return; -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), *PutVal); -StateNotFailed = -StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); -C.addTransition(StateNotFailed); - } - // Generate a transition for the success state of fgetc. + // Generate a transition for the success state of `fgetc`. // If we know the state to be FEOF at fgetc, do not add a success state. - else if (OldSS->ErrorState != ErrorFEof) { + if (OldSS->ErrorState != ErrorFEof) { NonLoc RetVal = makeRetVal(C, CE).castAs(); ProgramStateRef StateNotFailed = State->BindExpr(CE, C.getLocationContext(), RetVal); SValBuilder &SVB = C.getSValBuilder(); -auto &ASTC = C.getASTContext(); +ASTContext &ASTC = C.getASTContext(); // The returned 'unsigned char' of `fgetc` is converted to 'int', // so we need to check if it is in range [0, 255]. auto CondLow = @@ -824,20 +817,74 @@ void StreamChecker::evalFgetcFputc(const FnDescription *Desc, // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. - StreamErrorState NewES; - if (IsRead) -NewES = -OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; - else -NewES = ErrorFError; + StreamErrorState NewES = + OldSS->ErrorState == ErrorFEof ? ErrorFEof : ErrorFEof | ErrorFError; StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); StateFailed = StateFailed->set(StreamSym, NewSS); - if (IsRead && OldSS->ErrorState != ErrorFEof) + if (OldSS->ErrorState != ErrorFEof) C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); else C.addTransition(StateFailed); } +void StreamChecker::evalFputx(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C, bool IsSingleChar) const { + ProgramStateRef State = C.getState(); + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (!StreamSym) +return; + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + // `fputc` returns the written character on success, otherwise returns EOF. + // `fputs` returns a non negative value on sucecess, otherwise returns EOF. + + if (IsSingleChar) { +// Generate a transition for the success state of `fputc`. +std::optional PutVal = Call.getArgSVal(0).getAs(); +if (!PutVal) + return; +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), *PutVal); +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } else { +// Generate a transition for the success state of `fputs`. +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +SValBuilder &SVB = C.getSValBuilder(); +auto &ASTC = C.getASTContext(); +auto Cond = SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), + SVB.getConditionType()) +.getAs(); +if (!Cond) + return; +StateNotFailed = StateNotFailed->assume(*Cond, true); +if (!StateNotFailed) + return; +StateNotFailed = +StateNotFailed->set(StreamSym, StreamState::getOpened(Desc)); +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. The resulting value of the file + // position indicator for the stream is indeterminate. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamState NewSS = StreamState::getOpened(De
[clang] [clang][analyzer] Support `fputs` in the StreamChecker (PR #73335)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/73335 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fgets` in the SteamChecker (PR #73638)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/73638 >From c6cfabce282dc0a493c1d057c0c83274a1c51634 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Tue, 28 Nov 2023 19:27:21 +0800 Subject: [PATCH] [clang][analyzer] Support `fgets` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 94 --- .../Analysis/Inputs/system-header-simulator.h | 1 + clang/test/Analysis/stream-error.c| 23 + clang/test/Analysis/stream.c | 7 ++ 4 files changed, 89 insertions(+), 36 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index eccb2063fad10d0..a4799b5f762caee 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -252,7 +252,10 @@ class StreamChecker : public CheckerErrorState != ErrorFEof) { -NonLoc RetVal = makeRetVal(C, CE).castAs(); -ProgramStateRef StateNotFailed = -State->BindExpr(CE, C.getLocationContext(), RetVal); -SValBuilder &SVB = C.getSValBuilder(); -ASTContext &ASTC = C.getASTContext(); -// The returned 'unsigned char' of `fgetc` is converted to 'int', -// so we need to check if it is in range [0, 255]. -auto CondLow = -SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), - SVB.getConditionType()) -.getAs(); -auto CondHigh = -SVB.evalBinOp(State, BO_LE, RetVal, - SVB.makeIntVal(SVB.getBasicValueFactory() - .getMaxValue(ASTC.UnsignedCharTy) - .getLimitedValue(), - ASTC.IntTy), - SVB.getConditionType()) -.getAs(); -if (!CondLow || !CondHigh) - return; -StateNotFailed = StateNotFailed->assume(*CondLow, true); -if (!StateNotFailed) - return; -StateNotFailed = StateNotFailed->assume(*CondHigh, true); -if (!StateNotFailed) - return; -C.addTransition(StateNotFailed); +if (SingleChar) { + // Generate a transition for the success state of `fgetc`. + NonLoc RetVal = makeRetVal(C, CE).castAs(); + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), RetVal); + SValBuilder &SVB = C.getSValBuilder(); + ASTContext &ASTC = C.getASTContext(); + // The returned 'unsigned char' of `fgetc` is converted to 'int', + // so we need to check if it is in range [0, 255]. + auto CondLow = + SVB.evalBinOp(State, BO_GE, RetVal, SVB.makeZeroVal(ASTC.IntTy), +SVB.getConditionType()) + .getAs(); + auto CondHigh = + SVB.evalBinOp(State, BO_LE, RetVal, +SVB.makeIntVal(SVB.getBasicValueFactory() + .getMaxValue(ASTC.UnsignedCharTy) + .getLimitedValue(), + ASTC.IntTy), +SVB.getConditionType()) + .getAs(); + if (!CondLow || !CondHigh) +return; + StateNotFailed = StateNotFailed->assume(*CondLow, true); + if (!StateNotFailed) +return; + StateNotFailed = StateNotFailed->assume(*CondHigh, true); + if (!StateNotFailed) +return; + C.addTransition(StateNotFailed); +} else { + // Generate a transition for the success state of `fgets`. + std::optional GetBuf = + Call.getArgSVal(0).getAs(); + if (!GetBuf) +return; + ProgramStateRef StateNotFailed = + State->BindExpr(CE, C.getLocationContext(), *GetBuf); + StateNotFailed = StateNotFailed->set( + StreamSym, StreamState::getOpened(Desc)); + C.addTransition(StateNotFailed); +} } // Add transition for the failed state. - ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + ProgramStateRef StateFailed; + if (SingleChar) +StateFailed = bindInt(*EofVal, State, C, CE); + else +StateFailed = +State->BindExpr(CE, C.getLocationContext(), +C.getSValBuilder().makeNullWithType(CE->getType())); // If a (non-EOF) error occurs, the resulting value of the file position // indicator for the stream is indeterminate. diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7ef5f29fbf42cb1..7089bd8bfc9d983 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -49,6 +49,7 @@ int fclose(FILE *fp); size_t fread(void *restrict, size_t, size_t, FILE *restrict); size_t fwrite(const void *restrict, size_t, size_t, FILE *restrict); int fgetc(FILE *stream); +char *fgets(char *restrict str, int count, FILE *restrict stream
[clang] [clang][analyzer] Support `fgets` in the SteamChecker (PR #73638)
https://github.com/benshi001 edited https://github.com/llvm/llvm-project/pull/73638 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fgets` in the SteamChecker (PR #73638)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/73638 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer][NFC] Supplement comments in `evalFtell` of StreamChecker (PR #74291)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/74291 None >From 2b8903b380c38c2d7f50491da75cec25ff4b801c Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 16:41:46 +0800 Subject: [PATCH] [clang][analyzer][NFC] Supplement comments in `evalFtell` of the StreamChecker --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index a4799b5f762ca..925fc90e35543 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -1072,6 +1072,9 @@ void StreamChecker::evalFtell(const FnDescription *Desc, const CallEvent &Call, ProgramStateRef StateFailed = State->BindExpr( CE, C.getLocationContext(), SVB.makeIntVal(-1, C.getASTContext().LongTy)); + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + // StdLibraryFunctionsChecker can change these states (set the 'errno' state). C.addTransition(StateNotFailed); C.addTransition(StateFailed); } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 created https://github.com/llvm/llvm-project/pull/74296 None >From 2929f07a9ac2c462bf7aed9fe10307ef79659074 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index a4799b5f762ca..5744feba4d35b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d9..409a969a0d4cc 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca..aa5b6be851773 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From fbfe3492b66492948c9b0220af38d59345c5a793 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/5] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e355431..2c725c01dc285b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d98..409a969a0d4cce 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca7..aa5b6be851773a 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From fab80da9cf06bd0fa73dfdca1d4f2ed23ad060ba Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/5] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285b..a368619fd37d22 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773a..94787874cf8396 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 updated https://github.com/llvm/llvm-project/pull/74296 >From fbfe3492b66492948c9b0220af38d59345c5a793 Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Mon, 4 Dec 2023 15:51:20 +0800 Subject: [PATCH 1/6] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp| 16 .../Analysis/Inputs/system-header-simulator.h| 1 + clang/test/Analysis/stream-error.c | 9 + 3 files changed, 26 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 925fc90e355431..2c725c01dc285b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,7 @@ class StreamChecker : public CheckerStreamArgNo) + ->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { +ProgramStateRef State = C.getState(); +if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + C.addTransition(State); + } +} + ProgramStateRef StreamChecker::ensureStreamNonNull(SVal StreamVal, const Expr *StreamE, CheckerContext &C, diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h index 7089bd8bfc9d98..409a969a0d4cce 100644 --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -61,6 +61,7 @@ void clearerr(FILE *stream); int feof(FILE *stream); int ferror(FILE *stream); int fileno(FILE *stream); +int fflush(FILE *stream); size_t strlen(const char *); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index c8332bcbfa8ca7..aa5b6be851773a 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -299,6 +299,15 @@ void error_fseek_0(void) { fclose(F); } +void error_fflush(void) { + FILE *F = tmpfile(); + if (!F) +return; + fclose(F); + fflush(F);// expected-warning {{Stream might be already closed}} + fflush(NULL); // no-warning +} + void error_indeterminate(void) { FILE *F = fopen("file", "r+"); if (!F) >From fab80da9cf06bd0fa73dfdca1d4f2ed23ad060ba Mon Sep 17 00:00:00 2001 From: Ben Shi Date: Wed, 6 Dec 2023 15:47:35 +0800 Subject: [PATCH 2/6] [clang][analyzer] Support `fflush` in the StreamChecker --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 49 --- clang/test/Analysis/stream-error.c| 12 +++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 2c725c01dc285b..a368619fd37d22 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,7 +266,8 @@ class StreamChecker : public CheckerStreamArgNo) - ->isNullPointerConstant(C.getASTContext(), - Expr::NPC_ValueDependentIsNotNull)) { -ProgramStateRef State = C.getState(); -if (State = ensureStreamOpened(getStreamArg(Desc, Call), C, State)) + ProgramStateRef State = C.getState(); + SVal StreamVal = getStreamArg(Desc, Call); + std::optional Stream = StreamVal.getAs(); + if (!Stream) +return; + + ConstraintManager::ProgramStatePair SP = + C.getConstraintManager().assumeDual(State, *Stream); + if (State = SP.first) +if (State = ensureStreamOpened(StreamVal, C, State)) C.addTransition(State); +} + +void StreamChecker::evalFflush(const FnDescription *Desc, const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // We will check the result even if the input is `NULL`, + // but do nothing if the input state is unknown. + SymbolRef StreamSym = getStreamArg(Desc, Call).getAsSymbol(); + if (StreamSym) { +const StreamState *OldSS = State->get(StreamSym); +if (!OldSS) + return; +assertStreamStateOpened(OldSS); } + + const CallExpr *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) +return; + + // `fflush` returns 0 on success, otherwise returns EOF. + ProgramStateRef StateNotFailed = bindInt(0, State, C, CE); + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + + // This function does not affect the stream state. + // Still we add success and failure state with the appropriate return value. + C.addTransition(StateNotFailed); + C.addTransition(StateFailed); } ProgramStateRef diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index aa5b6be851773a..94787874cf8396 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -301,11 +301,17 @@ void error_fseek_0(void) { void error_fflush
[clang] [clang][analyzer] Support `fflush` in the StreamChecker (PR #74296)
https://github.com/benshi001 closed https://github.com/llvm/llvm-project/pull/74296 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits