Asan container-overflow

We are having a problem that seems to be unrelated to all the other problems we are having. When executing a toy model with StanHeaders 2.21.x on Linux with clang++-10, we are getting

https://www.stats.ox.ac.uk/pub/bdr/memtests/clang-ASAN/StanHeaders/00check.log

==437132==ERROR: AddressSanitizer: container-overflow on address 0x60800000b8e8 at pc 0x0000004a8eda bp 0x7fff4fa94a90 sp 0x7fff4fa94258
READ of size 24 at 0x60800000b8e8 thread T0
#0 0x4a8ed9 in __asan_memcpy /data/gannet/ripley/Sources2/LLVM/10.0.0/llvm-10.0.0.src/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3
#1 0x7f949b17d9cb in std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >::basic_string(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/string:1870:7
#2 0x7f949b17d9cb in void std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >::construct<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/memory:1876:31
#3 0x7f949b17d6cd in void std::__1::allocator_traits<std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::__construct_backward_with_exception_guarantees<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >
>(std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >&, std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&) /usr/local/bin/…/include/c++/v1/memory:1724:15
#4 0x7f949b17c5d9 in std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::__swap_out_circular_buffer(std::__1::__split_buffer<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >&>&) /usr/local/bin/…/include/c++/v1/vector:952:5
#5 0x7f949b17e157 in void std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::__push_back_slow_path<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/vector:1627:5
#6 0x7f949b2a6103 in std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::push_back(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/vector:1655:9
#7 0x7f949b2a6103 in stan::mcmc::base_nuts<stan::model::model_base, stan::mcmc::diag_e_metric, stan::mcmc::expl_leapfrog, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> > >::get_sampler_param_names(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/mcmc/hmc/nuts/base_nuts.hpp:210:11
#8 0x7f949b2834b6 in void stan::services::util::mcmc_writer::write_sample_namesstan::model::model_base(stan::mcmc::sample&, stan::mcmc::base_mcmc&, stan::model::model_base&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/services/util/mcmc_writer.hpp:72:13
#9 0x7f949b2aaef9 in void stan::services::util::run_adaptive_sampler<stan::mcmc::adapt_diag_e_nuts<stan::model::model_base, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> > >, stan::model::model_base, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> > >(stan::mcmc::adapt_diag_e_nuts<stan::model::model_base, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> > >&, stan::model::model_base&, std::__1::vector<double, std::__1::allocator >&, int, int, int, int, bool, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> >&, stan::callbacks::interrupt&, stan::callbacks::logger&, stan::callbacks::writer&, stan::callbacks::writer&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/services/util/run_adaptive_sampler.hpp:63:10
#10 0x7f949b2aa5ce in int stan::services::sample::hmc_nuts_diag_e_adaptstan::model::model_base(stan::model::model_base&, stan::io::var_context&, stan::io::var_context&, unsigned int, unsigned int, double, int, int, int, bool, int, double, double, int, double, double, double, double, unsigned int, unsigned int, unsigned int, stan::callbacks::interrupt&, stan::callbacks::logger&, stan::callbacks::writer&, stan::callbacks::writer&, stan::callbacks::writer&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/services/sample/hmc_nuts_diag_e_adapt.hpp:98:3
#11 0x7f949b245676 in int stan::services::sample::hmc_nuts_diag_e_adaptstan::model::model_base(stan::model::model_base&, stan::io::var_context&, unsigned int, unsigned int, double, int, int, int, bool, int, double, double, int, double, double, double, double, unsigned int, unsigned int, unsigned int, stan::callbacks::interrupt&, stan::callbacks::logger&, stan::callbacks::writer&, stan::callbacks::writer&, stan::callbacks::writer&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/services/sample/hmc_nuts_diag_e_adapt.hpp:150:10
#12 0x7f949b245676 in rstan::(anonymous namespace)::command(rstan::stan_args&, stan::model::model_base
, Rcpp::Vector<19, Rcpp::PreserveStorage>&, std::__1::vector<unsigned long, std::__1::allocator > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > > const&, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> >&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/stan_fit.cpp:654:25
#13 0x7f949b243298 in rstan::stan_fit::call_sampler(Rcpp::Vector<19, Rcpp::PreserveStorage>) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/stan_fit.cpp:1137:15
#14 0x7f948b89e8fb in rstan::stan_fit_proxy::call_sampler(Rcpp::Vector<19, Rcpp::PreserveStorage>) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/stan_fit_rccp.cpp:53:17
#15 0x7f948b8a046d in Rcpp::CppMethod1<rstan::stan_fit_proxy, Rcpp::Vector<19, Rcpp::PreserveStorage>, Rcpp::Vector<19, Rcpp::PreserveStorage> >::operator()(rstan::stan_fit_proxy*, SEXPREC**) /data/gannet/ripley/R/test-clang/Rcpp/include/Rcpp/module/Module_generated_CppMethod.h:111:55

