Skip to content

Commit

Permalink
add command line options to allow unsafe pointer dereferences
Browse files Browse the repository at this point in the history
added "--null-ptr-deref-prob <N>" for null pointer dereferences and "--dangling-ptr-deref-prob <N>" for dangling pointer dereferences where N >= 0 and N <= 100. By default N = 0, there are no unsafe pointer dereferences in the random program. These options will be used to generate pointer-unsafe C programs that can be test cases of static analyzers.
  • Loading branch information
Xuejun Yang committed Jun 18, 2011
1 parent d1ae3b2 commit 7e33250
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 54 deletions.
18 changes: 14 additions & 4 deletions src/CGContext.cpp
Expand Up @@ -30,6 +30,7 @@
#include <cassert>

#include "CGContext.h"
#include "CGOptions.h"
#include "Effect.h"
#include "Variable.h"
#include "Function.h"
Expand Down Expand Up @@ -232,6 +233,8 @@ bool CGContext::read_pointed(const ExpressionVariable* v, const std::vector<cons
assert(indirect > 0);
incr_counter(Bookkeeper::dereference_level_cnts, indirect);

bool allow_null_ptr = CGOptions::null_pointer_dereference_prob() > 0;
bool allow_dead_ptr = CGOptions::dead_pointer_dereference_prob() > 0;
if (!read_indices(v->get_var(), facts)) {
return false;
}
Expand All @@ -241,14 +244,16 @@ bool CGContext::read_pointed(const ExpressionVariable* v, const std::vector<cons
while (indirect-- > 0) {
tmp = FactPointTo::merge_pointees_of_pointers(tmp, facts);
// make sure there is no null/dead pointers
if (tmp.size()==0 || find_variable_in_set(tmp, FactPointTo::null_ptr)!=-1 || find_variable_in_set(tmp, FactPointTo::garbage_ptr) != -1) {
if (tmp.size()==0 ||
(!allow_null_ptr && is_variable_in_set(tmp, FactPointTo::null_ptr)) ||
(!allow_dead_ptr && is_variable_in_set(tmp, FactPointTo::garbage_ptr))) {
*effect_accum = effect_accum_copy;
return false;
}
// make sure the remaining pointee are readable in context
for (i=0; i<tmp.size(); i++) {
const Variable* pointee = tmp[i];
if (pointee != FactPointTo::tbd_ptr) {
if (!FactPointTo::is_special_ptr(pointee)) {
if (!check_read_var(pointee, facts)) {
*effect_accum = effect_accum_copy;
return false;
Expand All @@ -273,18 +278,23 @@ bool CGContext::write_pointed(const Lhs* v, const std::vector<const Fact*>& fact

vector<const Variable*> tmp;
tmp.push_back(v->get_var()->get_collective());

bool allow_null_ptr = CGOptions::null_pointer_dereference_prob() > 0;
bool allow_dead_ptr = CGOptions::dead_pointer_dereference_prob() > 0;
// recursively trace the pointer(s) to find real variables they point to
while (indirect-- > 0) {
tmp = FactPointTo::merge_pointees_of_pointers(tmp, facts);
// make sure there is no null/dead pointers
if (tmp.size()==0 || find_variable_in_set(tmp, FactPointTo::null_ptr)!=-1 || find_variable_in_set(tmp, FactPointTo::garbage_ptr) != -1) {
if (tmp.size()==0 ||
(!allow_null_ptr && is_variable_in_set(tmp, FactPointTo::null_ptr)) ||
(!allow_dead_ptr && is_variable_in_set(tmp, FactPointTo::garbage_ptr))) {
*effect_accum = effect_accum_copy;
return false;
}
// make sure the remaining pointee are readable or writable(if it is the ultimate pointee) in context
for (i=0; i<tmp.size(); i++) {
const Variable* pointee = tmp[i];
if (pointee != FactPointTo::tbd_ptr) {
if (!FactPointTo::is_special_ptr(pointee)) {
bool succ = false;
if (indirect==0) {
succ = check_write_var(pointee, facts);
Expand Down
4 changes: 4 additions & 0 deletions src/CGOptions.cpp
Expand Up @@ -161,6 +161,8 @@ DEFINE_GETTER_SETTER_BOOL(identify_wrappers)
DEFINE_GETTER_SETTER_BOOL(mark_mutable_const)
DEFINE_GETTER_SETTER_BOOL(force_globals_static)
DEFINE_GETTER_SETTER_BOOL(force_non_uniform_array_init)
DEFINE_GETTER_SETTER_INT(null_pointer_dereference_prob)
DEFINE_GETTER_SETTER_INT(dead_pointer_dereference_prob)

void
CGOptions::set_default_settings(void)
Expand Down Expand Up @@ -239,6 +241,8 @@ CGOptions::set_default_settings(void)
force_globals_static(true);
force_non_uniform_array_init(false);
max_array_num_in_loop(CGOPTIONS_DEFAULT_MAX_ARRAY_NUM_IN_LOOP);
null_pointer_dereference_prob(0);
dead_pointer_dereference_prob(0);
}

#define MAX_INTEGER_LENGTH 64
Expand Down
8 changes: 8 additions & 0 deletions src/CGOptions.h
Expand Up @@ -372,6 +372,12 @@ class CGOptions {
static bool force_non_uniform_array_init(void);
static bool force_non_uniform_array_init(bool p);

static int null_pointer_dereference_prob(void);
static int null_pointer_dereference_prob(int p);

static int dead_pointer_dereference_prob(void);
static int dead_pointer_dereference_prob(int p);

private:
static bool resolve_exhaustive_options();

Expand Down Expand Up @@ -479,6 +485,8 @@ class CGOptions {
static bool mark_mutable_const_;
static bool force_globals_static_;
static bool force_non_uniform_array_init_;
static int null_pointer_dereference_prob_;
static int dead_pointer_dereference_prob_;
static Reducer* reducer_;

private:
Expand Down
23 changes: 13 additions & 10 deletions src/ExpressionVariable.cpp
Expand Up @@ -86,17 +86,20 @@ ExpressionVariable::make_random(CGContext &cg_context, const Type* type, const C
FactPointTo::is_pointing_to_locals(var, cg_context.get_current_block(), indirection, fm->global_facts)) {
continue;
}
ExpressionVariable tmp(*var, type);
if (tmp.visit_facts(fm->global_facts, cg_context)) {
ev = tmp.get_indirect_level() == 0 ? new ExpressionVariable(*var) : new ExpressionVariable(*var, type);
cg_context.curr_blk = cg_context.get_current_block();
break;
}
else {
cg_context.reset_effect_accum(eff_accum);
cg_context.reset_effect_stm(eff_stmt);
dummy.push_back(var);
int valid = FactPointTo::opportunistic_validate(var, type, fm->global_facts);
if (valid) {
ExpressionVariable tmp(*var, type);
if (tmp.visit_facts(fm->global_facts, cg_context)) {
ev = tmp.get_indirect_level() == 0 ? new ExpressionVariable(*var) : new ExpressionVariable(*var, type);
cg_context.curr_blk = cg_context.get_current_block();
break;
}
else {
cg_context.reset_effect_accum(eff_accum);
cg_context.reset_effect_stm(eff_stmt);
}
}
dummy.push_back(var);
} while (true);

// statistics
Expand Down
75 changes: 42 additions & 33 deletions src/FactPointTo.cpp
Expand Up @@ -29,6 +29,7 @@

#include "FactPointTo.h"
#include <iostream>
#include "CGOptions.h"
#include "Fact.h"
#include "Type.h"
#include "VariableSelector.h"
Expand All @@ -44,6 +45,7 @@
#include "FunctionInvocationUser.h"
#include "FactMgr.h"
#include "Lhs.h"
#include "random.h"

#include <assert.h>

Expand Down Expand Up @@ -398,13 +400,16 @@ FactPointTo::point_to(const Variable* v) const

/*
* return true if ptr is either null nore dangling in the given context
* tell the analyzer sometimes it's ok to dereference null/dead pointers
*/
bool
FactPointTo::is_valid_ptr(const Variable* p, const std::vector<const Fact*>& facts)
{
FactPointTo fp(p);
FactPointTo fp(p);
const FactPointTo* fact = (const FactPointTo*)find_related_fact(facts, &fp);
return (fact && !fact->is_null() && !fact->is_dead());
return fact &&
(CGOptions::null_pointer_dereference_prob() > 0 || !fact->is_null()) &&
(CGOptions::dead_pointer_dereference_prob() > 0 || !fact->is_dead());
}

/*
Expand All @@ -425,6 +430,40 @@ FactPointTo::is_valid_ptr(const char* name, const std::vector<const Fact*>& fact
return true;
}

/*
* validate the pointer with some chance of overlooking safety check
* this can create some null/dangling pointer dereferences, which
* are used to test static analyzers (not compilers)
*/
int
FactPointTo::opportunistic_validate(const Variable* var, const Type* type, const std::vector<const Fact*>& facts)
{
if (var->type->get_indirect_level() <= type->get_indirect_level()) {
return 1;
}
FactPointTo tmp(var->get_collective());
const FactPointTo* fp = dynamic_cast<const FactPointTo*>(find_related_fact(facts, &tmp));
if (fp == 0) return 0;
int ret = 0;
if (fp->is_null()) {
if (rnd_flipcoin(CGOptions::null_pointer_dereference_prob())) {
ret = 2;
} else {
return 0;
}
} else {
ret = 1;
}
if (fp->is_dead()) {
if (rnd_flipcoin(CGOptions::dead_pointer_dereference_prob())) {
ret = 2;
} else {
return 0;
}
}
return ret;
}

/*
* return true if ptr is dangling in the given context
*/
Expand All @@ -433,37 +472,7 @@ FactPointTo::is_dangling_ptr(const Variable* p, const std::vector<const Fact*>&
{
FactPointTo fp(p);
const FactPointTo* fact = (const FactPointTo*)find_related_fact(facts, &fp);
return (fact && fact->is_dead());
}

/*
* return true if expression is a dangling expression
*/
bool
FactPointTo::is_dangling_expr(const Expression* e, const std::vector<const Fact*>& facts)
{
const Type& type = e->get_type();
if (type.eType == ePointer) {
if (e->term_type == eVariable) {
const ExpressionVariable* ev = (const ExpressionVariable*)e;
if (ev->get_indirect_level()==0) {
FactPointTo fp(ev->get_var());
const FactPointTo* fact = (const FactPointTo*)find_related_fact(facts, &fp);
return (fact && fact->is_dead());
}
}
else if (e->term_type == eFunction) {
const ExpressionFuncall* ef = (const ExpressionFuncall*)e;
if (ef->get_invoke()->invoke_type == eFuncCall) {
const FunctionInvocationUser* fiu = (const FunctionInvocationUser*)(ef->get_invoke());
const FactPointTo* fact = (const FactPointTo*)(get_return_fact_for_invocation(fiu, ePointTo));
if (fact) {
return fact->is_dead();
}
}
}
}
return false;
return (fact && (fact->is_dead() && CGOptions::dead_pointer_dereference_prob() == 0));
}

/* return true if the variable has any chance to be a local variable after dereference */
Expand Down
5 changes: 3 additions & 2 deletions src/FactPointTo.h
Expand Up @@ -40,6 +40,7 @@ class Variable;
class Function;
class Statement;
class Block;
class Type;
class StatementAssign;
class StatementReturn;

Expand Down Expand Up @@ -95,10 +96,10 @@ class FactPointTo : public Fact
static void update_facts_with_modified_index(std::vector<const Fact*>& facts, const Variable* var);
static void aggregate_all_pointto_sets(void);

static int opportunistic_validate(const Variable* var, const Type* type, const std::vector<const Fact*>& facts);
static bool is_valid_ptr(const Variable* p, const std::vector<const Fact*>& facts);
static bool is_valid_ptr(const char* name, const std::vector<const Fact*>& facts);
static bool is_dangling_ptr(const Variable* p, const std::vector<const Fact*>& facts);
static bool is_dangling_expr(const Expression* e, const std::vector<const Fact*>& facts);
static bool is_dangling_ptr(const Variable* p, const std::vector<const Fact*>& facts);
static bool is_special_ptr(const Variable* p) { return p==null_ptr || p==garbage_ptr || p==tbd_ptr;}
static bool is_pointing_to_locals(const Variable* v, const Block* b, int indirection, const vector<const Fact*>& facts);

Expand Down
8 changes: 3 additions & 5 deletions src/Lhs.cpp
Expand Up @@ -90,11 +90,9 @@ Lhs::make_random(CGContext &cg_context, const Type* t, const CVQualifiers* qfer)
assert(!var->qfer.is_const_after_deref(deref_level));
}
ERROR_GUARD(NULL);
assert(var);
bool valid = true;
if (var->type->get_indirect_level() > t->get_indirect_level()) {
valid = FactPointTo::is_valid_ptr(var, fm->global_facts) && FactPointTo::is_valid_ptr(var, fm->shadow_facts);
}
assert(var);
bool valid = FactPointTo::opportunistic_validate(var, t, fm->global_facts) &&
FactPointTo::opportunistic_validate(var, t, fm->shadow_facts);
if (valid) {
assert(var);
Lhs* lhs = new Lhs(*var, t);
Expand Down
24 changes: 24 additions & 0 deletions src/RandomProgramGenerator.cpp
Expand Up @@ -285,6 +285,10 @@ static void print_advanced_help()
cout << " --mark-mutable-const: mark constants that can be mutated with parentheses (disabled by default)." << endl << endl;

cout << " --force-non-uniform-arrays: force integer arrays to be initialized with multiple values (disabled by default)." << endl << endl;

cout << " --null-ptr-deref-prob <N>: allow null pointers to be dereferenced with probability N% (0 by default)." << endl << endl;

cout << " --dangling-ptr-deref-prob <N>: allow dangling pointers to be dereferenced with probability N% (0 by default)." << endl << endl;
}

void arg_check(int argc, int i)
Expand Down Expand Up @@ -1044,6 +1048,26 @@ main(int argc, char **argv)
continue;
}

if (strcmp (argv[i], "--null-ptr-deref-prob") ==0 ) {
unsigned long prob;
i++;
arg_check(argc, i);
if (!parse_int_arg(argv[i], &prob))
exit(-1);
CGOptions::null_pointer_dereference_prob(prob);
continue;
}

if (strcmp (argv[i], "--dangling-ptr-deref-prob") ==0 ) {
unsigned long prob;
i++;
arg_check(argc, i);
if (!parse_int_arg(argv[i], &prob))
exit(-1);
CGOptions::dead_pointer_dereference_prob(prob);
continue;
}

if (strcmp (argv[i], "--reduce") == 0) {
string filename;
i++;
Expand Down

0 comments on commit 7e33250

Please sign in to comment.