Skip to content

Range check via Cprover assert #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 22, 2019
Merged

Conversation

xbauch
Copy link
Contributor

@xbauch xbauch commented Dec 11, 2018

No description provided.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is going in the right direction but needs some cleanup and some rethinking of how we pass around ranges before it can go in.

There is also no test attached to this.

function Uint_To_Integer (Number : Uint) return Integer
is
begin
return Integer (UI_To_Int (Number));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This restricts us to 32 bit bounds (Int is basically a 32 bit signed integer). We need to find a better way around this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hannes-steffenhagen-diffblue I've added one commit that addresses that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xbauch could you reference the commit please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit is gone now, but basically we went from Int to Uint (which is of arbitrary size). It is now in separate package range_check.

function Do_Assert (N : Node_Id; Assertion : Irep; Description : Irep)
return Irep;

function Do_Range_Assert (N : Node_Id; Value : Irep; Value_Type : Irep)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 3 functions actually generate code, but they're using the naming scheme of the functions that handle an AST node. I think we need to come up with less confusing names for these.

@@ -928,8 +1029,10 @@ package body Tree_Walk is
end if;
return R : constant Irep := New_Irep (I_Bounded_Signedbv_Type) do
Set_Width (R, Get_Width (Resolved_Underlying));
Set_Lower_Bound (R, Do_Constant (Low_Bound (Range_Expr)));
Set_Upper_Bound (R, Do_Constant (High_Bound (Range_Expr)));
Set_Lower_Bound (R, Uint_To_Integer (Intval (Low_Bound

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think it makes much sense for these to be properties on the type; For dynamic bounds at least we'll need to carry around the bounds in variables/hidden parameters anyway

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is keeping us from using unbounded signedbv types for the bounds here by the way?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hannes-steffenhagen-diffblue I'm not sure what is going to be the best approach for dynamic bounds. As of now, the bounds are Uints (as per the last commit) and the Irep properties are Integer. Having any other numeric type inside Ireps would require changing the schemes, and the generating Python script.

@xbauch xbauch force-pushed the feature/range_check branch from e651ce3 to 0daa8bf Compare December 14, 2018 12:30
@xbauch
Copy link
Contributor Author

xbauch commented Dec 14, 2018

@hannes-steffenhagen-diffblue I've renamed the 3 functions that create code (to Make_*_Expr/Call). Changed the expected output of ranges test (which is intended as a test for this feature). And generalised the bounds to Uints which are stored in a separate hash-table and only indexed from Ireps for bounded types. I squashed it to 1 commit, but can break it again.

(Index_Type => Natural,
Element_Type => Uint);

Integer_Bounds_Table : Integer_Bounds_Vector.Vector;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ I think I'd be more comfortable if the above were in a separate package, also if the key had its own type (so we'd cast on access, but for here could keep the key something that is distinct from an integer). The latter just to prevent accidental misuse, the former because this package is already very crowded as is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to a separate package and declared a new type Bound_Type.

function Make_Sym_Range_Expression (I : Irep) return Irep
with Pre => Kind (I) = I_Symbol_Expr,
Post => Kind (Make_Sym_Range_Expression'Result) in Class_Expr;
function Make_Sym_Range_Expression (I : Irep; Val_Type : Irep) return Irep

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is called Sym_Range_Expression but we no longer demand the parameter to be a symbol?

declare
Cast_RHS : constant Irep := New_Irep (I_Op_Typecast);
begin
Set_Op0 (Cast_RHS, RHS);
Set_Type (Cast_RHS, Get_Type (LHS));
Set_Range_Check (Cast_RHS, True);
Set_Rhs (R, Cast_RHS);
Set_Rhs (R, Make_Range_Assert_Expr (N, Cast_RHS,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the range checks should happen before the typecast, in case of a cast into a smaller type? Then again, what happens when casting to a larger type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swapped. I guess casting to a larger type should simply pass the range check.

Value => Sym_Type);
Set_Value (I => Lower_Bound,
Value => Load_Bound_In_Hex (Get_Lower_Bound (Sym_Type),
Pos (Get_Width (Sym_Type))));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the width of the type isn't large enough to hold the value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that mean that the bound cannot be stored in a variable of that type?

@@ -1 +1,3 @@
VERIFICATION SUCCESSFUL
[assertion.1] Range Check: FAILURE
[assertion.2] Range Check: FAILURE

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd probably be better to have a test where both failure and successes happen, and that's also testing some things like casting into a larger or smaller type

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've extended the existing test with a few more cases, making the range check pass as well. I'm not sure it cover all reasonable cases.

@xbauch xbauch force-pushed the feature/range_check branch 4 times, most recently from c00213b to 2b05e8a Compare January 11, 2019 14:26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some preliminary comments, not quite done reviewing yet


function Build_Assert_Function return Symbol
is
Func_Type : constant Irep := New_Irep (I_Code_Type);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ All the ireps in here should have the source location of N attached to them, I don't think this necessarily needs to be done now though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Undone, only Class_Expr ireps can have source location.


declare
R : constant Irep := New_Irep (I_Code_Assign);
begin

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change from the return to the declare form? AFAICT they're equivalent right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return was getting uncomfortably long, other than that I also think they are equivalent.

(Intval (N), Pos (Constant_Width)));
else
Set_Value (Ret, UI_Image (Input => Intval (N),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bit is unrelated to the range checks right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, I might have introduced it because the ranges were integer literals and this function did not handle them correctly. Maybe.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xbauch this really needs some comments.
This change was prompted by something - please tell us the reason behind the 'else'.
If this is one of those 'this works' kind of things that is hazy - please tell us what didn't work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sonodtt I will find out and describe it in the code. It will take a bit longer though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to work now, I still have some (mostly nitpicky) concerns left though

@@ -29,6 +29,15 @@ package GOTO_Utils is
function Symbol_Expr (Sym : Symbol) return Irep
with Post => Kind (Symbol_Expr'Result) = I_Symbol_Expr;

function Create_Fun_Parameter (Fun_Name : String; Param_Name : String;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should have at least a one line comment explaining what this function does, same with pretty much all the other new functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Identifier => Unintern (Func_Param_Id));
begin
A_Symbol_Table.Insert (Func_Param_Id, Value_Symbol);
Set_Type (Value_Arg, Param_Type);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the I_Type parameter of Make_Code_Parameter to set this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

A_Symbol_Table.Insert (Func_Param_Id, Value_Symbol);
Set_Type (Value_Arg, Param_Type);
Set_Identifier (Value_Arg, Unintern (Func_Param_Id));
Set_Base_Name (Value_Arg, "value");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above two are already set by Make_Code_Parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

New_Symbol.Mode := Intern ("C");
New_Symbol.Value := Value;

if not (Global_Symbol_Table.Contains (Key => Intern (Name))) then

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The else keys in this instance seems like it'd be an error

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit pick - I lean towards renaming this 'Add_To_Symbol_Table'.
Really happy that we start to have more utility functions - I think of them as good documentation (and they add robustness).
Might be helpful to add a comment w.r. to in which scenario Name and Basename should differ?
(or it will get used that one time it should not)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

package body Tree_Walk is

procedure Add_Entity_Substitution (E : Entity_Id; Subst : Irep);

procedure Append_Declare_And_Init
(Symbol : Irep; Value : Irep; Block : Irep; Source_Loc : Source_Ptr);

function Add_To_Table (Name : String; Symbol_Type : Irep; Value : Irep)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use a slightly clearer name. Maybe New_Symbol_Table_Entry or something like that?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Func_Args : constant Irep := New_Irep (I_Parameter_List);
Func_Name : constant String :=
Fresh_Var_Name ("range_check");
Body_Block : constant Irep := New_Irep (I_Code_Block);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ I'd prefer Make_XXX functions in place of these

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Func_Args => Func_Params (?)
( we do have argument vs parameter list and append_parameter vs append_argument)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

function Make_Range_Assert_Expr (N : Node_Id; Value : Irep;
Value_Type : Irep)
return Irep
is

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use a comment showing (in pseudocode) what kind of code is being generated here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -1575,25 +1788,12 @@ package body Tree_Walk is
Sym_Nondet : constant Irep :=
Fresh_Var_Symbol_Expr (Type_Irep, Func_Str);
SE_Call_Expr : constant Irep :=
New_Irep (I_Side_Effect_Expr_Function_Call);
Make_Assume_Expr (N, Make_Range_Expression (Sym_Nondet,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⛏️ It might make sense to put the definition of Make_Assume_Expr and Make_Assert_Call in a separate commit, although I'd see that as pretty optional

New_Symbol.Mode := Intern ("C");
New_Symbol.Value := Value;

if not (Global_Symbol_Table.Contains (Key => Intern (Name))) then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit pick - I lean towards renaming this 'Add_To_Symbol_Table'.
Really happy that we start to have more utility functions - I think of them as good documentation (and they add robustness).
Might be helpful to add a comment w.r. to in which scenario Name and Basename should differ?
(or it will get used that one time it should not)

Func_Args : constant Irep := New_Irep (I_Parameter_List);
Func_Name : constant String :=
Fresh_Var_Name ("range_check");
Body_Block : constant Irep := New_Irep (I_Code_Block);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Func_Args => Func_Params (?)
( we do have argument vs parameter list and append_parameter vs append_argument)


Set_Parameters (Func_Type, Func_Args);
Set_Return_Type (Func_Type, Actual_Type);
Set_Ellipsis (Func_Type, False);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering if we know why 'Set_Ellipsis' is required and has value False

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was among the things we thought might fix the parameter-assignment problem. It wasn't, but I think it denotes the use of parameter packs. There should be none, hence false.

function Make_Assert_Call (N : Node_Id; Assertion : Irep;
Description : Irep)
return Irep is
SE_Call_Expr : constant Irep :=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'SE_Call_Expr' - in particular the SE. This is no longer a side effect call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@xbauch
Copy link
Contributor Author

xbauch commented Jan 16, 2019

Added a commit that follows the comment suggestions (separate now to ease review). Will squash/reorder commits once approved.

Copy link
Contributor

@NlightNFotis NlightNFotis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@xbauch xbauch force-pushed the feature/range_check branch from 728c065 to e25a0cb Compare January 17, 2019 09:26
@xbauch xbauch mentioned this pull request Jan 17, 2019
@chrisr-diffblue
Copy link
Contributor

Needs a rebase, but otherwise LGTM

Petr Bauch added 4 commits January 22, 2019 16:02
Introduces range check bounds stored in a vector and loaded explicitly when
building the assertion for checking the range.
Automates creation of function parameters (including appending to arg list).
Slight refactor of the methods for working with assume/assert.
@xbauch xbauch force-pushed the feature/range_check branch from e25a0cb to f442210 Compare January 22, 2019 16:02
@xbauch xbauch merged commit dfe5c87 into diffblue:master Jan 22, 2019
@xbauch xbauch deleted the feature/range_check branch March 14, 2019 10:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants