[Rcpp-devel] Passing pointers to objects between libs results in weird UBSAN warning

Dirk Eddelbuettel edd at debian.org
Mon Aug 20 16:01:31 CEST 2018


On 20 August 2018 at 15:35, Iñaki Ucar wrote:
| El lun., 20 ago. 2018 a las 9:01, Jan van der Laan
| (<rhelp at eoos.dds.nl>) escribió:
| >
| >
| > One of my packges gives a message with the undefined behaviour sanitiser
| > (UBSAN). I have difficulty tracking down/understanding the cause of this
| > message. However, I have been able to create a small example that
| > generates the same warning. The mail below is a bit on the long side,
| > and, I hope it is readable as it mixes quite a lot code with text. I am
| > sorry for that. A probably better readable version can be found at
| > https://github.com/djvanderlaan/lvec_ubsan where also the complete
| > source code and the docker image used for testing can be found.
| >
| > My example consists of two packages: pkg1 and pkg2. The package pkg2
| > links to pkg1. Package pkg1 defines a template class `FooBar<T>` which
| > inherits from virtual class `Foo`. Pointers to the these objects are
| > passed back to R using a `Rcpp:XPtr` object:
| >
| >      ========== pkg1/src/construct.cpp ================
| >      #include "../inst/include/pkg1.h"
| >
| >      // [[Rcpp::export]]
| >      RcppExport SEXP new_foobar(int i) {
| >        BEGIN_RCPP
| >        FooBar<int>* x = new FooBar<int>(i);
| >        return Rcpp::XPtr<Foo>(x, true);
| >        END_RCPP
| >      }
| 
| Note that RcppExport SEXP should be just SEXP. BEGIN_RCPP and END_RCPP
| should be dropped too.

+1

Or else don't use [[Rcpp::export]] and Rcpp::compileAttributes().

Dirk
 
