vmime icon indicating copy to clipboard operation
vmime copied to clipboard

*critical* bug

Open ctpo6 opened this issue 9 years ago • 5 comments

It's a really very dangerous bug in the core of library: vmime::object and vmime::net::folderAttributes classes have a flawed copy ctor. The same with operator=(). Because vmime::object is inherited from enable_shared_from_this, it's copy ctor operator=() MUST call inherited ones, but they don't, which leads to garbaged inner weak_ptr with unpredictable consequences. I had got a very nasty memleak in a daemon, not detectable even with valgrind. In my fork of vmime, which I'd made C++11 only, I defined copy ctor and operator=() of these classes as =default.

ctpo6 avatar Apr 04 '16 06:04 ctpo6

Hello!

I have addressed some issues with "enable_shared_from_this", and also moved it to the only classes where it is actually required (mainly the ones in the "net" namespace). I have also fixed some ambiguities about how some classes should be instantiated because of this (heap/stack).

Here is the related commit: b1c2d4b

vincent-richard avatar Apr 06 '16 06:04 vincent-richard

Vincent,

I'm test this commit and crash my service. This happens when I load my certificates

Before commit, this worked well

vmime::shared_ptrvmime::security::cert::X509Certificate CopyCert(vmime::shared_ptrvmime::security::cert::X509Certificate cert) { stringstream ss; vmime::utility::outputStreamAdapter out(ss); cert->write(out, vmime::security::cert::X509Certificate::FORMAT_PEM); vmime::utility::inputStreamAdapter in(ss); return vmime::security::cert::X509Certificate::import (in); }

** vmime::shared_ptrvmime::security::cert::defaultCertificateVerifier CCertificateHandler::getCertificateVerifier(const std::string& url) { vmime::shared_ptr<CustomCertificateVerifier> vrf = vmime::make_shared<CustomCertificateVerifier>(url);

boost::mutex::scoped_lock lock (m_mutex);

std::vector<vmime::shared_ptr<vmime::security::cert::X509Certificate> > certificados;   
for (unsigned int i = 0; i < m_certificates.size(); i++) {
    certificados.push_back(CopyCert(m_certificates[i]));
}
vrf->setX509RootCAs(certificados); <= THIS LINE =>
vrf->setX509TrustedCerts(certificados);
return vrf; 

} **

This is the core:

