From 6cab64daba6d0c2a39a7c5fca8014da12ad5e3ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Thu, 26 Mar 2026 17:18:06 +0100 Subject: [PATCH 1/3] Return null start for empty vectors --- ChangeLog | 5 +++++ inst/include/Rcpp/internal/r_vector.h | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 24f115bb9..3a6208966 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2026-03-26 Iñaki Ucar + + * inst/include/Rcpp/internal/r_vector.h: Return null start for empty vectors + instead of an invalid pointer, which causes UB in e.g. std::copy + 2026-03-06 Dirk Eddelbuettel * DESCRIPTION (Version, Date): Roll micro version and date (twice) diff --git a/inst/include/Rcpp/internal/r_vector.h b/inst/include/Rcpp/internal/r_vector.h index 589c75fed..c6a76b6ce 100644 --- a/inst/include/Rcpp/internal/r_vector.h +++ b/inst/include/Rcpp/internal/r_vector.h @@ -3,7 +3,8 @@ // // r_vector.h: Rcpp R/C++ interface class library -- information about R vectors // -// Copyright (C) 2010 - 2017 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain François +// Copyright (C) 2026 Dirk Eddelbuettel, Romain François and Iñaki Ucar // // This file is part of Rcpp. // @@ -36,6 +37,8 @@ typename Rcpp::traits::storage_type::type* r_vector_start(SEXP x) { #define RCPP_VECTOR_START_IMPL(__RTYPE__, __ACCESSOR__) \ template <> \ inline typename Rcpp::traits::storage_type<__RTYPE__>::type* r_vector_start<__RTYPE__>(SEXP x) { \ + if (Rf_xlength(x) == 0) \ + return NULL; \ return __ACCESSOR__(x); \ } From 950d8f5cacc7e34fecfd5391c184ca6c80d7aad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Fri, 27 Mar 2026 10:35:35 +0100 Subject: [PATCH 2/3] add a couple of tests --- ChangeLog | 2 ++ inst/tinytest/cpp/Vector.cpp | 10 +++++++++- inst/tinytest/test_vector.R | 8 +++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 90137ca61..c18ea7531 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,8 @@ * inst/include/Rcpp/internal/r_vector.h: Return null start for empty vectors instead of an invalid pointer, which causes UB in e.g. std::copy + * inst/tinytest/test_vector.R: Add tests for std::copy + * inst/tinytest/cpp/Vector.cpp: Idem 2026-03-26 Dirk Eddelbuettel diff --git a/inst/tinytest/cpp/Vector.cpp b/inst/tinytest/cpp/Vector.cpp index 4fc26b7e8..c324aa300 100644 --- a/inst/tinytest/cpp/Vector.cpp +++ b/inst/tinytest/cpp/Vector.cpp @@ -2,7 +2,8 @@ // // Vector.cpp: Rcpp R/C++ interface class library -- Vector unit tests // -// Copyright (C) 2012 - 2018 Dirk Eddelbuettel and Romain Francois +// Copyright (C) 2012 - 2025 Dirk Eddelbuettel and Romain François +// Copyright (C) 2026 Dirk Eddelbuettel, Romain François and Iñaki Ucar // // This file is part of Rcpp. // @@ -896,3 +897,10 @@ double NumericVector_test_out_of_bounds_read(NumericVector v, R_xlen_t i) { SEXP CharacterVector_test_out_of_bounds_read(CharacterVector v, R_xlen_t i) { return v[i]; } + +// [[Rcpp::export]] +NumericVector vec_copy(NumericVector vec1) { + NumericVector vec2(vec1.size()); + std::copy(vec1.begin(), vec1.end(), vec2.begin()); + return vec2; +} diff --git a/inst/tinytest/test_vector.R b/inst/tinytest/test_vector.R index e8138e24e..a062fba23 100644 --- a/inst/tinytest/test_vector.R +++ b/inst/tinytest/test_vector.R @@ -1,5 +1,6 @@ -## Copyright (C) 2010 - 2019 Dirk Eddelbuettel and Romain Francois +## Copyright (C) 2010 - 2025 Dirk Eddelbuettel and Romain François +## Copyright (C) 2026 Dirk Eddelbuettel, Romain François and Iñaki Ucar ## ## This file is part of Rcpp. ## @@ -702,3 +703,8 @@ expect_true( !CharacterVector_test_equality_crosspolicy("foo", "bar") ) #expect_warning(CharacterVector_test_out_of_bounds_read(character(0), 0)) #expect_warning(CharacterVector_test_out_of_bounds_read(character(1), 1)) + +# https://github.com/RcppCore/Rcpp/issues/1461 +expect_equal(vec_copy(as.numeric(1:10)), as.numeric(1:10)) +expect_equal(vec_copy(numeric(0)), numeric(0)) + From 21aca124db6f7daa7a7a631edc2ae392e349ae0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?I=C3=B1aki=20=C3=9Acar?= Date: Fri, 27 Mar 2026 17:45:04 +0100 Subject: [PATCH 3/3] go back to using dataptr() --- ChangeLog | 4 ++-- inst/include/Rcpp/internal/r_vector.h | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index c18ea7531..9b74cc01d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,7 @@ 2026-03-26 Iñaki Ucar - * inst/include/Rcpp/internal/r_vector.h: Return null start for empty vectors - instead of an invalid pointer, which causes UB in e.g. std::copy + * inst/include/Rcpp/internal/r_vector.h: Use dataptr() again to avoid an + invalid pointer for empty vectors, which causes UB in e.g. std::copy * inst/tinytest/test_vector.R: Add tests for std::copy * inst/tinytest/cpp/Vector.cpp: Idem diff --git a/inst/include/Rcpp/internal/r_vector.h b/inst/include/Rcpp/internal/r_vector.h index c6a76b6ce..a50d57a0e 100644 --- a/inst/include/Rcpp/internal/r_vector.h +++ b/inst/include/Rcpp/internal/r_vector.h @@ -33,23 +33,6 @@ typename Rcpp::traits::storage_type::type* r_vector_start(SEXP x) { return reinterpret_cast(dataptr(x)); } -// add specializations to avoid use of dataptr -#define RCPP_VECTOR_START_IMPL(__RTYPE__, __ACCESSOR__) \ - template <> \ - inline typename Rcpp::traits::storage_type<__RTYPE__>::type* r_vector_start<__RTYPE__>(SEXP x) { \ - if (Rf_xlength(x) == 0) \ - return NULL; \ - return __ACCESSOR__(x); \ - } - -RCPP_VECTOR_START_IMPL(LGLSXP, LOGICAL); -RCPP_VECTOR_START_IMPL(INTSXP, INTEGER); -RCPP_VECTOR_START_IMPL(RAWSXP, RAW); -RCPP_VECTOR_START_IMPL(CPLXSXP, COMPLEX); -RCPP_VECTOR_START_IMPL(REALSXP, REAL); - -#undef RCPP_VECTOR_START_IMPL - /** * The value 0 statically casted to the appropriate type for * the given SEXP type