[siof] understand that <iostream> initialises streams

Summary:
Inject a marker using a global variable in <iostream>, and whitelist it so that
the frontend translates it.

Use the marker in the SIOF checker to tell whether a file includes <iostream>.
If so, start the analysis of its methods assuming that the standard streams are
initialised.

Reviewed By: sblackshear

Differential Revision: D5941343

fbshipit-source-id: 3388d55
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent d70babb871
commit 22aca7494b

@ -0,0 +1,15 @@
/*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#pragma once
#include_next <iostream>
// this is a marker so that infer can tell whether iostream has been included by
// a give source file
std::ios_base::Init __infer_translation_unit_init_streams;

@ -0,0 +1,7 @@
#include <infer_model/portability.h>
#ifdef INFER_CPP11_ON
# include <infer_model/iostream.h>
#else
# include_next <iostream>
#endif

@ -28,17 +28,17 @@ type siof_model =
let parse_siof_model (qual_name, initialized_globals) = {qual_name; initialized_globals}
let models =
List.map ~f:parse_siof_model
[ ( "std::ios_base::Init::Init"
, [ "std::cerr"
; "std::wcerr"
; "std::cin"
; "std::wcin"
; "std::clog"
; "std::wclog"
; "std::cout"
; "std::wcout" ] ) ]
let standard_streams =
[ "std::cerr"
; "std::wcerr"
; "std::cin"
; "std::wcin"
; "std::clog"
; "std::wclog"
; "std::cout"
; "std::wcout" ]
let models = List.map ~f:parse_siof_model [("std::ios_base::Init::Init", standard_streams)]
let is_modelled =
let models_matcher =
@ -223,9 +223,30 @@ let siof_check pdesc gname (summary: Specs.summary) =
| Some (Bottom, _) | None
-> ()
let checker {Callbacks.proc_desc; tenv; summary} : Specs.summary =
let checker {Callbacks.proc_desc; tenv; summary; get_procs_in_file} : Specs.summary =
let standard_streams_initialized_in_tu =
let includes_iostream tu =
let magic_iostream_marker =
(* always [Some _] because we create a global variable with [mk_global] *)
Option.value_exn
( Pvar.mk_global
(Mangled.from_string
(* infer's C++ headers define this global variable in <iostream> *)
"__infer_translation_unit_init_streams") (TUFile tu)
|> Pvar.get_initializer_pname )
in
get_procs_in_file (Procdesc.get_proc_name proc_desc)
|> List.exists ~f:(Typ.Procname.equal magic_iostream_marker)
in
Option.value_map ~default:false ~f:includes_iostream
(Procdesc.get_attributes proc_desc).ProcAttributes.translation_unit
in
let proc_data = ProcData.make_default proc_desc tenv in
let initial = (Bottom, SiofDomain.VarNames.empty) in
let initial =
( Bottom
, if standard_streams_initialized_in_tu then SiofDomain.VarNames.of_list standard_streams
else SiofDomain.VarNames.empty )
in
let updated_summary =
match Analyzer.compute_post proc_data ~initial with
| Some post

@ -197,33 +197,41 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| None
-> false
let should_translate_decl trans_unit_ctx dec decl_trans_context =
let should_translate_decl trans_unit_ctx (dec: Clang_ast_t.decl) decl_trans_context =
let info = Clang_ast_proj.get_decl_tuple dec in
let source_range = info.Clang_ast_t.di_source_range in
let translate_when_used =
match dec with
| Clang_ast_t.FunctionDecl (_, name_info, _, _)
| Clang_ast_t.CXXMethodDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXConstructorDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXConversionDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXDestructorDecl (_, name_info, _, _, _)
| FunctionDecl (_, name_info, _, _)
| CXXMethodDecl (_, name_info, _, _, _)
| CXXConstructorDecl (_, name_info, _, _, _)
| CXXConversionDecl (_, name_info, _, _, _)
| CXXDestructorDecl (_, name_info, _, _, _)
-> is_whitelisted_cpp_method (CAst_utils.get_qualified_name name_info)
| _
-> false
in
let always_translate =
match dec with
| VarDecl (_, {ni_name}, _, _)
-> String.is_prefix ni_name ~prefix:"__infer_"
| _
-> false
in
let translate_location =
CLocation.should_translate_lib trans_unit_ctx source_range decl_trans_context
~translate_when_used
always_translate
|| CLocation.should_translate_lib trans_unit_ctx source_range decl_trans_context
~translate_when_used
in
let never_translate_decl =
match dec with
| Clang_ast_t.FunctionDecl (_, name_info, _, _)
| Clang_ast_t.CXXMethodDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXConstructorDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXConversionDecl (_, name_info, _, _, _)
| Clang_ast_t.CXXDestructorDecl (_, name_info, _, _, _)
| FunctionDecl (_, name_info, _, _)
| CXXMethodDecl (_, name_info, _, _, _)
| CXXConstructorDecl (_, name_info, _, _, _)
| CXXConversionDecl (_, name_info, _, _, _)
| CXXDestructorDecl (_, name_info, _, _, _)
-> let fun_name = name_info.Clang_ast_t.ni_name in
Str.string_match (Str.regexp "__infer_skip__") fun_name 0
String.is_prefix ~prefix:"__infer_skip__" fun_name
| _
-> false
in
@ -282,7 +290,8 @@ module CFrontend_decl_funct (T : CModule_type.CTranslation) : CModule_type.CFron
| None
-> () )
| VarDecl (decl_info, named_decl_info, qt, ({vdi_is_global; vdi_init_expr} as vdi))
when vdi_is_global && Option.is_some vdi_init_expr
when String.is_prefix ~prefix:"__infer_" named_decl_info.ni_name
|| vdi_is_global && Option.is_some vdi_init_expr
-> (* create a fake procedure that initializes the global variable so that the variable
initializer can be analyzed by the backend (eg, the SIOF checker) *)
let procname =

@ -20,6 +20,7 @@ SOURCES = \
siof/pod_across_translation_units-1.cpp \
siof/pod_across_translation_units-2.cpp \
siof/pod_same_translation_unit.cpp \
siof/include_iostream.cpp \
siof/siof.cpp \
siof/siof_templated.cpp \
siof/siof_different_tu.cpp \

@ -0,0 +1,13 @@
/*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <iostream>
void safe_streams() {
std::cout << "yay I'm safe thanks to having included <iostream>";
}

@ -12,6 +12,7 @@
extern SomeNonPODObject extern_global_object;
SomeNonPODObject global_object;
extern int access_to_non_pod();
void safe_streams();
struct SomeOtherNonPODObject {
SomeOtherNonPODObject() {
@ -21,6 +22,7 @@ struct SomeOtherNonPODObject {
SomeOtherNonPODObject(int i) {
global_object.some_method(); // OK, same translation unit
safe_streams(); // OK, that function is SIOF safe
};
};

@ -6,11 +6,23 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#include <iostream>
#include <istream>
#include <ostream>
namespace std {
extern std::istream cin;
extern std::wistream wcin;
extern std::ostream cerr;
extern std::wostream wcerr;
extern std::ostream clog;
extern std::wostream wclog;
extern std::ostream cout;
extern std::wostream wcout;
}; // namespace std
void printing_no_SIOF() {
std::ios_base::Init ioInit;
std::cerr << "I haz warning!";
std::cerr << "it's ok to print here";
}
void printing_SIOF() {

Loading…
Cancel
Save