compilerplugins: add OWeakObject::release() override check

Change-Id: I767857545d7c91615cf162790c04f0016de9fdf6
Reviewed-on: https://gerrit.libreoffice.org/26555
Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Tested-by: Stephan Bergmann <sbergman@redhat.com>
diff --git a/basic/source/classes/sbxmod.cxx b/basic/source/classes/sbxmod.cxx
index ef6a679..7970f04 100644
--- a/basic/source/classes/sbxmod.cxx
+++ b/basic/source/classes/sbxmod.cxx
@@ -175,21 +175,14 @@ DocObjectWrapper::DocObjectWrapper( SbModule* pVar ) : m_pMod( pVar ), mName( pV
void SAL_CALL
DocObjectWrapper::acquire() throw ()
{
    osl_atomic_increment( &m_refCount );
    OWeakObject::acquire();
    SAL_INFO("basic","DocObjectWrapper::acquire("<< OUStringToOString( mName, RTL_TEXTENCODING_UTF8 ).getStr() << ") 0x" << this << " refcount is now " << m_refCount );
}
void SAL_CALL
DocObjectWrapper::release() throw ()
{
    if ( osl_atomic_decrement( &m_refCount ) == 0 )
    {
        SAL_INFO("basic","DocObjectWrapper::release("<< OUStringToOString( mName, RTL_TEXTENCODING_UTF8 ).getStr() << ") 0x" << this << " refcount is now " << m_refCount );
        delete this;
    }
    else
    {
        SAL_INFO("basic","DocObjectWrapper::release("<< OUStringToOString( mName, RTL_TEXTENCODING_UTF8 ).getStr() << ") 0x" << this << " refcount is now " << m_refCount );
    }
    SAL_INFO("basic","DocObjectWrapper::release("<< OUStringToOString( mName, RTL_TEXTENCODING_UTF8 ).getStr() << ") 0x" << this << " decrementing refcount, was " << m_refCount );
    OWeakObject::release();
}

DocObjectWrapper::~DocObjectWrapper()
diff --git a/compilerplugins/clang/weakobject.cxx b/compilerplugins/clang/weakobject.cxx
new file mode 100644
index 0000000..42bdf6e
--- /dev/null
+++ b/compilerplugins/clang/weakobject.cxx
@@ -0,0 +1,154 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "plugin.hxx"
#include "typecheck.hxx"

/* OWeakObject::release() disposes weak references.  If that isn't done
 * because a sub-class improperly overrides release() then
 * OWeakConnectionPoint::m_pObject continues to point to the deleted object
 * and that could result in use-after-free.
 */

namespace {

class WeakObject
    : public clang::RecursiveASTVisitor<WeakObject>
    , public loplugin::Plugin
{

public:
    explicit WeakObject(InstantiationData const& rData) : Plugin(rData) {}

    void run() override {
        if (compiler.getLangOpts().CPlusPlus) { // no OWeakObject in C
            TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
        }
    }

    bool isDerivedFromOWeakObject(CXXMethodDecl const*const pMethodDecl)
    {
        QualType const pClass(pMethodDecl->getParent()->getTypeForDecl(), 0);
        if (loplugin::TypeCheck(pClass).Class("OWeakObject").Namespace("cppu"))
        {
            return true;
        }
        // hopefully it's faster to recurse overridden methods than the
        // thicket of WeakImplHelper32 but that is purely speculation
        for (auto it = pMethodDecl->begin_overridden_methods();
             it != pMethodDecl->end_overridden_methods(); ++it)
        {
            if (isDerivedFromOWeakObject(*it))
            {
                return true;
            }
        }
        return false;
    }

    bool VisitCXXMethodDecl(CXXMethodDecl const*const pMethodDecl)
    {
        if (ignoreLocation(pMethodDecl)) {
            return true;
        }
        if (!pMethodDecl->isThisDeclarationADefinition()) {
            return true;
        }
        if (!pMethodDecl->isInstance()) {
            return true;
        }
// this is too "simple", if a NamedDecl class has a getName() member expecting it to actually work would clearly be unreasonable    if (pMethodDecl->getName() != "release") {
        if (auto const i = pMethodDecl->getIdentifier()) {
            if (i != nullptr && !i->isStr("release")) {
                return true;
            }
        }
        if (pMethodDecl->getNumParams() != 0) {
            return true;
        }
        if (loplugin::TypeCheck(QualType(pMethodDecl->getParent()->getTypeForDecl(), 0)).Class("OWeakObject").Namespace("cppu"))
        {
            return true;
        }

        CXXMethodDecl const* pOverridden(nullptr);
        for (auto it = pMethodDecl->begin_overridden_methods();
             it != pMethodDecl->end_overridden_methods(); ++it)
        {
            if (auto const i = (*it)->getIdentifier()) {
                if (i != nullptr && i->isStr("release")) {
                    pOverridden = *it;
                    break;
                }
            }
        }
        if (pOverridden == nullptr)
        {
            return true;
        }
        if (!isDerivedFromOWeakObject(pOverridden))
        {
            return true;
        }
        CompoundStmt const*const pCompoundStatement(
                dyn_cast<CompoundStmt>(pMethodDecl->getBody()));
        for (auto const pStmt : pCompoundStatement->body())
        {
            // note: this is not a CXXMemberCallExpr
            CallExpr const*const pCallExpr(dyn_cast<CallExpr>(pStmt));
            if (pCallExpr)
            {
                // note: this is only sometimes a CXXMethodDecl
                FunctionDecl const*const pCalled(pCallExpr->getDirectCallee());
                if (pCalled->getName() == "release"
//this never works  && pCalled == pOverridden
                    && (pCalled->getParent() == pOverridden->getParent()
                        // allow this convenient shortcut
                        || loplugin::TypeCheck(QualType(pMethodDecl->getParent()->getTypeForDecl(), 0)).Class("OWeakObject").Namespace("cppu")
                        || loplugin::TypeCheck(QualType(pMethodDecl->getParent()->getTypeForDecl(), 0)).Class("OWeakAggObject").Namespace("cppu")))
                {
                    return true;
                }
                if (pCalled->getName() == "relase_ChildImpl") // FIXME remove this lunacy
                {
                    return true;
                }
            }
        }

        // whitelist
        auto const name(pMethodDecl->getParent()->getQualifiedNameAsString());
        if (   name == "cppu::OWeakAggObject" // conditional call
            || name == "cppu::WeakComponentImplHelperBase" // extra magic
            || name == "cppu::WeakAggComponentImplHelperBase" // extra magic
            || name == "DOM::CDOMImplementation" // a static oddity
            || name == "SwXTextFrame" // ambiguous, 3 parents
            || name == "SwXTextDocument" // ambiguous, ~4 parents
            || name == "SdStyleSheet" // same extra magic as WeakComponentImplHelperBase
            || name == "SdXImpressDocument" // same extra magic as WeakComponentImplHelperBase
           )
        {
            return true;
        }

        report(DiagnosticsEngine::Warning,
                "override of OWeakObject::release() does not call superclass release()",
                pMethodDecl->getLocation())
            << pMethodDecl->getSourceRange();

        return true;
    }

};

loplugin::Plugin::Registration<WeakObject> X("weakobject");

} // namespace

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/ui/inc/exsrcbrw.hxx b/dbaccess/source/ui/inc/exsrcbrw.hxx
index 3a533e8..53a10bb 100644
--- a/dbaccess/source/ui/inc/exsrcbrw.hxx
+++ b/dbaccess/source/ui/inc/exsrcbrw.hxx
@@ -50,7 +50,7 @@ namespace dbaui
                SAL_CALL Create(const css::uno::Reference< css::lang::XMultiServiceFactory >&);

        // UNO
        DECLARE_UNO3_DEFAULTS(SbaExternalSourceBrowser, OGenericUnoController)
        DECLARE_UNO3_DEFAULTS(SbaExternalSourceBrowser, SbaXDataBrowserController)
        virtual css::uno::Any  SAL_CALL queryInterface(const css::uno::Type& _rType) throw (css::uno::RuntimeException, std::exception) override;
        //  virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > >  getIdlClasses();

diff --git a/extensions/source/propctrlr/composeduiupdate.cxx b/extensions/source/propctrlr/composeduiupdate.cxx
index 8ad15cff..5fde3d3 100644
--- a/extensions/source/propctrlr/composeduiupdate.cxx
+++ b/extensions/source/propctrlr/composeduiupdate.cxx
@@ -211,14 +211,13 @@ namespace pcr

    void SAL_CALL CachedInspectorUI::acquire() throw()
    {
        osl_atomic_increment( &m_refCount );
        CachedInspectorUI_Base::acquire();
    }


    void SAL_CALL CachedInspectorUI::release() throw()
    {
        if ( 0 == osl_atomic_decrement( &m_refCount ) )
            delete this;
        CachedInspectorUI_Base::release();
    }


diff --git a/scripting/source/provider/BrowseNodeFactoryImpl.cxx b/scripting/source/provider/BrowseNodeFactoryImpl.cxx
index 863860e..a312ed4a 100644
--- a/scripting/source/provider/BrowseNodeFactoryImpl.cxx
+++ b/scripting/source/provider/BrowseNodeFactoryImpl.cxx
@@ -500,15 +500,12 @@ public:
        throw () override

    {
        osl_atomic_increment( &m_refCount );
        t_BrowseNodeBase::acquire();
    }
    virtual void SAL_CALL release()
        throw () override
    {
        if ( osl_atomic_decrement( &m_refCount ) == 0 )
        {
            delete this;
        }
        t_BrowseNodeBase::release();
    }
    // XTypeProvider (implemnented by base, but needs to be overridden for
    //                delegating to aggregate)
diff --git a/sw/source/core/access/acctable.hxx b/sw/source/core/access/acctable.hxx
index 84930ef..857636e 100644
--- a/sw/source/core/access/acctable.hxx
+++ b/sw/source/core/access/acctable.hxx
@@ -301,10 +301,10 @@ public:
        throw (css::uno::RuntimeException, std::exception) override;

    virtual void SAL_CALL acquire(  ) throw () override
        { SwAccessibleContext::acquire(); };
        { SwAccessibleTable::acquire(); };

    virtual void SAL_CALL release(  ) throw () override
        { SwAccessibleContext::release(); };
        { SwAccessibleTable::release(); };

    // XAccessibleContext