I'm using libTooling built from source (git tag: llvmorg-16.0.6
) for searching diff of AST level.
When using my built product, Segmentation fault occured.
Through inspection by gdb, I found that this is from libTooling,
and the segmentation fault occured at calling A->getSourceRange()
(source)
if (auto *A = dyn_cast<AccessSpecDecl>(D)) {
CharSourceRange Range(A->getSourceRange(), false);
return std::string(
Lexer::getSourceText(Range, AST.getSourceManager(), AST.getLangOpts()));
}
Also, gdb shows that A
's vtable pointer(_vptr.Decl
) is 0x0
.
(gdb) p *A
$1 = {<clang::Decl> = {_vptr.Decl = 0x0, NextInContextAndBits = {Value = 0},
DeclCtx = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 0, clang::DeclContext*, clang::Decl::MultipleDC*>> = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 1, clang::Decl::MultipleDC*>> = {<llvm::pointer_union_detail::PointerUnionMembers<llvm::PointerUnion<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPair<void*, 1, int, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*>, llvm::PointerIntPairInfo<void*, 1, llvm::pointer_union_detail::PointerUnionUIntTraits<clang::DeclContext*, clang::Decl::MultipleDC*> > >, 2>> = {Val = {Value = 0}}, <No data fields>}, <No data fields>}, <No data fields>}, Loc = {ID = 0}, DeclKind = 0, InvalidDecl = 0, HasAttrs = 0, Implicit = 0, Used = 0, Referenced = 0,
TopLevelDeclInObjCContainer = 0, static StatisticsEnabled = false, Access = 0, FromASTFile = 0, IdentifierNamespace = 0, CacheValidAndLinkage = 0}, ColonLoc = {ID = 0}}
I switch compiler from /usr/bin/c++
to clang++
, but problems are no solved. I also compile with libTooling and clang-15 from apt install
, but it also raise segmentation fault that seems occurred at other position (raised at clang::diff::SyntaxTree::Impl::getDeclValue
for clang built from source, and at clang::diff::SyntaxTree::Impl::getStmtValue
for clang-15 from apt)
Because two compiler's binaries (c++
and clang++
) cause same segmentation fault, I suspect that I did something wrong, but I do not know what.
Please teach me why there is NULL vtable pointer, and how to solve this.
The repository for reproduction is here.
The basic question, of how the vtable pointer can be NULL
, is already
asked and answered here:
However, the hypotheses suggested in the answers to that question are not a perfect match for the symptoms here, and the libtooling angle has some novel aspects, so I'll refrain from calling it a duplicate, even though it's quite close.
From the gdb
output, we can see that everything is zero, not just
the vtable pointer. That suggests that the pointer is pointing
somewhere it shouldn't, at memory which happens to contain all zeroes.
It's also possible something overwrote the entire AST memory with
zeroes.
As a demonstration, if we have a class with a vtable:
class C {
public:
int m_x, m_y;
virtual ~C() {}
};
and create a pointer pointing at zeroes:
unsigned char arr[80];
std::memset(arr, 0, sizeof(arr));
C *p = reinterpret_cast<C*>(&arr[0]);
then gdb
will report a zero vtable:
(gdb) print *p
$1 = {_vptr.C = 0x0, m_x = 0, m_y = 0}
Somehow you've done something to create a similar situation.
The pointer in question ostensibly points to a
libtooling
Decl
AST
node. But everything is zero, including the DeclKind
.
The reason the crash happens inside this conditional is
that a DeclKind
of zero happens to be the value used by
AccessSpecDecl
to identify itself to
dyn_cast
.
The reason it crashes on the call A->getSourceRange()
is that
getSourceRange
is a virtual
function, but the vtable pointer is
NULL
, so dereferencing that pointer causes a crash.
In a case like this, where the crash site itself does not offer much information, you want to start cutting out as much from your program as you can, while preserving the fact that it fails in this way. Presumably you started from some libtooling hello-world tutorial example that does not exhibit this behavior. Remove the pieces of your program, one by one, until you have nothing but the original tutorial code. At some point you'll remove a piece that causes the problem to go away; put that back and keep removing other pieces.
If, by doing that, you figure out the problem, then great, you're done. Otherwise you'll have a minimal reproducer that could be the basis of a new question. (But don't just link to a github repo. The question should be self-contained.)