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