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