Branch data Line data Source code
1 : : /* A state machine for detecting misuses of POSIX file descriptor APIs.
2 : : Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 : : Contributed by Immad Mir <mir@sourceware.org>.
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 : : #define INCLUDE_MEMORY
23 : : #include "system.h"
24 : : #include "coretypes.h"
25 : : #include "make-unique.h"
26 : : #include "tree.h"
27 : : #include "function.h"
28 : : #include "basic-block.h"
29 : : #include "gimple.h"
30 : : #include "options.h"
31 : : #include "diagnostic-path.h"
32 : : #include "analyzer/analyzer.h"
33 : : #include "diagnostic-event-id.h"
34 : : #include "analyzer/analyzer-logging.h"
35 : : #include "analyzer/sm.h"
36 : : #include "analyzer/pending-diagnostic.h"
37 : : #include "analyzer/function-set.h"
38 : : #include "analyzer/analyzer-selftests.h"
39 : : #include "stringpool.h"
40 : : #include "attribs.h"
41 : : #include "analyzer/call-string.h"
42 : : #include "analyzer/program-point.h"
43 : : #include "analyzer/store.h"
44 : : #include "analyzer/region-model.h"
45 : : #include "bitmap.h"
46 : : #include "analyzer/program-state.h"
47 : : #include "analyzer/supergraph.h"
48 : : #include "analyzer/analyzer-language.h"
49 : : #include "analyzer/call-details.h"
50 : : #include "analyzer/call-info.h"
51 : :
52 : : #if ENABLE_ANALYZER
53 : :
54 : : namespace ana {
55 : :
56 : : namespace {
57 : :
58 : : /* An enum for distinguishing between three different access modes. */
59 : :
60 : : enum access_mode
61 : : {
62 : : READ_WRITE,
63 : : READ_ONLY,
64 : : WRITE_ONLY
65 : : };
66 : :
67 : : enum access_directions
68 : : {
69 : : DIRS_READ_WRITE,
70 : : DIRS_READ,
71 : : DIRS_WRITE
72 : : };
73 : :
74 : : /* An enum for distinguishing between dup, dup2 and dup3. */
75 : : enum dup
76 : : {
77 : : DUP_1,
78 : : DUP_2,
79 : : DUP_3
80 : : };
81 : :
82 : : /* Enum for use by -Wanalyzer-fd-phase-mismatch. */
83 : :
84 : : enum expected_phase
85 : : {
86 : : EXPECTED_PHASE_CAN_TRANSFER, /* can "read"/"write". */
87 : : EXPECTED_PHASE_CAN_BIND,
88 : : EXPECTED_PHASE_CAN_LISTEN,
89 : : EXPECTED_PHASE_CAN_ACCEPT,
90 : : EXPECTED_PHASE_CAN_CONNECT
91 : : };
92 : :
93 : : class fd_state_machine : public state_machine
94 : : {
95 : : public:
96 : : fd_state_machine (logger *logger);
97 : :
98 : : bool
99 : 1209454 : inherited_state_p () const final override
100 : : {
101 : 1209454 : return false;
102 : : }
103 : :
104 : : state_machine::state_t
105 : 1208148 : get_default_state (const svalue *sval) const final override
106 : : {
107 : 1208148 : if (tree cst = sval->maybe_get_constant ())
108 : : {
109 : 185230 : if (TREE_CODE (cst) == INTEGER_CST)
110 : : {
111 : 180725 : int val = TREE_INT_CST_LOW (cst);
112 : 180725 : if (val >= 0)
113 : 176166 : return m_constant_fd;
114 : : else
115 : 4559 : return m_invalid;
116 : : }
117 : : }
118 : 1027423 : return m_start;
119 : : }
120 : :
121 : : bool on_stmt (sm_context *sm_ctxt, const supernode *node,
122 : : const gimple *stmt) const final override;
123 : :
124 : : void on_condition (sm_context *sm_ctxt, const supernode *node,
125 : : const gimple *stmt, const svalue *lhs, const tree_code op,
126 : : const svalue *rhs) const final override;
127 : :
128 : : bool can_purge_p (state_t s) const final override;
129 : : std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
130 : :
131 : : bool is_unchecked_fd_p (state_t s) const;
132 : : bool is_valid_fd_p (state_t s) const;
133 : : bool is_socket_fd_p (state_t s) const;
134 : : bool is_datagram_socket_fd_p (state_t s) const;
135 : : bool is_stream_socket_fd_p (state_t s) const;
136 : : bool is_closed_fd_p (state_t s) const;
137 : : bool is_constant_fd_p (state_t s) const;
138 : : bool is_readonly_fd_p (state_t s) const;
139 : : bool is_writeonly_fd_p (state_t s) const;
140 : : enum access_mode get_access_mode_from_flag (int flag) const;
141 : : /* Function for one-to-one correspondence between valid
142 : : and unchecked states. */
143 : : state_t valid_to_unchecked_state (state_t state) const;
144 : :
145 : : void mark_as_valid_fd (region_model *model,
146 : : sm_state_map *smap,
147 : : const svalue *fd_sval,
148 : : const extrinsic_state &ext_state) const;
149 : :
150 : : bool on_socket (const call_details &cd,
151 : : bool successful,
152 : : sm_context *sm_ctxt,
153 : : const extrinsic_state &ext_state) const;
154 : : bool on_bind (const call_details &cd,
155 : : bool successful,
156 : : sm_context *sm_ctxt,
157 : : const extrinsic_state &ext_state) const;
158 : : bool on_listen (const call_details &cd,
159 : : bool successful,
160 : : sm_context *sm_ctxt,
161 : : const extrinsic_state &ext_state) const;
162 : : bool on_accept (const call_details &cd,
163 : : bool successful,
164 : : sm_context *sm_ctxt,
165 : : const extrinsic_state &ext_state) const;
166 : : bool on_connect (const call_details &cd,
167 : : bool successful,
168 : : sm_context *sm_ctxt,
169 : : const extrinsic_state &ext_state) const;
170 : :
171 : : /* State for a constant file descriptor (>= 0) */
172 : : state_t m_constant_fd;
173 : :
174 : : /* States representing a file descriptor that hasn't yet been
175 : : checked for validity after opening, for three different
176 : : access modes. */
177 : : state_t m_unchecked_read_write;
178 : :
179 : : state_t m_unchecked_read_only;
180 : :
181 : : state_t m_unchecked_write_only;
182 : :
183 : : /* States for representing a file descriptor that is known to be valid (>=
184 : : 0), for three different access modes. */
185 : : state_t m_valid_read_write;
186 : :
187 : : state_t m_valid_read_only;
188 : :
189 : : state_t m_valid_write_only;
190 : :
191 : : /* State for a file descriptor that is known to be invalid (< 0). */
192 : : state_t m_invalid;
193 : :
194 : : /* State for a file descriptor that has been closed. */
195 : : state_t m_closed;
196 : :
197 : : /* States for FDs relating to socket APIs. */
198 : :
199 : : /* Result of successful "socket" with SOCK_DGRAM. */
200 : : state_t m_new_datagram_socket;
201 : : /* Result of successful "socket" with SOCK_STREAM. */
202 : : state_t m_new_stream_socket;
203 : : /* Result of successful "socket" with unknown type. */
204 : : state_t m_new_unknown_socket;
205 : :
206 : : /* The above after a successful call to "bind". */
207 : : state_t m_bound_datagram_socket;
208 : : state_t m_bound_stream_socket;
209 : : state_t m_bound_unknown_socket;
210 : :
211 : : /* A bound socket after a successful call to "listen" (stream or unknown). */
212 : : state_t m_listening_stream_socket;
213 : :
214 : : /* (i) the new FD as a result of a succesful call to "accept" on a
215 : : listening socket (via a passive open), or
216 : : (ii) an active socket after a successful call to "connect"
217 : : (via an active open). */
218 : : state_t m_connected_stream_socket;
219 : :
220 : : /* State for a file descriptor that we do not want to track anymore . */
221 : : state_t m_stop;
222 : :
223 : : /* Stashed constant values from the frontend. These could be NULL. */
224 : : tree m_O_ACCMODE;
225 : : tree m_O_RDONLY;
226 : : tree m_O_WRONLY;
227 : : tree m_SOCK_STREAM;
228 : : tree m_SOCK_DGRAM;
229 : :
230 : : private:
231 : : void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
232 : : const gcall *call) const;
233 : : void on_creat (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
234 : : const gcall *call) const;
235 : : void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
236 : : const gcall *call) const;
237 : : void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
238 : : const gcall *call, const tree callee_fndecl) const;
239 : : void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
240 : : const gcall *call, const tree callee_fndecl) const;
241 : : void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,
242 : : const gimple *stmt, const gcall *call,
243 : : const tree callee_fndecl,
244 : : enum access_directions access_fn) const;
245 : :
246 : : void make_valid_transitions_on_condition (sm_context *sm_ctxt,
247 : : const supernode *node,
248 : : const gimple *stmt,
249 : : const svalue *lhs) const;
250 : : void make_invalid_transitions_on_condition (sm_context *sm_ctxt,
251 : : const supernode *node,
252 : : const gimple *stmt,
253 : : const svalue *lhs) const;
254 : : void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,
255 : : const gimple *stmt, const gcall *call,
256 : : const tree callee_fndecl, const char *attr_name,
257 : : access_directions fd_attr_access_dir) const;
258 : : void check_for_dup (sm_context *sm_ctxt, const supernode *node,
259 : : const gimple *stmt, const gcall *call, const tree callee_fndecl,
260 : : enum dup kind) const;
261 : :
262 : : state_t get_state_for_socket_type (const svalue *socket_type_sval) const;
263 : :
264 : : bool check_for_socket_fd (const call_details &cd,
265 : : bool successful,
266 : : sm_context *sm_ctxt,
267 : : const svalue *fd_sval,
268 : : const supernode *node,
269 : : state_t old_state,
270 : : bool *complained = NULL) const;
271 : : bool check_for_new_socket_fd (const call_details &cd,
272 : : bool successful,
273 : : sm_context *sm_ctxt,
274 : : const svalue *fd_sval,
275 : : const supernode *node,
276 : : state_t old_state,
277 : : enum expected_phase expected_phase) const;
278 : : };
279 : :
280 : : /* Base diagnostic class relative to fd_state_machine. */
281 : 0 : class fd_diagnostic : public pending_diagnostic
282 : : {
283 : : public:
284 : 423 : fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
285 : : {
286 : : }
287 : :
288 : : bool
289 : 191 : subclass_equal_p (const pending_diagnostic &base_other) const override
290 : : {
291 : 191 : return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
292 : : }
293 : :
294 : : label_text
295 : 288 : describe_state_change (const evdesc::state_change &change) override
296 : : {
297 : 288 : if (change.m_old_state == m_sm.get_start_state ())
298 : : {
299 : 180 : if (change.m_new_state == m_sm.m_unchecked_read_write
300 : 180 : || change.m_new_state == m_sm.m_valid_read_write)
301 : 8 : return change.formatted_print ("opened here as read-write");
302 : :
303 : 172 : if (change.m_new_state == m_sm.m_unchecked_read_only
304 : 160 : || change.m_new_state == m_sm.m_valid_read_only)
305 : 12 : return change.formatted_print ("opened here as read-only");
306 : :
307 : 160 : if (change.m_new_state == m_sm.m_unchecked_write_only
308 : 146 : || change.m_new_state == m_sm.m_valid_write_only)
309 : 14 : return change.formatted_print ("opened here as write-only");
310 : :
311 : 146 : if (change.m_new_state == m_sm.m_new_datagram_socket)
312 : 18 : return change.formatted_print ("datagram socket created here");
313 : :
314 : 128 : if (change.m_new_state == m_sm.m_new_stream_socket)
315 : 22 : return change.formatted_print ("stream socket created here");
316 : :
317 : 106 : if (change.m_new_state == m_sm.m_new_unknown_socket
318 : 18 : || change.m_new_state == m_sm.m_connected_stream_socket)
319 : 96 : return change.formatted_print ("socket created here");
320 : : }
321 : :
322 : 118 : if (change.m_new_state == m_sm.m_bound_datagram_socket)
323 : 4 : return change.formatted_print ("datagram socket bound here");
324 : :
325 : 114 : if (change.m_new_state == m_sm.m_bound_stream_socket)
326 : 4 : return change.formatted_print ("stream socket bound here");
327 : :
328 : 110 : if (change.m_new_state == m_sm.m_bound_unknown_socket
329 : 98 : || change.m_new_state == m_sm.m_connected_stream_socket)
330 : 12 : return change.formatted_print ("socket bound here");
331 : :
332 : 98 : if (change.m_new_state == m_sm.m_listening_stream_socket)
333 : 8 : return change.formatted_print
334 : 8 : ("stream socket marked as passive here via %qs", "listen");
335 : :
336 : 90 : if (change.m_new_state == m_sm.m_closed)
337 : 0 : return change.formatted_print ("closed here");
338 : :
339 : 190 : if (m_sm.is_unchecked_fd_p (change.m_old_state)
340 : 180 : && m_sm.is_valid_fd_p (change.m_new_state))
341 : : {
342 : 80 : if (change.m_expr)
343 : 80 : return change.formatted_print (
344 : 80 : "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
345 : : else
346 : 0 : return change.formatted_print ("assuming a valid file descriptor");
347 : : }
348 : :
349 : 20 : if (m_sm.is_unchecked_fd_p (change.m_old_state)
350 : 10 : && change.m_new_state == m_sm.m_invalid)
351 : : {
352 : 10 : if (change.m_expr)
353 : 10 : return change.formatted_print (
354 : : "assuming %qE is an invalid file descriptor (< 0)",
355 : 10 : change.m_expr);
356 : : else
357 : 0 : return change.formatted_print ("assuming an invalid file descriptor");
358 : : }
359 : :
360 : 0 : return label_text ();
361 : : }
362 : :
363 : : diagnostic_event::meaning
364 : 120 : get_meaning_for_state_change (
365 : : const evdesc::state_change &change) const final override
366 : : {
367 : 120 : if (change.m_old_state == m_sm.get_start_state ()
368 : 120 : && (m_sm.is_unchecked_fd_p (change.m_new_state)
369 : 30 : || change.m_new_state == m_sm.m_new_datagram_socket
370 : 28 : || change.m_new_state == m_sm.m_new_stream_socket
371 : 26 : || change.m_new_state == m_sm.m_new_unknown_socket))
372 : 60 : return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
373 : 60 : diagnostic_event::NOUN_resource);
374 : 60 : if (change.m_new_state == m_sm.m_closed)
375 : 30 : return diagnostic_event::meaning (diagnostic_event::VERB_release,
376 : 30 : diagnostic_event::NOUN_resource);
377 : 30 : return diagnostic_event::meaning ();
378 : : }
379 : :
380 : : protected:
381 : : const fd_state_machine &m_sm;
382 : : tree m_arg;
383 : : };
384 : :
385 : 0 : class fd_param_diagnostic : public fd_diagnostic
386 : : {
387 : : public:
388 : 12 : fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
389 : : const char *attr_name, int arg_idx)
390 : 12 : : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
391 : 12 : m_attr_name (attr_name), m_arg_idx (arg_idx)
392 : : {
393 : : }
394 : :
395 : 220 : fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
396 : 220 : : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
397 : 220 : m_attr_name (NULL), m_arg_idx (-1)
398 : : {
399 : : }
400 : :
401 : : bool
402 : 172 : subclass_equal_p (const pending_diagnostic &base_other) const override
403 : : {
404 : 172 : const fd_param_diagnostic &sub_other
405 : : = (const fd_param_diagnostic &)base_other;
406 : 172 : return (same_tree_p (m_arg, sub_other.m_arg)
407 : 172 : && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
408 : 172 : && m_arg_idx == sub_other.m_arg_idx
409 : 344 : && ((m_attr_name)
410 : 12 : ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
411 : 172 : : true));
412 : : }
413 : :
414 : : void
415 : 65 : inform_filedescriptor_attribute (access_directions fd_dir)
416 : : {
417 : :
418 : 65 : if (m_attr_name)
419 : 12 : switch (fd_dir)
420 : : {
421 : 6 : case DIRS_READ_WRITE:
422 : 12 : inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
423 : : "argument %d of %qD must be an open file descriptor, due to "
424 : : "%<__attribute__((%s(%d)))%>",
425 : 6 : m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
426 : 6 : break;
427 : 3 : case DIRS_WRITE:
428 : 6 : inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
429 : : "argument %d of %qD must be a readable file descriptor, due "
430 : : "to %<__attribute__((%s(%d)))%>",
431 : 3 : m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
432 : 3 : break;
433 : 3 : case DIRS_READ:
434 : 6 : inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
435 : : "argument %d of %qD must be a writable file descriptor, due "
436 : : "to %<__attribute__((%s(%d)))%>",
437 : 3 : m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
438 : 3 : break;
439 : : }
440 : 65 : }
441 : :
442 : : protected:
443 : : tree m_callee_fndecl;
444 : : const char *m_attr_name;
445 : : /* ARG_IDX is 0-based. */
446 : : int m_arg_idx;
447 : : };
448 : :
449 : 0 : class fd_leak : public fd_diagnostic
450 : : {
451 : : public:
452 : 156 : fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
453 : :
454 : : const char *
455 : 613 : get_kind () const final override
456 : : {
457 : 613 : return "fd_leak";
458 : : }
459 : :
460 : : int
461 : 113 : get_controlling_option () const final override
462 : : {
463 : 113 : return OPT_Wanalyzer_fd_leak;
464 : : }
465 : :
466 : : bool
467 : 87 : emit (diagnostic_emission_context &ctxt) final override
468 : : {
469 : : /*CWE-775: Missing Release of File Descriptor or Handle after Effective
470 : : Lifetime
471 : : */
472 : 87 : ctxt.add_cwe (775);
473 : 87 : if (m_arg)
474 : 69 : return ctxt.warn ("leak of file descriptor %qE", m_arg);
475 : : else
476 : 18 : return ctxt.warn ("leak of file descriptor");
477 : : }
478 : :
479 : : label_text
480 : 162 : describe_state_change (const evdesc::state_change &change) final override
481 : : {
482 : 324 : if (m_sm.is_unchecked_fd_p (change.m_new_state))
483 : : {
484 : 42 : m_open_event = change.m_event_id;
485 : 42 : return label_text::borrow ("opened here");
486 : : }
487 : :
488 : 120 : return fd_diagnostic::describe_state_change (change);
489 : : }
490 : :
491 : : label_text
492 : 174 : describe_final_event (const evdesc::final_event &ev) final override
493 : : {
494 : 174 : if (m_open_event.known_p ())
495 : : {
496 : 42 : if (ev.m_expr)
497 : 42 : return ev.formatted_print ("%qE leaks here; was opened at %@",
498 : 42 : ev.m_expr, &m_open_event);
499 : : else
500 : 0 : return ev.formatted_print ("leaks here; was opened at %@",
501 : 0 : &m_open_event);
502 : : }
503 : : else
504 : : {
505 : 132 : if (ev.m_expr)
506 : 96 : return ev.formatted_print ("%qE leaks here", ev.m_expr);
507 : : else
508 : 36 : return ev.formatted_print ("leaks here");
509 : : }
510 : : }
511 : :
512 : : private:
513 : : diagnostic_event_id_t m_open_event;
514 : : };
515 : :
516 : 0 : class fd_access_mode_mismatch : public fd_param_diagnostic
517 : : {
518 : : public:
519 : 6 : fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
520 : : enum access_directions fd_dir,
521 : : const tree callee_fndecl, const char *attr_name,
522 : : int arg_idx)
523 : 6 : : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
524 : 6 : m_fd_dir (fd_dir)
525 : :
526 : : {
527 : : }
528 : :
529 : 6 : fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
530 : : enum access_directions fd_dir,
531 : : const tree callee_fndecl)
532 : 6 : : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
533 : : {
534 : : }
535 : :
536 : : const char *
537 : 48 : get_kind () const final override
538 : : {
539 : 48 : return "fd_access_mode_mismatch";
540 : : }
541 : :
542 : : int
543 : 24 : get_controlling_option () const final override
544 : : {
545 : 24 : return OPT_Wanalyzer_fd_access_mode_mismatch;
546 : : }
547 : :
548 : : bool
549 : 12 : emit (diagnostic_emission_context &ctxt) final override
550 : : {
551 : 12 : bool warned;
552 : 12 : switch (m_fd_dir)
553 : : {
554 : 5 : case DIRS_READ:
555 : 5 : warned = ctxt.warn ("%qE on read-only file descriptor %qE",
556 : : m_callee_fndecl, m_arg);
557 : 5 : break;
558 : 7 : case DIRS_WRITE:
559 : 7 : warned = ctxt.warn ("%qE on write-only file descriptor %qE",
560 : : m_callee_fndecl, m_arg);
561 : 7 : break;
562 : 0 : default:
563 : 0 : gcc_unreachable ();
564 : : }
565 : 12 : if (warned)
566 : 12 : inform_filedescriptor_attribute (m_fd_dir);
567 : 12 : return warned;
568 : : }
569 : :
570 : : label_text
571 : 24 : describe_final_event (const evdesc::final_event &ev) final override
572 : : {
573 : 24 : switch (m_fd_dir)
574 : : {
575 : 10 : case DIRS_READ:
576 : 10 : return ev.formatted_print ("%qE on read-only file descriptor %qE",
577 : 10 : m_callee_fndecl, m_arg);
578 : 14 : case DIRS_WRITE:
579 : 14 : return ev.formatted_print ("%qE on write-only file descriptor %qE",
580 : 14 : m_callee_fndecl, m_arg);
581 : 0 : default:
582 : 0 : gcc_unreachable ();
583 : : }
584 : : }
585 : :
586 : : private:
587 : : enum access_directions m_fd_dir;
588 : : };
589 : :
590 : 0 : class fd_double_close : public fd_diagnostic
591 : : {
592 : : public:
593 : 35 : fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
594 : : {
595 : : }
596 : :
597 : : const char *
598 : 140 : get_kind () const final override
599 : : {
600 : 140 : return "fd_double_close";
601 : : }
602 : :
603 : : int
604 : 70 : get_controlling_option () const final override
605 : : {
606 : 70 : return OPT_Wanalyzer_fd_double_close;
607 : : }
608 : : bool
609 : 35 : emit (diagnostic_emission_context &ctxt) final override
610 : : {
611 : : // CWE-1341: Multiple Releases of Same Resource or Handle
612 : 35 : ctxt.add_cwe (1341);
613 : 35 : return ctxt.warn ("double %<close%> of file descriptor %qE", m_arg);
614 : : }
615 : :
616 : : label_text
617 : 170 : describe_state_change (const evdesc::state_change &change) override
618 : : {
619 : 340 : if (m_sm.is_unchecked_fd_p (change.m_new_state))
620 : 60 : return label_text::borrow ("opened here");
621 : :
622 : 110 : if (change.m_new_state == m_sm.m_closed)
623 : : {
624 : 70 : m_first_close_event = change.m_event_id;
625 : 70 : return change.formatted_print ("first %qs here", "close");
626 : : }
627 : 40 : return fd_diagnostic::describe_state_change (change);
628 : : }
629 : :
630 : : label_text
631 : 70 : describe_final_event (const evdesc::final_event &ev) final override
632 : : {
633 : 70 : if (m_first_close_event.known_p ())
634 : 70 : return ev.formatted_print ("second %qs here; first %qs was at %@",
635 : 70 : "close", "close", &m_first_close_event);
636 : 0 : return ev.formatted_print ("second %qs here", "close");
637 : : }
638 : :
639 : : private:
640 : : diagnostic_event_id_t m_first_close_event;
641 : : };
642 : :
643 : 0 : class fd_use_after_close : public fd_param_diagnostic
644 : : {
645 : : public:
646 : 3 : fd_use_after_close (const fd_state_machine &sm, tree arg,
647 : : const tree callee_fndecl, const char *attr_name,
648 : : int arg_idx)
649 : 3 : : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
650 : : {
651 : : }
652 : :
653 : 35 : fd_use_after_close (const fd_state_machine &sm, tree arg,
654 : : const tree callee_fndecl)
655 : 35 : : fd_param_diagnostic (sm, arg, callee_fndecl)
656 : : {
657 : : }
658 : :
659 : : const char *
660 : 61 : get_kind () const final override
661 : : {
662 : 61 : return "fd_use_after_close";
663 : : }
664 : :
665 : : int
666 : 45 : get_controlling_option () const final override
667 : : {
668 : 45 : return OPT_Wanalyzer_fd_use_after_close;
669 : : }
670 : :
671 : : bool
672 : 7 : emit (diagnostic_emission_context &ctxt) final override
673 : : {
674 : 7 : bool warned = ctxt.warn ("%qE on closed file descriptor %qE",
675 : : m_callee_fndecl, m_arg);
676 : 7 : if (warned)
677 : 7 : inform_filedescriptor_attribute (DIRS_READ_WRITE);
678 : 7 : return warned;
679 : : }
680 : :
681 : : label_text
682 : 32 : describe_state_change (const evdesc::state_change &change) override
683 : : {
684 : 64 : if (m_sm.is_unchecked_fd_p (change.m_new_state))
685 : 14 : return label_text::borrow ("opened here");
686 : :
687 : 18 : if (change.m_new_state == m_sm.m_closed)
688 : : {
689 : 14 : m_first_close_event = change.m_event_id;
690 : 14 : return change.formatted_print ("closed here");
691 : : }
692 : :
693 : 4 : return fd_diagnostic::describe_state_change (change);
694 : : }
695 : :
696 : : label_text
697 : 14 : describe_final_event (const evdesc::final_event &ev) final override
698 : : {
699 : 14 : if (m_first_close_event.known_p ())
700 : 14 : return ev.formatted_print (
701 : : "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
702 : 14 : m_arg, "close", &m_first_close_event);
703 : : else
704 : 0 : return ev.formatted_print ("%qE on closed file descriptor %qE",
705 : 0 : m_callee_fndecl, m_arg);
706 : : }
707 : :
708 : : private:
709 : : diagnostic_event_id_t m_first_close_event;
710 : : };
711 : :
712 : 0 : class fd_use_without_check : public fd_param_diagnostic
713 : : {
714 : : public:
715 : 3 : fd_use_without_check (const fd_state_machine &sm, tree arg,
716 : : const tree callee_fndecl, const char *attr_name,
717 : : int arg_idx)
718 : 3 : : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
719 : : {
720 : : }
721 : :
722 : 95 : fd_use_without_check (const fd_state_machine &sm, tree arg,
723 : : const tree callee_fndecl)
724 : 95 : : fd_param_diagnostic (sm, arg, callee_fndecl)
725 : : {
726 : : }
727 : :
728 : : const char *
729 : 280 : get_kind () const final override
730 : : {
731 : 280 : return "fd_use_without_check";
732 : : }
733 : :
734 : : int
735 : 144 : get_controlling_option () const final override
736 : : {
737 : 144 : return OPT_Wanalyzer_fd_use_without_check;
738 : : }
739 : :
740 : : bool
741 : 46 : emit (diagnostic_emission_context &ctxt) final override
742 : : {
743 : 46 : bool warned = ctxt.warn ("%qE on possibly invalid file descriptor %qE",
744 : : m_callee_fndecl, m_arg);
745 : 46 : if (warned)
746 : 46 : inform_filedescriptor_attribute (DIRS_READ_WRITE);
747 : 46 : return warned;
748 : : }
749 : :
750 : : label_text
751 : 74 : describe_state_change (const evdesc::state_change &change) override
752 : : {
753 : 148 : if (m_sm.is_unchecked_fd_p (change.m_new_state))
754 : : {
755 : 64 : m_first_open_event = change.m_event_id;
756 : 64 : return label_text::borrow ("opened here");
757 : : }
758 : :
759 : 10 : return fd_diagnostic::describe_state_change (change);
760 : : }
761 : :
762 : : label_text
763 : 92 : describe_final_event (const evdesc::final_event &ev) final override
764 : : {
765 : 92 : if (m_first_open_event.known_p ())
766 : 64 : return ev.formatted_print (
767 : : "%qE could be invalid: unchecked value from %@", m_arg,
768 : 64 : &m_first_open_event);
769 : : else
770 : 28 : return ev.formatted_print ("%qE could be invalid", m_arg);
771 : : }
772 : :
773 : : private:
774 : : diagnostic_event_id_t m_first_open_event;
775 : : };
776 : :
777 : : /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch. */
778 : :
779 : 0 : class fd_phase_mismatch : public fd_param_diagnostic
780 : : {
781 : : public:
782 : 70 : fd_phase_mismatch (const fd_state_machine &sm, tree arg,
783 : : const tree callee_fndecl,
784 : : state_machine::state_t actual_state,
785 : : enum expected_phase expected_phase)
786 : 70 : : fd_param_diagnostic (sm, arg, callee_fndecl),
787 : 70 : m_actual_state (actual_state),
788 : 70 : m_expected_phase (expected_phase)
789 : : {
790 : 70 : gcc_assert (m_sm.is_socket_fd_p (actual_state));
791 : 70 : switch (expected_phase)
792 : : {
793 : 4 : case EXPECTED_PHASE_CAN_TRANSFER:
794 : 4 : gcc_assert (actual_state == m_sm.m_new_stream_socket
795 : : || actual_state == m_sm.m_bound_stream_socket
796 : : || actual_state == m_sm.m_listening_stream_socket);
797 : : break;
798 : 16 : case EXPECTED_PHASE_CAN_BIND:
799 : 16 : gcc_assert (actual_state == m_sm.m_bound_datagram_socket
800 : : || actual_state == m_sm.m_bound_stream_socket
801 : : || actual_state == m_sm.m_bound_unknown_socket
802 : : || actual_state == m_sm.m_connected_stream_socket
803 : : || actual_state == m_sm.m_listening_stream_socket);
804 : : break;
805 : 44 : case EXPECTED_PHASE_CAN_LISTEN:
806 : 44 : gcc_assert (actual_state == m_sm.m_new_stream_socket
807 : : || actual_state == m_sm.m_new_unknown_socket
808 : : || actual_state == m_sm.m_connected_stream_socket);
809 : : break;
810 : 4 : case EXPECTED_PHASE_CAN_ACCEPT:
811 : 4 : gcc_assert (actual_state == m_sm.m_new_stream_socket
812 : : || actual_state == m_sm.m_new_unknown_socket
813 : : || actual_state == m_sm.m_bound_stream_socket
814 : : || actual_state == m_sm.m_bound_unknown_socket
815 : : || actual_state == m_sm.m_connected_stream_socket);
816 : : break;
817 : 2 : case EXPECTED_PHASE_CAN_CONNECT:
818 : 2 : gcc_assert (actual_state == m_sm.m_bound_datagram_socket
819 : : || actual_state == m_sm.m_bound_stream_socket
820 : : || actual_state == m_sm.m_bound_unknown_socket
821 : : || actual_state == m_sm.m_listening_stream_socket
822 : : || actual_state == m_sm.m_connected_stream_socket);
823 : : break;
824 : : }
825 : 70 : }
826 : :
827 : : const char *
828 : 235 : get_kind () const final override
829 : : {
830 : 235 : return "fd_phase_mismatch";
831 : : }
832 : :
833 : : bool
834 : 70 : subclass_equal_p (const pending_diagnostic &base_other) const final override
835 : : {
836 : 70 : const fd_phase_mismatch &sub_other = (const fd_phase_mismatch &)base_other;
837 : 70 : if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
838 : : return false;
839 : 70 : return (m_actual_state == sub_other.m_actual_state
840 : 70 : && m_expected_phase == sub_other.m_expected_phase);
841 : : }
842 : :
843 : : int
844 : 95 : get_controlling_option () const final override
845 : : {
846 : 95 : return OPT_Wanalyzer_fd_phase_mismatch;
847 : : }
848 : :
849 : : bool
850 : 25 : emit (diagnostic_emission_context &ctxt) final override
851 : : {
852 : : /* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */
853 : 25 : ctxt.add_cwe (666);
854 : 25 : return ctxt.warn ("%qE on file descriptor %qE in wrong phase",
855 : 25 : m_callee_fndecl, m_arg);
856 : : }
857 : :
858 : : label_text
859 : 50 : describe_final_event (const evdesc::final_event &ev) final override
860 : : {
861 : 50 : switch (m_expected_phase)
862 : : {
863 : 6 : case EXPECTED_PHASE_CAN_TRANSFER:
864 : 6 : {
865 : 6 : if (m_actual_state == m_sm.m_new_stream_socket)
866 : 2 : return ev.formatted_print
867 : : ("%qE expects a stream socket to be connected via %qs"
868 : : " but %qE has not yet been bound",
869 : 2 : m_callee_fndecl, "accept", m_arg);
870 : 4 : if (m_actual_state == m_sm.m_bound_stream_socket)
871 : 0 : return ev.formatted_print
872 : : ("%qE expects a stream socket to be connected via %qs"
873 : : " but %qE is not yet listening",
874 : 0 : m_callee_fndecl, "accept", m_arg);
875 : 4 : if (m_actual_state == m_sm.m_listening_stream_socket)
876 : 4 : return ev.formatted_print
877 : : ("%qE expects a stream socket to be connected via"
878 : : " the return value of %qs"
879 : : " but %qE is listening; wrong file descriptor?",
880 : 4 : m_callee_fndecl, "accept", m_arg);
881 : : }
882 : : break;
883 : 8 : case EXPECTED_PHASE_CAN_BIND:
884 : 8 : {
885 : 8 : if (m_actual_state == m_sm.m_bound_datagram_socket
886 : 8 : || m_actual_state == m_sm.m_bound_stream_socket
887 : 8 : || m_actual_state == m_sm.m_bound_unknown_socket)
888 : 2 : return ev.formatted_print
889 : : ("%qE expects a new socket file descriptor"
890 : : " but %qE has already been bound",
891 : 2 : m_callee_fndecl, m_arg);
892 : 6 : if (m_actual_state == m_sm.m_connected_stream_socket)
893 : 2 : return ev.formatted_print
894 : : ("%qE expects a new socket file descriptor"
895 : : " but %qE is already connected",
896 : 2 : m_callee_fndecl, m_arg);
897 : 4 : if (m_actual_state == m_sm.m_listening_stream_socket)
898 : 4 : return ev.formatted_print
899 : : ("%qE expects a new socket file descriptor"
900 : : " but %qE is already listening",
901 : 4 : m_callee_fndecl, m_arg);
902 : : }
903 : : break;
904 : 30 : case EXPECTED_PHASE_CAN_LISTEN:
905 : 30 : {
906 : 30 : if (m_actual_state == m_sm.m_new_stream_socket
907 : 24 : || m_actual_state == m_sm.m_new_unknown_socket)
908 : 28 : return ev.formatted_print
909 : : ("%qE expects a bound stream socket file descriptor"
910 : : " but %qE has not yet been bound",
911 : 28 : m_callee_fndecl, m_arg);
912 : 2 : if (m_actual_state == m_sm.m_connected_stream_socket)
913 : 2 : return ev.formatted_print
914 : : ("%qE expects a bound stream socket file descriptor"
915 : : " but %qE is connected",
916 : 2 : m_callee_fndecl, m_arg);
917 : : }
918 : : break;
919 : 4 : case EXPECTED_PHASE_CAN_ACCEPT:
920 : 4 : {
921 : 4 : if (m_actual_state == m_sm.m_new_stream_socket
922 : 2 : || m_actual_state == m_sm.m_new_unknown_socket)
923 : 2 : return ev.formatted_print
924 : : ("%qE expects a listening stream socket file descriptor"
925 : : " but %qE has not yet been bound",
926 : 2 : m_callee_fndecl, m_arg);
927 : 2 : if (m_actual_state == m_sm.m_bound_stream_socket
928 : 2 : || m_actual_state == m_sm.m_bound_unknown_socket)
929 : 0 : return ev.formatted_print
930 : : ("%qE expects a listening stream socket file descriptor"
931 : : " whereas %qE is bound but not yet listening",
932 : 0 : m_callee_fndecl, m_arg);
933 : 2 : if (m_actual_state == m_sm.m_connected_stream_socket)
934 : 2 : return ev.formatted_print
935 : : ("%qE expects a listening stream socket file descriptor"
936 : : " but %qE is connected",
937 : 2 : m_callee_fndecl, m_arg);
938 : : }
939 : : break;
940 : 2 : case EXPECTED_PHASE_CAN_CONNECT:
941 : 2 : {
942 : 2 : if (m_actual_state == m_sm.m_bound_datagram_socket
943 : 2 : || m_actual_state == m_sm.m_bound_stream_socket
944 : 0 : || m_actual_state == m_sm.m_bound_unknown_socket)
945 : 2 : return ev.formatted_print
946 : : ("%qE expects a new socket file descriptor but %qE is bound",
947 : 2 : m_callee_fndecl, m_arg);
948 : : else
949 : 0 : return ev.formatted_print
950 : 0 : ("%qE expects a new socket file descriptor", m_callee_fndecl);
951 : : }
952 : 0 : break;
953 : : }
954 : 0 : gcc_unreachable ();
955 : : }
956 : :
957 : : private:
958 : : state_machine::state_t m_actual_state;
959 : : enum expected_phase m_expected_phase;
960 : : };
961 : :
962 : : /* Enum for use by -Wanalyzer-fd-type-mismatch. */
963 : :
964 : : enum expected_type
965 : : {
966 : : EXPECTED_TYPE_SOCKET,
967 : : EXPECTED_TYPE_STREAM_SOCKET
968 : : };
969 : :
970 : : /* Concrete pending_diagnostic subclass for -Wanalyzer-fd-type-mismatch. */
971 : :
972 : 0 : class fd_type_mismatch : public fd_param_diagnostic
973 : : {
974 : : public:
975 : 14 : fd_type_mismatch (const fd_state_machine &sm, tree arg,
976 : : const tree callee_fndecl,
977 : : state_machine::state_t actual_state,
978 : : enum expected_type expected_type)
979 : 14 : : fd_param_diagnostic (sm, arg, callee_fndecl),
980 : 14 : m_actual_state (actual_state),
981 : 14 : m_expected_type (expected_type)
982 : : {
983 : : }
984 : :
985 : : const char *
986 : 47 : get_kind () const final override
987 : : {
988 : 47 : return "fd_type_mismatch";
989 : : }
990 : :
991 : : bool
992 : 14 : subclass_equal_p (const pending_diagnostic &base_other) const final override
993 : : {
994 : 14 : const fd_type_mismatch &sub_other = (const fd_type_mismatch &)base_other;
995 : 14 : if (!fd_param_diagnostic ::subclass_equal_p (sub_other))
996 : : return false;
997 : 14 : return (m_actual_state == sub_other.m_actual_state
998 : 14 : && m_expected_type == sub_other.m_expected_type);
999 : : }
1000 : :
1001 : : int
1002 : 19 : get_controlling_option () const final override
1003 : : {
1004 : 19 : return OPT_Wanalyzer_fd_type_mismatch;
1005 : : }
1006 : :
1007 : : bool
1008 : 5 : emit (diagnostic_emission_context &ctxt) final override
1009 : : {
1010 : 5 : switch (m_expected_type)
1011 : : {
1012 : 0 : default:
1013 : 0 : gcc_unreachable ();
1014 : 1 : case EXPECTED_TYPE_SOCKET:
1015 : 1 : return ctxt.warn ("%qE on non-socket file descriptor %qE",
1016 : 1 : m_callee_fndecl, m_arg);
1017 : 4 : case EXPECTED_TYPE_STREAM_SOCKET:
1018 : 8 : if (m_sm.is_datagram_socket_fd_p (m_actual_state))
1019 : 4 : return ctxt.warn ("%qE on datagram socket file descriptor %qE",
1020 : 4 : m_callee_fndecl, m_arg);
1021 : : else
1022 : 0 : return ctxt.warn ("%qE on non-stream-socket file descriptor %qE",
1023 : 0 : m_callee_fndecl, m_arg);
1024 : : }
1025 : : }
1026 : :
1027 : : label_text
1028 : 10 : describe_final_event (const evdesc::final_event &ev) final override
1029 : : {
1030 : 10 : switch (m_expected_type)
1031 : : {
1032 : : default:
1033 : : break;
1034 : : gcc_unreachable ();
1035 : 10 : case EXPECTED_TYPE_SOCKET:
1036 : 10 : case EXPECTED_TYPE_STREAM_SOCKET:
1037 : 10 : if (!m_sm.is_socket_fd_p (m_actual_state))
1038 : 2 : return ev.formatted_print ("%qE expects a socket file descriptor"
1039 : : " but %qE is not a socket",
1040 : 2 : m_callee_fndecl, m_arg);
1041 : : }
1042 : 8 : gcc_assert (m_expected_type == EXPECTED_TYPE_STREAM_SOCKET);
1043 : 0 : gcc_assert (m_sm.is_datagram_socket_fd_p (m_actual_state));
1044 : 8 : return ev.formatted_print
1045 : : ("%qE expects a stream socket file descriptor"
1046 : : " but %qE is a datagram socket",
1047 : 8 : m_callee_fndecl, m_arg);
1048 : : }
1049 : :
1050 : : private:
1051 : : state_machine::state_t m_actual_state;
1052 : : enum expected_type m_expected_type;
1053 : : };
1054 : :
1055 : 3723 : fd_state_machine::fd_state_machine (logger *logger)
1056 : : : state_machine ("file-descriptor", logger),
1057 : 7446 : m_constant_fd (add_state ("fd-constant")),
1058 : 3723 : m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
1059 : 3723 : m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
1060 : 3723 : m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
1061 : 3723 : m_valid_read_write (add_state ("fd-valid-read-write")),
1062 : 3723 : m_valid_read_only (add_state ("fd-valid-read-only")),
1063 : 3723 : m_valid_write_only (add_state ("fd-valid-write-only")),
1064 : 3723 : m_invalid (add_state ("fd-invalid")),
1065 : 3723 : m_closed (add_state ("fd-closed")),
1066 : 3723 : m_new_datagram_socket (add_state ("fd-new-datagram-socket")),
1067 : 3723 : m_new_stream_socket (add_state ("fd-new-stream-socket")),
1068 : 3723 : m_new_unknown_socket (add_state ("fd-new-unknown-socket")),
1069 : 3723 : m_bound_datagram_socket (add_state ("fd-bound-datagram-socket")),
1070 : 3723 : m_bound_stream_socket (add_state ("fd-bound-stream-socket")),
1071 : 3723 : m_bound_unknown_socket (add_state ("fd-bound-unknown-socket")),
1072 : 3723 : m_listening_stream_socket (add_state ("fd-listening-stream-socket")),
1073 : 3723 : m_connected_stream_socket (add_state ("fd-connected-stream-socket")),
1074 : 3723 : m_stop (add_state ("fd-stop")),
1075 : 3723 : m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
1076 : 3723 : m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
1077 : 3723 : m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY")),
1078 : 3723 : m_SOCK_STREAM (get_stashed_constant_by_name ("SOCK_STREAM")),
1079 : 7446 : m_SOCK_DGRAM (get_stashed_constant_by_name ("SOCK_DGRAM"))
1080 : : {
1081 : 3723 : }
1082 : :
1083 : : bool
1084 : 1198568 : fd_state_machine::is_unchecked_fd_p (state_t s) const
1085 : : {
1086 : 1198568 : return (s == m_unchecked_read_write
1087 : 1198117 : || s == m_unchecked_read_only
1088 : 1198703 : || s == m_unchecked_write_only);
1089 : : }
1090 : :
1091 : : bool
1092 : 1198030 : fd_state_machine::is_valid_fd_p (state_t s) const
1093 : : {
1094 : 1198030 : return (s == m_valid_read_write
1095 : 1197863 : || s == m_valid_read_only
1096 : 1197962 : || s == m_valid_write_only);
1097 : : }
1098 : :
1099 : : bool
1100 : 1197216 : fd_state_machine::is_socket_fd_p (state_t s) const
1101 : : {
1102 : 1197216 : return (s == m_new_datagram_socket
1103 : 1197117 : || s == m_new_stream_socket
1104 : 1197036 : || s == m_new_unknown_socket
1105 : 1196598 : || s == m_bound_datagram_socket
1106 : 1196524 : || s == m_bound_stream_socket
1107 : 1196460 : || s == m_bound_unknown_socket
1108 : 1196116 : || s == m_listening_stream_socket
1109 : 2393211 : || s == m_connected_stream_socket);
1110 : : }
1111 : :
1112 : : bool
1113 : 12 : fd_state_machine::is_datagram_socket_fd_p (state_t s) const
1114 : : {
1115 : 12 : return (s == m_new_datagram_socket
1116 : 3 : || s == m_new_unknown_socket
1117 : 3 : || s == m_bound_datagram_socket
1118 : 12 : || s == m_bound_unknown_socket);
1119 : : }
1120 : :
1121 : : bool
1122 : 58 : fd_state_machine::is_stream_socket_fd_p (state_t s) const
1123 : : {
1124 : 58 : return (s == m_new_stream_socket
1125 : 48 : || s == m_new_unknown_socket
1126 : 14 : || s == m_bound_stream_socket
1127 : 14 : || s == m_bound_unknown_socket
1128 : 14 : || s == m_listening_stream_socket
1129 : 72 : || s == m_connected_stream_socket);
1130 : : }
1131 : :
1132 : : enum access_mode
1133 : 100 : fd_state_machine::get_access_mode_from_flag (int flag) const
1134 : : {
1135 : 100 : if (m_O_ACCMODE && TREE_CODE (m_O_ACCMODE) == INTEGER_CST)
1136 : : {
1137 : 37 : const unsigned HOST_WIDE_INT mask_val = TREE_INT_CST_LOW (m_O_ACCMODE);
1138 : 37 : const unsigned HOST_WIDE_INT masked_flag = flag & mask_val;
1139 : :
1140 : 37 : if (m_O_RDONLY && TREE_CODE (m_O_RDONLY) == INTEGER_CST)
1141 : 37 : if (masked_flag == TREE_INT_CST_LOW (m_O_RDONLY))
1142 : : return READ_ONLY;
1143 : :
1144 : 31 : if (m_O_WRONLY && TREE_CODE (m_O_WRONLY) == INTEGER_CST)
1145 : 31 : if (masked_flag == TREE_INT_CST_LOW (m_O_WRONLY))
1146 : 6 : return WRITE_ONLY;
1147 : : }
1148 : : return READ_WRITE;
1149 : : }
1150 : :
1151 : : bool
1152 : 82 : fd_state_machine::is_readonly_fd_p (state_t state) const
1153 : : {
1154 : 82 : return (state == m_unchecked_read_only || state == m_valid_read_only);
1155 : : }
1156 : :
1157 : : bool
1158 : 108 : fd_state_machine::is_writeonly_fd_p (state_t state) const
1159 : : {
1160 : 108 : return (state == m_unchecked_write_only || state == m_valid_write_only);
1161 : : }
1162 : :
1163 : : bool
1164 : 1438 : fd_state_machine::is_closed_fd_p (state_t state) const
1165 : : {
1166 : 1438 : return (state == m_closed);
1167 : : }
1168 : :
1169 : : bool
1170 : 117 : fd_state_machine::is_constant_fd_p (state_t state) const
1171 : : {
1172 : 117 : return (state == m_constant_fd);
1173 : : }
1174 : :
1175 : : fd_state_machine::state_t
1176 : 9 : fd_state_machine::valid_to_unchecked_state (state_t state) const
1177 : : {
1178 : 9 : if (state == m_valid_read_write)
1179 : 6 : return m_unchecked_read_write;
1180 : 3 : else if (state == m_valid_write_only)
1181 : 2 : return m_unchecked_write_only;
1182 : 1 : else if (state == m_valid_read_only)
1183 : 1 : return m_unchecked_read_only;
1184 : : else
1185 : 0 : gcc_unreachable ();
1186 : : return NULL;
1187 : : }
1188 : :
1189 : : void
1190 : 18 : fd_state_machine::mark_as_valid_fd (region_model *model,
1191 : : sm_state_map *smap,
1192 : : const svalue *fd_sval,
1193 : : const extrinsic_state &ext_state) const
1194 : : {
1195 : 0 : smap->set_state (model, fd_sval, m_valid_read_write, NULL, ext_state);
1196 : 0 : }
1197 : :
1198 : : bool
1199 : 318665 : fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
1200 : : const gimple *stmt) const
1201 : : {
1202 : 318665 : if (const gcall *call = dyn_cast<const gcall *> (stmt))
1203 : 66431 : if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1204 : : {
1205 : 64950 : if (is_named_call_p (callee_fndecl, "open", call, 2))
1206 : : {
1207 : 110 : on_open (sm_ctxt, node, stmt, call);
1208 : 110 : return true;
1209 : : } // "open"
1210 : :
1211 : 64840 : if (is_named_call_p (callee_fndecl, "creat", call, 2))
1212 : : {
1213 : 17 : on_creat (sm_ctxt, node, stmt, call);
1214 : 17 : return true;
1215 : : } // "creat"
1216 : :
1217 : 64823 : if (is_named_call_p (callee_fndecl, "close", call, 1))
1218 : : {
1219 : 567 : on_close (sm_ctxt, node, stmt, call);
1220 : 567 : return true;
1221 : : } // "close"
1222 : :
1223 : 64256 : if (is_named_call_p (callee_fndecl, "write", call, 3))
1224 : : {
1225 : 95 : on_write (sm_ctxt, node, stmt, call, callee_fndecl);
1226 : 95 : return true;
1227 : : } // "write"
1228 : :
1229 : 64161 : if (is_named_call_p (callee_fndecl, "read", call, 3))
1230 : : {
1231 : 121 : on_read (sm_ctxt, node, stmt, call, callee_fndecl);
1232 : 121 : return true;
1233 : : } // "read"
1234 : :
1235 : 64040 : if (is_named_call_p (callee_fndecl, "dup", call, 1))
1236 : : {
1237 : 17 : check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1);
1238 : 17 : return true;
1239 : : }
1240 : :
1241 : 64023 : if (is_named_call_p (callee_fndecl, "dup2", call, 2))
1242 : : {
1243 : 10 : check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2);
1244 : 10 : return true;
1245 : : }
1246 : :
1247 : 64013 : if (is_named_call_p (callee_fndecl, "dup3", call, 3))
1248 : : {
1249 : 4 : check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3);
1250 : 4 : return true;
1251 : : }
1252 : :
1253 : 64009 : {
1254 : : // Handle __attribute__((fd_arg))
1255 : :
1256 : 64009 : check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1257 : : "fd_arg", DIRS_READ_WRITE);
1258 : :
1259 : : // Handle __attribute__((fd_arg_read))
1260 : :
1261 : 64009 : check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1262 : : "fd_arg_read", DIRS_READ);
1263 : :
1264 : : // Handle __attribute__((fd_arg_write))
1265 : :
1266 : 64009 : check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1267 : : "fd_arg_write", DIRS_WRITE);
1268 : : }
1269 : : }
1270 : :
1271 : : return false;
1272 : : }
1273 : :
1274 : : void
1275 : 192027 : fd_state_machine::check_for_fd_attrs (
1276 : : sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1277 : : const gcall *call, const tree callee_fndecl, const char *attr_name,
1278 : : access_directions fd_attr_access_dir) const
1279 : : {
1280 : : /* Handle interesting fd attributes of the callee_fndecl,
1281 : : or prioritize those of the builtin that callee_fndecl is
1282 : : expected to be.
1283 : : Might want this to be controlled by a flag. */
1284 : 192027 : tree fndecl = callee_fndecl;
1285 : : /* If call is recognized as a builtin known_function,
1286 : : use that builtin's function_decl. */
1287 : 192027 : if (const region_model *old_model = sm_ctxt->get_old_region_model ())
1288 : 384054 : if (const builtin_known_function *builtin_kf
1289 : 192027 : = old_model->get_builtin_kf (call))
1290 : 79548 : fndecl = builtin_kf->builtin_decl ();
1291 : :
1292 : 192027 : tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
1293 : 192027 : attrs = lookup_attribute (attr_name, attrs);
1294 : 192027 : if (!attrs)
1295 : 192015 : return;
1296 : :
1297 : 12 : if (!TREE_VALUE (attrs))
1298 : : return;
1299 : :
1300 : 12 : auto_bitmap argmap;
1301 : :
1302 : 24 : for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
1303 : : {
1304 : 12 : unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
1305 : 12 : bitmap_set_bit (argmap, val);
1306 : : }
1307 : 12 : if (bitmap_empty_p (argmap))
1308 : 0 : return;
1309 : :
1310 : 24 : for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
1311 : : {
1312 : 12 : tree arg = gimple_call_arg (call, arg_idx);
1313 : 12 : tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1314 : 12 : state_t state = sm_ctxt->get_state (stmt, arg);
1315 : 12 : bool bit_set = bitmap_bit_p (argmap, arg_idx);
1316 : 12 : if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
1317 : 6 : continue;
1318 : 12 : if (bit_set) // Check if arg_idx is marked by any of the file descriptor
1319 : : // attributes
1320 : : {
1321 : :
1322 : : /* Do use the fndecl that caused the warning so that the
1323 : : misused attributes are printed and the user not confused. */
1324 : 12 : if (is_closed_fd_p (state))
1325 : : {
1326 : :
1327 : 3 : sm_ctxt->warn (node, stmt, arg,
1328 : : make_unique<fd_use_after_close>
1329 : 6 : (*this, diag_arg,
1330 : : fndecl, attr_name,
1331 : : arg_idx));
1332 : 3 : continue;
1333 : : }
1334 : :
1335 : 12 : if (!(is_valid_fd_p (state) || (state == m_stop)))
1336 : : {
1337 : 3 : if (!is_constant_fd_p (state))
1338 : : {
1339 : 3 : sm_ctxt->warn (node, stmt, arg,
1340 : : make_unique<fd_use_without_check>
1341 : 6 : (*this, diag_arg,
1342 : : fndecl, attr_name,
1343 : : arg_idx));
1344 : 3 : continue;
1345 : : }
1346 : : }
1347 : :
1348 : 6 : switch (fd_attr_access_dir)
1349 : : {
1350 : : case DIRS_READ_WRITE:
1351 : : break;
1352 : 3 : case DIRS_READ:
1353 : :
1354 : 3 : if (is_writeonly_fd_p (state))
1355 : : {
1356 : 3 : sm_ctxt->warn (
1357 : : node, stmt, arg,
1358 : 6 : make_unique<fd_access_mode_mismatch> (*this, diag_arg,
1359 : 6 : DIRS_WRITE,
1360 : : fndecl,
1361 : : attr_name,
1362 : : arg_idx));
1363 : : }
1364 : :
1365 : : break;
1366 : 3 : case DIRS_WRITE:
1367 : :
1368 : 9 : if (is_readonly_fd_p (state))
1369 : : {
1370 : 3 : sm_ctxt->warn (
1371 : : node, stmt, arg,
1372 : 6 : make_unique<fd_access_mode_mismatch> (*this, diag_arg,
1373 : 6 : DIRS_READ,
1374 : : fndecl,
1375 : : attr_name,
1376 : : arg_idx));
1377 : : }
1378 : :
1379 : : break;
1380 : : }
1381 : : }
1382 : : }
1383 : 12 : }
1384 : :
1385 : :
1386 : : void
1387 : 110 : fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
1388 : : const gimple *stmt, const gcall *call) const
1389 : : {
1390 : 110 : tree lhs = gimple_call_lhs (call);
1391 : 110 : if (lhs)
1392 : : {
1393 : 105 : tree arg = gimple_call_arg (call, 1);
1394 : 105 : enum access_mode mode = READ_WRITE;
1395 : 105 : if (TREE_CODE (arg) == INTEGER_CST)
1396 : : {
1397 : 100 : int flag = TREE_INT_CST_LOW (arg);
1398 : 100 : mode = get_access_mode_from_flag (flag);
1399 : : }
1400 : 100 : switch (mode)
1401 : : {
1402 : 6 : case READ_ONLY:
1403 : 6 : sm_ctxt->on_transition (node, stmt, lhs, m_start,
1404 : 6 : m_unchecked_read_only);
1405 : 6 : break;
1406 : 6 : case WRITE_ONLY:
1407 : 6 : sm_ctxt->on_transition (node, stmt, lhs, m_start,
1408 : 6 : m_unchecked_write_only);
1409 : 6 : break;
1410 : 93 : default:
1411 : 93 : sm_ctxt->on_transition (node, stmt, lhs, m_start,
1412 : 93 : m_unchecked_read_write);
1413 : : }
1414 : : }
1415 : : else
1416 : : {
1417 : 5 : sm_ctxt->warn (node, stmt, NULL_TREE,
1418 : 10 : make_unique<fd_leak> (*this, NULL_TREE));
1419 : : }
1420 : 110 : }
1421 : :
1422 : : void
1423 : 17 : fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node,
1424 : : const gimple *stmt, const gcall *call) const
1425 : : {
1426 : 17 : tree lhs = gimple_call_lhs (call);
1427 : 17 : if (lhs)
1428 : 12 : sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked_write_only);
1429 : : else
1430 : 5 : sm_ctxt->warn (node, stmt, NULL_TREE,
1431 : 10 : make_unique<fd_leak> (*this, NULL_TREE));
1432 : 17 : }
1433 : :
1434 : : void
1435 : 31 : fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
1436 : : const gimple *stmt, const gcall *call,
1437 : : const tree callee_fndecl, enum dup kind) const
1438 : : {
1439 : 31 : tree lhs = gimple_call_lhs (call);
1440 : 31 : tree arg_1 = gimple_call_arg (call, 0);
1441 : 31 : state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1);
1442 : 31 : if (state_arg_1 == m_stop)
1443 : : return;
1444 : 31 : if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)
1445 : 18 : || state_arg_1 == m_start))
1446 : : {
1447 : 7 : check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
1448 : : DIRS_READ_WRITE);
1449 : 7 : return;
1450 : : }
1451 : 24 : switch (kind)
1452 : : {
1453 : 15 : case DUP_1:
1454 : 15 : if (lhs)
1455 : : {
1456 : 15 : if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
1457 : 9 : sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1458 : : else
1459 : 6 : sm_ctxt->set_next_state (stmt, lhs,
1460 : : valid_to_unchecked_state (state_arg_1));
1461 : : }
1462 : 22 : break;
1463 : :
1464 : 9 : case DUP_2:
1465 : 9 : case DUP_3:
1466 : 9 : tree arg_2 = gimple_call_arg (call, 1);
1467 : 9 : state_t state_arg_2 = sm_ctxt->get_state (stmt, arg_2);
1468 : 9 : tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (arg_2);
1469 : 9 : if (state_arg_2 == m_stop)
1470 : 2 : return;
1471 : : /* Check if -1 was passed as second argument to dup2. */
1472 : 9 : if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)
1473 : 2 : || state_arg_2 == m_start))
1474 : : {
1475 : 2 : sm_ctxt->warn (
1476 : : node, stmt, arg_2,
1477 : 4 : make_unique<fd_use_without_check> (*this, diag_arg_2,
1478 : : callee_fndecl));
1479 : 2 : return;
1480 : : }
1481 : : /* dup2 returns value of its second argument on success.But, the
1482 : : access mode of the returned file descriptor depends on the duplicated
1483 : : file descriptor i.e the first argument. */
1484 : 7 : if (lhs)
1485 : : {
1486 : 7 : if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
1487 : 4 : sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1488 : : else
1489 : 3 : sm_ctxt->set_next_state (stmt, lhs,
1490 : : valid_to_unchecked_state (state_arg_1));
1491 : : }
1492 : :
1493 : : break;
1494 : : }
1495 : : }
1496 : :
1497 : : void
1498 : 567 : fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
1499 : : const gimple *stmt, const gcall *call) const
1500 : : {
1501 : 567 : tree arg = gimple_call_arg (call, 0);
1502 : 567 : state_t state = sm_ctxt->get_state (stmt, arg);
1503 : 567 : tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1504 : :
1505 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
1506 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed);
1507 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed);
1508 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed);
1509 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed);
1510 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
1511 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
1512 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
1513 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_new_datagram_socket, m_closed);
1514 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_new_stream_socket, m_closed);
1515 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_new_unknown_socket, m_closed);
1516 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_bound_datagram_socket, m_closed);
1517 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_bound_stream_socket, m_closed);
1518 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_bound_unknown_socket, m_closed);
1519 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_listening_stream_socket, m_closed);
1520 : 567 : sm_ctxt->on_transition (node, stmt, arg, m_connected_stream_socket, m_closed);
1521 : :
1522 : 567 : if (is_closed_fd_p (state))
1523 : : {
1524 : 35 : sm_ctxt->warn (node, stmt, arg,
1525 : 70 : make_unique<fd_double_close> (*this, diag_arg));
1526 : 35 : sm_ctxt->set_next_state (stmt, arg, m_stop);
1527 : : }
1528 : 567 : }
1529 : : void
1530 : 121 : fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node,
1531 : : const gimple *stmt, const gcall *call,
1532 : : const tree callee_fndecl) const
1533 : : {
1534 : 121 : check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);
1535 : 0 : }
1536 : : void
1537 : 95 : fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
1538 : : const gimple *stmt, const gcall *call,
1539 : : const tree callee_fndecl) const
1540 : : {
1541 : 95 : check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);
1542 : 0 : }
1543 : :
1544 : : void
1545 : 223 : fd_state_machine::check_for_open_fd (
1546 : : sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1547 : : const gcall *call, const tree callee_fndecl,
1548 : : enum access_directions callee_fndecl_dir) const
1549 : : {
1550 : 223 : tree arg = gimple_call_arg (call, 0);
1551 : 223 : tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1552 : 223 : state_t state = sm_ctxt->get_state (stmt, arg);
1553 : :
1554 : 223 : if (is_closed_fd_p (state))
1555 : : {
1556 : 35 : sm_ctxt->warn (node, stmt, arg,
1557 : 70 : make_unique<fd_use_after_close> (*this, diag_arg,
1558 : : callee_fndecl));
1559 : : }
1560 : :
1561 : : else
1562 : : {
1563 : 188 : if (state == m_new_stream_socket
1564 : 187 : || state == m_bound_stream_socket
1565 : 187 : || state == m_listening_stream_socket)
1566 : : /* Complain about fncall on socket in wrong phase. */
1567 : 4 : sm_ctxt->warn
1568 : 4 : (node, stmt, arg,
1569 : 8 : make_unique<fd_phase_mismatch> (*this, diag_arg,
1570 : : callee_fndecl,
1571 : : state,
1572 : 8 : EXPECTED_PHASE_CAN_TRANSFER));
1573 : 184 : else if (!(is_valid_fd_p (state)
1574 : 159 : || state == m_new_datagram_socket
1575 : 159 : || state == m_bound_unknown_socket
1576 : 157 : || state == m_connected_stream_socket
1577 : 134 : || state == m_start
1578 : 106 : || state == m_stop))
1579 : : {
1580 : 74 : if (!is_constant_fd_p (state))
1581 : 63 : sm_ctxt->warn (
1582 : : node, stmt, arg,
1583 : 126 : make_unique<fd_use_without_check> (*this, diag_arg,
1584 : : callee_fndecl));
1585 : : }
1586 : 188 : switch (callee_fndecl_dir)
1587 : : {
1588 : : case DIRS_READ_WRITE:
1589 : : break;
1590 : 105 : case DIRS_READ:
1591 : 105 : if (is_writeonly_fd_p (state))
1592 : : {
1593 : 4 : tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1594 : 4 : sm_ctxt->warn (node, stmt, arg,
1595 : 8 : make_unique<fd_access_mode_mismatch> (
1596 : 8 : *this, diag_arg, DIRS_WRITE, callee_fndecl));
1597 : : }
1598 : :
1599 : : break;
1600 : 79 : case DIRS_WRITE:
1601 : :
1602 : 79 : if (is_readonly_fd_p (state))
1603 : : {
1604 : 2 : tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1605 : 2 : sm_ctxt->warn (node, stmt, arg,
1606 : 4 : make_unique<fd_access_mode_mismatch> (
1607 : 4 : *this, diag_arg, DIRS_READ, callee_fndecl));
1608 : : }
1609 : : break;
1610 : : }
1611 : : }
1612 : 223 : }
1613 : :
1614 : : static bool
1615 : 370 : add_constraint_ge_zero (region_model *model,
1616 : : const svalue *fd_sval,
1617 : : region_model_context *ctxt)
1618 : : {
1619 : 370 : const svalue *zero
1620 : 370 : = model->get_manager ()->get_or_create_int_cst (integer_type_node, 0);
1621 : 370 : return model->add_constraint (fd_sval, GE_EXPR, zero, ctxt);
1622 : : }
1623 : :
1624 : : /* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
1625 : : a SOCK_* value. */
1626 : :
1627 : : state_machine::state_t
1628 : 118 : fd_state_machine::
1629 : : get_state_for_socket_type (const svalue *socket_type_sval) const
1630 : : {
1631 : 118 : if (tree socket_type_cst = socket_type_sval->maybe_get_constant ())
1632 : : {
1633 : : /* Attempt to use SOCK_* constants stashed from the frontend. */
1634 : 48 : if (tree_int_cst_equal (socket_type_cst, m_SOCK_STREAM))
1635 : 20 : return m_new_stream_socket;
1636 : 28 : if (tree_int_cst_equal (socket_type_cst, m_SOCK_DGRAM))
1637 : 16 : return m_new_datagram_socket;
1638 : : }
1639 : :
1640 : : /* Unrecognized constant, or a symbolic "type" value. */
1641 : 82 : return m_new_unknown_socket;
1642 : : }
1643 : :
1644 : : /* Update the model and fd state for an outcome of a call to "socket",
1645 : : where SUCCESSFUL indicate which of the two outcomes.
1646 : : Return true if the outcome is feasible, or false to reject it. */
1647 : :
1648 : : bool
1649 : 250 : fd_state_machine::on_socket (const call_details &cd,
1650 : : bool successful,
1651 : : sm_context *sm_ctxt,
1652 : : const extrinsic_state &ext_state) const
1653 : : {
1654 : 250 : const gcall *stmt = cd.get_call_stmt ();
1655 : 250 : engine *eng = ext_state.get_engine ();
1656 : 250 : const supergraph *sg = eng->get_supergraph ();
1657 : 250 : const supernode *node = sg->get_supernode_for_stmt (stmt);
1658 : 250 : region_model *model = cd.get_model ();
1659 : :
1660 : 250 : if (successful)
1661 : : {
1662 : 125 : if (gimple_call_lhs (stmt))
1663 : : {
1664 : 118 : conjured_purge p (model, cd.get_ctxt ());
1665 : 118 : region_model_manager *mgr = model->get_manager ();
1666 : 118 : const svalue *new_fd
1667 : 118 : = mgr->get_or_create_conjured_svalue (integer_type_node,
1668 : : stmt,
1669 : : cd.get_lhs_region (),
1670 : : p);
1671 : 118 : if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
1672 : 0 : return false;
1673 : :
1674 : 118 : const svalue *socket_type_sval = cd.get_arg_svalue (1);
1675 : 118 : state_machine::state_t new_state
1676 : 118 : = get_state_for_socket_type (socket_type_sval);
1677 : 118 : sm_ctxt->on_transition (node, stmt, new_fd, m_start, new_state);
1678 : 118 : model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
1679 : : }
1680 : : else
1681 : 7 : sm_ctxt->warn (node, stmt, NULL_TREE,
1682 : 14 : make_unique<fd_leak> (*this, NULL_TREE));
1683 : : }
1684 : : else
1685 : : {
1686 : : /* Return -1; set errno. */
1687 : 125 : model->update_for_int_cst_return (cd, -1, true);
1688 : 125 : model->set_errno (cd);
1689 : : }
1690 : :
1691 : : return true;
1692 : : }
1693 : :
1694 : : /* Check that FD_SVAL is usable by socket APIs.
1695 : : Complain if it has been closed, if it is a non-socket,
1696 : : or is invalid.
1697 : : If COMPLAINED is non-NULL and a problem is found,
1698 : : write *COMPLAINED = true.
1699 : :
1700 : : If SUCCESSFUL is true, attempt to add the constraint that FD_SVAL >= 0.
1701 : : Return true if this outcome is feasible. */
1702 : :
1703 : : bool
1704 : 492 : fd_state_machine::check_for_socket_fd (const call_details &cd,
1705 : : bool successful,
1706 : : sm_context *sm_ctxt,
1707 : : const svalue *fd_sval,
1708 : : const supernode *node,
1709 : : state_t old_state,
1710 : : bool *complained) const
1711 : : {
1712 : 492 : const gcall *stmt = cd.get_call_stmt ();
1713 : :
1714 : 492 : if (is_closed_fd_p (old_state))
1715 : : {
1716 : 0 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1717 : 0 : sm_ctxt->warn
1718 : 0 : (node, stmt, fd_sval,
1719 : 0 : make_unique<fd_use_after_close> (*this, diag_arg,
1720 : 0 : cd.get_fndecl_for_call ()));
1721 : 0 : if (complained)
1722 : 0 : *complained = true;
1723 : 0 : if (successful)
1724 : 0 : return false;
1725 : : }
1726 : 984 : else if (is_unchecked_fd_p (old_state) || is_valid_fd_p (old_state))
1727 : : {
1728 : : /* Complain about non-socket. */
1729 : 4 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1730 : 4 : sm_ctxt->warn
1731 : 4 : (node, stmt, fd_sval,
1732 : 8 : make_unique<fd_type_mismatch> (*this, diag_arg,
1733 : 4 : cd.get_fndecl_for_call (),
1734 : : old_state,
1735 : 4 : EXPECTED_TYPE_SOCKET));
1736 : 4 : if (complained)
1737 : 4 : *complained = true;
1738 : 4 : if (successful)
1739 : 2 : return false;
1740 : : }
1741 : 488 : else if (old_state == m_invalid)
1742 : : {
1743 : 30 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1744 : 30 : sm_ctxt->warn
1745 : 30 : (node, stmt, fd_sval,
1746 : 60 : make_unique<fd_use_without_check> (*this, diag_arg,
1747 : 30 : cd.get_fndecl_for_call ()));
1748 : 30 : if (complained)
1749 : 28 : *complained = true;
1750 : 30 : if (successful)
1751 : 15 : return false;
1752 : : }
1753 : :
1754 : 475 : if (successful)
1755 : 229 : if (!add_constraint_ge_zero (cd.get_model (), fd_sval, cd.get_ctxt ()))
1756 : : return false;
1757 : :
1758 : : return true;
1759 : : }
1760 : :
1761 : : /* For use by "bind" and "connect".
1762 : : As per fd_state_machine::check_for_socket_fd above,
1763 : : but also complain if we don't have a new socket, and check that
1764 : : we can read up to the size bytes from the address. */
1765 : :
1766 : : bool
1767 : 320 : fd_state_machine::check_for_new_socket_fd (const call_details &cd,
1768 : : bool successful,
1769 : : sm_context *sm_ctxt,
1770 : : const svalue *fd_sval,
1771 : : const supernode *node,
1772 : : state_t old_state,
1773 : : enum expected_phase expected_phase)
1774 : : const
1775 : : {
1776 : 320 : bool complained = false;
1777 : :
1778 : : /* Check address and len. */
1779 : 320 : const svalue *address_sval = cd.get_arg_svalue (1);
1780 : 320 : const svalue *len_sval = cd.get_arg_svalue (2);
1781 : :
1782 : : /* Check that we can read the given number of bytes from the
1783 : : address. */
1784 : 320 : region_model *model = cd.get_model ();
1785 : 320 : const region *address_reg
1786 : 320 : = model->deref_rvalue (address_sval, cd.get_arg_tree (1),
1787 : : cd.get_ctxt ());
1788 : 320 : const region *sized_address_reg
1789 : 320 : = model->get_manager ()->get_sized_region (address_reg,
1790 : : NULL_TREE,
1791 : : len_sval);
1792 : 320 : model->get_store_value (sized_address_reg, cd.get_ctxt ());
1793 : :
1794 : 320 : if (!check_for_socket_fd (cd, successful, sm_ctxt,
1795 : : fd_sval, node, old_state, &complained))
1796 : : return false;
1797 : 304 : else if (!complained
1798 : 288 : && !(old_state == m_new_stream_socket
1799 : 256 : || old_state == m_new_datagram_socket
1800 : 228 : || old_state == m_new_unknown_socket
1801 : 96 : || old_state == m_start
1802 : 54 : || old_state == m_stop
1803 : 50 : || old_state == m_constant_fd))
1804 : : {
1805 : : /* Complain about "bind" or "connect" in wrong phase. */
1806 : 18 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1807 : 18 : sm_ctxt->warn
1808 : 18 : (node, cd.get_call_stmt (), fd_sval,
1809 : 36 : make_unique<fd_phase_mismatch> (*this, diag_arg,
1810 : 18 : cd.get_fndecl_for_call (),
1811 : : old_state,
1812 : : expected_phase));
1813 : 18 : if (successful)
1814 : 9 : return false;
1815 : : }
1816 : 286 : else if (!successful)
1817 : : {
1818 : : /* If we were in the start state, assume we had a new socket. */
1819 : 151 : if (old_state == m_start)
1820 : 21 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
1821 : 21 : m_new_unknown_socket);
1822 : : }
1823 : :
1824 : : /* Passing NULL as the address will lead to failure. */
1825 : 9 : if (successful)
1826 : 135 : if (address_sval->all_zeroes_p ())
1827 : : return false;
1828 : :
1829 : : return true;
1830 : : }
1831 : :
1832 : : /* Update the model and fd state for an outcome of a call to "bind",
1833 : : where SUCCESSFUL indicate which of the two outcomes.
1834 : : Return true if the outcome is feasible, or false to reject it. */
1835 : :
1836 : : bool
1837 : 256 : fd_state_machine::on_bind (const call_details &cd,
1838 : : bool successful,
1839 : : sm_context *sm_ctxt,
1840 : : const extrinsic_state &ext_state) const
1841 : : {
1842 : 256 : const gcall *stmt = cd.get_call_stmt ();
1843 : 256 : engine *eng = ext_state.get_engine ();
1844 : 256 : const supergraph *sg = eng->get_supergraph ();
1845 : 256 : const supernode *node = sg->get_supernode_for_stmt (stmt);
1846 : 256 : const svalue *fd_sval = cd.get_arg_svalue (0);
1847 : 256 : region_model *model = cd.get_model ();
1848 : 256 : state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
1849 : :
1850 : 256 : if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
1851 : : fd_sval, node, old_state,
1852 : : EXPECTED_PHASE_CAN_BIND))
1853 : : return false;
1854 : :
1855 : 230 : if (successful)
1856 : : {
1857 : 102 : state_t next_state = NULL;
1858 : 102 : if (old_state == m_new_stream_socket)
1859 : 13 : next_state = m_bound_stream_socket;
1860 : 89 : else if (old_state == m_new_datagram_socket)
1861 : 14 : next_state = m_bound_datagram_socket;
1862 : 75 : else if (old_state == m_new_unknown_socket)
1863 : 52 : next_state = m_bound_unknown_socket;
1864 : 23 : else if (old_state == m_start
1865 : 9 : || old_state == m_constant_fd)
1866 : 21 : next_state = m_bound_unknown_socket;
1867 : 2 : else if (old_state == m_stop)
1868 : : next_state = m_stop;
1869 : : else
1870 : 0 : gcc_unreachable ();
1871 : 102 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval, next_state);
1872 : 102 : model->update_for_zero_return (cd, true);
1873 : : }
1874 : : else
1875 : : {
1876 : : /* Return -1; set errno. */
1877 : 128 : model->update_for_int_cst_return (cd, -1, true);
1878 : 128 : model->set_errno (cd);
1879 : : }
1880 : :
1881 : : return true;
1882 : : }
1883 : :
1884 : : /* Update the model and fd state for an outcome of a call to "listen",
1885 : : where SUCCESSFUL indicate which of the two outcomes.
1886 : : Return true if the outcome is feasible, or false to reject it. */
1887 : :
1888 : : bool
1889 : 118 : fd_state_machine::on_listen (const call_details &cd,
1890 : : bool successful,
1891 : : sm_context *sm_ctxt,
1892 : : const extrinsic_state &ext_state) const
1893 : : {
1894 : 118 : const gcall *stmt = cd.get_call_stmt ();
1895 : 118 : engine *eng = ext_state.get_engine ();
1896 : 118 : const supergraph *sg = eng->get_supergraph ();
1897 : 118 : const supernode *node = sg->get_supernode_for_stmt (cd.get_call_stmt ());
1898 : 118 : const svalue *fd_sval = cd.get_arg_svalue (0);
1899 : 118 : region_model *model = cd.get_model ();
1900 : 118 : state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
1901 : :
1902 : : /* We expect a stream socket that's had "bind" called on it. */
1903 : 118 : if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
1904 : : return false;
1905 : 117 : if (!(old_state == m_start
1906 : 103 : || old_state == m_constant_fd
1907 : 99 : || old_state == m_stop
1908 : 99 : || old_state == m_invalid
1909 : 98 : || old_state == m_bound_stream_socket
1910 : 86 : || old_state == m_bound_unknown_socket
1911 : : /* Assume it's OK to call "listen" more than once. */
1912 : 54 : || old_state == m_listening_stream_socket))
1913 : : {
1914 : : /* Complain about fncall on wrong type or in wrong phase. */
1915 : 52 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1916 : 52 : if (is_stream_socket_fd_p (old_state))
1917 : 44 : sm_ctxt->warn
1918 : 44 : (node, stmt, fd_sval,
1919 : 88 : make_unique<fd_phase_mismatch> (*this, diag_arg,
1920 : 44 : cd.get_fndecl_for_call (),
1921 : : old_state,
1922 : 88 : EXPECTED_PHASE_CAN_LISTEN));
1923 : : else
1924 : 8 : sm_ctxt->warn
1925 : 8 : (node, stmt, fd_sval,
1926 : 16 : make_unique<fd_type_mismatch> (*this, diag_arg,
1927 : 8 : cd.get_fndecl_for_call (),
1928 : : old_state,
1929 : 16 : EXPECTED_TYPE_STREAM_SOCKET));
1930 : 52 : if (successful)
1931 : 26 : return false;
1932 : : }
1933 : :
1934 : 91 : if (successful)
1935 : : {
1936 : 32 : model->update_for_zero_return (cd, true);
1937 : 32 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
1938 : 32 : m_listening_stream_socket);
1939 : : }
1940 : : else
1941 : : {
1942 : : /* Return -1; set errno. */
1943 : 59 : model->update_for_int_cst_return (cd, -1, true);
1944 : 59 : model->set_errno (cd);
1945 : 59 : if (old_state == m_start)
1946 : 7 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
1947 : 7 : m_bound_stream_socket);
1948 : : }
1949 : :
1950 : : return true;
1951 : : }
1952 : :
1953 : : /* Update the model and fd state for an outcome of a call to "accept",
1954 : : where SUCCESSFUL indicate which of the two outcomes.
1955 : : Return true if the outcome is feasible, or false to reject it. */
1956 : :
1957 : : bool
1958 : 54 : fd_state_machine::on_accept (const call_details &cd,
1959 : : bool successful,
1960 : : sm_context *sm_ctxt,
1961 : : const extrinsic_state &ext_state) const
1962 : : {
1963 : 54 : const gcall *stmt = cd.get_call_stmt ();
1964 : 54 : engine *eng = ext_state.get_engine ();
1965 : 54 : const supergraph *sg = eng->get_supergraph ();
1966 : 54 : const supernode *node = sg->get_supernode_for_stmt (stmt);
1967 : 54 : const svalue *fd_sval = cd.get_arg_svalue (0);
1968 : 54 : const svalue *address_sval = cd.get_arg_svalue (1);
1969 : 54 : const svalue *len_ptr_sval = cd.get_arg_svalue (2);
1970 : 54 : region_model *model = cd.get_model ();
1971 : 54 : state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
1972 : :
1973 : 54 : if (!address_sval->all_zeroes_p ())
1974 : : {
1975 : 18 : region_model_manager *mgr = model->get_manager ();
1976 : :
1977 : : /* We might have a union of various pointer types, rather than a
1978 : : pointer type; cast to (void *) before dereferencing. */
1979 : 18 : address_sval = mgr->get_or_create_cast (ptr_type_node, address_sval);
1980 : :
1981 : 18 : const region *address_reg
1982 : 18 : = model->deref_rvalue (address_sval, cd.get_arg_tree (1),
1983 : : cd.get_ctxt ());
1984 : 18 : const region *len_reg
1985 : 18 : = model->deref_rvalue (len_ptr_sval, cd.get_arg_tree (2),
1986 : : cd.get_ctxt ());
1987 : 18 : const svalue *old_len_sval
1988 : 18 : = model->get_store_value (len_reg, cd.get_ctxt ());
1989 : 18 : tree len_ptr = cd.get_arg_tree (2);
1990 : 18 : tree star_len_ptr = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (len_ptr)),
1991 : : len_ptr,
1992 : 18 : build_int_cst (TREE_TYPE (len_ptr), 0));
1993 : 18 : old_len_sval = model->check_for_poison (old_len_sval,
1994 : : star_len_ptr,
1995 : : len_reg,
1996 : : cd.get_ctxt ());
1997 : 18 : if (successful)
1998 : : {
1999 : 9 : conjured_purge p (model, cd.get_ctxt ());
2000 : 9 : const region *old_sized_address_reg
2001 : 9 : = mgr->get_sized_region (address_reg,
2002 : : NULL_TREE,
2003 : : old_len_sval);
2004 : 9 : const svalue *new_addr_sval
2005 : 9 : = mgr->get_or_create_conjured_svalue (NULL_TREE,
2006 : : stmt,
2007 : : old_sized_address_reg,
2008 : : p);
2009 : 9 : model->set_value (old_sized_address_reg, new_addr_sval,
2010 : : cd.get_ctxt ());
2011 : 9 : const svalue *new_addr_len
2012 : 9 : = mgr->get_or_create_conjured_svalue (NULL_TREE,
2013 : : stmt,
2014 : : len_reg,
2015 : : p);
2016 : 9 : model->set_value (len_reg, new_addr_len, cd.get_ctxt ());
2017 : : }
2018 : : }
2019 : :
2020 : : /* We expect a stream socket in the "listening" state. */
2021 : 54 : if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
2022 : : return false;
2023 : :
2024 : 54 : if (old_state == m_start || old_state == m_constant_fd)
2025 : : /* If we were in the start state (or a constant), assume we had the
2026 : : expected state. */
2027 : 24 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval,
2028 : 24 : m_listening_stream_socket);
2029 : 30 : else if (old_state == m_stop)
2030 : : {
2031 : : /* No further complaints. */
2032 : : }
2033 : 30 : else if (old_state != m_listening_stream_socket)
2034 : : {
2035 : : /* Complain about fncall on wrong type or in wrong phase. */
2036 : 6 : tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
2037 : 6 : if (is_stream_socket_fd_p (old_state))
2038 : 4 : sm_ctxt->warn
2039 : 4 : (node, stmt, fd_sval,
2040 : 8 : make_unique<fd_phase_mismatch> (*this, diag_arg,
2041 : 4 : cd.get_fndecl_for_call (),
2042 : : old_state,
2043 : 8 : EXPECTED_PHASE_CAN_ACCEPT));
2044 : : else
2045 : 2 : sm_ctxt->warn
2046 : 2 : (node, stmt, fd_sval,
2047 : 4 : make_unique<fd_type_mismatch> (*this, diag_arg,
2048 : 2 : cd.get_fndecl_for_call (),
2049 : : old_state,
2050 : 4 : EXPECTED_TYPE_STREAM_SOCKET));
2051 : 6 : if (successful)
2052 : 3 : return false;
2053 : : }
2054 : :
2055 : 51 : if (successful)
2056 : : {
2057 : : /* Return new conjured FD in "connected" state. */
2058 : 24 : if (gimple_call_lhs (stmt))
2059 : : {
2060 : 23 : conjured_purge p (model, cd.get_ctxt ());
2061 : 23 : region_model_manager *mgr = model->get_manager ();
2062 : 23 : const svalue *new_fd
2063 : 23 : = mgr->get_or_create_conjured_svalue (integer_type_node,
2064 : : stmt,
2065 : : cd.get_lhs_region (),
2066 : : p);
2067 : 23 : if (!add_constraint_ge_zero (model, new_fd, cd.get_ctxt ()))
2068 : 0 : return false;
2069 : 23 : sm_ctxt->on_transition (node, stmt, new_fd,
2070 : 23 : m_start, m_connected_stream_socket);
2071 : 23 : model->set_value (cd.get_lhs_region (), new_fd, cd.get_ctxt ());
2072 : : }
2073 : : else
2074 : 1 : sm_ctxt->warn (node, stmt, NULL_TREE,
2075 : 2 : make_unique<fd_leak> (*this, NULL_TREE));
2076 : : }
2077 : : else
2078 : : {
2079 : : /* Return -1; set errno. */
2080 : 27 : model->update_for_int_cst_return (cd, -1, true);
2081 : 27 : model->set_errno (cd);
2082 : : }
2083 : :
2084 : : return true;
2085 : : }
2086 : :
2087 : : /* Update the model and fd state for an outcome of a call to "connect",
2088 : : where SUCCESSFUL indicate which of the two outcomes.
2089 : : Return true if the outcome is feasible, or false to reject it. */
2090 : :
2091 : : bool
2092 : 64 : fd_state_machine::on_connect (const call_details &cd,
2093 : : bool successful,
2094 : : sm_context *sm_ctxt,
2095 : : const extrinsic_state &ext_state) const
2096 : : {
2097 : 64 : const gcall *stmt = cd.get_call_stmt ();
2098 : 64 : engine *eng = ext_state.get_engine ();
2099 : 64 : const supergraph *sg = eng->get_supergraph ();
2100 : 64 : const supernode *node = sg->get_supernode_for_stmt (stmt);
2101 : 64 : const svalue *fd_sval = cd.get_arg_svalue (0);
2102 : 64 : region_model *model = cd.get_model ();
2103 : 64 : state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
2104 : :
2105 : 64 : if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
2106 : : fd_sval, node, old_state,
2107 : : EXPECTED_PHASE_CAN_CONNECT))
2108 : : return false;
2109 : :
2110 : 61 : if (successful)
2111 : : {
2112 : 29 : model->update_for_zero_return (cd, true);
2113 : 29 : state_t next_state = NULL;
2114 : 29 : if (old_state == m_new_stream_socket)
2115 : 3 : next_state = m_connected_stream_socket;
2116 : 26 : else if (old_state == m_new_datagram_socket)
2117 : : /* It's legal to call connect on a datagram socket, potentially
2118 : : more than once. We don't transition states for this. */
2119 : : next_state = m_new_datagram_socket;
2120 : 26 : else if (old_state == m_new_unknown_socket)
2121 : 14 : next_state = m_stop;
2122 : 12 : else if (old_state == m_start
2123 : 7 : || old_state == m_constant_fd)
2124 : 12 : next_state = m_stop;
2125 : 0 : else if (old_state == m_stop)
2126 : : next_state = m_stop;
2127 : : else
2128 : 0 : gcc_unreachable ();
2129 : 29 : sm_ctxt->set_next_state (cd.get_call_stmt (), fd_sval, next_state);
2130 : : }
2131 : : else
2132 : : {
2133 : : /* Return -1; set errno. */
2134 : 32 : model->update_for_int_cst_return (cd, -1, true);
2135 : 32 : model->set_errno (cd);
2136 : : /* TODO: perhaps transition to a failed state, since the
2137 : : portable way to handle a failed "connect" is to close
2138 : : the socket and try again with a new socket. */
2139 : : }
2140 : :
2141 : : return true;
2142 : : }
2143 : :
2144 : : void
2145 : 35038 : fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
2146 : : const gimple *stmt, const svalue *lhs,
2147 : : enum tree_code op, const svalue *rhs) const
2148 : : {
2149 : 35038 : if (tree cst = rhs->maybe_get_constant ())
2150 : : {
2151 : 28329 : if (TREE_CODE (cst) == INTEGER_CST)
2152 : : {
2153 : 28319 : int val = TREE_INT_CST_LOW (cst);
2154 : 28319 : if (val == -1)
2155 : : {
2156 : 714 : if (op == NE_EXPR)
2157 : 357 : make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2158 : :
2159 : 357 : else if (op == EQ_EXPR)
2160 : 357 : make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
2161 : : lhs);
2162 : : }
2163 : : }
2164 : : }
2165 : :
2166 : 35038 : if (rhs->all_zeroes_p ())
2167 : : {
2168 : 22570 : if (op == GE_EXPR)
2169 : 670 : make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2170 : 21900 : else if (op == LT_EXPR)
2171 : 476 : make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2172 : : }
2173 : 35038 : }
2174 : :
2175 : : void
2176 : 1027 : fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt,
2177 : : const supernode *node,
2178 : : const gimple *stmt,
2179 : : const svalue *lhs) const
2180 : : {
2181 : 1027 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write,
2182 : 1027 : m_valid_read_write);
2183 : 1027 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only,
2184 : 1027 : m_valid_read_only);
2185 : 1027 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only,
2186 : 1027 : m_valid_write_only);
2187 : 1027 : }
2188 : :
2189 : : void
2190 : 833 : fd_state_machine::make_invalid_transitions_on_condition (
2191 : : sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
2192 : : const svalue *lhs) const
2193 : : {
2194 : 833 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid);
2195 : 833 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid);
2196 : 833 : sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid);
2197 : 833 : }
2198 : :
2199 : : bool
2200 : 1197478 : fd_state_machine::can_purge_p (state_t s) const
2201 : : {
2202 : 2394701 : if (is_unchecked_fd_p (s)
2203 : 1198773 : || is_valid_fd_p (s)
2204 : 1197136 : || is_socket_fd_p (s))
2205 : 1637 : return false;
2206 : : else
2207 : : return true;
2208 : : }
2209 : :
2210 : : std::unique_ptr<pending_diagnostic>
2211 : 138 : fd_state_machine::on_leak (tree var) const
2212 : : {
2213 : 138 : return make_unique<fd_leak> (*this, var);
2214 : : }
2215 : : } // namespace
2216 : :
2217 : : state_machine *
2218 : 3723 : make_fd_state_machine (logger *logger)
2219 : : {
2220 : 3723 : return new fd_state_machine (logger);
2221 : : }
2222 : :
2223 : : static bool
2224 : 2988 : get_fd_state (region_model_context *ctxt,
2225 : : sm_state_map **out_smap,
2226 : : const fd_state_machine **out_sm,
2227 : : unsigned *out_sm_idx,
2228 : : std::unique_ptr<sm_context> *out_sm_context)
2229 : : {
2230 : 2988 : if (!ctxt)
2231 : : return false;
2232 : :
2233 : 904 : const state_machine *sm;
2234 : 904 : if (!ctxt->get_fd_map (out_smap, &sm, out_sm_idx, out_sm_context))
2235 : : return false;
2236 : :
2237 : 904 : gcc_assert (sm);
2238 : :
2239 : 904 : *out_sm = (const fd_state_machine *)sm;
2240 : 904 : return true;
2241 : : }
2242 : :
2243 : : /* Specialcase hook for handling pipe, for use by
2244 : : kf_pipe::success::update_model. */
2245 : :
2246 : : void
2247 : 34 : region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
2248 : : {
2249 : 34 : sm_state_map *smap;
2250 : 34 : const fd_state_machine *fd_sm;
2251 : 34 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, NULL))
2252 : 16 : return;
2253 : 18 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2254 : 18 : if (!ext_state)
2255 : : return;
2256 : 18 : fd_sm->mark_as_valid_fd (this, smap, sval, *ext_state);
2257 : : }
2258 : :
2259 : : /* Handle calls to "socket".
2260 : : See e.g. https://man7.org/linux/man-pages/man3/socket.3p.html */
2261 : :
2262 : 3723 : class kf_socket : public known_function
2263 : : {
2264 : : public:
2265 : : class outcome_of_socket : public succeed_or_fail_call_info
2266 : : {
2267 : : public:
2268 : 294 : outcome_of_socket (const call_details &cd, bool success)
2269 : 294 : : succeed_or_fail_call_info (cd, success)
2270 : : {}
2271 : :
2272 : 1739 : bool update_model (region_model *model,
2273 : : const exploded_edge *,
2274 : : region_model_context *ctxt) const final override
2275 : : {
2276 : 1739 : const call_details cd (get_call_details (model, ctxt));
2277 : 1739 : sm_state_map *smap;
2278 : 1739 : const fd_state_machine *fd_sm;
2279 : 1739 : std::unique_ptr<sm_context> sm_ctxt;
2280 : 1739 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2281 : : {
2282 : 1489 : cd.set_any_lhs_with_defaults ();
2283 : 1489 : return true;
2284 : : }
2285 : 250 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2286 : 250 : if (!ext_state)
2287 : : {
2288 : 0 : cd.set_any_lhs_with_defaults ();
2289 : 0 : return true;
2290 : : }
2291 : :
2292 : 250 : return fd_sm->on_socket (cd, m_success, sm_ctxt.get (), *ext_state);
2293 : 1739 : }
2294 : : };
2295 : :
2296 : 3860 : bool matches_call_types_p (const call_details &cd) const final override
2297 : : {
2298 : 3860 : return cd.num_args () == 3;
2299 : : }
2300 : :
2301 : 1636 : void impl_call_post (const call_details &cd) const final override
2302 : : {
2303 : 1636 : if (cd.get_ctxt ())
2304 : : {
2305 : 147 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, false));
2306 : 147 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_socket> (cd, true));
2307 : 147 : cd.get_ctxt ()->terminate_path ();
2308 : : }
2309 : 1636 : }
2310 : : };
2311 : :
2312 : : /* Handle calls to "bind".
2313 : : See e.g. https://man7.org/linux/man-pages/man3/bind.3p.html */
2314 : :
2315 : 3723 : class kf_bind : public known_function
2316 : : {
2317 : : public:
2318 : : class outcome_of_bind : public succeed_or_fail_call_info
2319 : : {
2320 : : public:
2321 : 306 : outcome_of_bind (const call_details &cd, bool success)
2322 : 306 : : succeed_or_fail_call_info (cd, success)
2323 : : {}
2324 : :
2325 : 354 : bool update_model (region_model *model,
2326 : : const exploded_edge *,
2327 : : region_model_context *ctxt) const final override
2328 : : {
2329 : 354 : const call_details cd (get_call_details (model, ctxt));
2330 : 354 : sm_state_map *smap;
2331 : 354 : const fd_state_machine *fd_sm;
2332 : 354 : std::unique_ptr<sm_context> sm_ctxt;
2333 : 354 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2334 : : {
2335 : 98 : cd.set_any_lhs_with_defaults ();
2336 : 98 : return true;
2337 : : }
2338 : 256 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2339 : 256 : if (!ext_state)
2340 : : {
2341 : 0 : cd.set_any_lhs_with_defaults ();
2342 : 0 : return true;
2343 : : }
2344 : 256 : return fd_sm->on_bind (cd, m_success, sm_ctxt.get (), *ext_state);
2345 : 354 : }
2346 : : };
2347 : :
2348 : 1158 : bool matches_call_types_p (const call_details &cd) const final override
2349 : : {
2350 : 1158 : return (cd.num_args () == 3 && cd.arg_is_pointer_p (1));
2351 : : }
2352 : :
2353 : 251 : void impl_call_post (const call_details &cd) const final override
2354 : : {
2355 : 251 : if (cd.get_ctxt ())
2356 : : {
2357 : 153 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, false));
2358 : 153 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_bind> (cd, true));
2359 : 153 : cd.get_ctxt ()->terminate_path ();
2360 : : }
2361 : 251 : }
2362 : : };
2363 : :
2364 : : /* Handle calls to "listen".
2365 : : See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html */
2366 : :
2367 : 3723 : class kf_listen : public known_function
2368 : : {
2369 : : class outcome_of_listen : public succeed_or_fail_call_info
2370 : : {
2371 : : public:
2372 : 136 : outcome_of_listen (const call_details &cd, bool success)
2373 : 136 : : succeed_or_fail_call_info (cd, success)
2374 : : {}
2375 : :
2376 : 127 : bool update_model (region_model *model,
2377 : : const exploded_edge *,
2378 : : region_model_context *ctxt) const final override
2379 : : {
2380 : 127 : const call_details cd (get_call_details (model, ctxt));
2381 : 127 : sm_state_map *smap;
2382 : 127 : const fd_state_machine *fd_sm;
2383 : 127 : std::unique_ptr<sm_context> sm_ctxt;
2384 : 127 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2385 : : {
2386 : 9 : cd.set_any_lhs_with_defaults ();
2387 : 9 : return true;
2388 : : }
2389 : 118 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2390 : 118 : if (!ext_state)
2391 : : {
2392 : 0 : cd.set_any_lhs_with_defaults ();
2393 : 0 : return true;
2394 : : }
2395 : :
2396 : 118 : return fd_sm->on_listen (cd, m_success, sm_ctxt.get (), *ext_state);
2397 : 127 : }
2398 : : };
2399 : :
2400 : 426 : bool matches_call_types_p (const call_details &cd) const final override
2401 : : {
2402 : 426 : return cd.num_args () == 2;
2403 : : }
2404 : :
2405 : 77 : void impl_call_post (const call_details &cd) const final override
2406 : : {
2407 : 77 : if (cd.get_ctxt ())
2408 : : {
2409 : 68 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, false));
2410 : 68 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_listen> (cd, true));
2411 : 68 : cd.get_ctxt ()->terminate_path ();
2412 : : }
2413 : 77 : }
2414 : : };
2415 : :
2416 : : /* Handle calls to "accept".
2417 : : See e.g. https://man7.org/linux/man-pages/man3/accept.3p.html */
2418 : :
2419 : 3723 : class kf_accept : public known_function
2420 : : {
2421 : : class outcome_of_accept : public succeed_or_fail_call_info
2422 : : {
2423 : : public:
2424 : 82 : outcome_of_accept (const call_details &cd, bool success)
2425 : 82 : : succeed_or_fail_call_info (cd, success)
2426 : : {}
2427 : :
2428 : 66 : bool update_model (region_model *model,
2429 : : const exploded_edge *,
2430 : : region_model_context *ctxt) const final override
2431 : : {
2432 : 66 : const call_details cd (get_call_details (model, ctxt));
2433 : 66 : sm_state_map *smap;
2434 : 66 : const fd_state_machine *fd_sm;
2435 : 66 : std::unique_ptr<sm_context> sm_ctxt;
2436 : 66 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2437 : : {
2438 : 12 : cd.set_any_lhs_with_defaults ();
2439 : 12 : return true;
2440 : : }
2441 : 54 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2442 : 54 : if (!ext_state)
2443 : : {
2444 : 0 : cd.set_any_lhs_with_defaults ();
2445 : 0 : return true;
2446 : : }
2447 : :
2448 : 54 : return fd_sm->on_accept (cd, m_success, sm_ctxt.get (), *ext_state);
2449 : 66 : }
2450 : : };
2451 : :
2452 : 330 : bool matches_call_types_p (const call_details &cd) const final override
2453 : : {
2454 : 330 : return (cd.num_args () == 3
2455 : 330 : && cd.arg_is_pointer_p (1)
2456 : 600 : && cd.arg_is_pointer_p (2));
2457 : : }
2458 : :
2459 : 53 : void impl_call_post (const call_details &cd) const final override
2460 : : {
2461 : 53 : if (cd.get_ctxt ())
2462 : : {
2463 : 41 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, false));
2464 : 41 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_accept> (cd, true));
2465 : 41 : cd.get_ctxt ()->terminate_path ();
2466 : : }
2467 : 53 : }
2468 : : };
2469 : :
2470 : : /* Handle calls to "connect".
2471 : : See e.g. https://man7.org/linux/man-pages/man3/connect.3p.html */
2472 : :
2473 : 3723 : class kf_connect : public known_function
2474 : : {
2475 : : public:
2476 : : class outcome_of_connect : public succeed_or_fail_call_info
2477 : : {
2478 : : public:
2479 : 100 : outcome_of_connect (const call_details &cd, bool success)
2480 : 100 : : succeed_or_fail_call_info (cd, success)
2481 : : {}
2482 : :
2483 : 514 : bool update_model (region_model *model,
2484 : : const exploded_edge *,
2485 : : region_model_context *ctxt) const final override
2486 : : {
2487 : 514 : const call_details cd (get_call_details (model, ctxt));
2488 : 514 : sm_state_map *smap;
2489 : 514 : const fd_state_machine *fd_sm;
2490 : 514 : std::unique_ptr<sm_context> sm_ctxt;
2491 : 514 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2492 : : {
2493 : 450 : cd.set_any_lhs_with_defaults ();
2494 : 450 : return true;
2495 : : }
2496 : 64 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2497 : 64 : if (!ext_state)
2498 : : {
2499 : 0 : cd.set_any_lhs_with_defaults ();
2500 : 0 : return true;
2501 : : }
2502 : :
2503 : 64 : return fd_sm->on_connect (cd, m_success, sm_ctxt.get (), *ext_state);
2504 : 514 : }
2505 : : };
2506 : :
2507 : 1200 : bool matches_call_types_p (const call_details &cd) const final override
2508 : : {
2509 : 1200 : return (cd.num_args () == 3
2510 : 1200 : && cd.arg_is_pointer_p (1));
2511 : : }
2512 : :
2513 : 500 : void impl_call_post (const call_details &cd) const final override
2514 : : {
2515 : 500 : if (cd.get_ctxt ())
2516 : : {
2517 : 50 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, false));
2518 : 50 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_connect> (cd, true));
2519 : 50 : cd.get_ctxt ()->terminate_path ();
2520 : : }
2521 : 500 : }
2522 : : };
2523 : :
2524 : : /* Handler for "isatty"".
2525 : : See e.g. https://man7.org/linux/man-pages/man3/isatty.3.html */
2526 : :
2527 : 3723 : class kf_isatty : public known_function
2528 : : {
2529 : : class outcome_of_isatty : public succeed_or_fail_call_info
2530 : : {
2531 : : public:
2532 : 556 : outcome_of_isatty (const call_details &cd, bool success)
2533 : 556 : : succeed_or_fail_call_info (cd, success)
2534 : : {}
2535 : :
2536 : 308 : bool update_model (region_model *model,
2537 : : const exploded_edge *,
2538 : : region_model_context *ctxt) const final override
2539 : : {
2540 : 308 : const call_details cd (get_call_details (model, ctxt));
2541 : :
2542 : 308 : if (m_success)
2543 : : {
2544 : : /* Return 1. */
2545 : 154 : model->update_for_int_cst_return (cd, 1, true);
2546 : : }
2547 : : else
2548 : : {
2549 : : /* Return 0; set errno. */
2550 : 154 : model->update_for_int_cst_return (cd, 0, true);
2551 : 154 : model->set_errno (cd);
2552 : : }
2553 : :
2554 : 308 : return feasible_p (cd, ctxt);
2555 : : }
2556 : :
2557 : : private:
2558 : 308 : bool feasible_p (const call_details &cd,
2559 : : region_model_context *ctxt) const
2560 : : {
2561 : 308 : if (m_success)
2562 : : {
2563 : : /* Can't be "success" on a closed/invalid fd. */
2564 : 154 : sm_state_map *smap;
2565 : 154 : const fd_state_machine *fd_sm;
2566 : 154 : std::unique_ptr<sm_context> sm_ctxt;
2567 : 154 : if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
2568 : : return true;
2569 : 144 : const extrinsic_state *ext_state = ctxt->get_ext_state ();
2570 : 144 : if (!ext_state)
2571 : : return true;
2572 : :
2573 : 144 : const svalue *fd_sval = cd.get_arg_svalue (0);
2574 : 144 : state_machine::state_t old_state
2575 : 144 : = sm_ctxt->get_state (cd.get_call_stmt (), fd_sval);
2576 : :
2577 : 144 : if (fd_sm->is_closed_fd_p (old_state)
2578 : 144 : || old_state == fd_sm->m_invalid)
2579 : : return false;
2580 : 154 : }
2581 : : return true;
2582 : : }
2583 : : }; // class outcome_of_isatty
2584 : :
2585 : : public:
2586 : 1708 : bool matches_call_types_p (const call_details &cd) const final override
2587 : : {
2588 : 1708 : return cd.num_args () == 1;
2589 : : }
2590 : :
2591 : 298 : void impl_call_post (const call_details &cd) const final override
2592 : : {
2593 : 298 : if (cd.get_ctxt ())
2594 : : {
2595 : 278 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_isatty> (cd, false));
2596 : 278 : cd.get_ctxt ()->bifurcate (make_unique<outcome_of_isatty> (cd, true));
2597 : 278 : cd.get_ctxt ()->terminate_path ();
2598 : : }
2599 : 298 : }
2600 : : };
2601 : :
2602 : : /* Handler for calls to "pipe" and "pipe2".
2603 : : See e.g. https://www.man7.org/linux/man-pages/man2/pipe.2.html */
2604 : :
2605 : : class kf_pipe : public known_function
2606 : : {
2607 : : class failure : public failed_call_info
2608 : : {
2609 : : public:
2610 : 9 : failure (const call_details &cd) : failed_call_info (cd) {}
2611 : :
2612 : 21 : bool update_model (region_model *model,
2613 : : const exploded_edge *,
2614 : : region_model_context *ctxt) const final override
2615 : : {
2616 : : /* Return -1; everything else is unchanged. */
2617 : 21 : const call_details cd (get_call_details (model, ctxt));
2618 : 21 : model->update_for_int_cst_return (cd, -1, true);
2619 : 21 : return true;
2620 : : }
2621 : : };
2622 : :
2623 : : class success : public success_call_info
2624 : : {
2625 : : public:
2626 : 9 : success (const call_details &cd) : success_call_info (cd) {}
2627 : :
2628 : 17 : bool update_model (region_model *model,
2629 : : const exploded_edge *,
2630 : : region_model_context *ctxt) const final override
2631 : : {
2632 : 17 : const call_details cd (get_call_details (model, ctxt));
2633 : :
2634 : : /* Return 0. */
2635 : 17 : model->update_for_zero_return (cd, true);
2636 : :
2637 : : /* Update fd array. */
2638 : 17 : region_model_manager *mgr = cd.get_manager ();
2639 : 17 : tree arr_tree = cd.get_arg_tree (0);
2640 : 17 : const svalue *arr_sval = cd.get_arg_svalue (0);
2641 : 51 : for (int idx = 0; idx < 2; idx++)
2642 : : {
2643 : 34 : const region *arr_reg
2644 : 34 : = model->deref_rvalue (arr_sval, arr_tree, cd.get_ctxt ());
2645 : 34 : const svalue *idx_sval
2646 : 34 : = mgr->get_or_create_int_cst (integer_type_node, idx);
2647 : 34 : const region *element_reg
2648 : 34 : = mgr->get_element_region (arr_reg, integer_type_node, idx_sval);
2649 : 34 : conjured_purge p (model, cd.get_ctxt ());
2650 : 34 : const svalue *fd_sval
2651 : 34 : = mgr->get_or_create_conjured_svalue (integer_type_node,
2652 : 34 : cd.get_call_stmt (),
2653 : : element_reg,
2654 : : p);
2655 : 34 : model->set_value (element_reg, fd_sval, cd.get_ctxt ());
2656 : 34 : model->mark_as_valid_fd (fd_sval, cd.get_ctxt ());
2657 : : }
2658 : 17 : return true;
2659 : : }
2660 : : };
2661 : :
2662 : : public:
2663 : 7446 : kf_pipe (unsigned num_args)
2664 : 7446 : : m_num_args (num_args)
2665 : : {
2666 : 7446 : gcc_assert (num_args > 0);
2667 : 7446 : }
2668 : :
2669 : 124 : bool matches_call_types_p (const call_details &cd) const final override
2670 : : {
2671 : 124 : return (cd.num_args () == m_num_args && cd.arg_is_pointer_p (0));
2672 : : }
2673 : :
2674 : 29 : void impl_call_post (const call_details &cd) const final override
2675 : : {
2676 : 29 : if (cd.get_ctxt ())
2677 : : {
2678 : 9 : cd.get_ctxt ()->bifurcate (make_unique<failure> (cd));
2679 : 9 : cd.get_ctxt ()->bifurcate (make_unique<success> (cd));
2680 : 9 : cd.get_ctxt ()->terminate_path ();
2681 : : }
2682 : 29 : }
2683 : :
2684 : : private:
2685 : : unsigned m_num_args;
2686 : : };
2687 : :
2688 : : /* Handler for "read".
2689 : : ssize_t read(int fildes, void *buf, size_t nbyte);
2690 : : See e.g. https://man7.org/linux/man-pages/man2/read.2.html */
2691 : :
2692 : 3723 : class kf_read : public known_function
2693 : : {
2694 : : public:
2695 : 387 : bool matches_call_types_p (const call_details &cd) const final override
2696 : : {
2697 : 387 : return (cd.num_args () == 3
2698 : 387 : && cd.arg_is_pointer_p (1)
2699 : 774 : && cd.arg_is_size_p (2));
2700 : : }
2701 : :
2702 : : /* For now, assume that any call to "read" fully clobbers the buffer
2703 : : passed in. This isn't quite correct (e.g. errors, partial reads;
2704 : : see PR analyzer/108689), but at least stops us falsely complaining
2705 : : about the buffer being uninitialized. */
2706 : 90 : void impl_call_pre (const call_details &cd) const final override
2707 : : {
2708 : 90 : region_model *model = cd.get_model ();
2709 : 90 : const svalue *ptr_sval = cd.get_arg_svalue (1);
2710 : 90 : if (const region *reg = ptr_sval->maybe_get_region ())
2711 : : {
2712 : 71 : const region *base_reg = reg->get_base_region ();
2713 : 71 : const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
2714 : 71 : model->set_value (base_reg, new_sval, cd.get_ctxt ());
2715 : : }
2716 : 90 : cd.set_any_lhs_with_defaults ();
2717 : 90 : }
2718 : : };
2719 : :
2720 : :
2721 : : /* Populate KFM with instances of known functions relating to
2722 : : file descriptors. */
2723 : :
2724 : : void
2725 : 3723 : register_known_fd_functions (known_function_manager &kfm)
2726 : : {
2727 : 3723 : kfm.add ("accept", make_unique<kf_accept> ());
2728 : 3723 : kfm.add ("bind", make_unique<kf_bind> ());
2729 : 3723 : kfm.add ("connect", make_unique<kf_connect> ());
2730 : 3723 : kfm.add ("isatty", make_unique<kf_isatty> ());
2731 : 3723 : kfm.add ("listen", make_unique<kf_listen> ());
2732 : 3723 : kfm.add ("pipe", make_unique<kf_pipe> (1));
2733 : 3723 : kfm.add ("pipe2", make_unique<kf_pipe> (2));
2734 : 3723 : kfm.add ("read", make_unique<kf_read> ());
2735 : 3723 : kfm.add ("socket", make_unique<kf_socket> ());
2736 : 3723 : }
2737 : :
2738 : : } // namespace ana
2739 : :
2740 : : #endif // ENABLE_ANALYZER
|