[PATCH] D154838: [analyzer] Add check for null pointer passed to %p of printf family

Balázs Benics via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Thu Aug 10 10:56:48 PDT 2023


steakhal added a comment.

Looks pretty good.



================
Comment at: clang/docs/analyzer/checkers.rst:704
+optin.portabilityMinor.UnixAPI
+"""""""""""""""""""""""""
+Finds non-severe implementation-defined behavior in UNIX/Posix functions.
----------------
This line should be just as long, as the line above.


================
Comment at: clang/docs/analyzer/checkers.rst:705
+"""""""""""""""""""""""""
+Finds non-severe implementation-defined behavior in UNIX/Posix functions.
+
----------------
Our docs aren't great, but we should have a brief description what the checker detects, basically here it would be "Find null pointers being passed to printf-like functions. Usually, passing null pointers to such functions is implementation defined, thus non-portable. Printf-like functions are: x, y, z."
Illustrate one example for diagnosing a case. Also illustrate one case similar to the bad one, that is fixed (basically ensure that the pointer is not null there).
And that's it.


================
Comment at: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td:45-50
+// The PortabilityMinor package is for checkers that find non-severe portability
+// issues (see also the Portability package). Such checks may be unwanted for
+// developers who want to ignore minor portability issues, hence they are put in
+// a separate package.
+def PortabilityMinorOptIn : Package<"portabilityMinor">, ParentPackage<OptIn>;
+
----------------
I don't think we should create a separate package. A separate checker should be enough in `alpha.unix`, or `alpha.optin`, or just `optin`.


================
Comment at: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td:1671
+
+def UnixAPIPortabilityMinorChecker : Checker<"UnixAPI">,
+  HelpText<"Finds non-severe implementation-defined behavior in UNIX/Posix functions">,
----------------
Usually, the user-facing checker name which is the `<"...">` part there, matches the name of the checker's class name. It helps for maintenance.
I would strongly suggest keeping this naming convention.


================
Comment at: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td:1673
+  HelpText<"Finds non-severe implementation-defined behavior in UNIX/Posix functions">,
+  Documentation<NotDocumented>;
+
----------------
Docs are not really optional these days, but we can come back later once the checker is ironed out.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp:88
 
+class UnixAPIPortabilityMinorChecker
+    : public Checker<check::PreStmt<CallExpr>> {
----------------
Usually, we put separate checkers into their own file.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp:89
+class UnixAPIPortabilityMinorChecker
+    : public Checker<check::PreStmt<CallExpr>> {
+public:
----------------
Prefer using the `check::PreCall` callback over the `PreStmt`.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp:94
+private:
+  mutable std::unique_ptr<BugType> BT_printfPointerConversionSpecifierNULL;
+
----------------
Nowdays, if I'm not mistaken, we just inline initialize these eagerly.
That way you could also avoid marking it mutable.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp:102
+  void
+  ReportPrintfPointerConversionSpecifierNULL(clang::ento::CheckerContext &C,
+                                             ProgramStateRef nullState,
----------------
Feel free to just call it `reportBug`.
Also, in the cpp file, usually we use the namespaces `clang,llvm,ento` already. You don't need to fully-qualify these names.


================
Comment at: clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp:593-594
+
+  if (FName == "printf")
+    CheckPrintfPointerConversionSpecifierNULL(C, CE, 1);
+
----------------
For matching names, we usually use `CallDescriptions` to do it for us.
If think you could use them while also checking if the Call is a `CallEvent::isGlobalCFunction()`. I suspect, you only wanna check those.
I think you should define a `CallDescriptionMap`, because you will also need your specific `data_arg_index`...
Look for uses of such maps and you will get inspired.


================
Comment at: clang/test/Analysis/unix-fns.c:81-92
+#ifndef NULL
+#define NULL ((void*) 0)
+#endif
+
+struct FILE_t;
+typedef struct FILE_t FILE;
+
----------------
I'd highly recommend not touching this file to avoid the bulk change in the expected plist output. Please just introduce a new test file.


================
Comment at: clang/test/Analysis/unix-fns.c:280-289
+// Test pointer constraint.
+void printf_pointer_conversion_specifier_null_pointer_constraint(char *format, void *pointer1) {
+  void *pointer2 = NULL;
+  printf(format, pointer2); // expected-warning{{The result of passing a null pointer to the pointer conversion specifier of the printf family of functions is implementation defined}}
+  if (pointer1 != NULL) {
+    printf(format, pointer1); // no-warning
+    return;
----------------
I would appreciate a test demonstrating that the pointer argument won't get constrained after the `printf` call. I think such case was already discussed.

We should also demonstrate that unknown pointers (which doesn't known to be neither null, nor non-null) won't raise issues.

In the checker-logic, we have the `data_args_index` for each `printf`-like function.
Can you demonstrate that each is handled as expected?

Also have a test when the formatstring itself is null.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D154838/new/

https://reviews.llvm.org/D154838



More information about the cfe-commits mailing list