Import of the watch repository from Pebble

This commit is contained in:
Matthieu Jeanson 2024-12-12 16:43:03 -08:00 committed by Katharine Berry
commit 3b92768480
10334 changed files with 2564465 additions and 0 deletions

287
checkers/MutexChecker.cpp Normal file
View file

@ -0,0 +1,287 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
using namespace clang;
using namespace ento;
namespace std {
void terminate( void ) _NOEXCEPT {
abort();
}
}
/* This analyzer suffers from the major limitation that most of the mutexes in Pebble are globals,
* so all symbols and MemRegions refering to the mutexes are invalidated every time an unknown
* function is called. This analyzer instead associates mutexes with the declaration of their
* variables, which has the obvious limitation of not catching when mutexes are passed as
* arguments (which fortunately never? happens in pebble).
*/
class MutexState {
private:
bool locked;
bool recursive;
unsigned lockCount;
public:
MutexState(bool isLocked, bool isRecursive, unsigned startCount)
:
locked(isLocked),
recursive(isRecursive),
lockCount(startCount)
{}
MutexState getLocked() const {
if (recursive) {
if (locked) {
// Preserve the first lock function (it should be the last one to unlock)
return MutexState(true, true, lockCount + 1);
}
else {
return MutexState(true, true, lockCount + 1);
}
}
else {
return MutexState(true, false, 0);
}
}
MutexState getUnlocked(void) const {
if (recursive) {
// If lockCount is one, we unlock
return MutexState(lockCount > 1, true, lockCount - 1);
}
else {
return MutexState(false, false, 0);
}
}
bool isLocked(void) const {
return locked;
}
bool isRecursive(void) const {
return recursive;
}
bool operator==(const MutexState &other) const {
return locked == other.locked && recursive == other.recursive &&
lockCount == other.lockCount;
}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddBoolean(locked);
ID.AddBoolean(recursive);
ID.AddInteger(lockCount);
}
};
// Map mutex declarations to state info
REGISTER_MAP_WITH_PROGRAMSTATE(MutexMap, const Decl *, MutexState);
// Hold an ordered list of the mutexes to catch lock order reversal
REGISTER_LIST_WITH_PROGRAMSTATE(MutexList, const Decl *);
namespace {
class MutexChecker : public Checker<check::PostCall, check::EndFunction> {
std::unique_ptr<BugType> NoUnlockBugType;
std::unique_ptr<BugType> DoubleLockBugType;
std::unique_ptr<BugType> DoubleUnlockBugType;
std::unique_ptr<BugType> TooManyUnlocksBugType;
std::unique_ptr<BugType> UnlockNoLockBugType;
std::unique_ptr<BugType> LockReversalBugType;
void reportError(const std::unique_ptr<BugType> &bugType, StringRef msg, CheckerContext &C) const {
ExplodedNode *endNode = C.generateSink();
if (!endNode) {
return;
}
BugReport *bug = new BugReport(*bugType, msg, endNode);
C.emitReport(bug);
}
ProgramStateRef lockMutex(const Decl *mutexDecl, const MutexState *curMutex,
ProgramStateRef state, bool recursive = false) const {
state = state->add<MutexList>(mutexDecl);
if (curMutex) {
MutexState lockedMutex = curMutex->getLocked();
return state->set<MutexMap>(mutexDecl, lockedMutex);
}
else {
MutexState newMutex(true, recursive, recursive ? 1 : 0);
return state->set<MutexMap>(mutexDecl, newMutex);
}
}
const Decl * getMutexDecl(const Expr *argExpr) const {
const Expr *strippedExpr = argExpr->IgnoreParenCasts();
const DeclRefExpr *ref = dyn_cast<DeclRefExpr>(strippedExpr);
if (ref) {
return ref->getDecl();
}
// If it wasn't a DeclRef maybe it was a member?
const MemberExpr *member = dyn_cast<MemberExpr>(strippedExpr);
if (member) {
return member->getMemberDecl();
}
return NULL;
}
void handleLock(StringRef funcName, const CallEvent &call, CheckerContext &C) const {
const Decl *mutexDecl = getMutexDecl(call.getArgExpr(0));
if (!mutexDecl) {
return;
}
ProgramStateRef state = C.getState();
const MutexState *curMutex = state->get<MutexMap>(mutexDecl);
if (funcName.equals("mutex_lock") || funcName.equals("mutex_lock_with_lr")) {
if (curMutex) {
if (curMutex->isLocked()) {
reportError(DoubleLockBugType, "This lock was already locked", C);
return;
}
}
state = lockMutex(mutexDecl, curMutex, state);
C.addTransition(state);
}
else if (funcName.equals("mutex_lock_with_timeout")) {
if (curMutex) {
if (curMutex->isLocked()) {
reportError(DoubleLockBugType, "This lock was already locked", C);
return;
}
}
// diverge into two states, one where we get the mutex and one
// where we don't
ProgramStateRef lockedState, timeoutState;
DefinedSVal retVal = call.getReturnValue().castAs<DefinedSVal>();
std::tie(lockedState, timeoutState) = state->assume(retVal);
lockedState = lockMutex(mutexDecl, curMutex, lockedState);
C.addTransition(lockedState);
C.addTransition(timeoutState);
}
else if (funcName.equals("mutex_lock_recursive")) {
state = lockMutex(mutexDecl, curMutex, state, true);
C.addTransition(state);
}
else if (funcName.equals("mutex_lock_recursive_with_timeout") ||
funcName.equals("mutex_lock_recursive_with_timeout_and_lr")) {
ProgramStateRef lockedState, timeoutState;
DefinedSVal retVal = call.getReturnValue().castAs<DefinedSVal>();
std::tie(lockedState, timeoutState) = state->assume(retVal);
lockedState = lockMutex(mutexDecl, curMutex, lockedState, true);
C.addTransition(lockedState);
C.addTransition(timeoutState);
}
}
void handleUnlock(StringRef funcName, const CallEvent &call, CheckerContext &C) const {
if (!(funcName.equals("mutex_unlock") || funcName.equals("mutex_unlock_recursive"))) {
return;
}
ProgramStateRef state = C.getState();
const Decl *mutexDecl = getMutexDecl(call.getArgExpr(0));
const MutexState *curMutex = state->get<MutexMap>(mutexDecl);
// If it isn't in the map, we never locked it
if (!curMutex) {
reportError(UnlockNoLockBugType, "Mutex was never locked", C);
return;
}
// If it is in the map but unlocked, it was unlocked twice
if (!curMutex->isLocked()) {
if (curMutex->isRecursive()) {
reportError(TooManyUnlocksBugType, "Recursive mutex already fully unlocked", C);
}
else {
reportError(DoubleUnlockBugType, "Mutex already unlocked", C);
}
return;
}
const Decl *lastDecl = state->get<MutexList>().getHead();
if (mutexDecl != lastDecl) {
reportError(LockReversalBugType, "This was not the most recently acquired lock", C);
return;
}
state = state->set<MutexList>(state->get<MutexList>().getTail());
state = state->set<MutexMap>(mutexDecl, curMutex->getUnlocked());
C.addTransition(state);
}
public:
MutexChecker(void)
: NoUnlockBugType(new BugType(this, "Failure to call unlock", "Pebble Mutex Plugin")),
DoubleLockBugType(new BugType(this, "Double Lock", "Pebble Mutex Plugin")),
DoubleUnlockBugType(new BugType(this, "Double Unlock", "Pebble Mutex Plugin")),
TooManyUnlocksBugType(new BugType(this, "More unlocks than locks", "Pebble Mutex Plugin")),
UnlockNoLockBugType(new BugType(this, "Unlock called before lock", "Pebble Mutex Plugin")),
LockReversalBugType(new BugType(this, "Lock order reversal", "Pebble Mutex Plugin"))
{}
void checkPostCall(const CallEvent &call, CheckerContext &C) const {
const IdentifierInfo *identInfo = call.getCalleeIdentifier();
if(!identInfo) {
return;
}
StringRef funcName = identInfo->getName();
if (funcName.startswith("mutex_lock")) {
handleLock(funcName, call, C);
}
else if (funcName.startswith("mutex_unlock")) {
handleUnlock(funcName, call, C);
}
}
void checkEndFunction(CheckerContext &C) const {
ProgramStateRef state = C.getState();
if (C.inTopFrame()) {
// This path ends once this function ends
for (auto mutexPair : state->get<MutexMap>()) {
if (mutexPair.second.isLocked()) {
reportError(NoUnlockBugType, "Mutex still locked at end of path", C);
return;
}
}
}
}
};
}
extern "C" const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING;
extern "C" void clang_registerCheckers(CheckerRegistry &registry) {
registry.addChecker<MutexChecker>("pebble.MutexChecker", "Checker for use of mutex_lock()/mutex_unlock()");
}