0x60800000b8e8 is located 72 bytes inside of 96-byte region [0x60800000b8a0,0x60800000b900)
allocated by thread T0 here:
#0 0x4d921d in operator new(unsigned long) /data/gannet/ripley/Sources2/LLVM/10.0.0/llvm-10.0.0.src/projects/compiler-rt/lib/asan/asan_new_delete.cpp:99:3
#1 0x7f949b17bebb in std::__1::__libcpp_allocate(unsigned long, unsigned long) /usr/local/bin/…/include/c++/v1/new:253:10
#2 0x7f949b17bebb in std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >::allocate(unsigned long, void const*) /usr/local/bin/…/include/c++/v1/memory:1864:37
#3 0x7f949b17bebb in std::__1::allocator_traits<std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::allocate(std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >&, unsigned long) /usr/local/bin/…/include/c++/v1/memory:1581:21
#4 0x7f949b17bebb in std::__1::__split_buffer<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >&>::__split_buffer(unsigned long, unsigned long, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >&) /usr/local/bin/…/include/c++/v1/__split_buffer:318:29
#5 0x7f949b17e0d0 in void std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::__push_back_slow_path<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > >(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/vector:1623:49
#6 0x7f949b2a6025 in std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >::push_back(std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >&&) /usr/local/bin/…/include/c++/v1/vector:1655:9
#7 0x7f949b2a6025 in stan::mcmc::base_nuts<stan::model::model_base, stan::mcmc::diag_e_metric, stan::mcmc::expl_leapfrog, boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> > >::get_sampler_param_names(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits, std::__1::allocator > > >&) /tmp/RtmpsDMsn6/R.INSTALL19d602a7612d3/rstan/src/./stan/mcmc/hmc/nuts/base_nuts.hpp:208:11
HINT: if you don’t care about these errors you may set ASAN_OPTIONS=detect_container_overflow=0.
If you suspect a false positive see also: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow.
SUMMARY: AddressSanitizer: container-overflow /data/gannet/ripley/Sources2/LLVM/10.0.0/llvm-10.0.0.src/projects/compiler-rt/lib/asan/asan_interceptors_memintrinsics.cpp:22:3 in __asan_memcpy
Shadow bytes around the buggy address:
0x0c107fff96c0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c107fff96d0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c107fff96e0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c107fff96f0: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c107fff9700: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa =>0x0c107fff9710: fa fa fa fa 00 00 00 00 00 00 00 00 00[fc]fc fc 0x0c107fff9720: fa fa fa fa fd fd fd fd fd fd fd fd fd fd fd fd 0x0c107fff9730: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c107fff9740: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c107fff9750: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x0c107fff9760: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==437132==ABORTING

If you, like me, were wondering WTF are all these std::__1::basic_string things are doing in the backtrace, it appears to be because line 208 of stan/mcmc/hmc/nuts/base_nuts.hpp was (in the 2.21 release) in

  void get_sampler_param_names(std::vector<std::string>& names) {
    names.push_back("stepsize__");
    names.push_back("treedepth__");
    names.push_back("n_leapfrog__");
    names.push_back("divergent__");
    names.push_back("energy__");
  }

Argh… these things are a bummer. I can’t see anything wrong with the code you bring up. Why would anything go wrong with these lines is beyond me. Also… did anything change in this spot between 2.19 and 2.21??

I can look at this in the afternoon if it isn’t solved.

If I wanted to try to reproduce this in cmdstan these are the flags for the sanitizer -fsanitize=address,undefined -fno-sanitize=float-divide-by-zero -fno-sanitize=alignment -fno-omit-frame-pointer -frtti ?

Is this a particular model? Or just the bernoulli example?

A lot of things changed between 2.19 and 2.21 but it could also be due to checking with clang++-10 instead of an earlier version of clang++. I don’t think it would have anything to do with the particularities of the model if it is triggered by

  void get_sampler_param_names(std::vector<std::string>& names) {
    names.push_back("stepsize__");
    names.push_back("treedepth__");
    names.push_back("n_leapfrog__");
    names.push_back("divergent__");
    names.push_back("energy__");
  }