| >      // [[Rcpp::export]]
| >      RcppExport SEXP new_foobard(double i) {
| >        BEGIN_RCPP
| >        FooBar<double>* x = new FooBar<double>(i);
| >        return Rcpp::XPtr<Foo>(x, true);
| >        END_RCPP
| >      }
| >      ========== END pkg1/src/construct.cpp ================
| >
| > All headers of pkg1 are `inst/include`; these are included by `pkg2`:
| >
| >      ========== pkg1/inst/include/pkg1.h ================
| >      #include <Rcpp.h>
| >
| >      // ==== VISITOR
| >      template<typename T> class FooBar;
| >
| >      class FooBarVisitor {
| >        public:
| >          virtual void visit(FooBar<double>& vec) = 0;
| >          virtual void visit(FooBar<int>& vec) = 0;
| >      };
| >
| >      // ==== FOO
| >      class Foo {
| >        public:
| >          Foo() {};
| >          virtual ~Foo() {};
| >
| >          virtual void visit(FooBarVisitor* visitor) = 0;
| >      };
| >
| >      // ==== FOOBAR
| >      template<typename T>
| >      class FooBar : public Foo {
| >        public:
| >          FooBar(T i = 0) : Foo(), i_(i) {};
| >          ~FooBar() {};
| >
| >          T i() const { return i_;}
| >          void i(T i) { i_ = i;}
| >
| >          void visit(FooBarVisitor* visitor) {
| >            visitor->visit(*this);
| >          }
| >        private:
| >          T i_;
| >      };
| >      #endif
| >      ========== END pkg1/inst/include/pkg1.h ================
| >
| > The XPtr is passed to c++ code in pkg2 and methods of the Foo object are
| > called:
| >
| >      ========== pkg2/src/construct.cpp ================
| >      #include <Rcpp.h>
| >      #include <pkg1.h>
| >
| >      // [[Rcpp::export]]
| >      RcppExport int get_foobar(SEXP xp) {
| >        Rcpp::XPtr<Foo> x(xp);
| >        Foo* y1 = x;
| >        FooBar<int>* y2 = dynamic_cast<FooBar<int>*>(y1);
| >        return y2->i();
| >      }
| >      ========== END pkg2/src/construct.cpp ================
| >
| > This generates the following UBSAN messages:
| >
| >
| >      ========== WARNING MESSAGES ================
| >      Testing pkg2
| >      <U+221A> | OK F W S | Context
| >      / |  0       | fooconstruct.cpp:9:21: runtime error: <null> address
| > 0x000003c21810 which does not point to an object of type 'Foo'
| >      0x000003c21810: note: object is of type 'FooBar<int>'
| >       00 00 00 00  58 1c 79 fd 48 7f 00 00  0a 00 00 00 00 00 00 00  20
| > 00 00 00 00 00 00 00  01 03 00 00
| >                    ^~~~~~~~~~~~~~~~~~~~~~~
| >                    vptr for 'FooBar<int>'
| >      construct.cpp:10:14: runtime error: member call on address
| > 0x000003c21810 which does not point to an object of type 'FooBar<int>'
| >      0x000003c21810: note: object is of type 'FooBar<int>'
| >       00 00 00 00  58 1c 79 fd 48 7f 00 00  0a 00 00 00 00 00 00 00  20
| > 00 00 00 00 00 00 00  01 03 00 00
| >                    ^~~~~~~~~~~~~~~~~~~~~~~
| >                    vptr for 'FooBar<int>'
| >      /usr/local/lib/R/library/pkg1/include/pkg1.h:33:26: runtime error:
| > member access within address 0x000003c21810 which does not point to an
| > object of type 'const FooBar<int>'
| >      0x000003c21810: note: object is of type 'FooBar<int>'
| >       00 00 00 00  58 1c 79 fd 48 7f 00 00  0a 00 00 00 00 00 00 00  20
| > 00 00 00 00 00 00 00  01 03 00 00
| >                    ^~~~~~~~~~~~~~~~~~~~~~~
| >                    vptr for 'FooBar<int>'
| >      <U+221A> |  1       | foo
| >      ========== END WARNING MESSAGES ================
| >
| >
| > I have had real problem reproducing this error. Also searching for the
| > error give little clues as to what the problem is. The following message
| > on the Rcpp-devel list seems somewhat similar:
| > http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2017-July/009656.html.
| > I thought that it might be caused by the fact that I am passing pointers
| > to template classes between libraries. I also couldn't find much about
| > that. I did find some messages on passing pointers to STL objects
| > between libraries. That should be ok, as long as the same compiler is
| > used for both libraries. Although that might technically still make it
| > undefined behaviour.
| 
| pkg2 compiles a shared library using pkg1's headers, but it does not
| link against libpkg1.so (in general, this is not possible in R). So
| both libpkg2.so and libpkg1.so define two copies of the same types,
| Foo and FooBar. pkg2 allocates FooBar from libpkg1.so and then
| dynamically casts it as FooBar from libpkg2.so. This is why clang
| complains. It has to do with how RTTI comparisons are done across
| dynamic objects (see, e.g.,
| https://whatofhow.wordpress.com/2015/03/17/odr-rtti-dso/).
| 
| I'd say that these errors can be safely ignored, because types are
| exactly the same. Anyway, shouldn't it be easier to export get_foobar
| in pkg1?

The only thought I had so far (and I had no time to play with the code) is to
maybe wrap Rcpp::Shield<> around the XPtr object.

Dirk

| 
| Iñaki
| 
| >
| > So, help!?
| >
| > Jan
| >
| >
| >
| >
| >
| > _______________________________________________
| > Rcpp-devel mailing list
| > Rcpp-devel at lists.r-forge.r-project.org
| > https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
| 
| 
| 
| -- 
| Iñaki Ucar
| _______________________________________________
| Rcpp-devel mailing list
| Rcpp-devel at lists.r-forge.r-project.org
| https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
-- 
http://dirk.eddelbuettel.com | @eddelbuettel | edd at debian.org


More information about the Rcpp-devel mailing list