Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incomplete/incorrect PTA when using C++ namespaces & virtual functions #1369

Merged
merged 1 commit into from
Feb 13, 2024

Conversation

Johanmyst
Copy link
Contributor

@Johanmyst Johanmyst commented Feb 12, 2024

This pull request fixes a bug that caused call graphs to be incomplete/incorrect when using C++ namespaces and virtual functions.

Currently, whenever namespaces and virtual functions are used with C++ classes, the underlying object's name is used to find the VTable for a called function. However, the name used therein is incorrect (the namespace component is stripped off when it shouldn't be), and causes incomplete and incorrect results to be returned, leading to an incomplete callgraph.

For example, take the following snippets:

Example 1:

#include <cstdio>
#include <cstring>

class Parent
{
    public:
        Parent( )          = default;
        virtual ~Parent( ) = default;

        virtual void action( ) = 0;
};

class Child : public Parent
{
    public:
        Child( )  = default;
        ~Child( ) = default;

        void action( ) { printf("Doing stuff...\n"); }
};

int main(int argc, char *argv[])
{
    Child *obj = new Child;
    obj->action( );
    delete obj;
    return 0;
}

This corresponds to the following — expected — callgraph:
correct callgraph dot

Example 2:

#include <cstdio>
#include <cstring>

namespace n1
{

class Parent
{
    public:
        Parent( )          = default;
        virtual ~Parent( ) = default;

        virtual void action( ) = 0;
};

class Child : public Parent
{
    public:
        Child( )  = default;
        ~Child( ) = default;

        void action( ) { printf("Doing stuff...\n"); }
};

}    // namespace n1

int main(int argc, char *argv[])
{
    n1::Child *obj = new n1::Child;
    obj->action( );
    delete obj;
    return 0;
}

This results in the following, incomplete callgraph:
incorrect callgraph dot

The introduction of the namespaces causes the virtual call not to be found. This is because the set of indirect call edges found in the AndersenBase::solveConstraints() function (line 118) is empty in the second example.

This set is retrieved from the PAG's indirect callsites SVFIR::indCallSiteToFunPtrMap, which is then passed to Andersen::updateCallGraph(), which again calls BVDataPTAImpl::onTheFlyCallGraphSolve. This calls PointerAnalysis::resolveCPPIndCalls, which, by default, will not connect virtual calls based on the Call Hierarchy Analysis (CHA), and thus calls PointerAnalysis::getVFnsFromPts .

This function calls chgraph->csHasVtblsBasedonCHA(SVFUtil::getSVFCallSite(cs->getCallSite())), which returns false in the second example, because the CHG's CHG::csToCHAVtblsMap map has no VTable entries for the given callsite. During construction of the CHG however this map is correctly built (note: without stripping the namespace component from the objects) and contains mappings for n1::Child and such.

However, during lookup, the getClassNameOfThisPtr function returns only Child, rather than n1::Child, causing the lookup to fail, and no call-edge to be created. This is due to inferThisPtrClsName calling extractClsNamesFromFunc, which calls updateClassNameBeforeBrackets on the demangled name. However, this last function call strips the n1:: part from the demangled name incorrectly, causing the broken lookup. Since this function only gets called on this code-path, removing this stripping (which makes it consistent with other places) should be fine and produces correct call graphs for the above examples.

I believe this could also be the underlying issue for issue #1301 and possibly issue #1314.

… lookup to fix incomplete PTA when using C++ namespaces & virtual functions
@Johanmyst Johanmyst marked this pull request as draft February 12, 2024 16:52
Copy link

codecov bot commented Feb 12, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (dda7fe5) 67.87% compared to head (34ed2f7) 67.87%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1369      +/-   ##
==========================================
- Coverage   67.87%   67.87%   -0.01%     
==========================================
  Files         253      253              
  Lines       27703    27702       -1     
==========================================
- Hits        18804    18803       -1     
  Misses       8899     8899              
Files Coverage Δ
svf-llvm/lib/CppUtil.cpp 87.20% <ø> (-0.05%) ⬇️

@Johanmyst Johanmyst marked this pull request as ready for review February 12, 2024 17:24
@yuleisui
Copy link
Collaborator

Thanks for the pull request.

@jumormt could you help review this too? We will also need to add the test cases into SVF-Test-Suite for our CI.

@jumormt
Copy link
Contributor

jumormt commented Feb 13, 2024

extractClsNamesFromFunc

This makes sense. The semantic is different from `isConstructor'. I will add the test case to SVF Test Suite.

@yuleisui yuleisui merged commit 323ce24 into SVF-tools:master Feb 13, 2024
5 checks passed
@Johanmyst Johanmyst deleted the fixCPPVirtualFuncs branch February 13, 2024 08:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants