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