]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/analyzer/sm-fd.cc
c, analyzer: support named constants in analyzer [PR106302]
[thirdparty/gcc.git] / gcc / analyzer / sm-fd.cc
CommitLineData
9365b2bf
ML
1/* A state machine for detecting misuses of POSIX file descriptor APIs.
2 Copyright (C) 2019-2022 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"
6341f14e 22#define INCLUDE_MEMORY
9365b2bf
ML
23#include "system.h"
24#include "coretypes.h"
6341f14e 25#include "make-unique.h"
9365b2bf
ML
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 "diagnostic-metadata.h"
9365b2bf
ML
33#include "analyzer/analyzer.h"
34#include "diagnostic-event-id.h"
35#include "analyzer/analyzer-logging.h"
36#include "analyzer/sm.h"
37#include "analyzer/pending-diagnostic.h"
38#include "analyzer/function-set.h"
39#include "analyzer/analyzer-selftests.h"
9365b2bf
ML
40#include "stringpool.h"
41#include "attribs.h"
42#include "analyzer/call-string.h"
43#include "analyzer/program-point.h"
44#include "analyzer/store.h"
45#include "analyzer/region-model.h"
46#include "bitmap.h"
792f039f 47#include "analyzer/program-state.h"
9365b2bf
ML
48
49#if ENABLE_ANALYZER
50
51namespace ana {
52
53namespace {
54
55/* An enum for distinguishing between three different access modes. */
56
57enum access_mode
58{
59 READ_WRITE,
60 READ_ONLY,
61 WRITE_ONLY
62};
63
64enum access_directions
65{
66 DIRS_READ_WRITE,
67 DIRS_READ,
68 DIRS_WRITE
69};
70
6a11f2d9
IM
71/* An enum for distinguishing between dup, dup2 and dup3. */
72enum dup
73{
74 DUP_1,
75 DUP_2,
76 DUP_3
77};
78
9365b2bf
ML
79class fd_state_machine : public state_machine
80{
81public:
82 fd_state_machine (logger *logger);
83
84 bool
85 inherited_state_p () const final override
86 {
87 return false;
88 }
89
90 state_machine::state_t
91 get_default_state (const svalue *sval) const final override
92 {
93 if (tree cst = sval->maybe_get_constant ())
94 {
95 if (TREE_CODE (cst) == INTEGER_CST)
96 {
97 int val = TREE_INT_CST_LOW (cst);
98 if (val >= 0)
99 return m_constant_fd;
100 else
101 return m_invalid;
102 }
103 }
104 return m_start;
105 }
106
107 bool on_stmt (sm_context *sm_ctxt, const supernode *node,
108 const gimple *stmt) const final override;
109
110 void on_condition (sm_context *sm_ctxt, const supernode *node,
111 const gimple *stmt, const svalue *lhs, const tree_code op,
112 const svalue *rhs) const final override;
113
114 bool can_purge_p (state_t s) const final override;
6341f14e 115 std::unique_ptr<pending_diagnostic> on_leak (tree var) const final override;
9365b2bf
ML
116
117 bool is_unchecked_fd_p (state_t s) const;
118 bool is_valid_fd_p (state_t s) const;
119 bool is_closed_fd_p (state_t s) const;
120 bool is_constant_fd_p (state_t s) const;
121 bool is_readonly_fd_p (state_t s) const;
122 bool is_writeonly_fd_p (state_t s) const;
123 enum access_mode get_access_mode_from_flag (int flag) const;
6a11f2d9
IM
124 /* Function for one-to-one correspondence between valid
125 and unchecked states. */
126 state_t valid_to_unchecked_state (state_t state) const;
792f039f
DM
127
128 void mark_as_valid_fd (region_model *model,
129 sm_state_map *smap,
130 const svalue *fd_sval,
131 const extrinsic_state &ext_state) const;
132
9365b2bf
ML
133 /* State for a constant file descriptor (>= 0) */
134 state_t m_constant_fd;
135
136 /* States representing a file descriptor that hasn't yet been
137 checked for validity after opening, for three different
138 access modes. */
139 state_t m_unchecked_read_write;
140
141 state_t m_unchecked_read_only;
142
143 state_t m_unchecked_write_only;
144
145 /* States for representing a file descriptor that is known to be valid (>=
146 0), for three different access modes. */
147 state_t m_valid_read_write;
148
149 state_t m_valid_read_only;
150
151 state_t m_valid_write_only;
152
153 /* State for a file descriptor that is known to be invalid (< 0). */
154 state_t m_invalid;
155
156 /* State for a file descriptor that has been closed. */
157 state_t m_closed;
158
159 /* State for a file descriptor that we do not want to track anymore . */
160 state_t m_stop;
161
d8aba860
DM
162 /* Stashed constant values from the frontend. These could be NULL. */
163 tree m_O_ACCMODE;
164 tree m_O_RDONLY;
165 tree m_O_WRONLY;
166
9365b2bf
ML
167private:
168 void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
169 const gcall *call) const;
6a11f2d9
IM
170 void on_creat (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
171 const gcall *call) const;
9365b2bf
ML
172 void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
173 const gcall *call) const;
174 void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
175 const gcall *call, const tree callee_fndecl) const;
176 void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
177 const gcall *call, const tree callee_fndecl) const;
178 void check_for_open_fd (sm_context *sm_ctxt, const supernode *node,
179 const gimple *stmt, const gcall *call,
180 const tree callee_fndecl,
181 enum access_directions access_fn) const;
182
183 void make_valid_transitions_on_condition (sm_context *sm_ctxt,
184 const supernode *node,
185 const gimple *stmt,
186 const svalue *lhs) const;
187 void make_invalid_transitions_on_condition (sm_context *sm_ctxt,
188 const supernode *node,
189 const gimple *stmt,
190 const svalue *lhs) const;
191 void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node,
192 const gimple *stmt, const gcall *call,
193 const tree callee_fndecl, const char *attr_name,
194 access_directions fd_attr_access_dir) const;
6a11f2d9
IM
195 void check_for_dup (sm_context *sm_ctxt, const supernode *node,
196 const gimple *stmt, const gcall *call, const tree callee_fndecl,
197 enum dup kind) const;
9365b2bf
ML
198};
199
200/* Base diagnostic class relative to fd_state_machine. */
201class fd_diagnostic : public pending_diagnostic
202{
203public:
204 fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg)
205 {
206 }
207
208 bool
209 subclass_equal_p (const pending_diagnostic &base_other) const override
210 {
211 return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg);
212 }
213
214 label_text
215 describe_state_change (const evdesc::state_change &change) override
216 {
217 if (change.m_old_state == m_sm.get_start_state ()
792f039f
DM
218 && (m_sm.is_unchecked_fd_p (change.m_new_state)
219 || m_sm.is_valid_fd_p (change.m_new_state)))
9365b2bf 220 {
792f039f
DM
221 if (change.m_new_state == m_sm.m_unchecked_read_write
222 || change.m_new_state == m_sm.m_valid_read_write)
9365b2bf
ML
223 return change.formatted_print ("opened here as read-write");
224
792f039f
DM
225 if (change.m_new_state == m_sm.m_unchecked_read_only
226 || change.m_new_state == m_sm.m_valid_read_only)
9365b2bf
ML
227 return change.formatted_print ("opened here as read-only");
228
792f039f
DM
229 if (change.m_new_state == m_sm.m_unchecked_write_only
230 || change.m_new_state == m_sm.m_valid_write_only)
9365b2bf
ML
231 return change.formatted_print ("opened here as write-only");
232 }
233
234 if (change.m_new_state == m_sm.m_closed)
235 return change.formatted_print ("closed here");
236
237 if (m_sm.is_unchecked_fd_p (change.m_old_state)
238 && m_sm.is_valid_fd_p (change.m_new_state))
239 {
240 if (change.m_expr)
241 return change.formatted_print (
242 "assuming %qE is a valid file descriptor (>= 0)", change.m_expr);
243 else
244 return change.formatted_print ("assuming a valid file descriptor");
245 }
246
247 if (m_sm.is_unchecked_fd_p (change.m_old_state)
248 && change.m_new_state == m_sm.m_invalid)
249 {
250 if (change.m_expr)
251 return change.formatted_print (
252 "assuming %qE is an invalid file descriptor (< 0)",
253 change.m_expr);
254 else
255 return change.formatted_print ("assuming an invalid file descriptor");
256 }
257
258 return label_text ();
259 }
260
0f82c0ea
IM
261 diagnostic_event::meaning
262 get_meaning_for_state_change (
263 const evdesc::state_change &change) const final override
264 {
265 if (change.m_old_state == m_sm.get_start_state ()
266 && (m_sm.is_unchecked_fd_p (change.m_new_state)))
267 return diagnostic_event::meaning (diagnostic_event::VERB_acquire,
268 diagnostic_event::NOUN_resource);
269 if (change.m_new_state == m_sm.m_closed)
270 return diagnostic_event::meaning (diagnostic_event::VERB_release,
271 diagnostic_event::NOUN_resource);
272 return diagnostic_event::meaning ();
273 }
274
9365b2bf
ML
275protected:
276 const fd_state_machine &m_sm;
277 tree m_arg;
278};
279
280class fd_param_diagnostic : public fd_diagnostic
281{
282public:
283 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl,
284 const char *attr_name, int arg_idx)
285 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
286 m_attr_name (attr_name), m_arg_idx (arg_idx)
287 {
288 }
289
290 fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl)
291 : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl),
292 m_attr_name (NULL), m_arg_idx (-1)
293 {
294 }
295
296 bool
297 subclass_equal_p (const pending_diagnostic &base_other) const override
298 {
299 const fd_param_diagnostic &sub_other
300 = (const fd_param_diagnostic &)base_other;
301 return (same_tree_p (m_arg, sub_other.m_arg)
302 && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl)
303 && m_arg_idx == sub_other.m_arg_idx
304 && ((m_attr_name)
305 ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0)
306 : true));
307 }
308
309 void
310 inform_filedescriptor_attribute (access_directions fd_dir)
311 {
312
313 if (m_attr_name)
314 switch (fd_dir)
315 {
316 case DIRS_READ_WRITE:
317 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
318 "argument %d of %qD must be an open file descriptor, due to "
319 "%<__attribute__((%s(%d)))%>",
320 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
321 break;
322 case DIRS_WRITE:
323 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
324 "argument %d of %qD must be a readable file descriptor, due "
325 "to %<__attribute__((%s(%d)))%>",
326 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
327 break;
328 case DIRS_READ:
329 inform (DECL_SOURCE_LOCATION (m_callee_fndecl),
330 "argument %d of %qD must be a writable file descriptor, due "
331 "to %<__attribute__((%s(%d)))%>",
332 m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1);
333 break;
334 }
335 }
336
337protected:
338 tree m_callee_fndecl;
339 const char *m_attr_name;
340 /* ARG_IDX is 0-based. */
341 int m_arg_idx;
342};
343
344class fd_leak : public fd_diagnostic
345{
346public:
347 fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {}
348
349 const char *
350 get_kind () const final override
351 {
352 return "fd_leak";
353 }
354
355 int
356 get_controlling_option () const final override
357 {
358 return OPT_Wanalyzer_fd_leak;
359 }
360
361 bool
362 emit (rich_location *rich_loc) final override
363 {
364 /*CWE-775: Missing Release of File Descriptor or Handle after Effective
365 Lifetime
366 */
367 diagnostic_metadata m;
368 m.add_cwe (775);
369 if (m_arg)
370 return warning_meta (rich_loc, m, get_controlling_option (),
371 "leak of file descriptor %qE", m_arg);
372 else
373 return warning_meta (rich_loc, m, get_controlling_option (),
374 "leak of file descriptor");
375 }
376
377 label_text
378 describe_state_change (const evdesc::state_change &change) final override
379 {
380 if (m_sm.is_unchecked_fd_p (change.m_new_state))
381 {
382 m_open_event = change.m_event_id;
383 return label_text::borrow ("opened here");
384 }
385
386 return fd_diagnostic::describe_state_change (change);
387 }
388
389 label_text
390 describe_final_event (const evdesc::final_event &ev) final override
391 {
392 if (m_open_event.known_p ())
393 {
394 if (ev.m_expr)
395 return ev.formatted_print ("%qE leaks here; was opened at %@",
396 ev.m_expr, &m_open_event);
397 else
398 return ev.formatted_print ("leaks here; was opened at %@",
399 &m_open_event);
400 }
401 else
402 {
403 if (ev.m_expr)
404 return ev.formatted_print ("%qE leaks here", ev.m_expr);
405 else
406 return ev.formatted_print ("leaks here");
407 }
408 }
409
410private:
411 diagnostic_event_id_t m_open_event;
412};
413
414class fd_access_mode_mismatch : public fd_param_diagnostic
415{
416public:
417 fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
418 enum access_directions fd_dir,
419 const tree callee_fndecl, const char *attr_name,
420 int arg_idx)
421 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx),
422 m_fd_dir (fd_dir)
423
424 {
425 }
426
427 fd_access_mode_mismatch (const fd_state_machine &sm, tree arg,
428 enum access_directions fd_dir,
429 const tree callee_fndecl)
430 : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir)
431 {
432 }
433
434 const char *
435 get_kind () const final override
436 {
437 return "fd_access_mode_mismatch";
438 }
439
440 int
441 get_controlling_option () const final override
442 {
443 return OPT_Wanalyzer_fd_access_mode_mismatch;
444 }
445
446 bool
447 emit (rich_location *rich_loc) final override
448 {
449 bool warned;
450 switch (m_fd_dir)
451 {
452 case DIRS_READ:
453 warned = warning_at (rich_loc, get_controlling_option (),
454 "%qE on read-only file descriptor %qE",
455 m_callee_fndecl, m_arg);
456 break;
457 case DIRS_WRITE:
458 warned = warning_at (rich_loc, get_controlling_option (),
459 "%qE on write-only file descriptor %qE",
460 m_callee_fndecl, m_arg);
461 break;
462 default:
463 gcc_unreachable ();
464 }
465 if (warned)
466 inform_filedescriptor_attribute (m_fd_dir);
467 return warned;
468 }
469
470 label_text
471 describe_final_event (const evdesc::final_event &ev) final override
472 {
473 switch (m_fd_dir)
474 {
475 case DIRS_READ:
476 return ev.formatted_print ("%qE on read-only file descriptor %qE",
477 m_callee_fndecl, m_arg);
478 case DIRS_WRITE:
479 return ev.formatted_print ("%qE on write-only file descriptor %qE",
480 m_callee_fndecl, m_arg);
481 default:
482 gcc_unreachable ();
483 }
484 }
485
486private:
487 enum access_directions m_fd_dir;
488};
489
490class fd_double_close : public fd_diagnostic
491{
492public:
493 fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg)
494 {
495 }
496
497 const char *
498 get_kind () const final override
499 {
500 return "fd_double_close";
501 }
502
503 int
504 get_controlling_option () const final override
505 {
506 return OPT_Wanalyzer_fd_double_close;
507 }
508 bool
509 emit (rich_location *rich_loc) final override
510 {
511 diagnostic_metadata m;
512 // CWE-1341: Multiple Releases of Same Resource or Handle
513 m.add_cwe (1341);
514 return warning_meta (rich_loc, m, get_controlling_option (),
515 "double %<close%> of file descriptor %qE", m_arg);
516 }
517
518 label_text
519 describe_state_change (const evdesc::state_change &change) override
520 {
521 if (m_sm.is_unchecked_fd_p (change.m_new_state))
522 return label_text::borrow ("opened here");
523
524 if (change.m_new_state == m_sm.m_closed)
525 {
526 m_first_close_event = change.m_event_id;
527 return change.formatted_print ("first %qs here", "close");
528 }
529 return fd_diagnostic::describe_state_change (change);
530 }
531
532 label_text
533 describe_final_event (const evdesc::final_event &ev) final override
534 {
535 if (m_first_close_event.known_p ())
536 return ev.formatted_print ("second %qs here; first %qs was at %@",
537 "close", "close", &m_first_close_event);
538 return ev.formatted_print ("second %qs here", "close");
539 }
540
541private:
542 diagnostic_event_id_t m_first_close_event;
543};
544
545class fd_use_after_close : public fd_param_diagnostic
546{
547public:
548 fd_use_after_close (const fd_state_machine &sm, tree arg,
549 const tree callee_fndecl, const char *attr_name,
550 int arg_idx)
551 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
552 {
553 }
554
555 fd_use_after_close (const fd_state_machine &sm, tree arg,
556 const tree callee_fndecl)
557 : fd_param_diagnostic (sm, arg, callee_fndecl)
558 {
559 }
560
561 const char *
562 get_kind () const final override
563 {
564 return "fd_use_after_close";
565 }
566
567 int
568 get_controlling_option () const final override
569 {
570 return OPT_Wanalyzer_fd_use_after_close;
571 }
572
573 bool
574 emit (rich_location *rich_loc) final override
575 {
576 bool warned;
577 warned = warning_at (rich_loc, get_controlling_option (),
578 "%qE on closed file descriptor %qE", m_callee_fndecl,
579 m_arg);
580 if (warned)
581 inform_filedescriptor_attribute (DIRS_READ_WRITE);
582 return warned;
583 }
584
585 label_text
586 describe_state_change (const evdesc::state_change &change) override
587 {
588 if (m_sm.is_unchecked_fd_p (change.m_new_state))
589 return label_text::borrow ("opened here");
590
591 if (change.m_new_state == m_sm.m_closed)
592 {
593 m_first_close_event = change.m_event_id;
594 return change.formatted_print ("closed here");
595 }
596
597 return fd_diagnostic::describe_state_change (change);
598 }
599
600 label_text
601 describe_final_event (const evdesc::final_event &ev) final override
602 {
603 if (m_first_close_event.known_p ())
604 return ev.formatted_print (
605 "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl,
606 m_arg, "close", &m_first_close_event);
607 else
608 return ev.formatted_print ("%qE on closed file descriptor %qE",
609 m_callee_fndecl, m_arg);
610 }
611
612private:
613 diagnostic_event_id_t m_first_close_event;
614};
615
616class fd_use_without_check : public fd_param_diagnostic
617{
618public:
619 fd_use_without_check (const fd_state_machine &sm, tree arg,
620 const tree callee_fndecl, const char *attr_name,
621 int arg_idx)
622 : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx)
623 {
624 }
625
626 fd_use_without_check (const fd_state_machine &sm, tree arg,
627 const tree callee_fndecl)
628 : fd_param_diagnostic (sm, arg, callee_fndecl)
629 {
630 }
631
632 const char *
633 get_kind () const final override
634 {
635 return "fd_use_without_check";
636 }
637
638 int
639 get_controlling_option () const final override
640 {
641 return OPT_Wanalyzer_fd_use_without_check;
642 }
643
644 bool
645 emit (rich_location *rich_loc) final override
646 {
647 bool warned;
648 warned = warning_at (rich_loc, get_controlling_option (),
649 "%qE on possibly invalid file descriptor %qE",
650 m_callee_fndecl, m_arg);
651 if (warned)
652 inform_filedescriptor_attribute (DIRS_READ_WRITE);
653 return warned;
654 }
655
656 label_text
657 describe_state_change (const evdesc::state_change &change) override
658 {
659 if (m_sm.is_unchecked_fd_p (change.m_new_state))
660 {
661 m_first_open_event = change.m_event_id;
662 return label_text::borrow ("opened here");
663 }
664
665 return fd_diagnostic::describe_state_change (change);
666 }
667
668 label_text
669 describe_final_event (const evdesc::final_event &ev) final override
670 {
671 if (m_first_open_event.known_p ())
672 return ev.formatted_print (
673 "%qE could be invalid: unchecked value from %@", m_arg,
674 &m_first_open_event);
675 else
676 return ev.formatted_print ("%qE could be invalid", m_arg);
677 }
678
679private:
680 diagnostic_event_id_t m_first_open_event;
681};
682
683fd_state_machine::fd_state_machine (logger *logger)
684 : state_machine ("file-descriptor", logger),
685 m_constant_fd (add_state ("fd-constant")),
686 m_unchecked_read_write (add_state ("fd-unchecked-read-write")),
687 m_unchecked_read_only (add_state ("fd-unchecked-read-only")),
688 m_unchecked_write_only (add_state ("fd-unchecked-write-only")),
689 m_valid_read_write (add_state ("fd-valid-read-write")),
690 m_valid_read_only (add_state ("fd-valid-read-only")),
691 m_valid_write_only (add_state ("fd-valid-write-only")),
692 m_invalid (add_state ("fd-invalid")),
693 m_closed (add_state ("fd-closed")),
d8aba860
DM
694 m_stop (add_state ("fd-stop")),
695 m_O_ACCMODE (get_stashed_constant_by_name ("O_ACCMODE")),
696 m_O_RDONLY (get_stashed_constant_by_name ("O_RDONLY")),
697 m_O_WRONLY (get_stashed_constant_by_name ("O_WRONLY"))
9365b2bf
ML
698{
699}
700
701bool
702fd_state_machine::is_unchecked_fd_p (state_t s) const
703{
704 return (s == m_unchecked_read_write
705 || s == m_unchecked_read_only
706 || s == m_unchecked_write_only);
707}
708
709bool
710fd_state_machine::is_valid_fd_p (state_t s) const
711{
712 return (s == m_valid_read_write
713 || s == m_valid_read_only
714 || s == m_valid_write_only);
715}
716
717enum access_mode
718fd_state_machine::get_access_mode_from_flag (int flag) const
719{
d8aba860 720 if (m_O_ACCMODE && TREE_CODE (m_O_ACCMODE) == INTEGER_CST)
9365b2bf 721 {
d8aba860
DM
722 const unsigned HOST_WIDE_INT mask_val = TREE_INT_CST_LOW (m_O_ACCMODE);
723 const unsigned HOST_WIDE_INT masked_flag = flag & mask_val;
724
725 if (m_O_RDONLY && TREE_CODE (m_O_RDONLY) == INTEGER_CST)
726 if (masked_flag == TREE_INT_CST_LOW (m_O_RDONLY))
727 return READ_ONLY;
728
729 if (m_O_WRONLY && TREE_CODE (m_O_WRONLY) == INTEGER_CST)
730 if (masked_flag == TREE_INT_CST_LOW (m_O_WRONLY))
731 return WRITE_ONLY;
9365b2bf
ML
732 }
733 return READ_WRITE;
734}
735
736bool
737fd_state_machine::is_readonly_fd_p (state_t state) const
738{
739 return (state == m_unchecked_read_only || state == m_valid_read_only);
740}
741
742bool
743fd_state_machine::is_writeonly_fd_p (state_t state) const
744{
745 return (state == m_unchecked_write_only || state == m_valid_write_only);
746}
747
748bool
749fd_state_machine::is_closed_fd_p (state_t state) const
750{
751 return (state == m_closed);
752}
753
754bool
755fd_state_machine::is_constant_fd_p (state_t state) const
756{
757 return (state == m_constant_fd);
758}
759
6a11f2d9
IM
760fd_state_machine::state_t
761fd_state_machine::valid_to_unchecked_state (state_t state) const
762{
763 if (state == m_valid_read_write)
764 return m_unchecked_read_write;
765 else if (state == m_valid_write_only)
766 return m_unchecked_write_only;
767 else if (state == m_valid_read_only)
768 return m_unchecked_read_only;
769 else
770 gcc_unreachable ();
771 return NULL;
772}
773
792f039f
DM
774void
775fd_state_machine::mark_as_valid_fd (region_model *model,
776 sm_state_map *smap,
777 const svalue *fd_sval,
778 const extrinsic_state &ext_state) const
779{
780 smap->set_state (model, fd_sval, m_valid_read_write, NULL, ext_state);
781}
782
9365b2bf
ML
783bool
784fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node,
785 const gimple *stmt) const
786{
787 if (const gcall *call = dyn_cast<const gcall *> (stmt))
788 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call))
789 {
790 if (is_named_call_p (callee_fndecl, "open", call, 2))
791 {
792 on_open (sm_ctxt, node, stmt, call);
793 return true;
794 } // "open"
795
6a11f2d9
IM
796 if (is_named_call_p (callee_fndecl, "creat", call, 2))
797 {
798 on_creat (sm_ctxt, node, stmt, call);
792f039f 799 return true;
6a11f2d9
IM
800 } // "creat"
801
9365b2bf
ML
802 if (is_named_call_p (callee_fndecl, "close", call, 1))
803 {
804 on_close (sm_ctxt, node, stmt, call);
805 return true;
806 } // "close"
807
808 if (is_named_call_p (callee_fndecl, "write", call, 3))
809 {
810 on_write (sm_ctxt, node, stmt, call, callee_fndecl);
811 return true;
812 } // "write"
813
814 if (is_named_call_p (callee_fndecl, "read", call, 3))
815 {
816 on_read (sm_ctxt, node, stmt, call, callee_fndecl);
817 return true;
818 } // "read"
819
6a11f2d9
IM
820 if (is_named_call_p (callee_fndecl, "dup", call, 1))
821 {
822 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_1);
823 return true;
824 }
825
826 if (is_named_call_p (callee_fndecl, "dup2", call, 2))
827 {
828 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_2);
829 return true;
830 }
831
832 if (is_named_call_p (callee_fndecl, "dup3", call, 3))
833 {
834 check_for_dup (sm_ctxt, node, stmt, call, callee_fndecl, DUP_3);
835 return true;
836 }
9365b2bf
ML
837
838 {
839 // Handle __attribute__((fd_arg))
840
841 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
842 "fd_arg", DIRS_READ_WRITE);
843
844 // Handle __attribute__((fd_arg_read))
845
846 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
847 "fd_arg_read", DIRS_READ);
848
849 // Handle __attribute__((fd_arg_write))
850
851 check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl,
852 "fd_arg_write", DIRS_WRITE);
853 }
854 }
855
856 return false;
857}
858
859void
860fd_state_machine::check_for_fd_attrs (
861 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
862 const gcall *call, const tree callee_fndecl, const char *attr_name,
863 access_directions fd_attr_access_dir) const
864{
865
866 tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
867 attrs = lookup_attribute (attr_name, attrs);
868 if (!attrs)
869 return;
870
871 if (!TREE_VALUE (attrs))
872 return;
873
874 auto_bitmap argmap;
875
876 for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx))
877 {
878 unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1;
879 bitmap_set_bit (argmap, val);
880 }
881 if (bitmap_empty_p (argmap))
882 return;
883
884 for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++)
885 {
886 tree arg = gimple_call_arg (call, arg_idx);
887 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
888 state_t state = sm_ctxt->get_state (stmt, arg);
889 bool bit_set = bitmap_bit_p (argmap, arg_idx);
890 if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE)
891 continue;
892 if (bit_set) // Check if arg_idx is marked by any of the file descriptor
893 // attributes
894 {
895
896 if (is_closed_fd_p (state))
897 {
898
899 sm_ctxt->warn (node, stmt, arg,
6341f14e
DM
900 make_unique<fd_use_after_close>
901 (*this, diag_arg,
902 callee_fndecl, attr_name,
903 arg_idx));
9365b2bf
ML
904 continue;
905 }
906
907 if (!(is_valid_fd_p (state) || (state == m_stop)))
908 {
909 if (!is_constant_fd_p (state))
910 sm_ctxt->warn (node, stmt, arg,
6341f14e
DM
911 make_unique<fd_use_without_check>
912 (*this, diag_arg,
913 callee_fndecl, attr_name,
914 arg_idx));
9365b2bf
ML
915 }
916
917 switch (fd_attr_access_dir)
918 {
919 case DIRS_READ_WRITE:
920 break;
921 case DIRS_READ:
922
923 if (is_writeonly_fd_p (state))
924 {
925 sm_ctxt->warn (
926 node, stmt, arg,
6341f14e
DM
927 make_unique<fd_access_mode_mismatch> (*this, diag_arg,
928 DIRS_WRITE,
929 callee_fndecl,
930 attr_name,
931 arg_idx));
9365b2bf
ML
932 }
933
934 break;
935 case DIRS_WRITE:
936
937 if (is_readonly_fd_p (state))
938 {
939 sm_ctxt->warn (
940 node, stmt, arg,
6341f14e
DM
941 make_unique<fd_access_mode_mismatch> (*this, diag_arg,
942 DIRS_READ,
943 callee_fndecl,
944 attr_name,
945 arg_idx));
9365b2bf
ML
946 }
947
948 break;
949 }
950 }
951 }
952}
953
954
955void
956fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node,
957 const gimple *stmt, const gcall *call) const
958{
959 tree lhs = gimple_call_lhs (call);
960 if (lhs)
961 {
962 tree arg = gimple_call_arg (call, 1);
57bbf3a4 963 enum access_mode mode = READ_WRITE;
9365b2bf
ML
964 if (TREE_CODE (arg) == INTEGER_CST)
965 {
966 int flag = TREE_INT_CST_LOW (arg);
57bbf3a4
DM
967 mode = get_access_mode_from_flag (flag);
968 }
969 switch (mode)
970 {
971 case READ_ONLY:
972 sm_ctxt->on_transition (node, stmt, lhs, m_start,
973 m_unchecked_read_only);
974 break;
975 case WRITE_ONLY:
976 sm_ctxt->on_transition (node, stmt, lhs, m_start,
977 m_unchecked_write_only);
978 break;
979 default:
980 sm_ctxt->on_transition (node, stmt, lhs, m_start,
981 m_unchecked_read_write);
9365b2bf
ML
982 }
983 }
984 else
985 {
6341f14e
DM
986 sm_ctxt->warn (node, stmt, NULL_TREE,
987 make_unique<fd_leak> (*this, NULL_TREE));
9365b2bf
ML
988 }
989}
990
6a11f2d9
IM
991void
992fd_state_machine::on_creat (sm_context *sm_ctxt, const supernode *node,
993 const gimple *stmt, const gcall *call) const
994{
995 tree lhs = gimple_call_lhs (call);
996 if (lhs)
997 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked_write_only);
998 else
6341f14e
DM
999 sm_ctxt->warn (node, stmt, NULL_TREE,
1000 make_unique<fd_leak> (*this, NULL_TREE));
6a11f2d9
IM
1001}
1002
1003void
1004fd_state_machine::check_for_dup (sm_context *sm_ctxt, const supernode *node,
1005 const gimple *stmt, const gcall *call,
1006 const tree callee_fndecl, enum dup kind) const
1007{
1008 tree lhs = gimple_call_lhs (call);
1009 tree arg_1 = gimple_call_arg (call, 0);
1010 state_t state_arg_1 = sm_ctxt->get_state (stmt, arg_1);
1011 if (state_arg_1 == m_stop)
1012 return;
83714225
IM
1013 if (!(is_constant_fd_p (state_arg_1) || is_valid_fd_p (state_arg_1)
1014 || state_arg_1 == m_start))
6a11f2d9
IM
1015 {
1016 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl,
1017 DIRS_READ_WRITE);
ed7e7620 1018 return;
6a11f2d9
IM
1019 }
1020 switch (kind)
1021 {
1022 case DUP_1:
1023 if (lhs)
1024 {
83714225 1025 if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
6a11f2d9
IM
1026 sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1027 else
1028 sm_ctxt->set_next_state (stmt, lhs,
1029 valid_to_unchecked_state (state_arg_1));
1030 }
1031 break;
1032
1033 case DUP_2:
1034 case DUP_3:
1035 tree arg_2 = gimple_call_arg (call, 1);
1036 state_t state_arg_2 = sm_ctxt->get_state (stmt, arg_2);
1037 tree diag_arg_2 = sm_ctxt->get_diagnostic_tree (arg_2);
1038 if (state_arg_2 == m_stop)
1039 return;
1040 /* Check if -1 was passed as second argument to dup2. */
83714225
IM
1041 if (!(is_constant_fd_p (state_arg_2) || is_valid_fd_p (state_arg_2)
1042 || state_arg_2 == m_start))
6a11f2d9
IM
1043 {
1044 sm_ctxt->warn (
1045 node, stmt, arg_2,
6341f14e
DM
1046 make_unique<fd_use_without_check> (*this, diag_arg_2,
1047 callee_fndecl));
6a11f2d9
IM
1048 return;
1049 }
1050 /* dup2 returns value of its second argument on success.But, the
1051 access mode of the returned file descriptor depends on the duplicated
1052 file descriptor i.e the first argument. */
1053 if (lhs)
1054 {
83714225 1055 if (is_constant_fd_p (state_arg_1) || state_arg_1 == m_start)
6a11f2d9
IM
1056 sm_ctxt->set_next_state (stmt, lhs, m_unchecked_read_write);
1057 else
1058 sm_ctxt->set_next_state (stmt, lhs,
1059 valid_to_unchecked_state (state_arg_1));
1060 }
1061
1062 break;
1063 }
1064}
1065
9365b2bf
ML
1066void
1067fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node,
1068 const gimple *stmt, const gcall *call) const
1069{
1070 tree arg = gimple_call_arg (call, 0);
1071 state_t state = sm_ctxt->get_state (stmt, arg);
1072 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1073
1074 sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed);
1075 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed);
1076 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed);
1077 sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed);
1078 sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed);
1079 sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed);
1080 sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed);
1081 sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed);
1082
1083 if (is_closed_fd_p (state))
1084 {
6341f14e
DM
1085 sm_ctxt->warn (node, stmt, arg,
1086 make_unique<fd_double_close> (*this, diag_arg));
9365b2bf
ML
1087 sm_ctxt->set_next_state (stmt, arg, m_stop);
1088 }
1089}
1090void
1091fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node,
1092 const gimple *stmt, const gcall *call,
1093 const tree callee_fndecl) const
1094{
1095 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ);
1096}
1097void
1098fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node,
1099 const gimple *stmt, const gcall *call,
1100 const tree callee_fndecl) const
1101{
1102 check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE);
1103}
1104
1105void
1106fd_state_machine::check_for_open_fd (
1107 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1108 const gcall *call, const tree callee_fndecl,
1109 enum access_directions callee_fndecl_dir) const
1110{
1111 tree arg = gimple_call_arg (call, 0);
1112 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1113 state_t state = sm_ctxt->get_state (stmt, arg);
1114
1115 if (is_closed_fd_p (state))
1116 {
1117 sm_ctxt->warn (node, stmt, arg,
6341f14e
DM
1118 make_unique<fd_use_after_close> (*this, diag_arg,
1119 callee_fndecl));
9365b2bf
ML
1120 }
1121
1122 else
1123 {
57bbf3a4 1124 if (!(is_valid_fd_p (state) || state == m_start || state == m_stop))
9365b2bf
ML
1125 {
1126 if (!is_constant_fd_p (state))
1127 sm_ctxt->warn (
1128 node, stmt, arg,
6341f14e
DM
1129 make_unique<fd_use_without_check> (*this, diag_arg,
1130 callee_fndecl));
9365b2bf
ML
1131 }
1132 switch (callee_fndecl_dir)
1133 {
6a11f2d9
IM
1134 case DIRS_READ_WRITE:
1135 break;
9365b2bf
ML
1136 case DIRS_READ:
1137 if (is_writeonly_fd_p (state))
1138 {
1139 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1140 sm_ctxt->warn (node, stmt, arg,
6341f14e 1141 make_unique<fd_access_mode_mismatch> (
9365b2bf
ML
1142 *this, diag_arg, DIRS_WRITE, callee_fndecl));
1143 }
1144
1145 break;
1146 case DIRS_WRITE:
1147
1148 if (is_readonly_fd_p (state))
1149 {
1150 tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
1151 sm_ctxt->warn (node, stmt, arg,
6341f14e 1152 make_unique<fd_access_mode_mismatch> (
9365b2bf
ML
1153 *this, diag_arg, DIRS_READ, callee_fndecl));
1154 }
1155 break;
9365b2bf
ML
1156 }
1157 }
1158}
1159
1160void
1161fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node,
1162 const gimple *stmt, const svalue *lhs,
1163 enum tree_code op, const svalue *rhs) const
1164{
1165 if (tree cst = rhs->maybe_get_constant ())
1166 {
1167 if (TREE_CODE (cst) == INTEGER_CST)
1168 {
1169 int val = TREE_INT_CST_LOW (cst);
1170 if (val == -1)
1171 {
1172 if (op == NE_EXPR)
1173 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1174
1175 else if (op == EQ_EXPR)
1176 make_invalid_transitions_on_condition (sm_ctxt, node, stmt,
1177 lhs);
1178 }
1179 }
1180 }
1181
1182 if (rhs->all_zeroes_p ())
1183 {
1184 if (op == GE_EXPR)
1185 make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1186 else if (op == LT_EXPR)
1187 make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs);
1188 }
1189}
1190
1191void
1192fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt,
1193 const supernode *node,
1194 const gimple *stmt,
1195 const svalue *lhs) const
1196{
1197 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write,
1198 m_valid_read_write);
1199 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only,
1200 m_valid_read_only);
1201 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only,
1202 m_valid_write_only);
1203}
1204
1205void
1206fd_state_machine::make_invalid_transitions_on_condition (
1207 sm_context *sm_ctxt, const supernode *node, const gimple *stmt,
1208 const svalue *lhs) const
1209{
1210 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid);
1211 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid);
1212 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid);
1213}
1214
1215bool
1216fd_state_machine::can_purge_p (state_t s) const
1217{
1218 if (is_unchecked_fd_p (s) || is_valid_fd_p (s))
1219 return false;
1220 else
1221 return true;
1222}
1223
6341f14e 1224std::unique_ptr<pending_diagnostic>
9365b2bf
ML
1225fd_state_machine::on_leak (tree var) const
1226{
6341f14e 1227 return make_unique<fd_leak> (*this, var);
9365b2bf
ML
1228}
1229} // namespace
1230
1231state_machine *
1232make_fd_state_machine (logger *logger)
1233{
1234 return new fd_state_machine (logger);
1235}
792f039f
DM
1236
1237/* Specialcase hook for handling pipe, for use by
1238 region_model::impl_call_pipe::success::update_model. */
1239
1240void
1241region_model::mark_as_valid_fd (const svalue *sval, region_model_context *ctxt)
1242{
1243 if (!ctxt)
1244 return;
1245 const extrinsic_state *ext_state = ctxt->get_ext_state ();
1246 if (!ext_state)
1247 return;
1248
1249 sm_state_map *smap;
1250 const state_machine *sm;
1251 unsigned sm_idx;
1252 if (!ctxt->get_fd_map (&smap, &sm, &sm_idx))
1253 return;
1254
1255 gcc_assert (smap);
1256 gcc_assert (sm);
1257
1258 const fd_state_machine &fd_sm = (const fd_state_machine &)*sm;
1259
1260 fd_sm.mark_as_valid_fd (this, smap, sval, *ext_state);
1261}
1262
9365b2bf
ML
1263} // namespace ana
1264
1265#endif // ENABLE_ANALYZER