View file

@ -0,0 +1,267 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/CheckerRegistry.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
using namespace clang;
using namespace ento;
namespace std {
void terminate( void ) _NOEXCEPT {
abort();
}
}
// Need to specialize for any custom types used in traits
// Look in include/clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h
namespace clang {
namespace ento {
template <> struct ProgramStatePartialTrait<const FunctionDecl *> {
typedef const FunctionDecl * data_type;
static inline data_type MakeData(void *const* p) {
return p ? (const FunctionDecl *)*p : data_type();
}
static inline void *MakeVoidPtr(data_type d) {
return const_cast<FunctionDecl *>(d);
}
};
}
}
/* Ultimately this would work better / be more thorough if it made use of the Analyzer's
* taint checking, but there is no infrastructure to remove taint at the moment.
*/
REGISTER_TRAIT_WITH_PROGRAMSTATE(CurrentSyscallState, const FunctionDecl *);
// Args are tracked by their MemRegion
REGISTER_SET_WITH_PROGRAMSTATE(TaintedArgsState, const MemRegion *);
namespace {
class SyscallSecurityChecker :
public Checker< eval::Call, check::PreCall, check::Location, check::Bind, check::EndFunction > {
std::unique_ptr<BugType> NoBoundsCheckBugType;
std::unique_ptr<BugType> UnsafeCallBugType;
llvm::StringSet<> unsafeFunctions {};
const FunctionDecl * getCurrentSyscall(const ProgramStateRef state) const {
return state->get<CurrentSyscallState>();
}
bool inSyscall(const ProgramStateRef state) const {
return !!getCurrentSyscall(state);
}
ProgramStateRef setCurrentSyscall(const ProgramStateRef state, const FunctionDecl *FD) const {
return state->set<CurrentSyscallState>(FD);
}
bool isValTainted(const SVal &arg, const ProgramStateRef state) const {
const MemRegion *MR = arg.getAsRegion();
if (!MR) {
return false;
}
const MemRegion *baseMR = MR->getBaseRegion();
return state->contains<TaintedArgsState>(baseMR);
}
void reportUnsanitizedUse(const SVal &arg, const ProgramStateRef state, CheckerContext &C) const {
ExplodedNode *errNode = C.generateSink();
if (!errNode) {
// Already reported an error here
return;
}
BugReport *R = new BugReport(*NoBoundsCheckBugType,
"Used an unsanitized argument from syscall", errNode);
R->markInteresting(arg);
C.emitReport(R);
}
public:
SyscallSecurityChecker(void)
: NoBoundsCheckBugType(new BugType(this, "Failed to check bounds", "Pebble Syscall Plugin")),
UnsafeCallBugType(new BugType(this, "Syscall used dangerous function", "Pebble Syscall Plugin"))
{
StringRef funcs[] = { "task_malloc", "task_zalloc", "task_calloc", "app_malloc", "app_zalloc", "app_calloc" };
// It would be more efficient to look up the IdentifierInfos for each of these and compare against that
for (StringRef func : funcs) {
unsafeFunctions.insert(func);
}
}
bool evalCall(const CallExpr *call, CheckerContext &C) const {
if (!C.getCalleeName(call).equals("syscall_internal_elevate_privilege")) {
return false;
}
// Always return true from syscall_internal_elevate_privilege
// so the analyzer always thinks privileges have been elevated
ProgramStateRef state = C.getState();
SVal ret = C.getSValBuilder().makeTruthVal(true);
state = state->BindExpr(call, C.getLocationContext(), ret);
C.addTransition(state);
return true;
}
void checkPreCall(const CallEvent &call, CheckerContext &C) const {
const IdentifierInfo *identInfo = call.getCalleeIdentifier();
if(!identInfo) {
return;
}
StringRef funcName = identInfo->getName();
ProgramStateRef state = C.getState();
if (funcName.equals("syscall_internal_elevate_privilege")) {
const LocationContext *LCtx = C.getLocationContext();
const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
if (!FD) {
llvm::errs() << "Privileges elevated outside of function?\n";
return;
}
ExplodedNode *pred = NULL;
// If we're not at the top level, we generate two new transitions, one for the current syscall
// executing normally, and one which simulates execution starting at this syscall.
// This is important, because if a syscall is called by another function, the syscall
// will not be treated as an entry point by the analyzer.
if (!C.inTopFrame()) {
C.addTransition(state);
state = C.getStateManager().getInitialState(LCtx);
// Get the first node in the state graph
pred = C.getPredecessor();
while (pred->getFirstPred()) {
pred = pred->getFirstPred();
}
}
for (unsigned i = 0; i < FD->getNumParams(); i++) {
// We only care about tracking pointer arguments
const ParmVarDecl *ParamDecl = FD->getParamDecl(i);
if (ParamDecl->getType()->isPointerType()) {
// Find the MemRegion associated with the parameter.
// Seems very roundabout, but it works...
// Remember to look at state->getRegion
Loc lValue = state->getLValue(ParamDecl, LCtx);
SVal valRegion = state->getSVal(lValue);
if (valRegion == UnknownVal()) {
llvm::errs() << "Failed to get argument SymbolRef\n";
continue;
}
const MemRegion *MR = valRegion.getAsRegion();
if (!MR) {
llvm::errs() << "No region for ptr argument\n";
continue;
}
state = state->add<TaintedArgsState>(MR);
}
}
state = setCurrentSyscall(state, FD);
C.addTransition(state, pred, nullptr);
}
else if (inSyscall(state)) {
if (funcName.equals("syscall_assert_userspace_buffer")) {
const MemRegion *MR = call.getArgSVal(0).getAsRegion();
state = state->remove<TaintedArgsState>(MR);
}
else if (funcName.equals("memory_layout_is_cstring_in_region") ||
funcName.equals("memory_layout_is_pointer_in_region")) {
const MemRegion *MR = call.getArgSVal(1).getAsRegion();
state = state->remove<TaintedArgsState>(MR);
}
// Make sure the syscall isn't calling an unsafe function
else if (unsafeFunctions.count(funcName)) {
ExplodedNode *errNode = C.generateSink();
if (!errNode) {
// Already reported an error here
return;
}
BugReport *R = new BugReport(*UnsafeCallBugType,
"This function shouldn't be called from privileged code", errNode);
C.emitReport(R);
return;
}
else { // Any other function, just want to make sure it isn't getting the unsanitized args
for (unsigned i = 0; i < call.getNumArgs(); i++) {
SVal argVal = call.getArgSVal(i);
if (isValTainted(argVal, state)) {
reportUnsanitizedUse(argVal, state, C);
return;
}
}
}
C.addTransition(state);
}
}
void checkLocation(SVal loc, bool isLoad, const Stmt *S, CheckerContext &C) const {
ProgramStateRef state = C.getState();
if (isValTainted(loc, state)) {
reportUnsanitizedUse(loc, state, C);
}
}
void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const {
ProgramStateRef state = C.getState();
if (isValTainted(val, state)) {
reportUnsanitizedUse(val, state, C);
}
}
void checkEndFunction(CheckerContext &C) const {
ProgramStateRef state = C.getState();
const Decl *D = C.getLocationContext()->getDecl();
const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
if (!FD) {
// Not sure why this would ever be the case...
llvm::errs() << "Path ended outside of function?\n";
return;
}
if (FD != getCurrentSyscall(state)) {
return;
}
// Since we are effectively emulating every syscall as an entry point from
// the analyzer's perspective, once the syscall is done, end the path.
C.generateSink();
}
};
}
extern "C" const char clang_analyzerAPIVersionString[] = CLANG_ANALYZER_API_VERSION_STRING;
extern "C" void clang_registerCheckers(CheckerRegistry &registry) {
registry.addChecker<SyscallSecurityChecker>("pebble.SyscallSecurityChecker", "Checker that makes sure pointer arguments to syscalls are sanitized");
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdbool.h>
struct Mutex;
struct RecursiveMutex;
typedef struct Mutex * restrict mutex_t;
typedef struct RecursiveMutex * recursive_mutex_t;
extern void mutex_lock(mutex_t);
extern void mutex_unlock(mutex_t);
extern bool mutex_lock_with_timeout(mutex_t);
extern void mutex_lock_recursive(recursive_mutex_t);
extern void mutex_unlock_recursive(recursive_mutex_t);
static mutex_t global_lock = 0;
static mutex_t global_lock2;
mutex_t recursive_lock;
void nounlock() {
mutex_lock(global_lock);
}
void nolock() {
mutex_unlock(global_lock);
}
void normal() {
mutex_lock(global_lock);
mutex_unlock(global_lock);
}
struct handle {
mutex_t m;
} m_wrapper;
extern int do_stuff(struct handle * h);
void structthing(struct handle * h) {
mutex_lock(h->m);
do_stuff(h);
mutex_unlock(h->m);
}
extern int do_stuff2();
void stuff() {
mutex_lock(global_lock);
do_stuff2();
mutex_unlock(global_lock);
}
void stuff2() {
mutex_lock(m_wrapper.m);
do_stuff2();
mutex_unlock(m_wrapper.m);
}
void nest2() {
mutex_lock(global_lock);
printf("blah %p", global_lock);
mutex_unlock(global_lock);
}
void nest() {
nest2();
}
void cond(void *glob_ptr) {
mutex_lock(global_lock);
while (glob_ptr) {
printf("blah %p", glob_ptr);
}
mutex_unlock(global_lock);
}
void timeout() {
mutex_lock_with_timeout(global_lock);
mutex_unlock(global_lock);
}
void good_timeout() {
if (mutex_lock_with_timeout(global_lock)) {
mutex_unlock(global_lock);
}
}
void stupid_timeout() {
if (!mutex_lock_with_timeout(global_lock)) {
mutex_unlock(global_lock);
}
}
void reversal() {
mutex_lock(global_lock);
mutex_lock(global_lock2);
mutex_unlock(global_lock);
mutex_unlock(global_lock2);
}
// Trying to repro the false positives unsuccessfully...
extern bool decision();
inline void __attribute__((always_inline)) locker() {
mutex_lock(global_lock);
}
inline void __attribute__((always_inline)) unlocker() {
mutex_unlock(global_lock);
}
static inline void __attribute__((always_inline)) lock_wrap() {
locker();
if (decision()) {
unlocker();
}
}
static inline void __attribute__((always_inline)) unlock_wrap() {
unlocker();
}
void lockme() {
lock_wrap();
unlock_wrap();
}

View file

@ -0,0 +1,133 @@
/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
extern bool syscall_internal_elevate_privilege();
extern void syscall_assert_userspace_buffer(const void * check_buffer, int size);
extern void * app_malloc(unsigned size);
void do_stuff(void * buffer, int size) {
strncpy(buffer, "Woooooooo", size);
}
void good_syscall(void * buffer, int size) {
syscall_internal_elevate_privilege();
syscall_assert_userspace_buffer(buffer, size);
do_stuff(buffer, size);
}
void bad_syscall(void * buffer, int size) {
syscall_internal_elevate_privilege();
do_stuff(buffer, size);
}
void stupid_syscall(void * buffer, int size) {
void * stupid = (char *)buffer + 1;
syscall_internal_elevate_privilege();
do_stuff(stupid, size);
}
void not_syscall(void * buffer, int size) {
do_stuff(buffer, size);
}
void nested_syscall(void * buffer, int size) {
syscall_internal_elevate_privilege();
syscall_assert_userspace_buffer(buffer, size);
bad_syscall(buffer, size);
good_syscall(buffer, size);
}
void bad_nested_syscall(void * buffer, int size) {
syscall_internal_elevate_privilege();
bad_syscall(buffer, size);
}
void hidden_bad_syscall(void * buffer, int size) {
syscall_internal_elevate_privilege();
do_stuff(buffer, size);
}
void if_syscall(void * buffer, int size) {
if (syscall_internal_elevate_privilege()) {
syscall_assert_userspace_buffer(buffer, size);
}
do_stuff(buffer, size);
}
void wrapper() {
void * buffer = NULL;
int size = 0;
good_syscall(buffer, size);
// This tests to make sure analysis continues through good_syscall
hidden_bad_syscall(buffer, size);
}
bool cond(const char *font_key) {
return &cond == font_key;
}
void conditional_syscall(const char *font_key) {
syscall_internal_elevate_privilege();
if (font_key) {
if (!cond(font_key)) {
do_stuff(font_key, 5);
}
}
}
void store_syscall(char * buf, int size) {
syscall_internal_elevate_privilege();
buf[0] = 'a';
char * new = buf;
do_stuff(new, size);
}
void load_syscall(char * buf, int size) {
syscall_internal_elevate_privilege();
char test = buf[0];
do_stuff(&test, size);
}
void bind_syscall(char * buf, int size) {
syscall_internal_elevate_privilege();
char * new = buf;
do_stuff(new, size);
}
void malloc_syscall() {
syscall_internal_elevate_privilege();
void *buf = app_malloc(5);
syscall_assert_userspace_buffer(buf, 5);
do_stuff(buf, 5);
}

70
checkers/wscript Normal file
View file

@ -0,0 +1,70 @@
from os import path
def options(opt):
opt.load('compiler_cxx')
opt.add_option('--checker', action='store', default='all')
def configure(conf):
conf.env.CXX = 'clang++'
conf.load('compiler_cxx')
conf.env.append_value('DEFINES', ['__STDC_CONSTANT_MACROS',
'__STDC_LIMIT_MACROS'])
conf.check_cfg(msg='Checking for llvm config',
path='llvm-config',
package='',
args='--cxxflags --ldflags --libs --system-libs',
uselib_store='LLVM')
clang_libs = ['clang',
'clangARCMigrate',
'clangAST',
'clangASTMatchers',
'clangAnalysis',
'clangApplyReplacements',
'clangBasic',
'clangCodeGen',
'clangDriver',
'clangDynamicASTMatchers',
'clangEdit',
'clangFormat',
'clangFrontend',
'clangFrontendTool',
'clangIndex',
'clangLex',
'clangParse',
'clangQuery',
'clangRename',
'clangRewrite',
'clangRewriteFrontend',
'clangSema',
'clangSerialization',
'clangStaticAnalyzerCheckers',
'clangStaticAnalyzerCore',
'clangStaticAnalyzerFrontend',
'clangTooling',
'clangToolingCore']
conf.check_cxx(msg='Checking for clang++',
uselib_store='CLANG',
use=['LLVM'],
lib=clang_libs)
def build(bld):
checkers = []
if bld.options.checker == 'all':
checkers = bld.path.ant_glob('*.cpp')
else:
checkers = [ bld.path.make_node(bld.options.checker) ]
for checker in checkers:
source = [ checker ]
target = checker.change_ext('.dylib')
bld.shlib(source=source,
target=target,
use=['CLANG', 'LLVM'],
cppflags=['-fno-rtti', '-std=c++11', '-fPIC'])
# vim:filetype=python