This is reproducible in cmdstan with make/local:

CXX=clang++-10
CC=clang-10
O=g
CXXFLAGS=-fsanitize=address
LDFLAGS=-fsanitize=address

And model:

parameters {
  real x;
}

model {
  x ~ normal(1, 1);
}

Oh wait actually maybe not these look like memory leak warnings.

Alright nevermind, it didn’t reproduce in cmdstan.

How do I get a copy of the right Stanheaders to test? I’m trying to clone rstan from repo but getting strange errors:

bbales2@frog:~/tmp$ git clone --recursive https://github.com/stan-dev/rstan.git
Cloning into 'rstan'...
remote: Enumerating objects: 428, done.
remote: Counting objects: 100% (428/428), done.
remote: Compressing objects: 100% (253/253), done.
remote: Total 40555 (delta 287), reused 253 (delta 172), pack-reused 40127
Receiving objects: 100% (40555/40555), 172.09 MiB | 69.96 MiB/s, done.
Resolving deltas: 100% (19621/19621), done.
Submodule 'StanHeaders/inst/include/mathlib' (https://github.com/stan-dev/math.git) registered for path 'StanHeaders/inst/include/mathlib'
Submodule 'stan' (https://github.com/stan-dev/stan.git) registered for path 'StanHeaders/inst/include/upstream'
Cloning into '/home/bbales2/tmp/rstan/StanHeaders/inst/include/mathlib'...
remote: Enumerating objects: 3302, done.        
remote: Counting objects: 100% (3302/3302), done.        
remote: Compressing objects: 100% (1419/1419), done.        
remote: Total 499025 (delta 2475), reused 2641 (delta 1883), pack-reused 495723        
Receiving objects: 100% (499025/499025), 454.71 MiB | 52.80 MiB/s, done.
Resolving deltas: 100% (357627/357627), done.
Cloning into '/home/bbales2/tmp/rstan/StanHeaders/inst/include/upstream'...
remote: Enumerating objects: 94, done.        
remote: Counting objects: 100% (94/94), done.        
remote: Compressing objects: 100% (68/68), done.        
remote: Total 233598 (delta 42), reused 59 (delta 22), pack-reused 233504        
Receiving objects: 100% (233598/233598), 296.20 MiB | 56.70 MiB/s, done.
Resolving deltas: 100% (164682/164682), done.
fatal: remote error: upload-pack: not our ref 520cdec64c004d19d64b01edafe44379977d1a5e
fatal: The remote end hung up unexpectedly
Fetched in submodule path 'StanHeaders/inst/include/mathlib', but it did not contain 520cdec64c004d19d64b01edafe44379977d1a5e. Direct fetching of that commit failed.
b

Just gonna try the StanHeaders on cran but I’m assuming that one passed the tests?

You have to clone StanHeaders and then go into both the inst/include/mathlib and inst/include/upstream subrepos and make sure both are on the “StanHeaders_2.21” branch. One is for Stan Math and the other is for Stan Library and they are different even though they have the same branch name and are necessary to fix various incompatibilities but neither changed base_nuts.hpp .

Alright I’m set up but I need some more pointers on how to probe this.

I can do:

R CMD build StanHeaders

And things fail to build but for different reasons:

ar -rs ../lib/libStanHeaders.a cvodes/cvodes_spils.o cvodes/cvodes_io.o cvodes/cvodes_bbdpre.o cvodes/cvodes_nls_stg1.o cvodes/cvodes.o cvodes/cvodes_nls_sim.o cvodes/cvodes_diag.o cvodes/cvodes_ls.o cvodes/cvodes_direct.o cvodes/cvodes_nls.o cvodes/cvodes_nls_stg.o cvodes/cvodea.o cvodes/cvodea_io.o cvodes/cvodes_bandpre.o sundials/sundials_matrix.o sundials/sundials_band.o sundials/sundials_math.o sundials/sundials_linearsolver.o sundials/sundials_nvector.o sundials/sundials_direct.o sundials/sundials_dense.o sundials/sundials_version.o sundials/sundials_iterative.o sundials/sundials_nvector_senswrapper.o sundials/sundials_nonlinearsolver.o sunmatrix/band/sunmatrix_band.o sunmatrix/dense/sunmatrix_dense.o sunlinsol/band/sunlinsol_band.o sunlinsol/dense/sunlinsol_dense.o sunnonlinsol/newton/sunnonlinsol_newton.o sunnonlinsol/fixedpoint/sunnonlinsol_fixedpoint.o idas/idas_io.o idas/idas_nls_sim.o idas/idas_ls.o idas/idas_ic.o idas/idaa_io.o idas/idas_direct.o idas/idas_spils.o idas/idas_nls_stg.o idas/idas.o idas/idas_bbdpre.o idas/idas_nls.o idas/idaa.o nvector/serial/nvector_serial.o
ar: creating ../lib/libStanHeaders.a

=================================================================
==1594==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 450 byte(s) in 45 object(s) allocated from:
    #0 0x7fb1f10dfea4 in strdup /build/llvm-toolchain-9-uSl4bC/llvm-toolchain-9-9/projects/compiler-rt/lib/asan/asan_interceptors.cc:446:3
    #1 0x56397d0acac8 in xstrdup (/usr/bin/make+0x1bac8)

Direct leak of 240 byte(s) in 5 object(s) allocated from:
    #0 0x7fb1f10f40fd in malloc /build/llvm-toolchain-9-uSl4bC/llvm-toolchain-9-9/projects/compiler-rt/lib/asan/asan_malloc_linux.cc:145:3
    #1 0x56397d0ac8a4 in xmalloc (/usr/bin/make+0x1b8a4)

Indirect leak of 168 byte(s) in 5 object(s) allocated from:
    #0 0x7fb1f10dfea4 in strdup /build/llvm-toolchain-9-uSl4bC/llvm-toolchain-9-9/projects/compiler-rt/lib/asan/asan_interceptors.cc:446:3
    #1 0x56397d0acac8 in xstrdup (/usr/bin/make+0x1bac8)

SUMMARY: AddressSanitizer: 858 byte(s) leaked in 55 allocation(s).
ERROR: compilation failed for package ‘StanHeaders’
* removing ‘/tmp/RtmpUtGr4Z/Rinst5dd6dca065f/StanHeaders’

Edit: Was using the clang+±9 sanitizer. Now on the clang+±10 sanitizer with similar output.

I can’t remember the last time Stan built without memory issues. I think you can do something like

export LSAN_OPTIONS=detect_leaks=0
R CMD build StanHeaders
export LSAN_OPTIONS=detect_leaks=1

and then hopefully get at the runtime memory issues. It occurred to me it might have something to do with our tendency to pass a zero-length std::vector and then push.back each name. Possibly the thing is not always initialized safely when we pass it in.

If I turn off the leak detect like you do, the package builds, and the checks pass. If I turn leak checks on, things fail with leak errors.

Presumably I should have gotten out of bounds errors even with the leak checks off?

I don’t feel like I’m getting very far debugging this. Can do share screen debugging if that would be useful.

So, I spend my day on this and have not really made progress, but here are some more pieces. I did setup with some pain a fresh docker image based on this: https://github.com/rocker-org/r-devel-san-clang … I did bump the clang to version 10 manually.

Having that installed, I was able to install everything (after sorting out more hiccups) and get the test running. The results was that there were memory leaks, but these were only in V8 related libraries, not in stan (the sampling actually finished). To get rid of the V8 issues, I installed rstan 2.19.3 (while having StanHeaders 2.21.0-5 on disk). Then the test.R script just finished fine.

I then did some google search on this and found this r-package:

The respective package is the bts package if I recall right.

The commit fixing presumable the ASAN stuff is this: https://github.com/steve-the-bayesian/BOOM/commit/8f701426288a7ede6a3ae990df2231481185cd87

From the comment of the Git issue the solution is to use move explicitly. This would probably mean to swap the push_back with emplace_back.

However, there is also a speculation that this whole thing is actually a false positive, which happens whenever only parts of the code are build with the sanitiser stuff. See here: https://github.com/google/sanitizers/wiki/AddressSanitizerContainerOverflow#false-positives

This can easily happen if rstan was not installed with the sanitiser stuff and then these tests are run (actually I should try this). This gives you a mixed binary and as a result presumably this false error.

I hope this helps a bit…

Sebastian

EDIT: I tried to install rstan without the sanitiser thing, then switching on the sanitiser for running the test… but Stan sampled just fine.

So if we want to do “something”, we can turn push_back into emplace_back and hope that this circumvents the problem.

3 Likes

If we are pretty sure it is a false positive, I would rather remove the test for now and then we can change push_back into emplace_back in a regular PR that can be merged whenever.

1 Like