[clang] [clang][Analyzer][NFC] Simplify preDefault/preFseek/preFreadFwrite of StreamChecker (PR #71394)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-06 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-07 Thread Ben Shi via cfe-commits

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)

2023-11-08 Thread Ben Shi via cfe-commits

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)

2023-11-08 Thread Ben Shi via cfe-commits

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)

2023-11-10 Thread Ben Shi via cfe-commits

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)

2023-12-05 Thread Ben Shi via cfe-commits

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)

2023-12-05 Thread Ben Shi via cfe-commits

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)

2023-12-05 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-12-07 Thread Ben Shi via cfe-commits

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)

2023-11-14 Thread Ben Shi via cfe-commits

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)

2023-11-14 Thread Ben Shi via cfe-commits

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)

2023-11-14 Thread Ben Shi via cfe-commits

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)

2023-11-14 Thread Ben Shi via cfe-commits

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)

2023-11-15 Thread Ben Shi via cfe-commits

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)

2023-11-15 Thread Ben Shi via cfe-commits

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)

2023-11-15 Thread Ben Shi via cfe-commits

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)

2023-11-17 Thread Ben Shi via cfe-commits

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)

2023-11-17 Thread Ben Shi via cfe-commits

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)

2023-11-21 Thread Ben Shi via cfe-commits

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)

2023-11-21 Thread Ben Shi via cfe-commits

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)

2023-11-22 Thread Ben Shi via cfe-commits

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)

2023-11-22 Thread Ben Shi via cfe-commits

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)

2023-10-27 Thread Ben Shi via cfe-commits

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)

2023-10-27 Thread Ben Shi via cfe-commits

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)

2023-10-28 Thread Ben Shi via cfe-commits

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)

2023-10-30 Thread Ben Shi via cfe-commits

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)

2023-10-30 Thread Ben Shi via cfe-commits

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)

2023-10-30 Thread Ben Shi via cfe-commits

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)

2023-10-30 Thread Ben Shi via cfe-commits

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)

2023-10-30 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-10-31 Thread Ben Shi via cfe-commits


@@ -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)

2023-10-31 Thread Ben Shi via cfe-commits


@@ -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)

2023-10-31 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-01 Thread Ben Shi via cfe-commits

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)

2023-11-02 Thread Ben Shi via cfe-commits


@@ -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)

2023-11-02 Thread Ben Shi via cfe-commits

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)

2023-11-02 Thread Ben Shi via cfe-commits


@@ -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)

2023-11-02 Thread Ben Shi via cfe-commits

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)

2023-11-02 Thread Ben Shi via cfe-commits

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)

2023-12-13 Thread Ben Shi via cfe-commits

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)

2023-11-23 Thread Ben Shi via cfe-commits

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)

2023-11-23 Thread Ben Shi via cfe-commits

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)

2023-11-23 Thread Ben Shi via cfe-commits

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)

2023-11-23 Thread Ben Shi via cfe-commits

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)

2023-11-23 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-24 Thread Ben Shi via cfe-commits

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)

2023-11-27 Thread Ben Shi via cfe-commits

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)

2023-11-27 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-28 Thread Ben Shi via cfe-commits

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)

2023-11-29 Thread Ben Shi via cfe-commits

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)

2023-12-04 Thread Ben Shi via cfe-commits

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)

2023-12-04 Thread Ben Shi via cfe-commits

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)

2023-12-20 Thread Ben Shi via cfe-commits

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)

2023-12-21 Thread Ben Shi via cfe-commits

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)

2023-12-21 Thread Ben Shi via cfe-commits

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


  1   2   3   4   >