3d3dc8
From f9b19c9d4caaf870b30cce8a3d6be79eda099c4e Mon Sep 17 00:00:00 2001
3d3dc8
From: Frantisek Sumsal <frantisek@sumsal.cz>
3d3dc8
Date: Sun, 5 Dec 2021 16:11:35 +0100
3d3dc8
Subject: [PATCH] lgtm: detect more possible problematic scenarios
3d3dc8
3d3dc8
1) don't ignore stack-allocated variables, since they may hide
3d3dc8
   heap-allocated stuff (compound types)
3d3dc8
2) check if there's a return between the variable declaration and its
3d3dc8
   initialization; if so, treat the variable as uninitialized
3d3dc8
3) introduction of 2) increased the query runtime exponentially, so
3d3dc8
   introduce some optimizations to bring it back to some reasonable
3d3dc8
   values
3d3dc8
3d3dc8
(cherry picked from commit c8fec8bf9b086f9fc7638db0f1a613a00d7c63a3)
3d3dc8
3d3dc8
Related: #2017033
3d3dc8
---
3d3dc8
 .../UninitializedVariableWithCleanup.ql       | 48 ++++++++++---------
3d3dc8
 1 file changed, 25 insertions(+), 23 deletions(-)
3d3dc8
3d3dc8
diff --git a/.lgtm/cpp-queries/UninitializedVariableWithCleanup.ql b/.lgtm/cpp-queries/UninitializedVariableWithCleanup.ql
3d3dc8
index 8c24b6d8f1..6b3b62f8bc 100644
3d3dc8
--- a/.lgtm/cpp-queries/UninitializedVariableWithCleanup.ql
3d3dc8
+++ b/.lgtm/cpp-queries/UninitializedVariableWithCleanup.ql
3d3dc8
@@ -16,24 +16,6 @@
3d3dc8
 import cpp
3d3dc8
 import semmle.code.cpp.controlflow.StackVariableReachability
3d3dc8
 
3d3dc8
-/**
3d3dc8
- * Auxiliary predicate: Types that don't require initialization
3d3dc8
- * before they are used, since they're stack-allocated.
3d3dc8
- */
3d3dc8
-predicate allocatedType(Type t) {
3d3dc8
-  /* Arrays: "int foo[1]; foo[0] = 42;" is ok. */
3d3dc8
-  t instanceof ArrayType
3d3dc8
-  or
3d3dc8
-  /* Structs: "struct foo bar; bar.baz = 42" is ok. */
3d3dc8
-  t instanceof Class
3d3dc8
-  or
3d3dc8
-  /* Typedefs to other allocated types are fine. */
3d3dc8
-  allocatedType(t.(TypedefType).getUnderlyingType())
3d3dc8
-  or
3d3dc8
-  /* Type specifiers don't affect whether or not a type is allocated. */
3d3dc8
-  allocatedType(t.getUnspecifiedType())
3d3dc8
-}
3d3dc8
-
3d3dc8
 /** Auxiliary predicate: List cleanup functions we want to explicitly ignore
3d3dc8
   * since they don't do anything illegal even when the variable is uninitialized
3d3dc8
   */
3d3dc8
@@ -47,13 +29,11 @@ predicate cleanupFunctionDenyList(string fun) {
3d3dc8
  */
3d3dc8
 DeclStmt declWithNoInit(LocalVariable v) {
3d3dc8
   result.getADeclaration() = v and
3d3dc8
-  not exists(v.getInitializer()) and
3d3dc8
+  not v.hasInitializer() and
3d3dc8
   /* The variable has __attribute__((__cleanup__(...))) set */
3d3dc8
   v.getAnAttribute().hasName("cleanup") and
3d3dc8
   /* Check if the cleanup function is not on a deny list */
3d3dc8
-  not exists(Attribute a | a = v.getAnAttribute() and a.getName() = "cleanup" | cleanupFunctionDenyList(a.getAnArgument().getValueText())) and
3d3dc8
-  /* The type of the variable is not stack-allocated. */
3d3dc8
-  exists(Type t | t = v.getType() | not allocatedType(t))
3d3dc8
+  not cleanupFunctionDenyList(v.getAnAttribute().getAnArgument().getValueText())
3d3dc8
 }
3d3dc8
 
3d3dc8
 class UninitialisedLocalReachability extends StackVariableReachability {
3d3dc8
@@ -78,7 +58,29 @@ class UninitialisedLocalReachability extends StackVariableReachability {
3d3dc8
   override predicate isBarrier(ControlFlowNode node, StackVariable v) {
3d3dc8
     // only report the _first_ possibly uninitialized use
3d3dc8
     useOfVar(v, node) or
3d3dc8
-    definitionBarrier(v, node)
3d3dc8
+    (
3d3dc8
+      /* If there's an return statement somewhere between the variable declaration
3d3dc8
+       * and a possible definition, don't accept is as a valid initialization.
3d3dc8
+       *
3d3dc8
+       * E.g.:
3d3dc8
+       * _cleanup_free_ char *x;
3d3dc8
+       * ...
3d3dc8
+       * if (...)
3d3dc8
+       *    return;
3d3dc8
+       * ...
3d3dc8
+       * x = malloc(...);
3d3dc8
+       *
3d3dc8
+       * is not a valid initialization, since we might return from the function
3d3dc8
+       * _before_ the actual iniitialization (emphasis on _might_, since we
3d3dc8
+       * don't know if the return statement might ever evaluate to true).
3d3dc8
+       */
3d3dc8
+      definitionBarrier(v, node) and
3d3dc8
+      not exists(ReturnStmt rs |
3d3dc8
+                 /* The attribute check is "just" a complexity optimization */
3d3dc8
+                 v.getFunction() = rs.getEnclosingFunction() and v.getAnAttribute().hasName("cleanup") |
3d3dc8
+                 rs.getLocation().isBefore(node.getLocation())
3d3dc8
+      )
3d3dc8
+    )
3d3dc8
   }
3d3dc8
 }
3d3dc8