#0 0x0000000000440df6 in boost::detail::atomic_exchange_and_add (pw=0x22203a2274726f78, dv=-1) at /usr/local/include/boost-1_44/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:50 50 /usr/local/include/boost-1_44/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp: No such file or directory. in /usr/local/include/boost-1_44/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp (gdb) bt #0 0x0000000000440df6 in boost::detail::atomic_exchange_and_add (pw=0x22203a2274726f78, dv=-1) at /usr/local/include/boost-1_44/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:50 #1 0x0000000000440f05 in boost::detail::sp_counted_base::release (this=0x22203a2274726f70) at /usr/local/include/boost-1_44/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:143 #2 0x0000000000440f9f in ~shared_count (this=0x7f725c7755e8, in_chrg=) at /usr/local/include/boost-1_44/boost/smart_ptr/detail/shared_count.hpp:217 #3 0x000000000047158c in ~shared_ptr (this=0x7f725c7755e0, in_chrg=) at /usr/local/include/boost-1_44/boost/smart_ptr/shared_ptr.hpp:169 #4 0x0000000000472342 in boost::shared_ptrvmime::security::cert::X509Certificate::operator=(boost::shared_ptrvmime::security::cert::X509Certificate const&) () #5 0x000000000047518e in boost::shared_ptrvmime::security::cert::X509Certificate* std::copy_move<false, false, std::random_access_iterator_tag>::copy_m<boost::shared_ptr< vmime::security::cert::X509Certificate> const, boost::shared_ptrvmime::security::cert::X509Certificate>(boost::shared_ptrvmime::security::cert::X509Certificate const, boo st::shared_ptrvmime::security::cert::X509Certificate const, boost::shared_ptrvmime::security::cert::X509Certificate) () #6 0x00000000004749bb in boost::shared_ptrvmime::security::cert::X509Certificate std::copy_move_a<false, boost::shared_ptrvmime::security::cert::X509Certificate const, boost::shared_ptrvmime::security::cert::X509Certificate>(boost::shared_ptrvmime::security::cert::X509Certificate const, boost::shared_ptr<vmime::security::cert::X509Certif icate> const, boost::shared_ptrvmime::security::cert::X509Certificate_) () #7 0x0000000000473f67 in __gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vector<boost::shared_ptr<vmime::security::cert::X509Certi ficate>, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > > std::__copy_move_a2<false, __gnu_cxx::_normal_iterator<boost::shared_ptr<vmime::security ::cert::X509Certificate> const, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >, __gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::alloc atorboost::shared_ptr<vmime::security::cert::X509Certificate > > > >(__gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate const, std::vector boost::shared_ptr<vmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >, __gnu_cxx::_normal_iterator<boost::s hared_ptrvmime::security::cert::X509Certificate const, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocator<boost::shared_ptr<vmime::security ::cert::X509Certificate> > > >, __gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vector<boost::shared_ptr<vmime::security::cert::X50 9Certificate>, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >) () #8 0x0000000000473400 in __gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vector<boost::shared_ptr<vmime::security::cert::X509Certi ficate>, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > > std::copy<__gnu_cxx::_normal_iterator<boost::shared_ptr<vmime::security::cert::X509Certi ficate> const, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >, __gnu_cxx: :_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocator<boost::share d_ptrvmime::security::cert::X509Certificate > > > >(__gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate const, std::vector<boost::shared_pt rvmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >, __gnu_cxx::_normal_iterator<boost::shared_ptr<vmime:: security::cert::X509Certificate> const, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocator<boost::shared_ptr<vmime::security::cert::X509Certi ficate> > > >, __gnu_cxx::_normal_iteratorboost::shared_ptr<vmime::security::cert::X509Certificate, std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, st d::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > >) () #9 0x000000000047260b in std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > >::op erator=(std::vectorboost::shared_ptr<vmime::security::cert::X509Certificate, std::allocatorboost::shared_ptr<vmime::security::cert::X509Certificate > > const&) () #10 0x00000000004c4607 in vmime::security::cert::defaultCertificateVerifier::setX509RootCAs (this=0x13cccc0, caCerts=...) at /root/integration-build/sources/emailproxy/src/libvmime-master/src/vmime/security/cert/defaultCertificateVerifier.cpp:162 #11 0x0000000000470eb0 in CCertificateHandler::getCertificateVerifier (this=0x135fa10, url=...) at ../src/CertificateHandler.cpp:151 #12 0x0000000000443343 in CAccount::GetStore (this=0x13cbad0) at ../src/Account.cpp:311 #13 0x0000000000449268 in CAccount::CheckNewMail (this=0x13cbad0) at ../src/Account.cpp:729 #14 0x000000000047f271 in CEMailController::AsyncCheckNewMail (this=0x1358d90, wkptr=...) at ../src/EMailController.cpp:66 #15 0x0000000000489acf in boost::_mfi::mf1<void, CEMailController, boost::weak_ptr<CAccount> >::operator() (this=0x13c9c90, p=0x1358d90, a1=...) at /usr/local/include/boost-1_44/boost/bind/mem_fn_template.hpp:165 #16 0x0000000000489359 in boost::_bi::list2<boost::bi::value<CEMailController>, boost::_bi::valueboost::weak_ptr<CAccount > >::operator()<boost::_mfi::mf1<void, CEMailContro ller, boost::weak_ptr<CAccount> >, boost::_bi::list0> (this=0x13c9ca0, f=..., a=...) at /usr/local/include/boost-1_44/boost/bind/bind.hpp:313 #17 0x0000000000487de7 in boost::_bi::bind_t<void, boost::_mfi::mf1<void, CEMailController, boost::weak_ptr<CAccount> >, boost::_bi::list2boost::_bi::value<CEMailController*, boost::_bi::valueboost::weak_ptr<CAccount > > >::operator() (this=0x13c9c90) at /usr/local/include/boost-1_44/boost/bind/bind_template.hpp:20 #18 0x0000000000485f69 in boost::detail::function::void_function_obj_invoker0<boost::_bi::bind_t<void, boost::_mfi::mf1<void, CEMailController, boost::weak_ptr<CAccount> >, boos t::_bi::list2boost::_bi::value<CEMailController*, boost::_bi::valueboost::weak_ptr<CAccount > > >, void>::invoke (function_obj_ptr=...) at /usr/local/include/boost-1_44/boost/function/function_template.hpp:153 #19 0x00000000004660f1 in boost::function0::operator() (this=0x7f725c7766b0) at /usr/local/include/boost-1_44/boost/function/function_template.hpp:1013 #20 0x00000000004607cb in CAsyncCaller::Execute (this=0x1358e70, function=..., timer=...) at ../src/AsyncCaller.cpp:28

