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
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it
8under the terms of the GNU General Public License as published by
9the Free Software Foundation; either version 3, or (at your option)
10any later version.
11
12GCC is distributed in the hope that it will be useful, but
13WITHOUT ANY WARRANTY; without even the implied warranty of
14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15General Public License for more details.
16
17You should have received a copy of the GNU General Public License
18along 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
54namespace ana {
55
56namespace {
57
58/* An enum for distinguishing between three different access modes. */
59
60enum access_mode
61{
62 READ_WRITE,
63 READ_ONLY,
64 WRITE_ONLY
65};
66
67enum 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. */
75enum dup
76{
77 DUP_1,
78 DUP_2,
79 DUP_3
80};
81
82/* Enum for use by -Wanalyzer-fd-phase-mismatch. */
83
84enum 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
93class fd_state_machine : public state_machine
94{
95public:
96 fd_state_machine (logger *logger);
97
98 bool
99 inherited_state_p () const final override
100 {
101 return false;
102 }
103
104 state_machine::state_t
105 get_default_state (const svalue *sval) const final override
106 {
107 if (tree cst = sval->maybe_get_constant ())
108 {
109 if (TREE_CODE (cst) == INTEGER_CST)
110 {
111 int val = TREE_INT_CST_LOW (cst);
112 if (val >= 0)
113 return m_constant_fd;
114 else
115 return m_invalid;
116 }
117 }
118 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
230private:
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. */
281class fd_diagnostic : public pending_diagnostic
282{
283public:
284 fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
285 {
286 }
287
288 bool
289 subclass_equal_p (const pending_diagnostic &base_other) const override
290 {
291 return same_tree_p (t1: m_arg, t2: ((const fd_diagnostic &)base_other).m_arg);
292 }
293
294 label_text
295 describe_state_change (const evdesc::state_change &change) override
296 {
297 if (change.m_old_state == m_sm.get_start_state ())
298 {
299 if (change.m_new_state == m_sm.m_unchecked_read_write
300 || change.m_new_state == m_sm.m_valid_read_write)
301 return change.formatted_print (fmt: "opened here as read-write");
302
303 if (change.m_new_state == m_sm.m_unchecked_read_only
304 || change.m_new_state == m_sm.m_valid_read_only)
305 return change.formatted_print (fmt: "opened here as read-only");
306
307 if (change.m_new_state == m_sm.m_unchecked_write_only
308 || change.m_new_state == m_sm.m_valid_write_only)
309 return change.formatted_print (fmt: "opened here as write-only");
310
311 if (change.m_new_state == m_sm.m_new_datagram_socket)
312 return change.formatted_print (fmt: "datagram socket created here");
313
314 if (change.m_new_state == m_sm.m_new_stream_socket)
315 return change.formatted_print (fmt: "stream socket created here");
316
317 if (change.m_new_state == m_sm.m_new_unknown_socket
318 || change.m_new_state == m_sm.m_connected_stream_socket)
319 return change.formatted_print (fmt: "socket created here");
320 }
321
322 if (change.m_new_state == m_sm.m_bound_datagram_socket)
323 return change.formatted_print (fmt: "datagram socket bound here");
324
325 if (change.m_new_state == m_sm.m_bound_stream_socket)
326 return change.formatted_print (fmt: "stream socket bound here");
327
328 if (change.m_new_state == m_sm.m_bound_unknown_socket
329 || change.m_new_state == m_sm.m_connected_stream_socket)
330 return change.formatted_print (fmt: "socket bound here");
331
332 if (change.m_new_state == m_sm.m_listening_stream_socket)
333 return change.formatted_print
334 (fmt: "stream socket marked as passive here via %qs", "listen");
335
336 if (change.m_new_state == m_sm.m_closed)
337 return change.formatted_print (fmt: "closed here");
338
339 if (m_sm.is_unchecked_fd_p (s: change.m_old_state)
340 && m_sm.is_valid_fd_p (s: change.m_new_state))
341 {
342 if (change.m_expr)
343 return change.formatted_print (
344 fmt: "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
345 else
346 return change.formatted_print (fmt: "assuming a valid file descriptor");
347 }
348
349 if (m_sm.is_unchecked_fd_p (s: change.m_old_state)
350 && change.m_new_state == m_sm.m_invalid)
351 {
352 if (change.m_expr)
353 return change.formatted_print (
354 fmt: "assuming %qE is an invalid file descriptor (< 0)",
355 change.m_expr);
356 else
357 return change.formatted_print (fmt: "assuming an invalid file descriptor");
358 }
359
360 return label_text ();
361 }
362
363 diagnostic_event::meaning
364 get_meaning_for_state_change (
365 const evdesc::state_change &change) const final override
366 {
367 if (change.m_old_state == m_sm.get_start_state ()
368 && (m_sm.is_unchecked_fd_p (s: change.m_new_state)
369 || change.m_new_state == m_sm.m_new_datagram_socket
370 || change.m_new_state == m_sm.m_new_stream_socket
371 || change.m_new_state == m_sm.m_new_unknown_socket))
372 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
373 diagnostic_event::NOUN_resource);
374 if (change.m_new_state == m_sm.m_closed)
375 return diagnostic_event::meaning (diagnostic_event::VERB_release,
376 diagnostic_event::NOUN_resource);
377 return diagnostic_event::meaning ();
378 }
379
380protected:
381 const fd_state_machine &m_sm;
382 tree m_arg;
383};
384
385class fd_param_diagnostic : public fd_diagnostic
386{
387public:
388 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
389 const char *attr_name, int arg_idx)
390 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
391 m_attr_name (attr_name), m_arg_idx (arg_idx)
392 {
393 }
394
395 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
396 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
397 m_attr_name (NULL), m_arg_idx (-1)
398 {
399 }
400
401 bool
402 subclass_equal_p (const pending_diagnostic &base_other) const override
403 {
404 const fd_param_diagnostic &sub_other
405 = (const fd_param_diagnostic &)base_other;
406 return (same_tree_p (t1: m_arg, t2: sub_other.m_arg)
407 && same_tree_p (t1: m_callee_fndecl, t2: sub_other.m_callee_fndecl)
408 && m_arg_idx == sub_other.m_arg_idx
409 && ((m_attr_name)
410 ? (strcmp (s1: m_attr_name, s2: sub_other.m_attr_name) == 0)
411 : true));
412 }
413
414 void
415 inform_filedescriptor_attribute (access_directions fd_dir)
416 {
417
418 if (m_attr_name)
419 switch (fd_dir)
420 {
421 case DIRS_READ_WRITE:
422 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 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
426 break;
427 case DIRS_WRITE:
428 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 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
432 break;
433 case DIRS_READ:
434 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 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
438 break;
439 }
440 }
441
442protected:
443 tree m_callee_fndecl;
444 const char *m_attr_name;
445 /* ARG_IDX is 0-based. */
446 int m_arg_idx;
447};
448
449class fd_leak : public fd_diagnostic
450{
451public:
452 fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
453
454 const char *
455 get_kind () const final override
456 {
457 return "fd_leak";
458 }
459
460 int
461 get_controlling_option () const final override
462 {
463 return OPT_Wanalyzer_fd_leak;
464 }
465
466 bool
467 emit (diagnostic_emission_context &ctxt) final override
468 {
469 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
470 Lifetime
471 */
472 ctxt.add_cwe (cwe: 775);
473 if (m_arg)
474 return ctxt.warn ("leak of file descriptor %qE", m_arg);
475 else
476 return ctxt.warn ("leak of file descriptor");
477 }
478
479 label_text
480 describe_state_change (const evdesc::state_change &change) final override
481 {
482 if (m_sm.is_unchecked_fd_p (s: change.m_new_state))
483 {
484 m_open_event = change.m_event_id;
485 return label_text::borrow (buffer: "opened here");
486 }
487
488 return fd_diagnostic::describe_state_change (change);
489 }
490
491 label_text
492 describe_final_event (const evdesc::final_event &ev) final override
493 {
494 if (m_open_event.known_p ())
495 {
496 if (ev.m_expr)
497 return ev.formatted_print (fmt: "%qE leaks here; was opened at %@",
498 ev.m_expr, &m_open_event);
499 else
500 return ev.formatted_print (fmt: "leaks here; was opened at %@",
501 &m_open_event);
502 }
503 else
504 {
505 if (ev.m_expr)
506 return ev.formatted_print (fmt: "%qE leaks here", ev.m_expr);
507 else
508 return ev.formatted_print (fmt: "leaks here");
509 }
510 }
511
512private:
513 diagnostic_event_id_t m_open_event;
514};
515
516class fd_access_mode_mismatch : public fd_param_diagnostic
517{
518public:
519 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 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
524 m_fd_dir (fd_dir)
525
526 {
527 }
528
529 fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
530 enum access_directions fd_dir,
531 const tree callee_fndecl)
532 : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
533 {
534 }
535
536 const char *
537 get_kind () const final override
538 {
539 return "fd_access_mode_mismatch";
540 }
541
542 int
543 get_controlling_option () const final override
544 {
545 return OPT_Wanalyzer_fd_access_mode_mismatch;
546 }
547
548 bool
549 emit (diagnostic_emission_context &ctxt) final override
550 {
551 bool warned;
552 switch (m_fd_dir)
553 {
554 case DIRS_READ:
555 warned = ctxt.warn ("%qE on read-only file descriptor %qE",
556 m_callee_fndecl, m_arg);
557 break;
558 case DIRS_WRITE:
559 warned = ctxt.warn ("%qE on write-only file descriptor %qE",
560 m_callee_fndecl, m_arg);
561 break;
562 default:
563 gcc_unreachable ();
564 }
565 if (warned)
566 inform_filedescriptor_attribute (fd_dir: m_fd_dir);
567 return warned;
568 }
569
570 label_text
571 describe_final_event (const evdesc::final_event &ev) final override
572 {
573 switch (m_fd_dir)
574 {
575 case DIRS_READ:
576 return ev.formatted_print (fmt: "%qE on read-only file descriptor %qE",
577 m_callee_fndecl, m_arg);
578 case DIRS_WRITE:
579 return ev.formatted_print (fmt: "%qE on write-only file descriptor %qE",
580 m_callee_fndecl, m_arg);
581 default:
582 gcc_unreachable ();
583 }
584 }
585
586private:
587 enum access_directions m_fd_dir;
588};
589
590class fd_double_close : public fd_diagnostic
591{
592public:
593 fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
594 {
595 }
596
597 const char *
598 get_kind () const final override
599 {
600 return "fd_double_close";
601 }
602
603 int
604 get_controlling_option () const final override
605 {
606 return OPT_Wanalyzer_fd_double_close;
607 }
608 bool
609 emit (diagnostic_emission_context &ctxt) final override
610 {
611 // CWE-1341: Multiple Releases of Same Resource or Handle
612 ctxt.add_cwe (cwe: 1341);
613 return ctxt.warn ("double %<close%> of file descriptor %qE", m_arg);
614 }
615
616 label_text
617 describe_state_change (const evdesc::state_change &change) override
618 {
619 if (m_sm.is_unchecked_fd_p (s: change.m_new_state))
620 return label_text::borrow (buffer: "opened here");
621
622 if (change.m_new_state == m_sm.m_closed)
623 {
624 m_first_close_event = change.m_event_id;
625 return change.formatted_print (fmt: "first %qs here", "close");
626 }
627 return fd_diagnostic::describe_state_change (change);
628 }
629
630 label_text
631 describe_final_event (const evdesc::final_event &ev) final override
632 {
633 if (m_first_close_event.known_p ())
634 return ev.formatted_print (fmt: "second %qs here; first %qs was at %@",
635 "close", "close", &m_first_close_event);
636 return ev.formatted_print (fmt: "second %qs here", "close");
637 }
638
639private:
640 diagnostic_event_id_t m_first_close_event;
641};
642
643class fd_use_after_close : public fd_param_diagnostic
644{
645public:
646 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 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
650 {
651 }
652
653 fd_use_after_close (const fd_state_machine &sm, tree arg,
654 const tree callee_fndecl)
655 : fd_param_diagnostic (sm, arg, callee_fndecl)
656 {
657 }
658
659 const char *
660 get_kind () const final override
661 {
662 return "fd_use_after_close";
663 }
664
665 int
666 get_controlling_option () const final override
667 {
668 return OPT_Wanalyzer_fd_use_after_close;
669 }
670
671 bool
672 emit (diagnostic_emission_context &ctxt) final override
673 {
674 bool warned = ctxt.warn ("%qE on closed file descriptor %qE",
675 m_callee_fndecl, m_arg);
676 if (warned)
677 inform_filedescriptor_attribute (fd_dir: DIRS_READ_WRITE);
678 return warned;
679 }
680
681 label_text
682 describe_state_change (const evdesc::state_change &change) override
683 {
684 if (m_sm.is_unchecked_fd_p (s: change.m_new_state))
685 return label_text::borrow (buffer: "opened here");
686
687 if (change.m_new_state == m_sm.m_closed)
688 {
689 m_first_close_event = change.m_event_id;
690 return change.formatted_print (fmt: "closed here");
691 }
692
693 return fd_diagnostic::describe_state_change (change);
694 }
695
696 label_text
697 describe_final_event (const evdesc::final_event &ev) final override
698 {
699 if (m_first_close_event.known_p ())
700 return ev.formatted_print (
701 fmt: "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
702 m_arg, "close", &m_first_close_event);
703 else
704 return ev.formatted_print (fmt: "%qE on closed file descriptor %qE",
705 m_callee_fndecl, m_arg);
706 }
707
708private:
709 diagnostic_event_id_t m_first_close_event;
710};
711
712class fd_use_without_check : public fd_param_diagnostic
713{
714public:
715 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 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
719 {
720 }
721
722 fd_use_without_check (const fd_state_machine &sm, tree arg,
723 const tree callee_fndecl)
724 : fd_param_diagnostic (sm, arg, callee_fndecl)
725 {
726 }
727
728 const char *
729 get_kind () const final override
730 {
731 return "fd_use_without_check";
732 }
733
734 int
735 get_controlling_option () const final override
736 {
737 return OPT_Wanalyzer_fd_use_without_check;
738 }
739
740 bool
741 emit (diagnostic_emission_context &ctxt) final override
742 {
743 bool warned = ctxt.warn ("%qE on possibly invalid file descriptor %qE",
744 m_callee_fndecl, m_arg);
745 if (warned)
746 inform_filedescriptor_attribute (fd_dir: DIRS_READ_WRITE);
747 return warned;
748 }
749
750 label_text
751 describe_state_change (const evdesc::state_change &change) override
752 {
753 if (m_sm.is_unchecked_fd_p (s: change.m_new_state))
754 {
755 m_first_open_event = change.m_event_id;
756 return label_text::borrow (buffer: "opened here");
757 }
758
759 return fd_diagnostic::describe_state_change (change);
760 }
761
762 label_text
763 describe_final_event (const evdesc::final_event &ev) final override
764 {
765 if (m_first_open_event.known_p ())
766 return ev.formatted_print (
767 fmt: "%qE could be invalid: unchecked value from %@", m_arg,
768 &m_first_open_event);
769 else
770 return ev.formatted_print (fmt: "%qE could be invalid", m_arg);
771 }
772
773private:
774 diagnostic_event_id_t m_first_open_event;
775};
776
777/* Concrete pending_diagnostic subclass for -Wanalyzer-fd-phase-mismatch. */
778
779class fd_phase_mismatch : public fd_param_diagnostic
780{
781public:
782 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 : fd_param_diagnostic (sm, arg, callee_fndecl),
787 m_actual_state (actual_state),
788 m_expected_phase (expected_phase)
789 {
790 gcc_assert (m_sm.is_socket_fd_p (actual_state));
791 switch (expected_phase)
792 {
793 case EXPECTED_PHASE_CAN_TRANSFER:
794 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 case EXPECTED_PHASE_CAN_BIND:
799 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 case EXPECTED_PHASE_CAN_LISTEN:
806 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 case EXPECTED_PHASE_CAN_ACCEPT:
811 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 case EXPECTED_PHASE_CAN_CONNECT:
818 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 }
826
827 const char *
828 get_kind () const final override
829 {
830 return "fd_phase_mismatch";
831 }
832
833 bool
834 subclass_equal_p (const pending_diagnostic &base_other) const final override
835 {
836 const fd_phase_mismatch &sub_other = (const fd_phase_mismatch &)base_other;
837 if (!fd_param_diagnostic ::subclass_equal_p (base_other: sub_other))
838 return false;
839 return (m_actual_state == sub_other.m_actual_state
840 && m_expected_phase == sub_other.m_expected_phase);
841 }
842
843 int
844 get_controlling_option () const final override
845 {
846 return OPT_Wanalyzer_fd_phase_mismatch;
847 }
848
849 bool
850 emit (diagnostic_emission_context &ctxt) final override
851 {
852 /* CWE-666: Operation on Resource in Wrong Phase of Lifetime. */
853 ctxt.add_cwe (cwe: 666);
854 return ctxt.warn ("%qE on file descriptor %qE in wrong phase",
855 m_callee_fndecl, m_arg);
856 }
857
858 label_text
859 describe_final_event (const evdesc::final_event &ev) final override
860 {
861 switch (m_expected_phase)
862 {
863 case EXPECTED_PHASE_CAN_TRANSFER:
864 {
865 if (m_actual_state == m_sm.m_new_stream_socket)
866 return ev.formatted_print
867 (fmt: "%qE expects a stream socket to be connected via %qs"
868 " but %qE has not yet been bound",
869 m_callee_fndecl, "accept", m_arg);
870 if (m_actual_state == m_sm.m_bound_stream_socket)
871 return ev.formatted_print
872 (fmt: "%qE expects a stream socket to be connected via %qs"
873 " but %qE is not yet listening",
874 m_callee_fndecl, "accept", m_arg);
875 if (m_actual_state == m_sm.m_listening_stream_socket)
876 return ev.formatted_print
877 (fmt: "%qE expects a stream socket to be connected via"
878 " the return value of %qs"
879 " but %qE is listening; wrong file descriptor?",
880 m_callee_fndecl, "accept", m_arg);
881 }
882 break;
883 case EXPECTED_PHASE_CAN_BIND:
884 {
885 if (m_actual_state == m_sm.m_bound_datagram_socket
886 || m_actual_state == m_sm.m_bound_stream_socket
887 || m_actual_state == m_sm.m_bound_unknown_socket)
888 return ev.formatted_print
889 (fmt: "%qE expects a new socket file descriptor"
890 " but %qE has already been bound",
891 m_callee_fndecl, m_arg);
892 if (m_actual_state == m_sm.m_connected_stream_socket)
893 return ev.formatted_print
894 (fmt: "%qE expects a new socket file descriptor"
895 " but %qE is already connected",
896 m_callee_fndecl, m_arg);
897 if (m_actual_state == m_sm.m_listening_stream_socket)
898 return ev.formatted_print
899 (fmt: "%qE expects a new socket file descriptor"
900 " but %qE is already listening",
901 m_callee_fndecl, m_arg);
902 }
903 break;
904 case EXPECTED_PHASE_CAN_LISTEN:
905 {
906 if (m_actual_state == m_sm.m_new_stream_socket
907 || m_actual_state == m_sm.m_new_unknown_socket)
908 return ev.formatted_print
909 (fmt: "%qE expects a bound stream socket file descriptor"
910 " but %qE has not yet been bound",
911 m_callee_fndecl, m_arg);
912 if (m_actual_state == m_sm.m_connected_stream_socket)
913 return ev.formatted_print
914 (fmt: "%qE expects a bound stream socket file descriptor"
915 " but %qE is connected",
916 m_callee_fndecl, m_arg);
917 }
918 break;
919 case EXPECTED_PHASE_CAN_ACCEPT:
920 {
921 if (m_actual_state == m_sm.m_new_stream_socket
922 || m_actual_state == m_sm.m_new_unknown_socket)
923 return ev.formatted_print
924 (fmt: "%qE expects a listening stream socket file descriptor"
925 " but %qE has not yet been bound",
926 m_callee_fndecl, m_arg);
927 if (m_actual_state == m_sm.m_bound_stream_socket
928 || m_actual_state == m_sm.m_bound_unknown_socket)
929 return ev.formatted_print
930 (fmt: "%qE expects a listening stream socket file descriptor"
931 " whereas %qE is bound but not yet listening",
932 m_callee_fndecl, m_arg);
933 if (m_actual_state == m_sm.m_connected_stream_socket)
934 return ev.formatted_print
935 (fmt: "%qE expects a listening stream socket file descriptor"
936 " but %qE is connected",
937 m_callee_fndecl, m_arg);
938 }
939 break;
940 case EXPECTED_PHASE_CAN_CONNECT:
941 {
942 if (m_actual_state == m_sm.m_bound_datagram_socket
943 || m_actual_state == m_sm.m_bound_stream_socket
944 || m_actual_state == m_sm.m_bound_unknown_socket)
945 return ev.formatted_print
946 (fmt: "%qE expects a new socket file descriptor but %qE is bound",
947 m_callee_fndecl, m_arg);
948 else
949 return ev.formatted_print
950 (fmt: "%qE expects a new socket file descriptor", m_callee_fndecl);
951 }
952 break;
953 }
954 gcc_unreachable ();
955 }
956
957private:
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
964enum 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
972class fd_type_mismatch : public fd_param_diagnostic
973{
974public:
975 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 : fd_param_diagnostic (sm, arg, callee_fndecl),
980 m_actual_state (actual_state),
981 m_expected_type (expected_type)
982 {
983 }
984
985 const char *
986 get_kind () const final override
987 {
988 return "fd_type_mismatch";
989 }
990
991 bool
992 subclass_equal_p (const pending_diagnostic &base_other) const final override
993 {
994 const fd_type_mismatch &sub_other = (const fd_type_mismatch &)base_other;
995 if (!fd_param_diagnostic ::subclass_equal_p (base_other: sub_other))
996 return false;
997 return (m_actual_state == sub_other.m_actual_state
998 && m_expected_type == sub_other.m_expected_type);
999 }
1000
1001 int
1002 get_controlling_option () const final override
1003 {
1004 return OPT_Wanalyzer_fd_type_mismatch;
1005 }
1006
1007 bool
1008 emit (diagnostic_emission_context &ctxt) final override
1009 {
1010 switch (m_expected_type)
1011 {
1012 default:
1013 gcc_unreachable ();
1014 case EXPECTED_TYPE_SOCKET:
1015 return ctxt.warn ("%qE on non-socket file descriptor %qE",
1016 m_callee_fndecl, m_arg);
1017 case EXPECTED_TYPE_STREAM_SOCKET:
1018 if (m_sm.is_datagram_socket_fd_p (s: m_actual_state))
1019 return ctxt.warn ("%qE on datagram socket file descriptor %qE",
1020 m_callee_fndecl, m_arg);
1021 else
1022 return ctxt.warn ("%qE on non-stream-socket file descriptor %qE",
1023 m_callee_fndecl, m_arg);
1024 }
1025 }
1026
1027 label_text
1028 describe_final_event (const evdesc::final_event &ev) final override
1029 {
1030 switch (m_expected_type)
1031 {
1032 default:
1033 break;
1034 gcc_unreachable ();
1035 case EXPECTED_TYPE_SOCKET:
1036 case EXPECTED_TYPE_STREAM_SOCKET:
1037 if (!m_sm.is_socket_fd_p (s: m_actual_state))
1038 return ev.formatted_print (fmt: "%qE expects a socket file descriptor"
1039 " but %qE is not a socket",
1040 m_callee_fndecl, m_arg);
1041 }
1042 gcc_assert (m_expected_type == EXPECTED_TYPE_STREAM_SOCKET);
1043 gcc_assert (m_sm.is_datagram_socket_fd_p (m_actual_state));
1044 return ev.formatted_print
1045 (fmt: "%qE expects a stream socket file descriptor"
1046 " but %qE is a datagram socket",
1047 m_callee_fndecl, m_arg);
1048 }
1049
1050private:
1051 state_machine::state_t m_actual_state;
1052 enum expected_type m_expected_type;
1053};
1054
1055fd_state_machine::fd_state_machine (logger *logger)
1056 : state_machine ("file-descriptor", logger),
1057 m_constant_fd (add_state (name: "fd-constant")),
1058 m_unchecked_read_write (add_state (name: "fd-unchecked-read-write")),
1059 m_unchecked_read_only (add_state (name: "fd-unchecked-read-only")),
1060 m_unchecked_write_only (add_state (name: "fd-unchecked-write-only")),
1061 m_valid_read_write (add_state (name: "fd-valid-read-write")),
1062 m_valid_read_only (add_state (name: "fd-valid-read-only")),
1063 m_valid_write_only (add_state (name: "fd-valid-write-only")),
1064 m_invalid (add_state (name: "fd-invalid")),
1065 m_closed (add_state (name: "fd-closed")),
1066 m_new_datagram_socket (add_state (name: "fd-new-datagram-socket")),
1067 m_new_stream_socket (add_state (name: "fd-new-stream-socket")),
1068 m_new_unknown_socket (add_state (name: "fd-new-unknown-socket")),
1069 m_bound_datagram_socket (add_state (name: "fd-bound-datagram-socket")),
1070 m_bound_stream_socket (add_state (name: "fd-bound-stream-socket")),
1071 m_bound_unknown_socket (add_state (name: "fd-bound-unknown-socket")),
1072 m_listening_stream_socket (add_state (name: "fd-listening-stream-socket")),
1073 m_connected_stream_socket (add_state (name: "fd-connected-stream-socket")),
1074 m_stop (add_state (name: "fd-stop")),
1075 m_O_ACCMODE (get_stashed_constant_by_name (name: "O_ACCMODE")),
1076 m_O_RDONLY (get_stashed_constant_by_name (name: "O_RDONLY")),
1077 m_O_WRONLY (get_stashed_constant_by_name (name: "O_WRONLY")),
1078 m_SOCK_STREAM (get_stashed_constant_by_name (name: "SOCK_STREAM")),
1079 m_SOCK_DGRAM (get_stashed_constant_by_name (name: "SOCK_DGRAM"))
1080{
1081}
1082
1083bool
1084fd_state_machine::is_unchecked_fd_p (state_t s) const
1085{
1086 return (s == m_unchecked_read_write
1087 || s == m_unchecked_read_only
1088 || s == m_unchecked_write_only);
1089}
1090
1091bool
1092fd_state_machine::is_valid_fd_p (state_t s) const
1093{
1094 return (s == m_valid_read_write
1095 || s == m_valid_read_only
1096 || s == m_valid_write_only);
1097}
1098
1099bool
1100fd_state_machine::is_socket_fd_p (state_t s) const
1101{
1102 return (s == m_new_datagram_socket
1103 || s == m_new_stream_socket
1104 || s == m_new_unknown_socket
1105 || s == m_bound_datagram_socket
1106 || s == m_bound_stream_socket
1107 || s == m_bound_unknown_socket
1108 || s == m_listening_stream_socket
1109 || s == m_connected_stream_socket);
1110}
1111
1112bool
1113fd_state_machine::is_datagram_socket_fd_p (state_t s) const
1114{
1115 return (s == m_new_datagram_socket
1116 || s == m_new_unknown_socket
1117 || s == m_bound_datagram_socket
1118 || s == m_bound_unknown_socket);
1119}
1120
1121bool
1122fd_state_machine::is_stream_socket_fd_p (state_t s) const
1123{
1124 return (s == m_new_stream_socket
1125 || s == m_new_unknown_socket
1126 || s == m_bound_stream_socket
1127 || s == m_bound_unknown_socket
1128 || s == m_listening_stream_socket
1129 || s == m_connected_stream_socket);
1130}
1131
1132enum access_mode
1133fd_state_machine::get_access_mode_from_flag (int flag) const
1134{
1135 if (m_O_ACCMODE && TREE_CODE (m_O_ACCMODE) == INTEGER_CST)
1136 {
1137 const unsigned HOST_WIDE_INT mask_val = TREE_INT_CST_LOW (m_O_ACCMODE);
1138 const unsigned HOST_WIDE_INT masked_flag = flag & mask_val;
1139
1140 if (m_O_RDONLY && TREE_CODE (m_O_RDONLY) == INTEGER_CST)
1141 if (masked_flag == TREE_INT_CST_LOW (m_O_RDONLY))
1142 return READ_ONLY;
1143
1144 if (m_O_WRONLY && TREE_CODE (m_O_WRONLY) == INTEGER_CST)
1145 if (masked_flag == TREE_INT_CST_LOW (m_O_WRONLY))
1146 return WRITE_ONLY;
1147 }
1148 return READ_WRITE;
1149}
1150
1151bool
1152fd_state_machine::is_readonly_fd_p (state_t state) const
1153{
1154 return (state == m_unchecked_read_only || state == m_valid_read_only);
1155}
1156
1157bool
1158fd_state_machine::is_writeonly_fd_p (state_t state) const
1159{
1160 return (state == m_unchecked_write_only || state == m_valid_write_only);
1161}
1162
1163bool
1164fd_state_machine::is_closed_fd_p (state_t state) const
1165{
1166 return (state == m_closed);
1167}
1168
1169bool
1170fd_state_machine::is_constant_fd_p (state_t state) const
1171{
1172 return (state == m_constant_fd);
1173}
1174
1175fd_state_machine::state_t
1176fd_state_machine::valid_to_unchecked_state (state_t state) const
1177{
1178 if (state == m_valid_read_write)
1179 return m_unchecked_read_write;
1180 else if (state == m_valid_write_only)
1181 return m_unchecked_write_only;
1182 else if (state == m_valid_read_only)
1183 return m_unchecked_read_only;
1184 else
1185 gcc_unreachable ();
1186 return NULL;
1187}
1188
1189void
1190fd_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 smap->set_state (model, sval: fd_sval, state: m_valid_read_write, NULL, ext_state);
1196}
1197
1198bool
1199fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
1200 const gimple *stmt) const
1201{
1202 if (const gcall *call = dyn_cast<const gcall *> (p: stmt))
1203 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
1204 {
1205 if (is_named_call_p (fndecl: callee_fndecl, funcname: "open", call, num_args: 2))
1206 {
1207 on_open (sm_ctxt, node, stmt, call);
1208 return true;
1209 } // "open"
1210
1211 if (is_named_call_p (fndecl: callee_fndecl, funcname: "creat", call, num_args: 2))
1212 {
1213 on_creat (sm_ctxt, node, stmt, call);
1214 return true;
1215 } // "creat"
1216
1217 if (is_named_call_p (fndecl: callee_fndecl, funcname: "close", call, num_args: 1))
1218 {
1219 on_close (sm_ctxt, node, stmt, call);
1220 return true;
1221 } // "close"
1222
1223 if (is_named_call_p (fndecl: callee_fndecl, funcname: "write", call, num_args: 3))
1224 {
1225 on_write (sm_ctxt, node, stmt, call, callee_fndecl);
1226 return true;
1227 } // "write"
1228
1229 if (is_named_call_p (fndecl: callee_fndecl, funcname: "read", call, num_args: 3))
1230 {
1231 on_read (sm_ctxt, node, stmt, call, callee_fndecl);
1232 return true;
1233 } // "read"
1234
1235 if (is_named_call_p (fndecl: callee_fndecl, funcname: "dup", call, num_args: 1))
1236 {
1237 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, kind: DUP_1);
1238 return true;
1239 }
1240
1241 if (is_named_call_p (fndecl: callee_fndecl, funcname: "dup2", call, num_args: 2))
1242 {
1243 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, kind: DUP_2);
1244 return true;
1245 }
1246
1247 if (is_named_call_p (fndecl: callee_fndecl, funcname: "dup3", call, num_args: 3))
1248 {
1249 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, kind: DUP_3);
1250 return true;
1251 }
1252
1253 {
1254 // Handle __attribute__((fd_arg))
1255
1256 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1257 attr_name: "fd_arg", fd_attr_access_dir: DIRS_READ_WRITE);
1258
1259 // Handle __attribute__((fd_arg_read))
1260
1261 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1262 attr_name: "fd_arg_read", fd_attr_access_dir: DIRS_READ);
1263
1264 // Handle __attribute__((fd_arg_write))
1265
1266 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
1267 attr_name: "fd_arg_write", fd_attr_access_dir: DIRS_WRITE);
1268 }
1269 }
1270
1271 return false;
1272}
1273
1274void
1275fd_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 tree fndecl = callee_fndecl;
1285 /* If call is recognized as a builtin known_function,
1286 use that builtin's function_decl. */
1287 if (const region_model *old_model = sm_ctxt->get_old_region_model ())
1288 if (const builtin_known_function *builtin_kf
1289 = old_model->get_builtin_kf (call))
1290 fndecl = builtin_kf->builtin_decl ();
1291
1292 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
1293 attrs = lookup_attribute (attr_name, list: attrs);
1294 if (!attrs)
1295 return;
1296
1297 if (!TREE_VALUE (attrs))
1298 return;
1299
1300 auto_bitmap argmap;
1301
1302 for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
1303 {
1304 unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
1305 bitmap_set_bit (argmap, val);
1306 }
1307 if (bitmap_empty_p (map: argmap))
1308 return;
1309
1310 for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (gs: call); arg_idx++)
1311 {
1312 tree arg = gimple_call_arg (gs: call, index: arg_idx);
1313 tree diag_arg = sm_ctxt->get_diagnostic_tree (expr: arg);
1314 state_t state = sm_ctxt->get_state (stmt, var: arg);
1315 bool bit_set = bitmap_bit_p (argmap, arg_idx);
1316 if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
1317 continue;
1318 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 if (is_closed_fd_p (state))
1325 {
1326
1327 sm_ctxt->warn (node, stmt, var: arg,
1328 d: make_unique<fd_use_after_close>
1329 (args: *this, args&: diag_arg,
1330 args&: fndecl, args&: attr_name,
1331 args&: arg_idx));
1332 continue;
1333 }
1334
1335 if (!(is_valid_fd_p (s: state) || (state == m_stop)))
1336 {
1337 if (!is_constant_fd_p (state))
1338 {
1339 sm_ctxt->warn (node, stmt, var: arg,
1340 d: make_unique<fd_use_without_check>
1341 (args: *this, args&: diag_arg,
1342 args&: fndecl, args&: attr_name,
1343 args&: arg_idx));
1344 continue;
1345 }
1346 }
1347
1348 switch (fd_attr_access_dir)
1349 {
1350 case DIRS_READ_WRITE:
1351 break;
1352 case DIRS_READ:
1353
1354 if (is_writeonly_fd_p (state))
1355 {
1356 sm_ctxt->warn (
1357 node, stmt, var: arg,
1358 d: make_unique<fd_access_mode_mismatch> (args: *this, args&: diag_arg,
1359 args: DIRS_WRITE,
1360 args&: fndecl,
1361 args&: attr_name,
1362 args&: arg_idx));
1363 }
1364
1365 break;
1366 case DIRS_WRITE:
1367
1368 if (is_readonly_fd_p (state))
1369 {
1370 sm_ctxt->warn (
1371 node, stmt, var: arg,
1372 d: make_unique<fd_access_mode_mismatch> (args: *this, args&: diag_arg,
1373 args: DIRS_READ,
1374 args&: fndecl,
1375 args&: attr_name,
1376 args&: arg_idx));
1377 }
1378
1379 break;
1380 }
1381 }
1382 }
1383}
1384
1385
1386void
1387fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
1388 const gimple *stmt, const gcall *call) const
1389{
1390 tree lhs = gimple_call_lhs (gs: call);
1391 if (lhs)
1392 {
1393 tree arg = gimple_call_arg (gs: call, index: 1);
1394 enum access_mode mode = READ_WRITE;
1395 if (TREE_CODE (arg) == INTEGER_CST)
1396 {
1397 int flag = TREE_INT_CST_LOW (arg);
1398 mode = get_access_mode_from_flag (flag);
1399 }
1400 switch (mode)
1401 {
1402 case READ_ONLY:
1403 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_start,
1404 to: m_unchecked_read_only);
1405 break;
1406 case WRITE_ONLY:
1407 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_start,
1408 to: m_unchecked_write_only);
1409 break;
1410 default:
1411 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_start,
1412 to: m_unchecked_read_write);
1413 }
1414 }
1415 else
1416 {
1417 sm_ctxt->warn (node, stmt, NULL_TREE,
1418 d: make_unique<fd_leak> (args: *this, NULL_TREE));
1419 }
1420}
1421
1422void
1423fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node,
1424 const gimple *stmt, const gcall *call) const
1425{
1426 tree lhs = gimple_call_lhs (gs: call);
1427 if (lhs)
1428 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_start, to: m_unchecked_write_only);
1429 else
1430 sm_ctxt->warn (node, stmt, NULL_TREE,
1431 d: make_unique<fd_leak> (args: *this, NULL_TREE));
1432}
1433
1434void
1435fd_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 tree lhs = gimple_call_lhs (gs: call);
1440 tree arg_1 = gimple_call_arg (gs: call, index: 0);
1441 state_t state_arg_1 = sm_ctxt->get_state (stmt, var: arg_1);
1442 if (state_arg_1 == m_stop)
1443 return;
1444 if (!(is_constant_fd_p (state: state_arg_1) || is_valid_fd_p (s: state_arg_1)
1445 || state_arg_1 == m_start))
1446 {
1447 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
1448 access_fn: DIRS_READ_WRITE);
1449 return;
1450 }
1451 switch (kind)
1452 {
1453 case DUP_1:
1454 if (lhs)
1455 {
1456 if (is_constant_fd_p (state: state_arg_1) || state_arg_1 == m_start)
1457 sm_ctxt->set_next_state (stmt, var: lhs, to: m_unchecked_read_write);
1458 else
1459 sm_ctxt->set_next_state (stmt, var: lhs,
1460 to: valid_to_unchecked_state (state: state_arg_1));
1461 }
1462 break;
1463
1464 case DUP_2:
1465 case DUP_3:
1466 tree arg_2 = gimple_call_arg (gs: call, index: 1);
1467 state_t state_arg_2 = sm_ctxt->get_state (stmt, var: arg_2);
1468 tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (expr: arg_2);
1469 if (state_arg_2 == m_stop)
1470 return;
1471 /* Check if -1 was passed as second argument to dup2. */
1472 if (!(is_constant_fd_p (state: state_arg_2) || is_valid_fd_p (s: state_arg_2)
1473 || state_arg_2 == m_start))
1474 {
1475 sm_ctxt->warn (
1476 node, stmt, var: arg_2,
1477 d: make_unique<fd_use_without_check> (args: *this, args&: diag_arg_2,
1478 args: callee_fndecl));
1479 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 if (lhs)
1485 {
1486 if (is_constant_fd_p (state: state_arg_1) || state_arg_1 == m_start)
1487 sm_ctxt->set_next_state (stmt, var: lhs, to: m_unchecked_read_write);
1488 else
1489 sm_ctxt->set_next_state (stmt, var: lhs,
1490 to: valid_to_unchecked_state (state: state_arg_1));
1491 }
1492
1493 break;
1494 }
1495}
1496
1497void
1498fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
1499 const gimple *stmt, const gcall *call) const
1500{
1501 tree arg = gimple_call_arg (gs: call, index: 0);
1502 state_t state = sm_ctxt->get_state (stmt, var: arg);
1503 tree diag_arg = sm_ctxt->get_diagnostic_tree (expr: arg);
1504
1505 sm_ctxt->on_transition (node, stmt, var: arg, from: m_start, to: m_closed);
1506 sm_ctxt->on_transition (node, stmt, var: arg, from: m_unchecked_read_write, to: m_closed);
1507 sm_ctxt->on_transition (node, stmt, var: arg, from: m_unchecked_read_only, to: m_closed);
1508 sm_ctxt->on_transition (node, stmt, var: arg, from: m_unchecked_write_only, to: m_closed);
1509 sm_ctxt->on_transition (node, stmt, var: arg, from: m_valid_read_write, to: m_closed);
1510 sm_ctxt->on_transition (node, stmt, var: arg, from: m_valid_read_only, to: m_closed);
1511 sm_ctxt->on_transition (node, stmt, var: arg, from: m_valid_write_only, to: m_closed);
1512 sm_ctxt->on_transition (node, stmt, var: arg, from: m_constant_fd, to: m_closed);
1513 sm_ctxt->on_transition (node, stmt, var: arg, from: m_new_datagram_socket, to: m_closed);
1514 sm_ctxt->on_transition (node, stmt, var: arg, from: m_new_stream_socket, to: m_closed);
1515 sm_ctxt->on_transition (node, stmt, var: arg, from: m_new_unknown_socket, to: m_closed);
1516 sm_ctxt->on_transition (node, stmt, var: arg, from: m_bound_datagram_socket, to: m_closed);
1517 sm_ctxt->on_transition (node, stmt, var: arg, from: m_bound_stream_socket, to: m_closed);
1518 sm_ctxt->on_transition (node, stmt, var: arg, from: m_bound_unknown_socket, to: m_closed);
1519 sm_ctxt->on_transition (node, stmt, var: arg, from: m_listening_stream_socket, to: m_closed);
1520 sm_ctxt->on_transition (node, stmt, var: arg, from: m_connected_stream_socket, to: m_closed);
1521
1522 if (is_closed_fd_p (state))
1523 {
1524 sm_ctxt->warn (node, stmt, var: arg,
1525 d: make_unique<fd_double_close> (args: *this, args&: diag_arg));
1526 sm_ctxt->set_next_state (stmt, var: arg, to: m_stop);
1527 }
1528}
1529void
1530fd_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 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, access_fn: DIRS_READ);
1535}
1536void
1537fd_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 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, access_fn: DIRS_WRITE);
1542}
1543
1544void
1545fd_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 tree arg = gimple_call_arg (gs: call, index: 0);
1551 tree diag_arg = sm_ctxt->get_diagnostic_tree (expr: arg);
1552 state_t state = sm_ctxt->get_state (stmt, var: arg);
1553
1554 if (is_closed_fd_p (state))
1555 {
1556 sm_ctxt->warn (node, stmt, var: arg,
1557 d: make_unique<fd_use_after_close> (args: *this, args&: diag_arg,
1558 args: callee_fndecl));
1559 }
1560
1561 else
1562 {
1563 if (state == m_new_stream_socket
1564 || state == m_bound_stream_socket
1565 || state == m_listening_stream_socket)
1566 /* Complain about fncall on socket in wrong phase. */
1567 sm_ctxt->warn
1568 (node, stmt, var: arg,
1569 d: make_unique<fd_phase_mismatch> (args: *this, args&: diag_arg,
1570 args: callee_fndecl,
1571 args&: state,
1572 args: EXPECTED_PHASE_CAN_TRANSFER));
1573 else if (!(is_valid_fd_p (s: state)
1574 || state == m_new_datagram_socket
1575 || state == m_bound_unknown_socket
1576 || state == m_connected_stream_socket
1577 || state == m_start
1578 || state == m_stop))
1579 {
1580 if (!is_constant_fd_p (state))
1581 sm_ctxt->warn (
1582 node, stmt, var: arg,
1583 d: make_unique<fd_use_without_check> (args: *this, args&: diag_arg,
1584 args: callee_fndecl));
1585 }
1586 switch (callee_fndecl_dir)
1587 {
1588 case DIRS_READ_WRITE:
1589 break;
1590 case DIRS_READ:
1591 if (is_writeonly_fd_p (state))
1592 {
1593 tree diag_arg = sm_ctxt->get_diagnostic_tree (expr: arg);
1594 sm_ctxt->warn (node, stmt, var: arg,
1595 d: make_unique<fd_access_mode_mismatch> (
1596 args: *this, args&: diag_arg, args: DIRS_WRITE, args: callee_fndecl));
1597 }
1598
1599 break;
1600 case DIRS_WRITE:
1601
1602 if (is_readonly_fd_p (state))
1603 {
1604 tree diag_arg = sm_ctxt->get_diagnostic_tree (expr: arg);
1605 sm_ctxt->warn (node, stmt, var: arg,
1606 d: make_unique<fd_access_mode_mismatch> (
1607 args: *this, args&: diag_arg, args: DIRS_READ, args: callee_fndecl));
1608 }
1609 break;
1610 }
1611 }
1612}
1613
1614static bool
1615add_constraint_ge_zero (region_model *model,
1616 const svalue *fd_sval,
1617 region_model_context *ctxt)
1618{
1619 const svalue *zero
1620 = model->get_manager ()->get_or_create_int_cst (integer_type_node, cst: 0);
1621 return model->add_constraint (lhs: fd_sval, op: GE_EXPR, rhs: zero, ctxt);
1622}
1623
1624/* Get the state for a new socket type based on SOCKET_TYPE_SVAL,
1625 a SOCK_* value. */
1626
1627state_machine::state_t
1628fd_state_machine::
1629get_state_for_socket_type (const svalue *socket_type_sval) const
1630{
1631 if (tree socket_type_cst = socket_type_sval->maybe_get_constant ())
1632 {
1633 /* Attempt to use SOCK_* constants stashed from the frontend. */
1634 if (tree_int_cst_equal (socket_type_cst, m_SOCK_STREAM))
1635 return m_new_stream_socket;
1636 if (tree_int_cst_equal (socket_type_cst, m_SOCK_DGRAM))
1637 return m_new_datagram_socket;
1638 }
1639
1640 /* Unrecognized constant, or a symbolic "type" value. */
1641 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
1648bool
1649fd_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 const gcall *stmt = cd.get_call_stmt ();
1655 engine *eng = ext_state.get_engine ();
1656 const supergraph *sg = eng->get_supergraph ();
1657 const supernode *node = sg->get_supernode_for_stmt (stmt);
1658 region_model *model = cd.get_model ();
1659
1660 if (successful)
1661 {
1662 if (gimple_call_lhs (gs: stmt))
1663 {
1664 conjured_purge p (model, cd.get_ctxt ());
1665 region_model_manager *mgr = model->get_manager ();
1666 const svalue *new_fd
1667 = mgr->get_or_create_conjured_svalue (integer_type_node,
1668 stmt,
1669 id_reg: cd.get_lhs_region (),
1670 p);
1671 if (!add_constraint_ge_zero (model, fd_sval: new_fd, ctxt: cd.get_ctxt ()))
1672 return false;
1673
1674 const svalue *socket_type_sval = cd.get_arg_svalue (idx: 1);
1675 state_machine::state_t new_state
1676 = get_state_for_socket_type (socket_type_sval);
1677 sm_ctxt->on_transition (node, stmt, var: new_fd, from: m_start, to: new_state);
1678 model->set_value (lhs_reg: cd.get_lhs_region (), rhs_sval: new_fd, ctxt: cd.get_ctxt ());
1679 }
1680 else
1681 sm_ctxt->warn (node, stmt, NULL_TREE,
1682 d: make_unique<fd_leak> (args: *this, NULL_TREE));
1683 }
1684 else
1685 {
1686 /* Return -1; set errno. */
1687 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
1688 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
1703bool
1704fd_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 const gcall *stmt = cd.get_call_stmt ();
1713
1714 if (is_closed_fd_p (state: old_state))
1715 {
1716 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1717 sm_ctxt->warn
1718 (node, stmt, var: fd_sval,
1719 d: make_unique<fd_use_after_close> (args: *this, args&: diag_arg,
1720 args: cd.get_fndecl_for_call ()));
1721 if (complained)
1722 *complained = true;
1723 if (successful)
1724 return false;
1725 }
1726 else if (is_unchecked_fd_p (s: old_state) || is_valid_fd_p (s: old_state))
1727 {
1728 /* Complain about non-socket. */
1729 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1730 sm_ctxt->warn
1731 (node, stmt, var: fd_sval,
1732 d: make_unique<fd_type_mismatch> (args: *this, args&: diag_arg,
1733 args: cd.get_fndecl_for_call (),
1734 args&: old_state,
1735 args: EXPECTED_TYPE_SOCKET));
1736 if (complained)
1737 *complained = true;
1738 if (successful)
1739 return false;
1740 }
1741 else if (old_state == m_invalid)
1742 {
1743 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1744 sm_ctxt->warn
1745 (node, stmt, var: fd_sval,
1746 d: make_unique<fd_use_without_check> (args: *this, args&: diag_arg,
1747 args: cd.get_fndecl_for_call ()));
1748 if (complained)
1749 *complained = true;
1750 if (successful)
1751 return false;
1752 }
1753
1754 if (successful)
1755 if (!add_constraint_ge_zero (model: cd.get_model (), fd_sval, ctxt: 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
1766bool
1767fd_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 bool complained = false;
1777
1778 /* Check address and len. */
1779 const svalue *address_sval = cd.get_arg_svalue (idx: 1);
1780 const svalue *len_sval = cd.get_arg_svalue (idx: 2);
1781
1782 /* Check that we can read the given number of bytes from the
1783 address. */
1784 region_model *model = cd.get_model ();
1785 const region *address_reg
1786 = model->deref_rvalue (ptr_sval: address_sval, ptr_tree: cd.get_arg_tree (idx: 1),
1787 ctxt: cd.get_ctxt ());
1788 const region *sized_address_reg
1789 = model->get_manager ()->get_sized_region (parent: address_reg,
1790 NULL_TREE,
1791 byte_size_sval: len_sval);
1792 model->get_store_value (reg: sized_address_reg, ctxt: cd.get_ctxt ());
1793
1794 if (!check_for_socket_fd (cd, successful, sm_ctxt,
1795 fd_sval, node, old_state, complained: &complained))
1796 return false;
1797 else if (!complained
1798 && !(old_state == m_new_stream_socket
1799 || old_state == m_new_datagram_socket
1800 || old_state == m_new_unknown_socket
1801 || old_state == m_start
1802 || old_state == m_stop
1803 || old_state == m_constant_fd))
1804 {
1805 /* Complain about "bind" or "connect" in wrong phase. */
1806 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1807 sm_ctxt->warn
1808 (node, stmt: cd.get_call_stmt (), var: fd_sval,
1809 d: make_unique<fd_phase_mismatch> (args: *this, args&: diag_arg,
1810 args: cd.get_fndecl_for_call (),
1811 args&: old_state,
1812 args&: expected_phase));
1813 if (successful)
1814 return false;
1815 }
1816 else if (!successful)
1817 {
1818 /* If we were in the start state, assume we had a new socket. */
1819 if (old_state == m_start)
1820 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval,
1821 to: m_new_unknown_socket);
1822 }
1823
1824 /* Passing NULL as the address will lead to failure. */
1825 if (successful)
1826 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
1836bool
1837fd_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 const gcall *stmt = cd.get_call_stmt ();
1843 engine *eng = ext_state.get_engine ();
1844 const supergraph *sg = eng->get_supergraph ();
1845 const supernode *node = sg->get_supernode_for_stmt (stmt);
1846 const svalue *fd_sval = cd.get_arg_svalue (idx: 0);
1847 region_model *model = cd.get_model ();
1848 state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
1849
1850 if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
1851 fd_sval, node, old_state,
1852 expected_phase: EXPECTED_PHASE_CAN_BIND))
1853 return false;
1854
1855 if (successful)
1856 {
1857 state_t next_state = NULL;
1858 if (old_state == m_new_stream_socket)
1859 next_state = m_bound_stream_socket;
1860 else if (old_state == m_new_datagram_socket)
1861 next_state = m_bound_datagram_socket;
1862 else if (old_state == m_new_unknown_socket)
1863 next_state = m_bound_unknown_socket;
1864 else if (old_state == m_start
1865 || old_state == m_constant_fd)
1866 next_state = m_bound_unknown_socket;
1867 else if (old_state == m_stop)
1868 next_state = m_stop;
1869 else
1870 gcc_unreachable ();
1871 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval, to: next_state);
1872 model->update_for_zero_return (cd, unmergeable: true);
1873 }
1874 else
1875 {
1876 /* Return -1; set errno. */
1877 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
1878 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
1888bool
1889fd_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 const gcall *stmt = cd.get_call_stmt ();
1895 engine *eng = ext_state.get_engine ();
1896 const supergraph *sg = eng->get_supergraph ();
1897 const supernode *node = sg->get_supernode_for_stmt (stmt: cd.get_call_stmt ());
1898 const svalue *fd_sval = cd.get_arg_svalue (idx: 0);
1899 region_model *model = cd.get_model ();
1900 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 if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
1904 return false;
1905 if (!(old_state == m_start
1906 || old_state == m_constant_fd
1907 || old_state == m_stop
1908 || old_state == m_invalid
1909 || old_state == m_bound_stream_socket
1910 || old_state == m_bound_unknown_socket
1911 /* Assume it's OK to call "listen" more than once. */
1912 || old_state == m_listening_stream_socket))
1913 {
1914 /* Complain about fncall on wrong type or in wrong phase. */
1915 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
1916 if (is_stream_socket_fd_p (s: old_state))
1917 sm_ctxt->warn
1918 (node, stmt, var: fd_sval,
1919 d: make_unique<fd_phase_mismatch> (args: *this, args&: diag_arg,
1920 args: cd.get_fndecl_for_call (),
1921 args&: old_state,
1922 args: EXPECTED_PHASE_CAN_LISTEN));
1923 else
1924 sm_ctxt->warn
1925 (node, stmt, var: fd_sval,
1926 d: make_unique<fd_type_mismatch> (args: *this, args&: diag_arg,
1927 args: cd.get_fndecl_for_call (),
1928 args&: old_state,
1929 args: EXPECTED_TYPE_STREAM_SOCKET));
1930 if (successful)
1931 return false;
1932 }
1933
1934 if (successful)
1935 {
1936 model->update_for_zero_return (cd, unmergeable: true);
1937 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval,
1938 to: m_listening_stream_socket);
1939 }
1940 else
1941 {
1942 /* Return -1; set errno. */
1943 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
1944 model->set_errno (cd);
1945 if (old_state == m_start)
1946 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval,
1947 to: 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
1957bool
1958fd_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 const gcall *stmt = cd.get_call_stmt ();
1964 engine *eng = ext_state.get_engine ();
1965 const supergraph *sg = eng->get_supergraph ();
1966 const supernode *node = sg->get_supernode_for_stmt (stmt);
1967 const svalue *fd_sval = cd.get_arg_svalue (idx: 0);
1968 const svalue *address_sval = cd.get_arg_svalue (idx: 1);
1969 const svalue *len_ptr_sval = cd.get_arg_svalue (idx: 2);
1970 region_model *model = cd.get_model ();
1971 state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
1972
1973 if (!address_sval->all_zeroes_p ())
1974 {
1975 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 address_sval = mgr->get_or_create_cast (ptr_type_node, arg: address_sval);
1980
1981 const region *address_reg
1982 = model->deref_rvalue (ptr_sval: address_sval, ptr_tree: cd.get_arg_tree (idx: 1),
1983 ctxt: cd.get_ctxt ());
1984 const region *len_reg
1985 = model->deref_rvalue (ptr_sval: len_ptr_sval, ptr_tree: cd.get_arg_tree (idx: 2),
1986 ctxt: cd.get_ctxt ());
1987 const svalue *old_len_sval
1988 = model->get_store_value (reg: len_reg, ctxt: cd.get_ctxt ());
1989 tree len_ptr = cd.get_arg_tree (idx: 2);
1990 tree star_len_ptr = build2 (MEM_REF, TREE_TYPE (TREE_TYPE (len_ptr)),
1991 len_ptr,
1992 build_int_cst (TREE_TYPE (len_ptr), 0));
1993 old_len_sval = model->check_for_poison (sval: old_len_sval,
1994 expr: star_len_ptr,
1995 src_region: len_reg,
1996 ctxt: cd.get_ctxt ());
1997 if (successful)
1998 {
1999 conjured_purge p (model, cd.get_ctxt ());
2000 const region *old_sized_address_reg
2001 = mgr->get_sized_region (parent: address_reg,
2002 NULL_TREE,
2003 byte_size_sval: old_len_sval);
2004 const svalue *new_addr_sval
2005 = mgr->get_or_create_conjured_svalue (NULL_TREE,
2006 stmt,
2007 id_reg: old_sized_address_reg,
2008 p);
2009 model->set_value (lhs_reg: old_sized_address_reg, rhs_sval: new_addr_sval,
2010 ctxt: cd.get_ctxt ());
2011 const svalue *new_addr_len
2012 = mgr->get_or_create_conjured_svalue (NULL_TREE,
2013 stmt,
2014 id_reg: len_reg,
2015 p);
2016 model->set_value (lhs_reg: len_reg, rhs_sval: new_addr_len, ctxt: cd.get_ctxt ());
2017 }
2018 }
2019
2020 /* We expect a stream socket in the "listening" state. */
2021 if (!check_for_socket_fd (cd, successful, sm_ctxt, fd_sval, node, old_state))
2022 return false;
2023
2024 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 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval,
2028 to: m_listening_stream_socket);
2029 else if (old_state == m_stop)
2030 {
2031 /* No further complaints. */
2032 }
2033 else if (old_state != m_listening_stream_socket)
2034 {
2035 /* Complain about fncall on wrong type or in wrong phase. */
2036 tree diag_arg = sm_ctxt->get_diagnostic_tree (fd_sval);
2037 if (is_stream_socket_fd_p (s: old_state))
2038 sm_ctxt->warn
2039 (node, stmt, var: fd_sval,
2040 d: make_unique<fd_phase_mismatch> (args: *this, args&: diag_arg,
2041 args: cd.get_fndecl_for_call (),
2042 args&: old_state,
2043 args: EXPECTED_PHASE_CAN_ACCEPT));
2044 else
2045 sm_ctxt->warn
2046 (node, stmt, var: fd_sval,
2047 d: make_unique<fd_type_mismatch> (args: *this, args&: diag_arg,
2048 args: cd.get_fndecl_for_call (),
2049 args&: old_state,
2050 args: EXPECTED_TYPE_STREAM_SOCKET));
2051 if (successful)
2052 return false;
2053 }
2054
2055 if (successful)
2056 {
2057 /* Return new conjured FD in "connected" state. */
2058 if (gimple_call_lhs (gs: stmt))
2059 {
2060 conjured_purge p (model, cd.get_ctxt ());
2061 region_model_manager *mgr = model->get_manager ();
2062 const svalue *new_fd
2063 = mgr->get_or_create_conjured_svalue (integer_type_node,
2064 stmt,
2065 id_reg: cd.get_lhs_region (),
2066 p);
2067 if (!add_constraint_ge_zero (model, fd_sval: new_fd, ctxt: cd.get_ctxt ()))
2068 return false;
2069 sm_ctxt->on_transition (node, stmt, var: new_fd,
2070 from: m_start, to: m_connected_stream_socket);
2071 model->set_value (lhs_reg: cd.get_lhs_region (), rhs_sval: new_fd, ctxt: cd.get_ctxt ());
2072 }
2073 else
2074 sm_ctxt->warn (node, stmt, NULL_TREE,
2075 d: make_unique<fd_leak> (args: *this, NULL_TREE));
2076 }
2077 else
2078 {
2079 /* Return -1; set errno. */
2080 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
2081 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
2091bool
2092fd_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 const gcall *stmt = cd.get_call_stmt ();
2098 engine *eng = ext_state.get_engine ();
2099 const supergraph *sg = eng->get_supergraph ();
2100 const supernode *node = sg->get_supernode_for_stmt (stmt);
2101 const svalue *fd_sval = cd.get_arg_svalue (idx: 0);
2102 region_model *model = cd.get_model ();
2103 state_t old_state = sm_ctxt->get_state (stmt, fd_sval);
2104
2105 if (!check_for_new_socket_fd (cd, successful, sm_ctxt,
2106 fd_sval, node, old_state,
2107 expected_phase: EXPECTED_PHASE_CAN_CONNECT))
2108 return false;
2109
2110 if (successful)
2111 {
2112 model->update_for_zero_return (cd, unmergeable: true);
2113 state_t next_state = NULL;
2114 if (old_state == m_new_stream_socket)
2115 next_state = m_connected_stream_socket;
2116 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 else if (old_state == m_new_unknown_socket)
2121 next_state = m_stop;
2122 else if (old_state == m_start
2123 || old_state == m_constant_fd)
2124 next_state = m_stop;
2125 else if (old_state == m_stop)
2126 next_state = m_stop;
2127 else
2128 gcc_unreachable ();
2129 sm_ctxt->set_next_state (stmt: cd.get_call_stmt (), var: fd_sval, to: next_state);
2130 }
2131 else
2132 {
2133 /* Return -1; set errno. */
2134 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
2135 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
2144void
2145fd_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 if (tree cst = rhs->maybe_get_constant ())
2150 {
2151 if (TREE_CODE (cst) == INTEGER_CST)
2152 {
2153 int val = TREE_INT_CST_LOW (cst);
2154 if (val == -1)
2155 {
2156 if (op == NE_EXPR)
2157 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2158
2159 else if (op == EQ_EXPR)
2160 make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
2161 lhs);
2162 }
2163 }
2164 }
2165
2166 if (rhs->all_zeroes_p ())
2167 {
2168 if (op == GE_EXPR)
2169 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2170 else if (op == LT_EXPR)
2171 make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
2172 }
2173}
2174
2175void
2176fd_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 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_read_write,
2182 to: m_valid_read_write);
2183 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_read_only,
2184 to: m_valid_read_only);
2185 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_write_only,
2186 to: m_valid_write_only);
2187}
2188
2189void
2190fd_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 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_read_write, to: m_invalid);
2195 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_read_only, to: m_invalid);
2196 sm_ctxt->on_transition (node, stmt, var: lhs, from: m_unchecked_write_only, to: m_invalid);
2197}
2198
2199bool
2200fd_state_machine::can_purge_p (state_t s) const
2201{
2202 if (is_unchecked_fd_p (s)
2203 || is_valid_fd_p (s)
2204 || is_socket_fd_p (s))
2205 return false;
2206 else
2207 return true;
2208}
2209
2210std::unique_ptr<pending_diagnostic>
2211fd_state_machine::on_leak (tree var) const
2212{
2213 return make_unique<fd_leak> (args: *this, args&: var);
2214}
2215} // namespace
2216
2217state_machine *
2218make_fd_state_machine (logger *logger)
2219{
2220 return new fd_state_machine (logger);
2221}
2222
2223static bool
2224get_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 if (!ctxt)
2231 return false;
2232
2233 const state_machine *sm;
2234 if (!ctxt->get_fd_map (out_smap, out_sm: &sm, out_sm_idx, out_sm_context))
2235 return false;
2236
2237 gcc_assert (sm);
2238
2239 *out_sm = (const fd_state_machine *)sm;
2240 return true;
2241}
2242
2243/* Specialcase hook for handling pipe, for use by
2244 kf_pipe::success::update_model. */
2245
2246void
2247region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
2248{
2249 sm_state_map *smap;
2250 const fd_state_machine *fd_sm;
2251 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, NULL))
2252 return;
2253 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2254 if (!ext_state)
2255 return;
2256 fd_sm->mark_as_valid_fd (model: this, smap, fd_sval: sval, ext_state: *ext_state);
2257}
2258
2259/* Handle calls to "socket".
2260 See e.g. https://man7.org/linux/man-pages/man3/socket.3p.html */
2261
2262class kf_socket : public known_function
2263{
2264public:
2265 class outcome_of_socket : public succeed_or_fail_call_info
2266 {
2267 public:
2268 outcome_of_socket (const call_details &cd, bool success)
2269 : succeed_or_fail_call_info (cd, success)
2270 {}
2271
2272 bool update_model (region_model *model,
2273 const exploded_edge *,
2274 region_model_context *ctxt) const final override
2275 {
2276 const call_details cd (get_call_details (model, ctxt));
2277 sm_state_map *smap;
2278 const fd_state_machine *fd_sm;
2279 std::unique_ptr<sm_context> sm_ctxt;
2280 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2281 {
2282 cd.set_any_lhs_with_defaults ();
2283 return true;
2284 }
2285 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2286 if (!ext_state)
2287 {
2288 cd.set_any_lhs_with_defaults ();
2289 return true;
2290 }
2291
2292 return fd_sm->on_socket (cd, successful: m_success, sm_ctxt: sm_ctxt.get (), ext_state: *ext_state);
2293 }
2294 };
2295
2296 bool matches_call_types_p (const call_details &cd) const final override
2297 {
2298 return cd.num_args () == 3;
2299 }
2300
2301 void impl_call_post (const call_details &cd) const final override
2302 {
2303 if (cd.get_ctxt ())
2304 {
2305 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_socket> (args: cd, args: false));
2306 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_socket> (args: cd, args: true));
2307 cd.get_ctxt ()->terminate_path ();
2308 }
2309 }
2310};
2311
2312/* Handle calls to "bind".
2313 See e.g. https://man7.org/linux/man-pages/man3/bind.3p.html */
2314
2315class kf_bind : public known_function
2316{
2317public:
2318 class outcome_of_bind : public succeed_or_fail_call_info
2319 {
2320 public:
2321 outcome_of_bind (const call_details &cd, bool success)
2322 : succeed_or_fail_call_info (cd, success)
2323 {}
2324
2325 bool update_model (region_model *model,
2326 const exploded_edge *,
2327 region_model_context *ctxt) const final override
2328 {
2329 const call_details cd (get_call_details (model, ctxt));
2330 sm_state_map *smap;
2331 const fd_state_machine *fd_sm;
2332 std::unique_ptr<sm_context> sm_ctxt;
2333 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2334 {
2335 cd.set_any_lhs_with_defaults ();
2336 return true;
2337 }
2338 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2339 if (!ext_state)
2340 {
2341 cd.set_any_lhs_with_defaults ();
2342 return true;
2343 }
2344 return fd_sm->on_bind (cd, successful: m_success, sm_ctxt: sm_ctxt.get (), ext_state: *ext_state);
2345 }
2346 };
2347
2348 bool matches_call_types_p (const call_details &cd) const final override
2349 {
2350 return (cd.num_args () == 3 && cd.arg_is_pointer_p (idx: 1));
2351 }
2352
2353 void impl_call_post (const call_details &cd) const final override
2354 {
2355 if (cd.get_ctxt ())
2356 {
2357 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_bind> (args: cd, args: false));
2358 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_bind> (args: cd, args: true));
2359 cd.get_ctxt ()->terminate_path ();
2360 }
2361 }
2362};
2363
2364/* Handle calls to "listen".
2365 See e.g. https://man7.org/linux/man-pages/man3/listen.3p.html */
2366
2367class kf_listen : public known_function
2368{
2369 class outcome_of_listen : public succeed_or_fail_call_info
2370 {
2371 public:
2372 outcome_of_listen (const call_details &cd, bool success)
2373 : succeed_or_fail_call_info (cd, success)
2374 {}
2375
2376 bool update_model (region_model *model,
2377 const exploded_edge *,
2378 region_model_context *ctxt) const final override
2379 {
2380 const call_details cd (get_call_details (model, ctxt));
2381 sm_state_map *smap;
2382 const fd_state_machine *fd_sm;
2383 std::unique_ptr<sm_context> sm_ctxt;
2384 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2385 {
2386 cd.set_any_lhs_with_defaults ();
2387 return true;
2388 }
2389 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2390 if (!ext_state)
2391 {
2392 cd.set_any_lhs_with_defaults ();
2393 return true;
2394 }
2395
2396 return fd_sm->on_listen (cd, successful: m_success, sm_ctxt: sm_ctxt.get (), ext_state: *ext_state);
2397 }
2398 };
2399
2400 bool matches_call_types_p (const call_details &cd) const final override
2401 {
2402 return cd.num_args () == 2;
2403 }
2404
2405 void impl_call_post (const call_details &cd) const final override
2406 {
2407 if (cd.get_ctxt ())
2408 {
2409 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_listen> (args: cd, args: false));
2410 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_listen> (args: cd, args: true));
2411 cd.get_ctxt ()->terminate_path ();
2412 }
2413 }
2414};
2415
2416/* Handle calls to "accept".
2417 See e.g. https://man7.org/linux/man-pages/man3/accept.3p.html */
2418
2419class kf_accept : public known_function
2420{
2421 class outcome_of_accept : public succeed_or_fail_call_info
2422 {
2423 public:
2424 outcome_of_accept (const call_details &cd, bool success)
2425 : succeed_or_fail_call_info (cd, success)
2426 {}
2427
2428 bool update_model (region_model *model,
2429 const exploded_edge *,
2430 region_model_context *ctxt) const final override
2431 {
2432 const call_details cd (get_call_details (model, ctxt));
2433 sm_state_map *smap;
2434 const fd_state_machine *fd_sm;
2435 std::unique_ptr<sm_context> sm_ctxt;
2436 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2437 {
2438 cd.set_any_lhs_with_defaults ();
2439 return true;
2440 }
2441 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2442 if (!ext_state)
2443 {
2444 cd.set_any_lhs_with_defaults ();
2445 return true;
2446 }
2447
2448 return fd_sm->on_accept (cd, successful: m_success, sm_ctxt: sm_ctxt.get (), ext_state: *ext_state);
2449 }
2450 };
2451
2452 bool matches_call_types_p (const call_details &cd) const final override
2453 {
2454 return (cd.num_args () == 3
2455 && cd.arg_is_pointer_p (idx: 1)
2456 && cd.arg_is_pointer_p (idx: 2));
2457 }
2458
2459 void impl_call_post (const call_details &cd) const final override
2460 {
2461 if (cd.get_ctxt ())
2462 {
2463 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_accept> (args: cd, args: false));
2464 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_accept> (args: cd, args: true));
2465 cd.get_ctxt ()->terminate_path ();
2466 }
2467 }
2468};
2469
2470/* Handle calls to "connect".
2471 See e.g. https://man7.org/linux/man-pages/man3/connect.3p.html */
2472
2473class kf_connect : public known_function
2474{
2475public:
2476 class outcome_of_connect : public succeed_or_fail_call_info
2477 {
2478 public:
2479 outcome_of_connect (const call_details &cd, bool success)
2480 : succeed_or_fail_call_info (cd, success)
2481 {}
2482
2483 bool update_model (region_model *model,
2484 const exploded_edge *,
2485 region_model_context *ctxt) const final override
2486 {
2487 const call_details cd (get_call_details (model, ctxt));
2488 sm_state_map *smap;
2489 const fd_state_machine *fd_sm;
2490 std::unique_ptr<sm_context> sm_ctxt;
2491 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2492 {
2493 cd.set_any_lhs_with_defaults ();
2494 return true;
2495 }
2496 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2497 if (!ext_state)
2498 {
2499 cd.set_any_lhs_with_defaults ();
2500 return true;
2501 }
2502
2503 return fd_sm->on_connect (cd, successful: m_success, sm_ctxt: sm_ctxt.get (), ext_state: *ext_state);
2504 }
2505 };
2506
2507 bool matches_call_types_p (const call_details &cd) const final override
2508 {
2509 return (cd.num_args () == 3
2510 && cd.arg_is_pointer_p (idx: 1));
2511 }
2512
2513 void impl_call_post (const call_details &cd) const final override
2514 {
2515 if (cd.get_ctxt ())
2516 {
2517 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_connect> (args: cd, args: false));
2518 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_connect> (args: cd, args: true));
2519 cd.get_ctxt ()->terminate_path ();
2520 }
2521 }
2522};
2523
2524/* Handler for "isatty"".
2525 See e.g. https://man7.org/linux/man-pages/man3/isatty.3.html */
2526
2527class kf_isatty : public known_function
2528{
2529 class outcome_of_isatty : public succeed_or_fail_call_info
2530 {
2531 public:
2532 outcome_of_isatty (const call_details &cd, bool success)
2533 : succeed_or_fail_call_info (cd, success)
2534 {}
2535
2536 bool update_model (region_model *model,
2537 const exploded_edge *,
2538 region_model_context *ctxt) const final override
2539 {
2540 const call_details cd (get_call_details (model, ctxt));
2541
2542 if (m_success)
2543 {
2544 /* Return 1. */
2545 model->update_for_int_cst_return (cd, retval: 1, unmergeable: true);
2546 }
2547 else
2548 {
2549 /* Return 0; set errno. */
2550 model->update_for_int_cst_return (cd, retval: 0, unmergeable: true);
2551 model->set_errno (cd);
2552 }
2553
2554 return feasible_p (cd, ctxt);
2555 }
2556
2557 private:
2558 bool feasible_p (const call_details &cd,
2559 region_model_context *ctxt) const
2560 {
2561 if (m_success)
2562 {
2563 /* Can't be "success" on a closed/invalid fd. */
2564 sm_state_map *smap;
2565 const fd_state_machine *fd_sm;
2566 std::unique_ptr<sm_context> sm_ctxt;
2567 if (!get_fd_state (ctxt, out_smap: &smap, out_sm: &fd_sm, NULL, out_sm_context: &sm_ctxt))
2568 return true;
2569 const extrinsic_state *ext_state = ctxt->get_ext_state ();
2570 if (!ext_state)
2571 return true;
2572
2573 const svalue *fd_sval = cd.get_arg_svalue (idx: 0);
2574 state_machine::state_t old_state
2575 = sm_ctxt->get_state (stmt: cd.get_call_stmt (), fd_sval);
2576
2577 if (fd_sm->is_closed_fd_p (state: old_state)
2578 || old_state == fd_sm->m_invalid)
2579 return false;
2580 }
2581 return true;
2582 }
2583 }; // class outcome_of_isatty
2584
2585public:
2586 bool matches_call_types_p (const call_details &cd) const final override
2587 {
2588 return cd.num_args () == 1;
2589 }
2590
2591 void impl_call_post (const call_details &cd) const final override
2592 {
2593 if (cd.get_ctxt ())
2594 {
2595 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_isatty> (args: cd, args: false));
2596 cd.get_ctxt ()->bifurcate (info: make_unique<outcome_of_isatty> (args: cd, args: true));
2597 cd.get_ctxt ()->terminate_path ();
2598 }
2599 }
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
2605class kf_pipe : public known_function
2606{
2607 class failure : public failed_call_info
2608 {
2609 public:
2610 failure (const call_details &cd) : failed_call_info (cd) {}
2611
2612 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 const call_details cd (get_call_details (model, ctxt));
2618 model->update_for_int_cst_return (cd, retval: -1, unmergeable: true);
2619 return true;
2620 }
2621 };
2622
2623 class success : public success_call_info
2624 {
2625 public:
2626 success (const call_details &cd) : success_call_info (cd) {}
2627
2628 bool update_model (region_model *model,
2629 const exploded_edge *,
2630 region_model_context *ctxt) const final override
2631 {
2632 const call_details cd (get_call_details (model, ctxt));
2633
2634 /* Return 0. */
2635 model->update_for_zero_return (cd, unmergeable: true);
2636
2637 /* Update fd array. */
2638 region_model_manager *mgr = cd.get_manager ();
2639 tree arr_tree = cd.get_arg_tree (idx: 0);
2640 const svalue *arr_sval = cd.get_arg_svalue (idx: 0);
2641 for (int idx = 0; idx < 2; idx++)
2642 {
2643 const region *arr_reg
2644 = model->deref_rvalue (ptr_sval: arr_sval, ptr_tree: arr_tree, ctxt: cd.get_ctxt ());
2645 const svalue *idx_sval
2646 = mgr->get_or_create_int_cst (integer_type_node, cst: idx);
2647 const region *element_reg
2648 = mgr->get_element_region (parent: arr_reg, integer_type_node, index: idx_sval);
2649 conjured_purge p (model, cd.get_ctxt ());
2650 const svalue *fd_sval
2651 = mgr->get_or_create_conjured_svalue (integer_type_node,
2652 stmt: cd.get_call_stmt (),
2653 id_reg: element_reg,
2654 p);
2655 model->set_value (lhs_reg: element_reg, rhs_sval: fd_sval, ctxt: cd.get_ctxt ());
2656 model->mark_as_valid_fd (sval: fd_sval, ctxt: cd.get_ctxt ());
2657 }
2658 return true;
2659 }
2660 };
2661
2662public:
2663 kf_pipe (unsigned num_args)
2664 : m_num_args (num_args)
2665 {
2666 gcc_assert (num_args > 0);
2667 }
2668
2669 bool matches_call_types_p (const call_details &cd) const final override
2670 {
2671 return (cd.num_args () == m_num_args && cd.arg_is_pointer_p (idx: 0));
2672 }
2673
2674 void impl_call_post (const call_details &cd) const final override
2675 {
2676 if (cd.get_ctxt ())
2677 {
2678 cd.get_ctxt ()->bifurcate (info: make_unique<failure> (args: cd));
2679 cd.get_ctxt ()->bifurcate (info: make_unique<success> (args: cd));
2680 cd.get_ctxt ()->terminate_path ();
2681 }
2682 }
2683
2684private:
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
2692class kf_read : public known_function
2693{
2694public:
2695 bool matches_call_types_p (const call_details &cd) const final override
2696 {
2697 return (cd.num_args () == 3
2698 && cd.arg_is_pointer_p (idx: 1)
2699 && cd.arg_is_size_p (idx: 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 void impl_call_pre (const call_details &cd) const final override
2707 {
2708 region_model *model = cd.get_model ();
2709 const svalue *ptr_sval = cd.get_arg_svalue (idx: 1);
2710 if (const region *reg = ptr_sval->maybe_get_region ())
2711 {
2712 const region *base_reg = reg->get_base_region ();
2713 const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
2714 model->set_value (lhs_reg: base_reg, rhs_sval: new_sval, ctxt: cd.get_ctxt ());
2715 }
2716 cd.set_any_lhs_with_defaults ();
2717 }
2718};
2719
2720
2721/* Populate KFM with instances of known functions relating to
2722 file descriptors. */
2723
2724void
2725register_known_fd_functions (known_function_manager &kfm)
2726{
2727 kfm.add (name: "accept", kf: make_unique<kf_accept> ());
2728 kfm.add (name: "bind", kf: make_unique<kf_bind> ());
2729 kfm.add (name: "connect", kf: make_unique<kf_connect> ());
2730 kfm.add (name: "isatty", kf: make_unique<kf_isatty> ());
2731 kfm.add (name: "listen", kf: make_unique<kf_listen> ());
2732 kfm.add (name: "pipe", kf: make_unique<kf_pipe> (args: 1));
2733 kfm.add (name: "pipe2", kf: make_unique<kf_pipe> (args: 2));
2734 kfm.add (name: "read", kf: make_unique<kf_read> ());
2735 kfm.add (name: "socket", kf: make_unique<kf_socket> ());
2736}
2737
2738} // namespace ana
2739
2740#endif // ENABLE_ANALYZER
2741

source code of gcc/analyzer/sm-fd.cc