1 /* An experimental state machine, for tracking bad calls from within 2 signal handlers. 3 4 Copyright (C) 2019-2020 Free Software Foundation, Inc. 5 Contributed by David Malcolm <dmalcolm@redhat.com>. 6 7 This file is part of GCC. 8 9 GCC is free software; you can redistribute it and/or modify it 10 under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3, or (at your option) 12 any later version. 13 14 GCC is distributed in the hope that it will be useful, but 15 WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with GCC; see the file COPYING3. If not see 21 <http://www.gnu.org/licenses/>. */ 22 23 #include "config.h" 24 #include "system.h" 25 #include "coretypes.h" 26 #include "tree.h" 27 #include "function.h" 28 #include "basic-block.h" 29 #include "gimple.h" 30 #include "options.h" 31 #include "bitmap.h" 32 #include "diagnostic-path.h" 33 #include "diagnostic-metadata.h" 34 #include "function.h" 35 #include "analyzer/analyzer.h" 36 #include "diagnostic-event-id.h" 37 #include "analyzer/analyzer-logging.h" 38 #include "analyzer/sm.h" 39 #include "analyzer/pending-diagnostic.h" 40 #include "sbitmap.h" 41 #include "tristate.h" 42 #include "ordered-hash-map.h" 43 #include "selftest.h" 44 #include "analyzer/region-model.h" 45 #include "analyzer/program-state.h" 46 #include "analyzer/checker-path.h" 47 #include "digraph.h" 48 #include "cfg.h" 49 #include "gimple-iterator.h" 50 #include "cgraph.h" 51 #include "analyzer/supergraph.h" 52 #include "analyzer/call-string.h" 53 #include "analyzer/program-point.h" 54 #include "alloc-pool.h" 55 #include "fibonacci_heap.h" 56 #include "analyzer/diagnostic-manager.h" 57 #include "shortest-paths.h" 58 #include "analyzer/exploded-graph.h" 59 #include "analyzer/function-set.h" 60 #include "analyzer/analyzer-selftests.h" 61 62 #if ENABLE_ANALYZER 63 64 namespace ana { 65 66 namespace { 67 68 /* An experimental state machine, for tracking calls to async-signal-unsafe 69 functions from within signal handlers. */ 70 71 class signal_state_machine : public state_machine 72 { 73 public: 74 signal_state_machine (logger *logger); 75 76 bool inherited_state_p () const FINAL OVERRIDE { return false; } 77 78 bool on_stmt (sm_context *sm_ctxt, 79 const supernode *node, 80 const gimple *stmt) const FINAL OVERRIDE; 81 82 void on_condition (sm_context *sm_ctxt, 83 const supernode *node, 84 const gimple *stmt, 85 tree lhs, 86 enum tree_code op, 87 tree rhs) const FINAL OVERRIDE; 88 89 bool can_purge_p (state_t s) const FINAL OVERRIDE; 90 91 /* These states are "global", rather than per-expression. */ 92 93 /* Start state. */ 94 state_t m_start; 95 96 /* State for when we're in a signal handler. */ 97 state_t m_in_signal_handler; 98 99 /* Stop state. */ 100 state_t m_stop; 101 }; 102 103 /* Concrete subclass for describing call to an async-signal-unsafe function 104 from a signal handler. */ 105 106 class signal_unsafe_call 107 : public pending_diagnostic_subclass<signal_unsafe_call> 108 { 109 public: 110 signal_unsafe_call (const signal_state_machine &sm, const gcall *unsafe_call, 111 tree unsafe_fndecl) 112 : m_sm (sm), m_unsafe_call (unsafe_call), m_unsafe_fndecl (unsafe_fndecl) 113 { 114 gcc_assert (m_unsafe_fndecl); 115 } 116 117 const char *get_kind () const FINAL OVERRIDE { return "signal_unsafe_call"; } 118 119 bool operator== (const signal_unsafe_call &other) const 120 { 121 return m_unsafe_call == other.m_unsafe_call; 122 } 123 124 bool emit (rich_location *rich_loc) FINAL OVERRIDE 125 { 126 diagnostic_metadata m; 127 /* CWE-479: Signal Handler Use of a Non-reentrant Function. */ 128 m.add_cwe (479); 129 return warning_meta (rich_loc, m, 130 OPT_Wanalyzer_unsafe_call_within_signal_handler, 131 "call to %qD from within signal handler", 132 m_unsafe_fndecl); 133 } 134 135 label_text describe_state_change (const evdesc::state_change &change) 136 FINAL OVERRIDE 137 { 138 if (change.is_global_p () 139 && change.m_new_state == m_sm.m_in_signal_handler) 140 { 141 function *handler 142 = change.m_event.m_dst_state.m_region_model->get_current_function (); 143 return change.formatted_print ("registering %qD as signal handler", 144 handler->decl); 145 } 146 return label_text (); 147 } 148 149 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 150 { 151 return ev.formatted_print ("call to %qD from within signal handler", 152 m_unsafe_fndecl); 153 } 154 155 private: 156 const signal_state_machine &m_sm; 157 const gcall *m_unsafe_call; 158 tree m_unsafe_fndecl; 159 }; 160 161 /* signal_state_machine's ctor. */ 162 163 signal_state_machine::signal_state_machine (logger *logger) 164 : state_machine ("signal", logger) 165 { 166 m_start = add_state ("start"); 167 m_in_signal_handler = add_state ("in_signal_handler"); 168 m_stop = add_state ("stop"); 169 } 170 171 /* Update MODEL for edges that simulate HANDLER_FUN being called as 172 an signal-handler in response to a signal. */ 173 174 static void 175 update_model_for_signal_handler (region_model *model, 176 function *handler_fun) 177 { 178 /* Purge all state within MODEL. */ 179 *model = region_model (); 180 model->push_frame (handler_fun, NULL, NULL); 181 } 182 183 /* Custom exploded_edge info: entry into a signal-handler. */ 184 185 class signal_delivery_edge_info_t : public exploded_edge::custom_info_t 186 { 187 public: 188 void print (pretty_printer *pp) FINAL OVERRIDE 189 { 190 pp_string (pp, "signal delivered"); 191 } 192 193 void update_model (region_model *model, 194 const exploded_edge &eedge) FINAL OVERRIDE 195 { 196 update_model_for_signal_handler (model, eedge.m_dest->get_function ()); 197 } 198 199 void add_events_to_path (checker_path *emission_path, 200 const exploded_edge &eedge ATTRIBUTE_UNUSED) 201 FINAL OVERRIDE 202 { 203 emission_path->add_event 204 (new custom_event (UNKNOWN_LOCATION, NULL_TREE, 0, 205 "later on," 206 " when the signal is delivered to the process")); 207 } 208 }; 209 210 /* Concrete subclass of custom_transition for modeling registration of a 211 signal handler and the signal handler later being called. */ 212 213 class register_signal_handler : public custom_transition 214 { 215 public: 216 register_signal_handler (const signal_state_machine &sm, 217 tree fndecl) 218 : m_sm (sm), m_fndecl (fndecl) {} 219 220 /* Model a signal-handler FNDECL being called at some later point 221 by injecting an edge to a new function-entry node with an empty 222 callstring, setting the 'in-signal-handler' global state 223 on the node. */ 224 void impl_transition (exploded_graph *eg, 225 exploded_node *src_enode, 226 int sm_idx) FINAL OVERRIDE 227 { 228 function *handler_fun = DECL_STRUCT_FUNCTION (m_fndecl); 229 if (!handler_fun) 230 return; 231 program_point entering_handler 232 = program_point::from_function_entry (eg->get_supergraph (), 233 handler_fun); 234 235 program_state state_entering_handler (eg->get_ext_state ()); 236 update_model_for_signal_handler (state_entering_handler.m_region_model, 237 handler_fun); 238 state_entering_handler.m_checker_states[sm_idx]->set_global_state 239 (m_sm.m_in_signal_handler); 240 241 exploded_node *dst_enode = eg->get_or_create_node (entering_handler, 242 state_entering_handler, 243 NULL); 244 if (dst_enode) 245 eg->add_edge (src_enode, dst_enode, NULL, state_change (), 246 new signal_delivery_edge_info_t ()); 247 } 248 249 const signal_state_machine &m_sm; 250 tree m_fndecl; 251 }; 252 253 /* Get a set of functions that are known to be unsafe to call from an 254 async signal handler. */ 255 256 static function_set 257 get_async_signal_unsafe_fns () 258 { 259 // TODO: populate this list more fully 260 static const char * const async_signal_unsafe_fns[] = { 261 /* This array must be kept sorted. */ 262 "fprintf", 263 "free", 264 "malloc", 265 "printf", 266 "snprintf", 267 "sprintf", 268 "vfprintf", 269 "vprintf", 270 "vsnprintf", 271 "vsprintf" 272 }; 273 const size_t count 274 = sizeof(async_signal_unsafe_fns) / sizeof (async_signal_unsafe_fns[0]); 275 function_set fs (async_signal_unsafe_fns, count); 276 return fs; 277 }; 278 279 /* Return true if FNDECL is known to be unsafe to call from a signal 280 handler. */ 281 282 static bool 283 signal_unsafe_p (tree fndecl) 284 { 285 function_set fs = get_async_signal_unsafe_fns (); 286 return fs.contains_decl_p (fndecl); 287 } 288 289 /* Implementation of state_machine::on_stmt vfunc for signal_state_machine. */ 290 291 bool 292 signal_state_machine::on_stmt (sm_context *sm_ctxt, 293 const supernode *node, 294 const gimple *stmt) const 295 { 296 const state_t global_state = sm_ctxt->get_global_state (); 297 if (global_state == m_start) 298 { 299 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 300 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) 301 if (is_named_call_p (callee_fndecl, "signal", call, 2)) 302 { 303 tree handler = gimple_call_arg (call, 1); 304 if (TREE_CODE (handler) == ADDR_EXPR 305 && TREE_CODE (TREE_OPERAND (handler, 0)) == FUNCTION_DECL) 306 { 307 tree fndecl = TREE_OPERAND (handler, 0); 308 register_signal_handler rsh (*this, fndecl); 309 sm_ctxt->on_custom_transition (&rsh); 310 } 311 } 312 } 313 else if (global_state == m_in_signal_handler) 314 { 315 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 316 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) 317 if (signal_unsafe_p (callee_fndecl)) 318 sm_ctxt->warn_for_state (node, stmt, NULL_TREE, m_in_signal_handler, 319 new signal_unsafe_call (*this, call, 320 callee_fndecl)); 321 } 322 323 return false; 324 } 325 326 /* Implementation of state_machine::on_condition vfunc for 327 signal_state_machine. */ 328 329 void 330 signal_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED, 331 const supernode *node ATTRIBUTE_UNUSED, 332 const gimple *stmt ATTRIBUTE_UNUSED, 333 tree lhs ATTRIBUTE_UNUSED, 334 enum tree_code op ATTRIBUTE_UNUSED, 335 tree rhs ATTRIBUTE_UNUSED) const 336 { 337 // Empty 338 } 339 340 bool 341 signal_state_machine::can_purge_p (state_t s ATTRIBUTE_UNUSED) const 342 { 343 return true; 344 } 345 346 } // anonymous namespace 347 348 /* Internal interface to this file. */ 349 350 state_machine * 351 make_signal_state_machine (logger *logger) 352 { 353 return new signal_state_machine (logger); 354 } 355 356 #if CHECKING_P 357 358 namespace selftest { 359 360 /* Run all of the selftests within this file. */ 361 362 void 363 analyzer_sm_signal_cc_tests () 364 { 365 function_set fs = get_async_signal_unsafe_fns (); 366 fs.assert_sorted (); 367 fs.assert_sane (); 368 } 369 370 } // namespace selftest 371 372 #endif /* CHECKING_P */ 373 374 } // namespace ana 375 376 #endif /* #if ENABLE_ANALYZER */ 377