Commit bd02172d authored by Sergio Costas's avatar Sergio Costas

Added special support for malloc, calloc, realloc and free functions.

parent e80f1b4f
......@@ -44,13 +44,16 @@ The available tags are:
* __crust__ : specifies that this is a CRUST element, and must
be tracked by the CRUST static analyzer. It can be applied only to
single-indirection pointers.
* __crust_borrow__ : when used with an argument in a function
definition, it means that the pointer is borrowed to that function,
so the calling function retains the ownership. When used with the
return value of a function, it means that the calling function won't
receive the ownership, but it is retained by the called function.
* __crust_not_null__ : specifies that an argument in a function or
the return value will never be NULL.
* __crust_recycle__ : when a function returns a CRUST pointer, and
has at least one CRUST argument, it is possible to mark one (and
only one) of those CRUST arguments as *recyclable*. That means that
......@@ -58,27 +61,33 @@ The available tags are:
return value if available. In other words: if the *recycle* argument
is not NULL, the return value will never be NULL, but if the *recycle*
argument is NULL, then the return value can be NULL.
* __crust_alias__ : an alias variable is a variable that can point
to the same block pointer by other CRUST-type variable, in which
case it will have exactly the same state than the main pointer. But
it can also point to a block not pointed by any other CRUST pointer.
It is useful for FOR loops to operate over a linked list of CRUST
elements without freeing them.
* __crust_disable__ : disables the error messages. Useful when there
is no way of implementing something without violating the rules.
* __crust_enable__ : after a __crust_disable__ statement, enables
again the error messages. If there are nested tags, it is needed as
many *enable* tags as *disable* to re-enable the messages.
* __crust_full_enable__ : enables the error messages without taking
into account the disable counter (three *disable* tags require three
*enable* tags to re-enable the messages, but only one *full_enable*
tag).
* __crust_no_0__ : by default, when analyzing FOR and WHILE loops,
CRUST presumes that it is possible to never run the code inside (if
the condition is met before running the loop). If it is known for
sure that the loop will run at least once, but CRUST can't infer it,
it is possible to append this tag to the loop to specify that the
code inside must be evaluated at least once.
* __crust_debug__ : shows the status of the analysis. Useful when
writing unitary tests, to ensure that the pointers have the right
status.
......
......@@ -628,7 +628,8 @@ class crust(object):
if (orig_eval["type"] == self.TYPE_NO_CRUST):
self._add_error(orig_eval["thread_status"], self.MSG_ERROR, "Assigning the non-crust-type result value of function '{:s}' at line {:d} to the crust variable '{:s}'", orig_data["name"], statement.line, dest_data["name"])
else:
self._add_error(orig_eval["thread_status"], self.MSG_WARNING, "Assigning, with a typecast, the non-crust-type result value of function '{:s}' at line {:d} to the crust variable '{:s}'", orig_data["name"], statement.line, dest_data["name"])
if (orig_data["name"] != "malloc") and (orig_data["name"] != "calloc") and (orig_data["name"] != "realloc"):
self._add_error(orig_eval["thread_status"], self.MSG_WARNING, "Assigning, with a typecast, the non-crust-type result value of function '{:s}' at line {:d} to the crust variable '{:s}'", orig_data["name"], statement.line, dest_data["name"])
if ret_value["borrowed"] and (not dest_data["borrowed"]):
self._add_error(orig_eval["thread_status"], self.MSG_ERROR, "Assigning the borrowed result value of function '{:s}' to the non-borrowed variable '{:s}' at line {:d}", orig_data["name"], dest_data["name"], statement.line)
if dest_data["borrowed"] and (not ret_value["borrowed"]):
......@@ -695,7 +696,7 @@ class crust(object):
function_status = function_data_base["value"]
has_ellipsis = function_data_base["ellipsis"]
if function_data is None:
if (function_data is None):
retval_type = self.TYPE_NO_MATTER
retval_value = self.VALUE_NOT_NULL_OR_NULL
else:
......@@ -754,7 +755,9 @@ class crust(object):
variable = statement.function_params[pos]
tmp_retvals = []
# Evaluate each argument in each possible execution branch
nparam = -1
for rval in retvals:
nparam += 1
threads = self._eval_statement(variable, rval["thread_status"])
for thread in threads:
# Update the state of the variables
......@@ -787,7 +790,7 @@ class crust(object):
error_found = True
continue
else:
if passed_is_crust:
if passed_is_crust and (statement.name != "free") and (statement.name != "realloc"):
if variable_parameters:
self._add_error(thread["thread_status"], self.MSG_CRITICAL, "Passed a __crust__ variable as argument {:d} when calling function '{:s}' at line {:d}, but __crust__ variables are not allowed for optional arguments in functions with variable number of arguments", pos+1, statement.name, statement.line)
else:
......@@ -795,8 +798,13 @@ class crust(object):
error_found = True
continue
if not param_is_crust:
continue
if (statement.name != "free") and (statement.name != "realloc"):
if (not param_is_crust):
continue
if statement.name == "realloc":
if (nparam == 0) and (not passed_is_crust):
continue
if var_data["value"] == self.VALUE_UNINITIALIZED:
self._add_error(thread["thread_status"], self.MSG_ERROR, "Argument {:d} when calling function '{:s}' at line {:d} isn't initialized", pos+1, statement.name, statement.line)
......
......@@ -916,6 +916,29 @@ class Test(unittest.TestCase):
def test214InitializeStructs(self):
self._all_fine_test("unitest/test214.c")
def test215TestFree(self):
self._all_fine_test("unitest/test215.c")
def test216TestRealloc(self):
self._all_fine_test("unitest/test216.c")
def test217TestRealloc2(self):
self._generic_test("unitest/test217.c", [ (crust.crust.MSG_ERROR, "Memory block '{:s}', initialized at line {:d}, is still in use at exit point in line {:d}", "var2", 17, 18) ])
def test218TestMalloc(self):
self._all_fine_test("unitest/test218.c")
def test219TestMalloc2(self):
self._generic_test("unitest/test219.c", [ (crust.crust.MSG_ERROR, "Memory block '{:s}', initialized at line {:d}, is still in use at exit point in line {:d}", "var", 17, 18) ])
def test220TestCalloc(self):
self._all_fine_test("unitest/test220.c")
def test221TestCalloc2(self):
self._generic_test("unitest/test221.c", [ (crust.crust.MSG_ERROR, "Memory block '{:s}', initialized at line {:d}, is still in use at exit point in line {:d}", "var", 17, 18) ])
if __name__ == '__main__':
try:
os.remove("error_list.txt")
......
#define NULL ((void *)0)
void free(void *);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
void function(crust_t var1, char *var2) {
free(var1);
free(var2);
}
#define NULL ((void *)0)
void *realloc(void *ptr, int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
crust_t function(crust_t var1) {
return (crust_t) realloc(var1, 800);
}
#define NULL ((void *)0)
void *realloc(void *ptr, int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
void function(crust_t var1) {
crust_t var2 = NULL;
var2 = (crust_t) realloc(var1, 800);
}
#define NULL ((void *)0)
void *malloc(int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
crust_t function() {
return (crust_t) malloc(800);
}
#define NULL ((void *)0)
void *malloc(int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
void function() {
crust_t var = NULL;
var = (crust_t) malloc(800);
}
#define NULL ((void *)0)
void *calloc(int nmemb, int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
crust_t function() {
return (crust_t) calloc(1, 800);
}
#define NULL ((void *)0)
void *calloc(int nmemb, int size);
struct crust_ts {
char var1;
int var2;
struct crust_ts *next;
};
typedef __crust__ struct crust_ts* crust_t;
void function() {
crust_t var = NULL;
var = (crust_t) calloc(1, 800);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment