1 /* A state machine for detecting misuses of the malloc/free API.
2    Copyright (C) 2019-2020 Free Software Foundation, Inc.
3    Contributed by David Malcolm <dmalcolm@redhat.com>.
4 
5 This file is part of GCC.
6 
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
10 any later version.
11 
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3.  If not see
19 <http://www.gnu.org/licenses/>.  */
20 
21 #include "config.h"
22 #include "system.h"
23 #include "coretypes.h"
24 #include "tree.h"
25 #include "function.h"
26 #include "basic-block.h"
27 #include "gimple.h"
28 #include "options.h"
29 #include "bitmap.h"
30 #include "diagnostic-path.h"
31 #include "diagnostic-metadata.h"
32 #include "function.h"
33 #include "analyzer/analyzer.h"
34 #include "diagnostic-event-id.h"
35 #include "analyzer/analyzer-logging.h"
36 #include "analyzer/sm.h"
37 #include "analyzer/pending-diagnostic.h"
38 
39 #if ENABLE_ANALYZER
40 
41 namespace ana {
42 
43 namespace {
44 
45 /* A state machine for detecting misuses of the malloc/free API.
46 
47    See sm-malloc.dot for an overview (keep this in-sync with that file).  */
48 
49 class malloc_state_machine : public state_machine
50 {
51 public:
52   malloc_state_machine (logger *logger);
53 
inherited_state_p() const54   bool inherited_state_p () const FINAL OVERRIDE { return false; }
55 
56   bool on_stmt (sm_context *sm_ctxt,
57 		const supernode *node,
58 		const gimple *stmt) const FINAL OVERRIDE;
59 
60   void on_phi (sm_context *sm_ctxt,
61 	       const supernode *node,
62 	       const gphi *phi,
63 	       tree rhs) const FINAL OVERRIDE;
64 
65   void on_condition (sm_context *sm_ctxt,
66 		     const supernode *node,
67 		     const gimple *stmt,
68 		     tree lhs,
69 		     enum tree_code op,
70 		     tree rhs) const FINAL OVERRIDE;
71 
72   bool can_purge_p (state_t s) const FINAL OVERRIDE;
73   pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE;
74 
75   /* Start state.  */
76   state_t m_start;
77 
78   /* State for a pointer returned from malloc that hasn't been checked for
79      NULL.
80      It could be a pointer to heap-allocated memory, or could be NULL.  */
81   state_t m_unchecked;
82 
83   /* State for a pointer that's known to be NULL.  */
84   state_t m_null;
85 
86   /* State for a pointer to heap-allocated memory, known to be non-NULL.  */
87   state_t m_nonnull;
88 
89   /* State for a pointer to freed memory.  */
90   state_t m_freed;
91 
92   /* State for a pointer that's known to not be on the heap (e.g. to a local
93      or global).  */
94   state_t m_non_heap; // TODO: or should this be a different state machine?
95   // or do we need child values etc?
96 
97   /* Stop state, for pointers we don't want to track any more.  */
98   state_t m_stop;
99 
100 private:
101   void on_zero_assignment (sm_context *sm_ctxt,
102 			   const supernode *node,
103 			   const gimple *stmt,
104 			   tree lhs) const;
105 };
106 
107 /* Class for diagnostics relating to malloc_state_machine.  */
108 
109 class malloc_diagnostic : public pending_diagnostic
110 {
111 public:
malloc_diagnostic(const malloc_state_machine & sm,tree arg)112   malloc_diagnostic (const malloc_state_machine &sm, tree arg)
113   : m_sm (sm), m_arg (arg)
114   {}
115 
subclass_equal_p(const pending_diagnostic & base_other) const116   bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE
117   {
118     return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg);
119   }
120 
describe_state_change(const evdesc::state_change & change)121   label_text describe_state_change (const evdesc::state_change &change)
122     OVERRIDE
123   {
124     if (change.m_old_state == m_sm.m_start
125 	&& change.m_new_state == m_sm.m_unchecked)
126       // TODO: verify that it's the allocation stmt, not a copy
127       return label_text::borrow ("allocated here");
128     if (change.m_old_state == m_sm.m_unchecked
129 	&& change.m_new_state == m_sm.m_nonnull)
130       return change.formatted_print ("assuming %qE is non-NULL",
131 				     change.m_expr);
132     if (change.m_new_state == m_sm.m_null)
133       {
134 	if (change.m_old_state == m_sm.m_unchecked)
135 	  return change.formatted_print ("assuming %qE is NULL",
136 					 change.m_expr);
137 	else
138 	  return change.formatted_print ("%qE is NULL",
139 					 change.m_expr);
140       }
141 
142     return label_text ();
143   }
144 
145 protected:
146   const malloc_state_machine &m_sm;
147   tree m_arg;
148 };
149 
150 /* Concrete subclass for reporting double-free diagnostics.  */
151 
152 class double_free : public malloc_diagnostic
153 {
154 public:
double_free(const malloc_state_machine & sm,tree arg)155   double_free (const malloc_state_machine &sm, tree arg)
156   : malloc_diagnostic (sm, arg)
157   {}
158 
get_kind() const159   const char *get_kind () const FINAL OVERRIDE { return "double_free"; }
160 
emit(rich_location * rich_loc)161   bool emit (rich_location *rich_loc) FINAL OVERRIDE
162   {
163     auto_diagnostic_group d;
164     diagnostic_metadata m;
165     m.add_cwe (415); /* CWE-415: Double Free.  */
166     return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free,
167 			 "double-%<free%> of %qE", m_arg);
168   }
169 
describe_state_change(const evdesc::state_change & change)170   label_text describe_state_change (const evdesc::state_change &change)
171     FINAL OVERRIDE
172   {
173     if (change.m_new_state == m_sm.m_freed)
174       {
175 	m_first_free_event = change.m_event_id;
176 	return change.formatted_print ("first %qs here", "free");
177       }
178     return malloc_diagnostic::describe_state_change (change);
179   }
180 
describe_call_with_state(const evdesc::call_with_state & info)181   label_text describe_call_with_state (const evdesc::call_with_state &info)
182     FINAL OVERRIDE
183   {
184     if (info.m_state == m_sm.m_freed)
185       return info.formatted_print
186 	("passing freed pointer %qE in call to %qE from %qE",
187 	 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl);
188     return label_text ();
189   }
190 
describe_final_event(const evdesc::final_event & ev)191   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
192   {
193     if (m_first_free_event.known_p ())
194       return ev.formatted_print ("second %qs here; first %qs was at %@",
195 				 "free", "free",
196 				 &m_first_free_event);
197     return ev.formatted_print ("second %qs here", "free");
198   }
199 
200 private:
201   diagnostic_event_id_t m_first_free_event;
202 };
203 
204 /* Abstract subclass for describing possible bad uses of NULL.
205    Responsible for describing the call that could return NULL.  */
206 
207 class possible_null : public malloc_diagnostic
208 {
209 public:
possible_null(const malloc_state_machine & sm,tree arg)210   possible_null (const malloc_state_machine &sm, tree arg)
211   : malloc_diagnostic (sm, arg)
212   {}
213 
describe_state_change(const evdesc::state_change & change)214   label_text describe_state_change (const evdesc::state_change &change)
215     FINAL OVERRIDE
216   {
217     if (change.m_old_state == m_sm.m_start
218 	&& change.m_new_state == m_sm.m_unchecked)
219       {
220 	m_origin_of_unchecked_event = change.m_event_id;
221 	return label_text::borrow ("this call could return NULL");
222       }
223     return malloc_diagnostic::describe_state_change (change);
224   }
225 
describe_return_of_state(const evdesc::return_of_state & info)226   label_text describe_return_of_state (const evdesc::return_of_state &info)
227     FINAL OVERRIDE
228   {
229     if (info.m_state == m_sm.m_unchecked)
230       return info.formatted_print ("possible return of NULL to %qE from %qE",
231 				   info.m_caller_fndecl, info.m_callee_fndecl);
232     return label_text ();
233   }
234 
235 protected:
236   diagnostic_event_id_t m_origin_of_unchecked_event;
237 };
238 
239 /* Concrete subclass for describing dereference of a possible NULL
240    value.  */
241 
242 class possible_null_deref : public possible_null
243 {
244 public:
possible_null_deref(const malloc_state_machine & sm,tree arg)245   possible_null_deref (const malloc_state_machine &sm, tree arg)
246   : possible_null (sm, arg)
247   {}
248 
get_kind() const249   const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; }
250 
emit(rich_location * rich_loc)251   bool emit (rich_location *rich_loc) FINAL OVERRIDE
252   {
253     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
254     diagnostic_metadata m;
255     m.add_cwe (690);
256     return warning_meta (rich_loc, m,
257 			 OPT_Wanalyzer_possible_null_dereference,
258 			 "dereference of possibly-NULL %qE", m_arg);
259   }
260 
describe_final_event(const evdesc::final_event & ev)261   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
262   {
263     if (m_origin_of_unchecked_event.known_p ())
264       return ev.formatted_print ("%qE could be NULL: unchecked value from %@",
265 				 ev.m_expr,
266 				 &m_origin_of_unchecked_event);
267     else
268       return ev.formatted_print ("%qE could be NULL", ev.m_expr);
269   }
270 
271 };
272 
273 /* Subroutine for use by possible_null_arg::emit and null_arg::emit.
274    Issue a note informing that the pertinent argument must be non-NULL.  */
275 
276 static void
inform_nonnull_attribute(tree fndecl,int arg_idx)277 inform_nonnull_attribute (tree fndecl, int arg_idx)
278 {
279   inform (DECL_SOURCE_LOCATION (fndecl),
280 	  "argument %u of %qD must be non-null",
281 	  arg_idx + 1, fndecl);
282   /* Ideally we would use the location of the parm and underline the
283      attribute also - but we don't have the location_t values at this point
284      in the middle-end.
285      For reference, the C and C++ FEs have get_fndecl_argument_location.  */
286 }
287 
288 /* Concrete subclass for describing passing a possibly-NULL value to a
289    function marked with __attribute__((nonnull)).  */
290 
291 class possible_null_arg : public possible_null
292 {
293 public:
possible_null_arg(const malloc_state_machine & sm,tree arg,tree fndecl,int arg_idx)294   possible_null_arg (const malloc_state_machine &sm, tree arg,
295 		     tree fndecl, int arg_idx)
296   : possible_null (sm, arg),
297     m_fndecl (fndecl), m_arg_idx (arg_idx)
298   {}
299 
get_kind() const300   const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; }
301 
subclass_equal_p(const pending_diagnostic & base_other) const302   bool subclass_equal_p (const pending_diagnostic &base_other) const
303   {
304     const possible_null_arg &sub_other
305       = (const possible_null_arg &)base_other;
306     return (same_tree_p (m_arg, sub_other.m_arg)
307 	    && m_fndecl == sub_other.m_fndecl
308 	    && m_arg_idx == sub_other.m_arg_idx);
309   }
310 
311 
emit(rich_location * rich_loc)312   bool emit (rich_location *rich_loc) FINAL OVERRIDE
313   {
314     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
315     auto_diagnostic_group d;
316     diagnostic_metadata m;
317     m.add_cwe (690);
318     bool warned
319       = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument,
320 		      "use of possibly-NULL %qE where non-null expected",
321 		      m_arg);
322     if (warned)
323       inform_nonnull_attribute (m_fndecl, m_arg_idx);
324     return warned;
325   }
326 
describe_final_event(const evdesc::final_event & ev)327   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
328   {
329     if (m_origin_of_unchecked_event.known_p ())
330       return ev.formatted_print ("argument %u (%qE) from %@ could be NULL"
331 				 " where non-null expected",
332 				 m_arg_idx + 1, ev.m_expr,
333 				 &m_origin_of_unchecked_event);
334     else
335       return ev.formatted_print ("argument %u (%qE) could be NULL"
336 				 " where non-null expected",
337 				 m_arg_idx + 1, ev.m_expr);
338   }
339 
340 private:
341   tree m_fndecl;
342   int m_arg_idx;
343 };
344 
345 /* Concrete subclass for describing a dereference of a NULL value.  */
346 
347 class null_deref : public malloc_diagnostic
348 {
349 public:
null_deref(const malloc_state_machine & sm,tree arg)350   null_deref (const malloc_state_machine &sm, tree arg)
351   : malloc_diagnostic (sm, arg) {}
352 
get_kind() const353   const char *get_kind () const FINAL OVERRIDE { return "null_deref"; }
354 
emit(rich_location * rich_loc)355   bool emit (rich_location *rich_loc) FINAL OVERRIDE
356   {
357     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
358     diagnostic_metadata m;
359     m.add_cwe (690);
360     return warning_meta (rich_loc, m,
361 			 OPT_Wanalyzer_null_dereference,
362 			 "dereference of NULL %qE", m_arg);
363   }
364 
describe_return_of_state(const evdesc::return_of_state & info)365   label_text describe_return_of_state (const evdesc::return_of_state &info)
366     FINAL OVERRIDE
367   {
368     if (info.m_state == m_sm.m_null)
369       return info.formatted_print ("return of NULL to %qE from %qE",
370 				   info.m_caller_fndecl, info.m_callee_fndecl);
371     return label_text ();
372   }
373 
describe_final_event(const evdesc::final_event & ev)374   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
375   {
376     return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
377   }
378 };
379 
380 /* Concrete subclass for describing passing a NULL value to a
381    function marked with __attribute__((nonnull)).  */
382 
383 class null_arg : public malloc_diagnostic
384 {
385 public:
null_arg(const malloc_state_machine & sm,tree arg,tree fndecl,int arg_idx)386   null_arg (const malloc_state_machine &sm, tree arg,
387 	    tree fndecl, int arg_idx)
388   : malloc_diagnostic (sm, arg),
389     m_fndecl (fndecl), m_arg_idx (arg_idx)
390   {}
391 
get_kind() const392   const char *get_kind () const FINAL OVERRIDE { return "null_arg"; }
393 
subclass_equal_p(const pending_diagnostic & base_other) const394   bool subclass_equal_p (const pending_diagnostic &base_other) const
395   {
396     const null_arg &sub_other
397       = (const null_arg &)base_other;
398     return (same_tree_p (m_arg, sub_other.m_arg)
399 	    && m_fndecl == sub_other.m_fndecl
400 	    && m_arg_idx == sub_other.m_arg_idx);
401   }
402 
emit(rich_location * rich_loc)403   bool emit (rich_location *rich_loc) FINAL OVERRIDE
404   {
405     /* CWE-690: Unchecked Return Value to NULL Pointer Dereference.  */
406     auto_diagnostic_group d;
407     diagnostic_metadata m;
408     m.add_cwe (690);
409     bool warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument,
410 				"use of NULL %qE where non-null expected",
411 				m_arg);
412     if (warned)
413       inform_nonnull_attribute (m_fndecl, m_arg_idx);
414     return warned;
415   }
416 
describe_final_event(const evdesc::final_event & ev)417   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
418   {
419     return ev.formatted_print ("argument %u (%qE) NULL"
420 			       " where non-null expected",
421 			       m_arg_idx + 1, ev.m_expr);
422   }
423 
424 private:
425   tree m_fndecl;
426   int m_arg_idx;
427 };
428 
429 class use_after_free : public malloc_diagnostic
430 {
431 public:
use_after_free(const malloc_state_machine & sm,tree arg)432   use_after_free (const malloc_state_machine &sm, tree arg)
433   : malloc_diagnostic (sm, arg)
434   {}
435 
get_kind() const436   const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; }
437 
emit(rich_location * rich_loc)438   bool emit (rich_location *rich_loc) FINAL OVERRIDE
439   {
440     /* CWE-416: Use After Free.  */
441     diagnostic_metadata m;
442     m.add_cwe (416);
443     return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free,
444 			 "use after %<free%> of %qE", m_arg);
445   }
446 
describe_state_change(const evdesc::state_change & change)447   label_text describe_state_change (const evdesc::state_change &change)
448     FINAL OVERRIDE
449   {
450     if (change.m_new_state == m_sm.m_freed)
451       {
452 	m_free_event = change.m_event_id;
453 	return label_text::borrow ("freed here");
454       }
455     return malloc_diagnostic::describe_state_change (change);
456   }
457 
describe_final_event(const evdesc::final_event & ev)458   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
459   {
460     if (m_free_event.known_p ())
461       return ev.formatted_print ("use after %<free%> of %qE; freed at %@",
462 				 ev.m_expr, &m_free_event);
463     else
464       return ev.formatted_print ("use after %<free%> of %qE", ev.m_expr);
465   }
466 
467 private:
468   diagnostic_event_id_t m_free_event;
469 };
470 
471 class malloc_leak : public malloc_diagnostic
472 {
473 public:
malloc_leak(const malloc_state_machine & sm,tree arg)474   malloc_leak (const malloc_state_machine &sm, tree arg)
475   : malloc_diagnostic (sm, arg) {}
476 
get_kind() const477   const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; }
478 
emit(rich_location * rich_loc)479   bool emit (rich_location *rich_loc) FINAL OVERRIDE
480   {
481     diagnostic_metadata m;
482     m.add_cwe (401);
483     return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak,
484 			 "leak of %qE", m_arg);
485   }
486 
describe_state_change(const evdesc::state_change & change)487   label_text describe_state_change (const evdesc::state_change &change)
488     FINAL OVERRIDE
489   {
490     if (change.m_new_state == m_sm.m_unchecked)
491       {
492 	m_malloc_event = change.m_event_id;
493 	return label_text::borrow ("allocated here");
494       }
495     return malloc_diagnostic::describe_state_change (change);
496   }
497 
describe_final_event(const evdesc::final_event & ev)498   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
499   {
500     if (m_malloc_event.known_p ())
501       return ev.formatted_print ("%qE leaks here; was allocated at %@",
502 				 ev.m_expr, &m_malloc_event);
503     else
504       return ev.formatted_print ("%qE leaks here", ev.m_expr);
505   }
506 
507 private:
508   diagnostic_event_id_t m_malloc_event;
509 };
510 
511 class free_of_non_heap : public malloc_diagnostic
512 {
513 public:
free_of_non_heap(const malloc_state_machine & sm,tree arg)514   free_of_non_heap (const malloc_state_machine &sm, tree arg)
515   : malloc_diagnostic (sm, arg), m_kind (KIND_UNKNOWN)
516   {
517   }
518 
get_kind() const519   const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; }
520 
subclass_equal_p(const pending_diagnostic & base_other) const521   bool subclass_equal_p (const pending_diagnostic &base_other) const
522     FINAL OVERRIDE
523   {
524     const free_of_non_heap &other = (const free_of_non_heap &)base_other;
525     return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind);
526   }
527 
emit(rich_location * rich_loc)528   bool emit (rich_location *rich_loc) FINAL OVERRIDE
529   {
530     auto_diagnostic_group d;
531     diagnostic_metadata m;
532     m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap.  */
533     switch (m_kind)
534       {
535       default:
536 	gcc_unreachable ();
537       case KIND_UNKNOWN:
538 	return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
539 			     "%<free%> of %qE which points to memory"
540 			     " not on the heap",
541 			     m_arg);
542 	break;
543       case KIND_ALLOCA:
544 	return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap,
545 			     "%<free%> of memory allocated on the stack by"
546 			     " %qs (%qE) will corrupt the heap",
547 			     "alloca", m_arg);
548 	break;
549       }
550   }
551 
describe_state_change(const evdesc::state_change & change)552   label_text describe_state_change (const evdesc::state_change &change)
553     FINAL OVERRIDE
554   {
555     /* Attempt to reconstruct what kind of pointer it is.
556        (It seems neater for this to be a part of the state, though).  */
557     if (TREE_CODE (change.m_expr) == SSA_NAME)
558       {
559 	gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr);
560 	if (gcall *call = dyn_cast <gcall *> (def_stmt))
561 	  {
562 	    if (is_special_named_call_p (call, "alloca", 1)
563 		|| is_special_named_call_p (call, "__builtin_alloca", 1))
564 	      {
565 		m_kind = KIND_ALLOCA;
566 		return label_text::borrow
567 		  ("memory is allocated on the stack here");
568 	      }
569 	  }
570       }
571     return label_text::borrow ("pointer is from here");
572   }
573 
describe_final_event(const evdesc::final_event & ev)574   label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
575   {
576     return ev.formatted_print ("call to %qs here", "free");
577   }
578 
579 private:
580   enum kind
581   {
582     KIND_UNKNOWN,
583     KIND_ALLOCA
584   };
585   enum kind m_kind;
586 };
587 
588 /* malloc_state_machine's ctor.  */
589 
malloc_state_machine(logger * logger)590 malloc_state_machine::malloc_state_machine (logger *logger)
591 : state_machine ("malloc", logger)
592 {
593   m_start = add_state ("start");
594   m_unchecked = add_state ("unchecked");
595   m_null = add_state ("null");
596   m_nonnull = add_state ("nonnull");
597   m_freed = add_state ("freed");
598   m_non_heap = add_state ("non-heap");
599   m_stop = add_state ("stop");
600 }
601 
602 /* Implementation of state_machine::on_stmt vfunc for malloc_state_machine.  */
603 
604 bool
on_stmt(sm_context * sm_ctxt,const supernode * node,const gimple * stmt) const605 malloc_state_machine::on_stmt (sm_context *sm_ctxt,
606 			       const supernode *node,
607 			       const gimple *stmt) const
608 {
609   if (const gcall *call = dyn_cast <const gcall *> (stmt))
610     if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
611       {
612 	if (is_named_call_p (callee_fndecl, "malloc", call, 1)
613 	    || is_named_call_p (callee_fndecl, "calloc", call, 2)
614 	    || is_std_named_call_p (callee_fndecl, "malloc", call, 1)
615 	    || is_std_named_call_p (callee_fndecl, "calloc", call, 2)
616 	    || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1)
617 	    || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2))
618 	  {
619 	    tree lhs = gimple_call_lhs (call);
620 	    if (lhs)
621 	      {
622 		lhs = sm_ctxt->get_readable_tree (lhs);
623 		sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked);
624 	      }
625 	    else
626 	      {
627 		/* TODO: report leak.  */
628 	      }
629 	    return true;
630 	  }
631 
632 	if (is_named_call_p (callee_fndecl, "alloca", call, 1)
633 	    || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1))
634 	  {
635 	    tree lhs = gimple_call_lhs (call);
636 	    if (lhs)
637 	      {
638 		lhs = sm_ctxt->get_readable_tree (lhs);
639 		sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
640 	      }
641 	    return true;
642 	  }
643 
644 	if (is_named_call_p (callee_fndecl, "free", call, 1)
645 	    || is_std_named_call_p (callee_fndecl, "free", call, 1)
646 	    || is_named_call_p (callee_fndecl, "__builtin_free", call, 1))
647 	  {
648 	    tree arg = gimple_call_arg (call, 0);
649 
650 	    arg = sm_ctxt->get_readable_tree (arg);
651 
652 	    /* start/unchecked/nonnull -> freed.  */
653 	    sm_ctxt->on_transition (node, stmt, arg, m_start, m_freed);
654 	    sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_freed);
655 	    sm_ctxt->on_transition (node, stmt, arg, m_nonnull, m_freed);
656 
657 	    /* Keep state "null" as-is, rather than transitioning to "free";
658 	       we don't want to complain about double-free of NULL.  */
659 
660 	    /* freed -> stop, with warning.  */
661 	    sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
662 				     new double_free (*this, arg));
663 	    sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
664 
665 	    /* non-heap -> stop, with warning.  */
666 	    sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap,
667 				     new free_of_non_heap (*this, arg));
668 	    sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop);
669 	    return true;
670 	  }
671 
672 	/* Handle "__attribute__((nonnull))".   */
673 	{
674 	  tree fntype = TREE_TYPE (callee_fndecl);
675 	  bitmap nonnull_args = get_nonnull_args (fntype);
676 	  if (nonnull_args)
677 	    {
678 	      for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
679 		{
680 		  tree arg = gimple_call_arg (stmt, i);
681 		  if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
682 		    continue;
683 		  /* If we have a nonnull-args, and either all pointers, or just
684 		     the specified pointers.  */
685 		  if (bitmap_empty_p (nonnull_args)
686 		      || bitmap_bit_p (nonnull_args, i))
687 		    {
688 		      sm_ctxt->warn_for_state
689 			(node, stmt, arg, m_unchecked,
690 			 new possible_null_arg (*this, arg, callee_fndecl, i));
691 		      sm_ctxt->on_transition (node, stmt, arg, m_unchecked,
692 					      m_nonnull);
693 
694 		      sm_ctxt->warn_for_state
695 			(node, stmt, arg, m_null,
696 			 new null_arg (*this, arg, callee_fndecl, i));
697 		      sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
698 		    }
699 		}
700 	      BITMAP_FREE (nonnull_args);
701 	    }
702 	}
703       }
704 
705   if (tree lhs = is_zero_assignment (stmt))
706     if (any_pointer_p (lhs))
707       on_zero_assignment (sm_ctxt, node, stmt,lhs);
708 
709   if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt))
710     {
711       enum tree_code op = gimple_assign_rhs_code (assign_stmt);
712       if (op == ADDR_EXPR)
713 	{
714 	  tree lhs = gimple_assign_lhs (assign_stmt);
715 	  if (lhs)
716 	    {
717 	      lhs = sm_ctxt->get_readable_tree (lhs);
718 	      sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap);
719 	    }
720 	}
721     }
722 
723   /* Handle dereferences.  */
724   for (unsigned i = 0; i < gimple_num_ops (stmt); i++)
725     {
726       tree op = gimple_op (stmt, i);
727       if (!op)
728 	continue;
729       if (TREE_CODE (op) == COMPONENT_REF)
730 	op = TREE_OPERAND (op, 0);
731 
732       if (TREE_CODE (op) == MEM_REF)
733 	{
734 	  tree arg = TREE_OPERAND (op, 0);
735 	  arg = sm_ctxt->get_readable_tree (arg);
736 
737 	  sm_ctxt->warn_for_state (node, stmt, arg, m_unchecked,
738 				   new possible_null_deref (*this, arg));
739 	  sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull);
740 
741 	  sm_ctxt->warn_for_state (node, stmt, arg, m_null,
742 				   new null_deref (*this, arg));
743 	  sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop);
744 
745 	  sm_ctxt->warn_for_state (node, stmt, arg, m_freed,
746 				   new use_after_free (*this, arg));
747 	  sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop);
748 	}
749     }
750   return false;
751 }
752 
753 /* Implementation of state_machine::on_phi vfunc for malloc_state_machine.  */
754 
755 void
on_phi(sm_context * sm_ctxt,const supernode * node,const gphi * phi,tree rhs) const756 malloc_state_machine::on_phi (sm_context *sm_ctxt,
757 			      const supernode *node,
758 			      const gphi *phi,
759 			      tree rhs) const
760 {
761   if (zerop (rhs))
762     {
763       tree lhs = gimple_phi_result (phi);
764       on_zero_assignment (sm_ctxt, node, phi, lhs);
765     }
766 }
767 
768 /* Implementation of state_machine::on_condition vfunc for malloc_state_machine.
769    Potentially transition state 'unchecked' to 'nonnull' or to 'null'.  */
770 
771 void
on_condition(sm_context * sm_ctxt,const supernode * node,const gimple * stmt,tree lhs,enum tree_code op,tree rhs) const772 malloc_state_machine::on_condition (sm_context *sm_ctxt,
773 				    const supernode *node,
774 				    const gimple *stmt,
775 				    tree lhs,
776 				    enum tree_code op,
777 				    tree rhs) const
778 {
779   if (!zerop (rhs))
780     return;
781 
782   if (!any_pointer_p (lhs))
783     return;
784   if (!any_pointer_p (rhs))
785     return;
786 
787   if (op == NE_EXPR)
788     {
789       log ("got 'ARG != 0' match");
790       sm_ctxt->on_transition (node, stmt,
791 			      lhs, m_unchecked, m_nonnull);
792     }
793   else if (op == EQ_EXPR)
794     {
795       log ("got 'ARG == 0' match");
796       sm_ctxt->on_transition (node, stmt,
797 			      lhs, m_unchecked, m_null);
798     }
799 }
800 
801 /* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine.
802    Don't allow purging of pointers in state 'unchecked' or 'nonnull'
803    (to avoid false leak reports).  */
804 
805 bool
can_purge_p(state_t s) const806 malloc_state_machine::can_purge_p (state_t s) const
807 {
808   return s != m_unchecked && s != m_nonnull;
809 }
810 
811 /* Implementation of state_machine::on_leak vfunc for malloc_state_machine
812    (for complaining about leaks of pointers in state 'unchecked' and
813    'nonnull').  */
814 
815 pending_diagnostic *
on_leak(tree var) const816 malloc_state_machine::on_leak (tree var) const
817 {
818   return new malloc_leak (*this, var);
819 }
820 
821 /* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that
822    assign zero to LHS.  */
823 
824 void
on_zero_assignment(sm_context * sm_ctxt,const supernode * node,const gimple * stmt,tree lhs) const825 malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt,
826 					  const supernode *node,
827 					  const gimple *stmt,
828 					  tree lhs) const
829 {
830   sm_ctxt->on_transition (node, stmt, lhs, m_start, m_null);
831   sm_ctxt->on_transition (node, stmt, lhs, m_unchecked, m_null);
832   sm_ctxt->on_transition (node, stmt, lhs, m_nonnull, m_null);
833   sm_ctxt->on_transition (node, stmt, lhs, m_freed, m_null);
834 }
835 
836 } // anonymous namespace
837 
838 /* Internal interface to this file. */
839 
840 state_machine *
make_malloc_state_machine(logger * logger)841 make_malloc_state_machine (logger *logger)
842 {
843   return new malloc_state_machine (logger);
844 }
845 
846 } // namespace ana
847 
848 #endif /* #if ENABLE_ANALYZER */
849