Skip to content

Instantly share code, notes, and snippets.

@mmha
Created September 19, 2016 01:56
Show Gist options
  • Save mmha/e874d43199cdfa1fbda164fe9d1fce2f to your computer and use it in GitHub Desktop.
Save mmha/e874d43199cdfa1fbda164fe9d1fce2f to your computer and use it in GitHub Desktop.
Clang Static Analyzer Plugin for glBegin()/glEnd() (Old toy code, written against Clang 3.6.0)
#include "GLBeginEndChecker.h"
#include <iostream>
REGISTER_MAP_WITH_PROGRAMSTATE(GLBES, int, GLBeginEndState)
namespace clang
{
namespace ento
{
void registerGLBeginEndChecker(CheckerManager &mgr)
{
mgr.registerChecker< GLBeginEndChecker >();
}
}
}
GLBeginEndChecker::GLBeginEndChecker()
: NoBeginBug(new BugType(this, "No glBegin", "OpenGL Error"))
, DoubleBeginBug(new BugType(this, "Double glBegin", "OpenGL Error"))
, NoEndBug(new BugType(this, "No glEnd after glBegin", "OpenGL Error"))
, InvalidCallInBlockBug(new BugType(this, "Illegal call", "OpenGL Error"))
{
}
void GLBeginEndChecker::reportNoBegin(const CallEvent &call, CheckerContext &context) const
{
auto errNode = context.generateSink();
if(!errNode)
return;
auto report = new BugReport(*NoBeginBug, "No glBegin preceding glEnd", errNode);
report->addRange(call.getSourceRange());
context.emitReport(report);
}
void GLBeginEndChecker::reportDoubleBegin(const CallEvent &call, CheckerContext &context) const
{
auto errNode = context.generateSink();
if(!errNode)
return;
auto report = new BugReport(*NoBeginBug, "Two glBegin calls without a glEnd call between them", errNode);
report->addRange(call.getSourceRange());
context.emitReport(report);
}
void GLBeginEndChecker::reportNoEnd(const CallEvent &call, CheckerContext &context) const
{
auto errNode = context.generateSink();
if(!errNode)
return;
auto report = new BugReport(*NoBeginBug, "Illegal call in a glBegin/glEnd Block", errNode);
report->addRange(call.getSourceRange());
context.emitReport(report);
}
void GLBeginEndChecker::reportInvalidCallInBlock(const CallEvent &call, CheckerContext &context) const
{
// TODO
}
bool GLBeginEndChecker::hasNoEnd(SymbolRef sym, const GLBeginEndState &state, bool isSymDead)
{
if(isSymDead && state.isInBlock())
{
return true;
}
return false;
}
void GLBeginEndChecker::checkPostCall(const CallEvent &call, CheckerContext &context) const
{
auto programState = context.getState();
const auto glState = programState->get< GLBES >(1);
std::cout << call.getCalleeIdentifier()->getName().str() << std::endl;
if(call.getCalleeIdentifier()->getName().str() == "glBegin")
{
if(glState && glState->isInBlock())
{
std::cout << "Found glBegin\n";
reportDoubleBegin(call, context);
return;
}
programState = programState->set< GLBES >(1, GLBeginEndState::State::IN_BLOCK);
context.addTransition(programState);
return;
}
else if(call.getCalleeIdentifier()->getName().str() == "glEnd")
{
if(glState && !glState->isInBlock())
{
std::cout << "Found glEnd\n";
reportNoBegin(call, context);
return;
}
}
else
{
if(glState && glState->isInBlock())
{
const std::array< std::string, 16 > validBlockCalls = {"glVertex",
"glColor",
"glSecondaryColor",
"glIndex",
"glNormal",
"glFogCoord",
"glTexCoord",
"glMultiTexCoord",
"glVertexAttrib",
"glEvalCoord",
"glEvalPoint",
"glArrayElement",
"glMaterial",
"glEdgeFlag",
"glCallList",
"glCallLists"};
auto callName = call.getCalleeIdentifier()->getName();
for(auto &str : validBlockCalls)
{
if(!boost::starts_with(callName, str))
{
reportInvalidCallInBlock(call, context);
return;
}
}
}
}
}
void GLBeginEndChecker::checkEndAnalysis(ExplodedGraph &graph, BugReporter &reporter, ExprEngine &engine) const
{
// TODO
}
#ifndef GLBEGINEND_CHECKER_H
#define GLBEGINEND_CHECKER_H
#include "GLBeginEndState.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include <boost/algorithm/string/predicate.hpp>
#include <memory>
#include <string>
#include <array>
using namespace clang;
using namespace ento;
class GLBeginEndChecker : public Checker< check::PostCall, check::EndAnalysis >
{
std::unique_ptr<BugType> NoBeginBug, DoubleBeginBug, NoEndBug, InvalidCallInBlockBug;
std::array<IdentifierInfo, 16> validBlockCallIdentifiers;
void reportNoBegin(const CallEvent &call, CheckerContext &context) const;
void reportDoubleBegin(const CallEvent &call, CheckerContext &context) const;
void reportNoEnd(const CallEvent &call, CheckerContext &context) const;
void reportInvalidCallInBlock(const CallEvent &call, CheckerContext &context) const;
bool hasNoEnd(SymbolRef sym, const GLBeginEndState &state, bool isSymDead);
public:
GLBeginEndChecker();
void checkPostCall(const CallEvent &call, CheckerContext &context) const;
void checkEndAnalysis(ExplodedGraph &graph, BugReporter &reporter, ExprEngine &engine) const;
};
#endif
#ifndef GLBEGINEND_STATE_H
#define GLBEGINEND_STATE_H
#include <llvm/ADT/FoldingSet.h>
class GLBeginEndState
{
public:
enum class State
{
OUT_OF_BLOCK = 0,
IN_BLOCK = 1
};
GLBeginEndState(State s)
: state(s)
{
}
bool isInBlock() const
{
return state == State::IN_BLOCK;
}
bool operator==(GLBeginEndState other) const
{
return state == other.state;
}
void Profile(llvm::FoldingSetNodeID &ID) const
{
ID.AddInteger((int) state);
}
protected:
State state;
};
#endif
#include <GL/glew.h>
#include <GL/gl.h>
void foo(int x)
{
if(x >= 0 && x < 5)
{
glBegin(GL_TRIANGLES);
}
if(x == 1)
{
glBegin(GL_TRIANGLES);
}
if(x == 2)
{
glClear(GL_COLOR_BUFFER_BIT);
}
if(x == 3 || x == 4)
{
glEnd();
}
if(x == 4)
{
glEnd();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment