[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
=?utf-8?q?Balázs_Kéri?= Message-ID: In-Reply-To: steakhal wrote: > This patch breaks a downstream test, like this: > > ```c++ > void test_fscanf_2() { > FILE *F1 = tmpfile(); > if (!F1) > return; > > int a; > unsigned b; > fscanf(F1, "%d %u", , ); > clang_analyzer_dump_int(a); // FP warning: 1st function call argument is an > uninitialized value > fclose(F1); > } > ``` > > The FP is present, even if I guard the dump with `if (ret == 2)`. >I think this can be caused by missing the default evalCall for fscanf, but did >not find the exact reason. Now I know what's going on - after cherry-picking like 15 StreamChecker patches :sweat_smile: (Yea, some fun for the last couple of days) Previously the call was default eval called, thus arguments escaped. This is no longer the case, thus the regions ``, `` won't escape, thus preserves their original values (which was `UndefinedVal()`) This is a regression compared to default eval calling "fscanf". https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
balazske wrote: > This patch breaks a downstream test, like this: I think this can be caused by missing the default `evalCall` for `fscanf`, but did not find the exact reason. https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
=?utf-8?q?Balázs_Kéri?= Message-ID: In-Reply-To: steakhal wrote: This patch breaks a downstream test, like this: ```c++ void test_fscanf_2() { FILE *F1 = tmpfile(); if (!F1) return; int a; unsigned b; fscanf(F1, "%d %u", , ); clang_analyzer_dump_int(a); // FP warning: 1st function call argument is an uninitialized value fclose(F1); } ``` The FP is present, even if I guard the dump with `if (ret == 2)`. https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
https://github.com/balazske closed https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
=?utf-8?q?Bal=C3=A1zs_K=C3=A9ri?= Message-ID: In-Reply-To: https://github.com/NagyDonat approved this pull request. https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
=?utf-8?q?Bal=C3=A1zs_K=C3=A9ri?= Message-ID: In-Reply-To: https://github.com/benshi001 approved this pull request. https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
https://github.com/balazske updated https://github.com/llvm/llvm-project/pull/78180 From aacfc3f06ee51ede08464cb23ec32b210e703b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= Date: Mon, 15 Jan 2024 16:41:34 +0100 Subject: [PATCH 1/2] [clang][analyzer] Add function 'fscanf' to StreamChecker. --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 +++ clang/test/Analysis/stream-error.c| 25 clang/test/Analysis/stream.c | 6 ++ 3 files changed, 92 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 95c7503e49e0d3..ab23a428e9397f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,9 @@ class StreamChecker : public Checker(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + SValBuilder = C.getSValBuilder(); + ASTContext = C.getASTContext(); + + if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto RetGeZero = +SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, + SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) +.getAs(); +if (!RetGeZero) + return; +StateNotFailed = StateNotFailed->assume(*RetGeZero, true); + +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // Error occurs if nothing is matched yet and reading the input fails. + // Error can be EOF, or other error. At "other error" FERROR or 'errno' can + // be set but it is not further specified if all are required to be set. + // Documentation does not mention, but file position will be set to + // indeterminate similarly as at 'fread'. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) + ? ErrorFEof + : ErrorNone | ErrorFEof | ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + if (OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); +} + void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent , CheckerContext ) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 0f7fdddc0dd4cd..2cf46e1d4ad51f 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -208,6 +208,31 @@ void error_fprintf(void) { fprintf(F, "ccc"); // expected-warning {{Stream might be already closed}} } +void error_fscanf(int *A) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fscanf(F, "a%ib", A); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fscanf(F, "bbb"); // no-warning + } else { +if (ferror(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} else if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} +} else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); + fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}} +} + void error_ungetc() { FILE *F = tmpfile(); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index e8f06922bdb2f3..36a9b4e26b07a2 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -45,6 +45,12 @@ void check_fprintf(void) { fclose(fp); } +void check_fscanf(void) { + FILE *fp = tmpfile(); + fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_ungetc(void) { FILE *fp = tmpfile(); ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}} From 128b59ebe046994f38f2e488ce44e891c0b1b0d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= Date: Tue, 16 Jan 2024 11:27:44 +0100 Subject: [PATCH 2/2] added clarifying comment --- clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp | 8 1 file changed, 8 insertions(+) diff --git
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
@@ -975,6 +981,61 @@ void StreamChecker::evalFprintf(const FnDescription *Desc, C.addTransition(StateFailed); } +void StreamChecker::evalFscanf(const FnDescription *Desc, const CallEvent , + CheckerContext ) const { + ProgramStateRef State = C.getState(); + if (Call.getNumArgs() < 2) +return; + 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); + + SValBuilder = C.getSValBuilder(); + ASTContext = C.getASTContext(); + + if (OldSS->ErrorState != ErrorFEof) { benshi001 wrote: Shall we add a comment like ``` 'fscanf' return matched input items, this can be from zero to the total number of input items. If the stream read is successful but match fails, we still think 'fscanf' is success. ``` Something like that. You can reorganize my words. https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
llvmbot wrote: @llvm/pr-subscribers-clang Author: Balázs Kéri (balazske) Changes --- Full diff: https://github.com/llvm/llvm-project/pull/78180.diff 3 Files Affected: - (modified) clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (+61) - (modified) clang/test/Analysis/stream-error.c (+25) - (modified) clang/test/Analysis/stream.c (+6) ``diff diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 95c7503e49e0d32..ab23a428e9397f2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,9 @@ class StreamChecker : public Checker(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + SValBuilder = C.getSValBuilder(); + ASTContext = C.getASTContext(); + + if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto RetGeZero = +SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, + SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) +.getAs(); +if (!RetGeZero) + return; +StateNotFailed = StateNotFailed->assume(*RetGeZero, true); + +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // Error occurs if nothing is matched yet and reading the input fails. + // Error can be EOF, or other error. At "other error" FERROR or 'errno' can + // be set but it is not further specified if all are required to be set. + // Documentation does not mention, but file position will be set to + // indeterminate similarly as at 'fread'. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) + ? ErrorFEof + : ErrorNone | ErrorFEof | ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + if (OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); +} + void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent , CheckerContext ) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 0f7fdddc0dd4cd7..2cf46e1d4ad51f1 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -208,6 +208,31 @@ void error_fprintf(void) { fprintf(F, "ccc"); // expected-warning {{Stream might be already closed}} } +void error_fscanf(int *A) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fscanf(F, "a%ib", A); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fscanf(F, "bbb"); // no-warning + } else { +if (ferror(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} else if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} +} else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); + fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}} +} + void error_ungetc() { FILE *F = tmpfile(); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index e8f06922bdb2f37..36a9b4e26b07a28 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -45,6 +45,12 @@ void check_fprintf(void) { fclose(fp); } +void check_fscanf(void) { + FILE *fp = tmpfile(); + fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_ungetc(void) { FILE *fp = tmpfile(); ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}} `` https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
llvmbot wrote: @llvm/pr-subscribers-clang-static-analyzer-1 Author: Balázs Kéri (balazske) Changes --- Full diff: https://github.com/llvm/llvm-project/pull/78180.diff 3 Files Affected: - (modified) clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp (+61) - (modified) clang/test/Analysis/stream-error.c (+25) - (modified) clang/test/Analysis/stream.c (+6) ``diff diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 95c7503e49e0d32..ab23a428e9397f2 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,9 @@ class StreamChecker : public Checker(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + SValBuilder = C.getSValBuilder(); + ASTContext = C.getASTContext(); + + if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto RetGeZero = +SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, + SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) +.getAs(); +if (!RetGeZero) + return; +StateNotFailed = StateNotFailed->assume(*RetGeZero, true); + +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // Error occurs if nothing is matched yet and reading the input fails. + // Error can be EOF, or other error. At "other error" FERROR or 'errno' can + // be set but it is not further specified if all are required to be set. + // Documentation does not mention, but file position will be set to + // indeterminate similarly as at 'fread'. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) + ? ErrorFEof + : ErrorNone | ErrorFEof | ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + if (OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); +} + void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent , CheckerContext ) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 0f7fdddc0dd4cd7..2cf46e1d4ad51f1 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -208,6 +208,31 @@ void error_fprintf(void) { fprintf(F, "ccc"); // expected-warning {{Stream might be already closed}} } +void error_fscanf(int *A) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fscanf(F, "a%ib", A); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fscanf(F, "bbb"); // no-warning + } else { +if (ferror(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} else if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} +} else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); + fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}} +} + void error_ungetc() { FILE *F = tmpfile(); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index e8f06922bdb2f37..36a9b4e26b07a28 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -45,6 +45,12 @@ void check_fprintf(void) { fclose(fp); } +void check_fscanf(void) { + FILE *fp = tmpfile(); + fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_ungetc(void) { FILE *fp = tmpfile(); ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}} `` https://github.com/llvm/llvm-project/pull/78180 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang][analyzer] Add function 'fscanf' to StreamChecker. (PR #78180)
https://github.com/balazske created https://github.com/llvm/llvm-project/pull/78180 None From aacfc3f06ee51ede08464cb23ec32b210e703b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= Date: Mon, 15 Jan 2024 16:41:34 +0100 Subject: [PATCH] [clang][analyzer] Add function 'fscanf' to StreamChecker. --- .../StaticAnalyzer/Checkers/StreamChecker.cpp | 61 +++ clang/test/Analysis/stream-error.c| 25 clang/test/Analysis/stream.c | 6 ++ 3 files changed, 92 insertions(+) diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 95c7503e49e0d3..ab23a428e9397f 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -266,6 +266,9 @@ class StreamChecker : public Checker(Call.getOriginExpr()); + if (!CE) +return; + + const StreamState *OldSS = State->get(StreamSym); + if (!OldSS) +return; + + assertStreamStateOpened(OldSS); + + SValBuilder = C.getSValBuilder(); + ASTContext = C.getASTContext(); + + if (OldSS->ErrorState != ErrorFEof) { +NonLoc RetVal = makeRetVal(C, CE).castAs(); +ProgramStateRef StateNotFailed = +State->BindExpr(CE, C.getLocationContext(), RetVal); +auto RetGeZero = +SVB.evalBinOp(StateNotFailed, BO_GE, RetVal, + SVB.makeZeroVal(ACtx.IntTy), SVB.getConditionType()) +.getAs(); +if (!RetGeZero) + return; +StateNotFailed = StateNotFailed->assume(*RetGeZero, true); + +C.addTransition(StateNotFailed); + } + + // Add transition for the failed state. + // Error occurs if nothing is matched yet and reading the input fails. + // Error can be EOF, or other error. At "other error" FERROR or 'errno' can + // be set but it is not further specified if all are required to be set. + // Documentation does not mention, but file position will be set to + // indeterminate similarly as at 'fread'. + ProgramStateRef StateFailed = bindInt(*EofVal, State, C, CE); + StreamErrorState NewES = (OldSS->ErrorState == ErrorFEof) + ? ErrorFEof + : ErrorNone | ErrorFEof | ErrorFError; + StreamState NewSS = StreamState::getOpened(Desc, NewES, !NewES.isFEof()); + StateFailed = StateFailed->set(StreamSym, NewSS); + if (OldSS->ErrorState != ErrorFEof) +C.addTransition(StateFailed, constructSetEofNoteTag(C, StreamSym)); + else +C.addTransition(StateFailed); +} + void StreamChecker::evalUngetc(const FnDescription *Desc, const CallEvent , CheckerContext ) const { ProgramStateRef State = C.getState(); diff --git a/clang/test/Analysis/stream-error.c b/clang/test/Analysis/stream-error.c index 0f7fdddc0dd4cd..2cf46e1d4ad51f 100644 --- a/clang/test/Analysis/stream-error.c +++ b/clang/test/Analysis/stream-error.c @@ -208,6 +208,31 @@ void error_fprintf(void) { fprintf(F, "ccc"); // expected-warning {{Stream might be already closed}} } +void error_fscanf(int *A) { + FILE *F = tmpfile(); + if (!F) +return; + int Ret = fscanf(F, "a%ib", A); + if (Ret >= 0) { +clang_analyzer_eval(feof(F) || ferror(F)); // expected-warning {{FALSE}} +fscanf(F, "bbb"); // no-warning + } else { +if (ferror(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} else if (feof(F)) { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{is in EOF state}} + clang_analyzer_eval(feof(F)); // expected-warning {{TRUE}} +} else { + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + fscanf(F, "bbb"); // expected-warning {{might be 'indeterminate'}} +} + } + fclose(F); + fscanf(F, "ccc"); // expected-warning {{Stream might be already closed}} +} + void error_ungetc() { FILE *F = tmpfile(); if (!F) diff --git a/clang/test/Analysis/stream.c b/clang/test/Analysis/stream.c index e8f06922bdb2f3..36a9b4e26b07a2 100644 --- a/clang/test/Analysis/stream.c +++ b/clang/test/Analysis/stream.c @@ -45,6 +45,12 @@ void check_fprintf(void) { fclose(fp); } +void check_fscanf(void) { + FILE *fp = tmpfile(); + fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}} + fclose(fp); +} + void check_ungetc(void) { FILE *fp = tmpfile(); ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}} ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits