Commit 10f87b45 authored by Sergio Costas's avatar Sergio Costas

Added extra state, for when a pointer freed was NOT_NULL_OR_NULL

parent 8e1a5170
......@@ -16,7 +16,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from ctypes import *
from crust import tokens
import sys
......@@ -43,12 +42,14 @@ class crust(object):
VALUE_NOT_NULL = 2
# when a variable has been initialized to a value that can be NULL or not NULL
VALUE_NOT_NULL_OR_NULL = 3
# when a variable is presumed to have been freed after have been passed to a function
# when a variable NOT NULL is presumed to have been freed after have been passed on to a function
VALUE_FREED = 4
# when a variable NULL_OR_NOT_NULL is presumed to have been freed after have been passed on to a function
VALUE_FREED_OR_NULL = 5
# the variable is global, so it is presumed that, when used, it is INITIALIZED_OR_NULL, and when assigned is FREED
VALUE_GLOBAL = 5
VALUE_GLOBAL = 6
# the variable is a function
VALUE_FUNCTION = 6
VALUE_FUNCTION = 7
CONDITION_FALSE = 0
CONDITION_TRUE = 1
......@@ -63,7 +64,7 @@ class crust(object):
TYPE_NO_CRUST = 1
TYPE_CRUST = 2
state_str = ["VALUE_UNINITIALIZED", "VALUE_NULL", "VALUE_NOT_NULL", "VALUE_NOT_NULL_OR_NULL", "VALUE_FREED", "VALUE_GLOBAL", "VALUE_FUNCTION" ]
state_str = ["VALUE_UNINITIALIZED", "VALUE_NULL", "VALUE_NOT_NULL", "VALUE_NOT_NULL_OR_NULL", "VALUE_FREED", "VALUE_FREED_OR_NULL", "VALUE_GLOBAL", "VALUE_FUNCTION" ]
class VarNotFoundException(Exception):
pass
......@@ -354,11 +355,17 @@ class crust(object):
for varname in blocks:
if blocks[varname]["uid"] == block_id:
blocks[varname]["init_line"] = line_number
blocks[varname]["value"] = self.VALUE_FREED
if blocks[varname]["value"] == self.VALUE_NOT_NULL:
blocks[varname]["value"] = self.VALUE_FREED
elif blocks[varname]["value"] == self.VALUE_NOT_NULL_OR_NULL:
blocks[varname]["value"] = self.VALUE_FREED_OR_NULL
blocks[varname]["uid"] = None
else:
variable["init_line"] = line_number
variable["value"] = self.VALUE_FREED
if variable["value"] == self.VALUE_NOT_NULL:
variable["value"] = self.VALUE_FREED
elif variable["value"] == self.VALUE_NOT_NULL_OR_NULL:
variable["value"] = self.VALUE_FREED_OR_NULL
variable["uid"] = None
def _set_var_value(self, thread_status, var_name, value, line_number, force = False):
......@@ -423,7 +430,7 @@ class crust(object):
if varname["value"] == crust.VALUE_UNINITIALIZED:
self._add_error(thread_status, self.MSG_ERROR, "Using uninitialized variable '{:s}' at line {:d}", variable.name, line_number)
return
if varname["value"] == crust.VALUE_FREED:
if (varname["value"] == crust.VALUE_FREED) or (varname["value"] == crust.VALUE_FREED_OR_NULL):
self._add_error(thread_status, self.MSG_ERROR, "Using variable '{:s}' at line {:d}, after being freed at line {:d}", variable.name, line_number, varname["init_line"])
return
......@@ -672,7 +679,7 @@ class crust(object):
elif dest_data["value"] == self.VALUE_UNINITIALIZED:
if (dest_var.right is None) or (dest_var.right[0].type != "."):
self._add_error(orig_eval["thread_status"], self.MSG_ERROR, "Using variable '{:s}' at line {:d}, but it hasn't been initialized yet", dest_data["name"], statement.line)
elif dest_data["value"] == self.VALUE_FREED:
elif (dest_data["value"] == self.VALUE_FREED) or (dest_data["value"] == self.VALUE_FREED_OR_NULL):
self._add_error(orig_eval["thread_status"], self.MSG_ERROR, "Using variable '{:s}' at line {:d}, but it has been already freed", dest_data["name"], statement.line)
old_block = None
......@@ -819,7 +826,7 @@ class crust(object):
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)
error_found = True
continue
if var_data["value"] == self.VALUE_FREED:
if (var_data["value"] == self.VALUE_FREED) or (var_data["value"] == self.VALUE_FREED_OR_NULL):
self._add_error(thread["thread_status"], self.MSG_ERROR, "Argument {:d} when calling function '{:s}' at line {:d} was freed at line {:d}", pos+1, statement.name, statement.line, var_data["init_line"])
error_found = True
continue
......@@ -1049,6 +1056,8 @@ class crust(object):
for thread2 in threads2:
if ((thread1["value"] == self.VALUE_FREED) or
(thread2["value"] == self.VALUE_FREED) or
(thread1["value"] == self.VALUE_FREED_OR_NULL) or
(thread2["value"] == self.VALUE_FREED_OR_NULL) or
(thread1["value"] == self.VALUE_UNINITIALIZED) or
(thread2["value"] == self.VALUE_UNINITIALIZED)):
new_status = self.VALUE_UNINITIALIZED
......@@ -1095,7 +1104,7 @@ class crust(object):
status = var_data["value"]
if status == crust.VALUE_UNINITIALIZED:
self._add_error(thread_status, self.MSG_ERROR, "Using uninitialized variable '{:s}' at line {:d}", statement.name, statement.line)
elif status == crust.VALUE_FREED:
elif (status == crust.VALUE_FREED) or (status == crust.VALUE_FREED_OR_NULL):
self._add_error(thread_status, self.MSG_ERROR, "Using variable '{:s}' at line {:d}, after being freed at line {:d}", statement.name, statement.line, var_data["init_line"])
......@@ -1462,7 +1471,7 @@ class crust(object):
# we create two copies to be able to check if a block is pointed by more than one global variable
copy_var[variable] = data.copy()
namesvar.append(variable)
if data["value"] == self.VALUE_FREED:
if (data["value"] == self.VALUE_FREED) or (data["value"] == self.VALUE_FREED_OR_NULL):
self._add_error(thread_status, self.MSG_ERROR, "At exit point in line {:d}, global variable '{:s}' points to a block freed at line {:d}.", line, variable, data["init_line"])
counter = 0
......@@ -1600,7 +1609,6 @@ class crust(object):
self._process_enum(node, thread_status)
if node.t_typedef:
return self._check_return_block(tree[1:], thread_status)
varname = node.name
var_defined = self._get_variable_properties(thread_status, node, node.pointer, False)
if node.arrays is None:
if node.t_static:
......@@ -1669,7 +1677,7 @@ class crust(object):
var_data = self._find_variable(tmp_status, thread["node"].name, node.line)
if var_data["value"] == self.VALUE_UNINITIALIZED:
self._add_error(tmp_status, self.MSG_ERROR, "Returning variable '{:s}' at line {:d} is uninitialized",thread["node"].name, node.line)
if var_data["value"] == self.VALUE_FREED:
if (var_data["value"] == self.VALUE_FREED) or (var_data["value"] == self.VALUE_FREED_OR_NULL):
self._add_error(tmp_status, self.MSG_ERROR, "Returning variable '{:s}' at line {:d} was freed at line {:d}",thread["node"].name, node.line, var_data["init_line"])
if (self._check_statement_is_crust(thread["node"], var_data)) and (thread["type"] == self.TYPE_CRUST) and (not var_data["borrowed"]):
self._free_block(tmp_status, thread["node"].name, node.line)
......
......@@ -171,6 +171,7 @@ class Test(unittest.TestCase):
self._generic_test("unitest/test20.c", [ (crust.crust.MSG_ERROR, "Using uninitialized variable '{:s}' at line {:d}", "param4", 5) ])
def test021FreedVariable(self):
#self._all_fine_test("unitest/test21.c")
self._generic_test("unitest/test21.c", [ (crust.crust.MSG_ERROR, "Using variable '{:s}' at line {:d}, after being freed at line {:d}", "param4", 8, 7) ])
def test022AssignFreedVariable(self):
......@@ -330,8 +331,8 @@ class Test(unittest.TestCase):
def test072AliasFreesBlock(self):
lib = self._all_fine_test("unitest/test72.c")
self._check_var_state(lib, "param", crust.crust.VALUE_FREED)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED)
self._check_var_state(lib, "param", crust.crust.VALUE_FREED_OR_NULL)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED_OR_NULL)
def test073AliasFreesBlock2(self):
self._generic_test("unitest/test73.c", [ (crust.crust.MSG_ERROR, "Argument {:d} when calling function '{:s}' at line {:d} was freed at line {:d}", 1, "main", 9, 8) ])
......@@ -342,21 +343,21 @@ class Test(unittest.TestCase):
def test075DecoupleAlias(self):
lib = self._generic_test("unitest/test75.c", [ (crust.crust.MSG_ERROR, "Argument {:d} when calling function '{:s}' at line {:d} was freed at line {:d}", 1, "main", 13, 9) ])
self._check_var_state(lib, "an_alias", crust.crust.VALUE_NOT_NULL_OR_NULL, 0)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED, 1)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED_OR_NULL, 1)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_NULL, 2)
def test076DecoupleAlias2(self):
lib = self._all_fine_test("unitest/test76.c")
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED, 0)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED_OR_NULL, 0)
self._check_var_state(lib, "param2", crust.crust.VALUE_NOT_NULL_OR_NULL, 0)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_NOT_NULL_OR_NULL, 0)
self._check_var_state(lib, "param2", crust.crust.VALUE_FREED, 1)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED, 1)
self._check_var_state(lib, "param2", crust.crust.VALUE_FREED_OR_NULL, 1)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_FREED_OR_NULL, 1)
def test077DecoupleAlias3(self):
lib = self._generic_test("unitest/test77.c", [ (crust.crust.MSG_ERROR, "Assigning the crust-type result value of function '{:s}' to the alias '{:s}' at line {:d}", "function", "an_alias", 11) ])
self._check_var_state(lib, "param", crust.crust.VALUE_FREED, 1)
self._check_var_state(lib, "param", crust.crust.VALUE_FREED_OR_NULL, 1)
self._check_var_state(lib, "an_alias", crust.crust.VALUE_NOT_NULL_OR_NULL, 1)
def test078FunctionPointerNull(self):
......@@ -559,10 +560,10 @@ class Test(unittest.TestCase):
self._check_block_id_is_the_same(lib, "param2", "alias1", 3)
self._check_block_id_is_the_same(lib, "param1", "alias2", 3)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED_OR_NULL, 4)
self._check_var_state(lib, "param2", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias2", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias2", crust.crust.VALUE_FREED_OR_NULL, 4)
def test130AssignAliasFromAlias(self):
......@@ -589,7 +590,7 @@ class Test(unittest.TestCase):
self._check_block_id_is_the_same(lib, "param", "alias2", 3)
self._check_var_state(lib, "param", crust.crust.VALUE_NOT_NULL, 4)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED_OR_NULL, 4)
self._check_var_state(lib, "alias2", crust.crust.VALUE_NOT_NULL, 4)
self._check_block_id_is_not_same(lib, "param", "alias1", 4)
self._check_block_id_is_the_same(lib, "param", "alias2", 4)
......@@ -639,15 +640,15 @@ class Test(unittest.TestCase):
self._check_block_id_is_the_same(lib, "param1", "alias1", 2)
self._check_block_id_is_the_same(lib, "param2", "alias2", 2)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED, 3)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED_OR_NULL, 3)
self._check_var_state(lib, "param2", crust.crust.VALUE_NOT_NULL, 3)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED, 3)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED_OR_NULL, 3)
self._check_var_state(lib, "alias2", crust.crust.VALUE_NOT_NULL, 3)
self._check_block_id_is_the_same(lib, "param2", "alias2", 3)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "param1", crust.crust.VALUE_FREED_OR_NULL, 4)
self._check_var_state(lib, "param2", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED, 4)
self._check_var_state(lib, "alias1", crust.crust.VALUE_FREED_OR_NULL, 4)
self._check_var_state(lib, "alias2", crust.crust.VALUE_FREED, 4)
......@@ -988,6 +989,9 @@ class Test(unittest.TestCase):
def test238CompareWithCrustPointerAloneFreed(self):
self._generic_test("unitest/test238.c", [ (crust.crust.MSG_ERROR, "Using variable '{:s}' at line {:d}, after being freed at line {:d}", "p", 9, 7) ])
def test239NullCrustPointerIsNotFreed(self):
self._all_fine_test("unitest/test239.c")
if __name__ == '__main__':
try:
os.remove("error_list.txt")
......
......@@ -21,7 +21,7 @@ void main(crust_t param1, crust_t __crust_not_null__ param2) {
__crust_debug__ // here, 'param1', 'alias1' must be NOT_NULL_OR_NULL and share UID, 'param2', 'alias2' must be NOT_NULL and share UID
alias2->next = alias1;
__crust_debug__ // here 'param1', 'alias1' must be FREED, 'param2', 'alias2' must be NOT_NULL and share UID
__crust_debug__ // here 'param1', 'alias1' must be FREED_OR_NULL, 'param2', 'alias2' must be NOT_NULL and share UID
function(param2);
__crust_debug__ // here 'param1', 'alias1', 'param2', 'alias2' must be FREED
__crust_debug__ // here 'param1' and 'alias1' must be FREED_OR_NULL, and 'param2', 'alias2' must be FREED
}
......@@ -2,9 +2,9 @@ typedef __crust__ unsigned char *crust_t;
int function2(crust_t param1);
void function(crust_t param1) {
crust_t param4 = NULL;
void function(crust_t param1, crust_t param4) {
int retval = function2(param4);
retval = (param4 == param1) ? 1 : 0; // ERROR: condition depends on a freed variable
retval = (param4 == param1) ? 1 : 0; // ERROR: param4 has already been freed
retval = function2(param1);
}
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