From 58f1fd8b32b0a010a767fef10e22a9c21d91fc58 Mon Sep 17 00:00:00 2001 From: Daiva Naudziuniene Date: Mon, 9 Nov 2020 13:41:23 -0800 Subject: [PATCH] [pulse] Optional Empty Access for std::optional Reviewed By: jvillard Differential Revision: D24760820 fbshipit-source-id: bedf6aee3 --- infer/src/pulse/PulseModels.ml | 40 ++++++++ .../tests/codetoanalyze/cpp/pulse/issues.exp | 7 ++ .../codetoanalyze/cpp/pulse/optional.cpp | 92 +++++++++++++++++++ 3 files changed, 139 insertions(+) diff --git a/infer/src/pulse/PulseModels.ml b/infer/src/pulse/PulseModels.ml index 5eb81630d..340241558 100644 --- a/infer/src/pulse/PulseModels.ml +++ b/infer/src/pulse/PulseModels.ml @@ -969,6 +969,46 @@ module ProcNameDispatcher = struct $+...$--> Optional.value ~desc:"folly::Optional::value()" ; -"folly" &:: "Optional" &:: "value_or" $ capt_arg_payload $+ capt_arg_payload $+...$--> Optional.value_or ~desc:"folly::Optional::value_or()" + (* std::optional *) + ; -"std" &:: "optional" &:: "optional" $ capt_arg_payload + $+ any_arg_of_typ (-"std" &:: "nullopt_t") + $--> Optional.assign_none ~desc:"std::optional::optional(=nullopt)" + ; -"std" &:: "optional" &:: "optional" $ capt_arg_payload + $--> Optional.assign_none ~desc:"std::optional::optional()" + ; -"std" &:: "optional" &:: "optional" $ capt_arg_payload + $+ capt_arg_payload_of_typ (-"std" &:: "optional") + $--> Optional.assign_optional_value + ~desc:"std::optional::optional(std::optional arg)" + ; -"std" &:: "optional" &:: "optional" $ capt_arg_payload $+ capt_arg_payload + $+...$--> Optional.assign_value ~desc:"std::optional::optional(Value arg)" + ; -"std" &:: "optional" &:: "operator=" <>$ capt_arg_payload + $+ any_arg_of_typ (-"std" &:: "nullopt_t") + $--> Optional.assign_none ~desc:"std::optional::operator=(None)" + ; -"std" &:: "optional" &:: "operator=" <>$ capt_arg_payload + $+ capt_arg_payload_of_typ (-"std" &:: "optional") + $--> Optional.assign_optional_value + ~desc:"std::optional::operator=(std::optional arg)" + ; -"std" &:: "optional" &:: "operator=" <>$ capt_arg_payload $+ capt_arg_payload + $+...$--> Optional.assign_value ~desc:"std::optional::operator=(Value arg)" + ; -"std" &:: "optional" &:: "emplace<>" $ capt_arg_payload + $+...$--> Optional.emplace ~desc:"std::optional::emplace()" + ; -"std" &:: "optional" &:: "emplace" $ capt_arg_payload + $+...$--> Optional.emplace ~desc:"std::optional::emplace()" + ; -"std" &:: "optional" &:: "has_value" <>$ capt_arg_payload + $+...$--> Optional.has_value ~desc:"std::optional::has_value()" + ; -"std" &:: "optional" &:: "operator_bool" <>$ capt_arg_payload + $+...$--> Optional.has_value ~desc:"std::optional::operator_bool()" + ; -"std" &:: "optional" &:: "reset" <>$ capt_arg_payload + $+...$--> Optional.assign_none ~desc:"std::optional::reset()" + ; -"std" &:: "optional" &:: "value" <>$ capt_arg_payload + $+...$--> Optional.value ~desc:"std::optional::value()" + ; -"std" &:: "optional" &:: "operator*" <>$ capt_arg_payload + $+...$--> Optional.value ~desc:"std::optional::operator*()" + ; -"std" &:: "optional" &:: "operator->" <>$ capt_arg_payload + $+...$--> Optional.value ~desc:"std::optional::operator->()" + ; -"std" &:: "optional" &:: "value_or" $ capt_arg_payload $+ capt_arg_payload + $+...$--> Optional.value_or ~desc:"std::optional::value_or()" + (* end std::optional *) ; -"std" &:: "basic_string" &:: "data" <>$ capt_arg_payload $--> StdBasicString.data ; -"std" &:: "basic_string" &:: "~basic_string" <>$ capt_arg_payload $--> StdBasicString.destructor diff --git a/infer/tests/codetoanalyze/cpp/pulse/issues.exp b/infer/tests/codetoanalyze/cpp/pulse/issues.exp index 21d4ec34e..3304e3fc2 100644 --- a/infer/tests/codetoanalyze/cpp/pulse/issues.exp +++ b/infer/tests/codetoanalyze/cpp/pulse/issues.exp @@ -52,6 +52,13 @@ codetoanalyze/cpp/pulse/optional.cpp, none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, n codetoanalyze/cpp/pulse/optional.cpp, none_no_check_bad, 2, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, not_none_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, operator_arrow_bad, 0, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),when calling `emplace` here,parameter `state` of emplace,passed as argument to `folly::Optional::operator->`,return from call to `folly::Optional::operator->`,invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_assign2_bad, 4, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::operator=(None)` (modelled),return from call to `std::optional::operator=(None)` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_assign_bad, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::operator=(std::optional arg)` (modelled),return from call to `std::optional::operator=(std::optional arg)` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_none_copy_bad, 3, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),passed as argument to `std::optional::optional(std::optional arg)` (modelled),return from call to `std::optional::optional(std::optional arg)` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_none_no_check_bad, 2, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_not_none_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_operator_arrow_bad, 0, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),when calling `std_emplace` here,parameter `state` of std_emplace,passed as argument to `std::optional::operator->()` (modelled),return from call to `std::optional::operator->()` (modelled),invalid access occurs here] +codetoanalyze/cpp/pulse/optional.cpp, std_value_or_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `std::optional::optional(=nullopt)` (modelled),return from call to `std::optional::optional(=nullopt)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, test_trace_ref, 4, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,when calling `folly::Optional::operator=` here,passed as argument to `folly::Optional::reset()` (modelled),return from call to `folly::Optional::reset()` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::operator=`,passed as argument to `folly::Optional::reset()` (modelled),return from call to `folly::Optional::reset()` (modelled),return from call to `folly::Optional::operator=`,invalid access occurs here] codetoanalyze/cpp/pulse/optional.cpp, value_or_check_value_ok_FP, 5, OPTIONAL_EMPTY_ACCESS, no_bucket, ERROR, [invalidation part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),is optional empty,use-after-lifetime part of the trace starts here,passed as argument to `folly::Optional::Optional(=None)` (modelled),return from call to `folly::Optional::Optional(=None)` (modelled),invalid access occurs here] codetoanalyze/cpp/pulse/path.cpp, faulty_call_bad, 0, NULLPTR_DEREFERENCE, no_bucket, ERROR, [calling context starts here,in call to `only_bad_on_42_latent`,invalidation part of the trace starts here,when calling `may_return_null` here,assigned,is the null pointer,use-after-lifetime part of the trace starts here,passed as argument to `may_return_null`,return from call to `may_return_null`,assigned,invalid access occurs here] diff --git a/infer/tests/codetoanalyze/cpp/pulse/optional.cpp b/infer/tests/codetoanalyze/cpp/pulse/optional.cpp index ba6715d08..f87f443fc 100644 --- a/infer/tests/codetoanalyze/cpp/pulse/optional.cpp +++ b/infer/tests/codetoanalyze/cpp/pulse/optional.cpp @@ -7,6 +7,7 @@ #include #include +#include namespace folly { @@ -185,3 +186,94 @@ struct StringWrapper { std::string get_optional_string_wrapper_ok() { return StringWrapper::get_optional().value().x.data(); } + +int std_not_none_ok() { + std::optional foo{5}; + return foo.value(); +} + +int std_not_none_check_value_ok_FP() { + std::optional foo{5}; + int x = foo.value(); + if (x != 5) { + std::optional foo{std::nullopt}; + return foo.value(); + } + return x; +} + +int std_none_check_ok() { + std::optional foo{std::nullopt}; + if (foo) { + return foo.value(); + } + return -1; +} + +int std_none_no_check_bad() { + std::optional foo{std::nullopt}; + return foo.value(); +} + +int std_none_copy_ok() { + std::optional foo{5}; + std::optional bar{foo}; + return bar.value(); +} + +int std_none_copy_bad() { + std::optional foo{std::nullopt}; + std::optional bar{foo}; + return bar.value(); +} + +int std_assign_ok() { + std::optional foo{5}; + std::optional bar{foo}; + foo = std::nullopt; + return bar.value(); +} + +int std_assign_bad() { + std::optional foo{std::nullopt}; + std::optional bar{5}; + int sum = bar.value(); + bar = foo; + sum += bar.value(); + return sum; +} + +int std_assign2_bad() { + std::optional foo{5}; + int sum = foo.value(); + foo = std::nullopt; + sum += foo.value(); + return sum; +} + +void std_emplace(std::optional state) { + if (state) { + state.emplace(); + } + auto pos = state->vec.begin(); +} + +void std_operator_arrow_bad() { std_emplace(std::nullopt); } + +int std_value_or_check_empty_ok() { + std::optional foo{std::nullopt}; + if (foo.value_or(0) > 0) { + return foo.value(); + } + return -1; +} + +int std_value_or_check_value_ok_FP() { + std::optional foo{5}; + int x = foo.value_or(0); + if (x != 5) { + std::optional foo{std::nullopt}; + return foo.value(); + } + return -1; +}