{"action":"created","issue":{"url":"https://api.github.com/repos/RcppCore/Rcpp/issues/286","labels_url":"https://api.github.com/repos/RcppCore/Rcpp/issues/286/labels{/name}","comments_url":"https://api.github.com/repos/RcppCore/Rcpp/issues/286/comments","events_url":"https://api.github.com/repos/RcppCore/Rcpp/issues/286/events","html_url":"https://github.com/RcppCore/Rcpp/issues/286","id":67535098,"number":286,"title":"Potential leakage with exceptions","user":{"login":"romainfrancois","id":2625526,"avatar_url":"https://avatars.githubusercontent.com/u/2625526?v=3","gravatar_id":"","url":"https://api.github.com/users/romainfrancois","html_url":"https://github.com/romainfrancois","followers_url":"https://api.github.com/users/romainfrancois/followers","following_url":"https://api.github.com/users/romainfrancois/following{/other_user}","gists_url":"https://api.github.com/users/romainfrancois/gists{/gist_id}","starred_url":"https://api.github.com/users/romainfrancois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/romainfrancois/subscriptions","organizations_url":"https://api.github.com/users/romainfrancois/orgs","repos_url":"https://api.github.com/users/romainfrancois/repos","events_url":"https://api.github.com/users/romainfrancois/events{/privacy}","received_events_url":"https://api.github.com/users/romainfrancois/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":5,"created_at":"2015-04-10T07:47:27Z","updated_at":"2015-04-10T10:28:11Z","closed_at":null,"body":"While looking at https://github.com/hadley/dplyr/issues/1062, I need up examining yet another time the whole exception business in Rcpp. I think there is a problem. \r\n\r\n```\r\n#include <Rcpp.h>\r\nusing namespace Rcpp ;\r\n\r\n// [[Rcpp::export]]\r\nvoid foo(){\r\n        stop( \"foo %s\", \"bar\" ) ;\r\n}\r\n\r\n/*** R\r\n        foo()\r\n\r\n*/\r\n```\r\n\r\nWhen I `sourceCpp` this through valgrind, I get: \r\n\r\n```\r\n> Rcpp::sourceCpp( \"/tmp/test.cpp\")\r\n\r\n> foo()\r\nError in eval(expr, envir, enclos) : foo bar\r\nCalls: <Anonymous> ... source -> withVisible -> eval -> eval -> foo -> <Anonymous>\r\nExecution halted\r\n==68583==\r\n==68583== HEAP SUMMARY:\r\n==68583==     in use at exit: 33,147,961 bytes in 15,982 blocks\r\n==68583==   total heap usage: 41,762 allocs, 25,780 frees, 67,956,199 bytes allocated\r\n==68583==\r\n==68583== 32 bytes in 1 blocks are possibly lost in loss record 21 of 1,770\r\n==68583==    at 0x4C2B0E0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\r\n==68583==    by 0xC9563B8: std::string::_Rep::_S_create(unsigned long, unsigned long, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)\r\n==68583==    by 0xC957AE0: char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)\r\n==68583==    by 0xC957EF7: std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)\r\n==68583==    by 0xD5BA785: Rcpp::exception::exception(char const*) (exceptions.h:31)\r\n==68583==    by 0xD5BC5F7: void Rcpp::stop<char [4]>(char const*, char const (&) [4]) (exceptions.h:256)\r\n==68583==    by 0xD5B9EA6: foo() (test.cpp:6)\r\n==68583==    by 0xD5B9ED9: sourceCpp_34287_foo (test.cpp:21)\r\n==68583==    by 0x4EC84F4: ??? (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F08055: Rf_eval (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F0B631: ??? (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F07F30: Rf_eval (in /usr/lib/R/lib/libR.so)\r\n==68583==\r\n==68583== 144 bytes in 1 blocks are possibly lost in loss record 37 of 1,770\r\n==68583==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)\r\n==68583==    by 0xC8F94E2: __cxa_allocate_exception (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19)\r\n==68583==    by 0xD5BC5D7: void Rcpp::stop<char [4]>(char const*, char const (&) [4]) (exceptions.h:256)\r\n==68583==    by 0xD5B9EA6: foo() (test.cpp:6)\r\n==68583==    by 0xD5B9ED9: sourceCpp_34287_foo (test.cpp:21)\r\n==68583==    by 0x4EC84F4: ??? (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F08055: Rf_eval (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F0B631: ??? (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F07F30: Rf_eval (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F09144: Rf_applyClosure (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F07C0D: Rf_eval (in /usr/lib/R/lib/libR.so)\r\n==68583==    by 0x4F0C4F3: ??? (in /usr/lib/R/lib/libR.so)\r\n==68583==\r\n==68583== LEAK SUMMARY:\r\n==68583==    definitely lost: 0 bytes in 0 blocks\r\n==68583==    indirectly lost: 0 bytes in 0 blocks\r\n==68583==      possibly lost: 176 bytes in 2 blocks\r\n==68583==    still reachable: 33,147,785 bytes in 15,980 blocks\r\n==68583==         suppressed: 0 bytes in 0 blocks\r\n==68583== Reachable blocks (those to which a pointer was found) are not shown.\r\n==68583== To see them, rerun with: --leak-check=full --show-leak-kinds=all\r\n==68583==\r\n==68583== For counts of detected and suppressed errors, rerun with: -v\r\n==68583== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)\r\n``` \r\n\r\nI believe that the problem is that the destructor for the exception is never called because of jumping. i.e. `stop` generates an `Rcpp::exception`: \r\n\r\n```\r\nclass exception : public std::exception {\r\n    public:\r\n        explicit exception(const std::string& message_) : message(message_){}\r\n        exception(const std::string& message_, const char* file, int line ) : message(message_){\r\n            rcpp_set_stack_trace( stack_trace(file,line) ) ;\r\n        }\r\n        virtual ~exception() throw(){}\r\n        virtual const char* what() const throw() {\r\n            return message.c_str() ;\r\n        }\r\n    private:\r\n        std::string message ;\r\n    } ;\r\n```\r\n\r\nthat is caught though `END_RCPP` : \r\n\r\n```\r\n#ifndef VOID_END_RCPP\r\n#define VOID_END_RCPP                                                          \\\r\n  }                                                                            \\\r\n  catch (Rcpp::internal::InterruptedException &__ex__) {                       \\\r\n    Rf_onintr();                                                               \\\r\n  }                                                                            \\\r\n  catch (std::exception &__ex__) {                                             \\\r\n    forward_exception_to_r(__ex__);                                            \\\r\n  }                                                                            \\\r\n  catch (...) {                                                                \\\r\n    ::Rf_error(\"c++ exception (unknown reason)\");                              \\\r\n  }\r\n#endif\r\n\r\n#ifndef END_RCPP\r\n#define END_RCPP VOID_END_RCPP return R_NilValue;\r\n#endif\r\n```\r\n\r\nand then in `forward_exception_to_r` we end up jumping with a callback to R's `stop`  : \r\n\r\n```\r\ninline void forward_exception_to_r( const std::exception& ex){\r\n    SEXP stop_sym  = Rf_install( \"stop\" ) ;\r\n    Rcpp::Shield<SEXP> condition( exception_to_r_condition(ex) );\r\n    Rcpp::Shield<SEXP> expr( Rf_lang2( stop_sym , condition ) ) ;\r\n    Rf_eval( expr, R_GlobalEnv ) ;\r\n}\r\n```\r\n\r\nThe problem here is that the exception is never destructed, and therefore the `std::string message` it holds is not destructed either, hence the complaints from valgrind. \r\n\r\nSee this simpler example: \r\n\r\n```\r\n#include <Rcpp.h>\r\nusing namespace Rcpp ;\r\n\r\n\r\nstruct dummy{\r\n        dummy(int value_) : value(value_){}\r\n\r\n        ~dummy(){\r\n                Rprintf( \"~dummy\" ) ;\r\n        }\r\n        int value ;\r\n} ;\r\n\r\nstruct dummy_exception : public std::exception {\r\n        dummy_exception(int value) : exception(), x(value) {}\r\n\r\n        ~dummy_exception() throw(){}\r\n\r\n        dummy x ;\r\n} ;\r\n\r\n\r\n// [[Rcpp::export]]\r\nvoid foo(){\r\n        throw dummy_exception(3) ;\r\n}\r\n\r\n/*** R\r\n        foo()\r\n\r\n*/\r\n```\r\n\r\nThe point here is that I want to see `~dummy`, and I don't: \r\n\r\n```\r\n$ R --vanilla < test.R\r\n\r\nR version 3.1.3 (2015-03-09) -- \"Smooth Sidewalk\"\r\nCopyright (C) 2015 The R Foundation for Statistical Computing\r\nPlatform: x86_64-pc-linux-gnu (64-bit)\r\n\r\nR is free software and comes with ABSOLUTELY NO WARRANTY.\r\nYou are welcome to redistribute it under certain conditions.\r\nType 'license()' or 'licence()' for distribution details.\r\n\r\n  Natural language support but running in an English locale\r\n\r\nR is a collaborative project with many contributors.\r\nType 'contributors()' for more information and\r\n'citation()' on how to cite R or R packages in publications.\r\n\r\nType 'demo()' for some demos, 'help()' for on-line help, or\r\n'help.start()' for an HTML browser interface to help.\r\nType 'q()' to quit R.\r\n\r\n> Rcpp::sourceCpp( \"/tmp/test.cpp\")\r\n\r\n> foo()\r\nError in eval(expr, envir, enclos) : std::exception\r\nCalls: <Anonymous> ... source -> withVisible -> eval -> eval -> foo -> <Anonymous>\r\nExecution halted\r\n```\r\n\r\nwhereas if I handle the try-catch manually without then calling R's `stop` the exception is properly destructed: \r\n\r\n```\r\n// [[Rcpp::export]]\r\nvoid foo(){\r\n        try{\r\n                throw dummy_exception(3) ;\r\n        } catch( const std::exception& ex ){\r\n\r\n        }\r\n}\r\n```\r\n\r\nwith this I get: \r\n\r\n```\r\n$ R --vanilla < test.R\r\n\r\nR version 3.1.3 (2015-03-09) -- \"Smooth Sidewalk\"\r\nCopyright (C) 2015 The R Foundation for Statistical Computing\r\nPlatform: x86_64-pc-linux-gnu (64-bit)\r\n\r\nR is free software and comes with ABSOLUTELY NO WARRANTY.\r\nYou are welcome to redistribute it under certain conditions.\r\nType 'license()' or 'licence()' for distribution details.\r\n\r\n  Natural language support but running in an English locale\r\n\r\nR is a collaborative project with many contributors.\r\nType 'contributors()' for more information and\r\n'citation()' on how to cite R or R packages in publications.\r\n\r\nType 'demo()' for some demos, 'help()' for on-line help, or\r\n'help.start()' for an HTML browser interface to help.\r\nType 'q()' to quit R.\r\n\r\n> Rcpp::sourceCpp( \"/tmp/test.cpp\")\r\n\r\n> foo()\r\n~dummy>\r\n>\r\n```\r\n\r\nNow leaking a `std::string` might not be a big deal, but it's definitely real. \r\n\r\n"},"comment":{"url":"https://api.github.com/repos/RcppCore/Rcpp/issues/comments/91508458","html_url":"https://github.com/RcppCore/Rcpp/issues/286#issuecomment-91508458","issue_url":"https://api.github.com/repos/RcppCore/Rcpp/issues/286","id":91508458,"user":{"login":"romainfrancois","id":2625526,"avatar_url":"https://avatars.githubusercontent.com/u/2625526?v=3","gravatar_id":"","url":"https://api.github.com/users/romainfrancois","html_url":"https://github.com/romainfrancois","followers_url":"https://api.github.com/users/romainfrancois/followers","following_url":"https://api.github.com/users/romainfrancois/following{/other_user}","gists_url":"https://api.github.com/users/romainfrancois/gists{/gist_id}","starred_url":"https://api.github.com/users/romainfrancois/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/romainfrancois/subscriptions","organizations_url":"https://api.github.com/users/romainfrancois/orgs","repos_url":"https://api.github.com/users/romainfrancois/repos","events_url":"https://api.github.com/users/romainfrancois/events{/privacy}","received_events_url":"https://api.github.com/users/romainfrancois/received_events","type":"User","site_admin":false},"created_at":"2015-04-10T10:28:11Z","updated_at":"2015-04-10T10:28:11Z","body":"Yep, I don't think what I see in this PR is the right way to handle the situation. \r\n\r\nI think the data should be sent back to the R side without jumping (i.e. by calling R's stop) and `stop` should be called on the R side. \r\n\r\nIn short, this needs changes in: \r\n - code generation from `Rcpp::export`, mostly on the R side\r\n - not call stop from `forward_exception_to_r` \r\n\r\nI guess the handling of `InterruptedException` with `Rf_onintr` would produce the same kind of leakage\r\n\r\n"}}