7i77an avatar Apr 06 '16 20:04 7i77an

Hello! I'm currently investigating on this issue...

vincent-richard avatar Apr 10 '16 18:04 vincent-richard

I'm not able to reproduce your issue with the latest VMime code from today. Maybe there is some other code of your own that is involved in the problem?

As far as I understand, the crash occurs when copying a std::vector of shared_ptr <X509Certificate>, that's what I tried to reproduce.

Here is the test program I used (also ran it through gdb and Valgrind):

#include <iostream>
#include <fstream>

#include "vmime/vmime.hpp"
#include "vmime/platforms/posix/posixHandler.hpp"

vmime::shared_ptr <vmime::security::cert::X509Certificate> CopyCert(
    vmime::shared_ptr <vmime::security::cert::X509Certificate> cert
) {

    std::stringstream ss;
    vmime::utility::outputStreamAdapter out(ss);
    cert->write(out, vmime::security::cert::X509Certificate::FORMAT_PEM);
    vmime::utility::inputStreamAdapter in(ss);
    return vmime::security::cert::X509Certificate::import(in);
}

int main() {

    const char *certs[] = {
        // Put paths to any certificates here...
        "/usr/share/ca-certificates/mozilla/QuoVadis_Root_CA_3.crt",
        "/usr/share/ca-certificates/mozilla/NetLock_Notary_=Class_A=_Root.crt",
        "/usr/share/ca-certificates/mozilla/SwissSign_Silver_CA_-_G2.crt",
        "/usr/share/ca-certificates/mozilla/TWCA_Root_Certification_Authority.crt",
    };

    // Test adding certificates
    std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > certificates;

    for (unsigned i = 0 ; i < sizeof(certs) / sizeof(certs[0]) ; ++i) {

        std::ifstream is(certs[i]);
        vmime::utility::inputStreamAdapter vis(is);

        vmime::shared_ptr <vmime::security::cert::X509Certificate> cert =
             vmime::security::cert::X509Certificate::import(vis);

        certificates.push_back(CopyCert(cert));
    }

    vmime::shared_ptr <vmime::security::cert::defaultCertificateVerifier> vrf =
        vmime::make_shared <vmime::security::cert::defaultCertificateVerifier>();

    vrf->setX509RootCAs(certificates);
    vrf->setX509TrustedCerts(certificates);

    // Test copying vector
    std::vector <vmime::shared_ptr <vmime::security::cert::X509Certificate> > certificatesCopy = certificates;
    std::cout << "count = " << certificatesCopy.size() << std::endl;

    // Test using object
    std::vector <vmime::shared_ptr <vmime::security::cert::certificate> > certificates2;

    for (unsigned i = 0 ; i < certificates.size() ; ++i) {
        certificates2.push_back(certificates[i]);
    }

    vmime::shared_ptr <vmime::security::cert::certificateChain> chain =
        vmime::make_shared <vmime::security::cert::certificateChain>(certificates2);

    try {
        vrf->verify(chain, "test.com");
    } catch (vmime::exception &e) {
        // verify() will fail
    }

    return 0;
}

vincent-richard avatar Apr 10 '16 18:04 vincent-richard

Mmmm ok Vincent, I will review again.

7i77an avatar Apr 13 '16 00:04 7i77an