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