Optimize normalization, substitution, renaming of array types

Summary:
Array types where the length is not statically known were represented
using fresh variables.  This diff:

- Makes array type length optional, reducing the amount of work needed
  for renaming, substitution, and normalization.

- Revises uses of array length so that the length component of a
  Tarray type represents only the statically determined constant
  length of an array type, and the length component of a Sizeof
  expression represents the dynamically determined length of an array
  value.

- Restricts the type of static lengths from a general expression
  (Sil.exp) to an integer (Sil.Int.t), enforcing that static types are
  constant.  This in particular ensures that types contain no
  variables, and so are invariant under operations such as renaming
  and substitution.

- Removes the type substitution and renaming functions typ_sub,
  typ_normalize, and typ_captured_ren.  Now that array type lengths
  are constant integers, all of these functions are the identity.

Reviewed By: cristianoc

Differential Revision: D3387343

fbshipit-source-id: b5db768
master
Josh Berdine 9 years ago committed by Facebook Github Bot 6
parent eb69ce79bf
commit 229ca26c8b

@ -33,11 +33,11 @@ void* g_realloc(void* ptr, size_t size) {
return NULL;
}
int old_size;
old_size = __get_array_size(ptr); // force ptr to be an array
old_size = __get_array_length(ptr); // force ptr to be an array
int can_enlarge; // nondeterministically choose whether the current block can
// be enlarged
if (can_enlarge) {
__set_array_size(ptr, size); // enlarge the block
__set_array_length(ptr, size); // enlarge the block
return ptr;
}
int* newblock = (int*)malloc(size);

@ -61,13 +61,13 @@ clock_t __infer_nondet_clock_t();
extern void __require_allocated_array(const void* arr);
// builtin: return the size of arr
extern size_t __get_array_size(const void* arr);
extern size_t __get_array_length(const void* arr);
// builtin: change the attribute of ret to a file attribute
extern void __set_file_attribute(void* ret);
// builtin: change the size of the array to size
extern void __set_array_size(void* ptr, size_t size);
extern void __set_array_length(void* ptr, size_t size);
// builtin: set the flag to the given value for the procedure where this call
// appears

@ -84,9 +84,9 @@ char* strcpy(char* s1, const char* s2) {
__infer_set_flag("ignore_return",
""); // no warnings if the return value is ignored
__require_allocated_array(s1);
size1 = __get_array_size(s1);
size1 = __get_array_length(s1);
__require_allocated_array(s2);
size2 = __get_array_size(s2);
size2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION(size2 > size1);
return s1;
}
@ -96,7 +96,7 @@ char* strcpy(char* s1, const char* s2) {
char* strdup(const char* s) {
int size;
__require_allocated_array(s);
size = __get_array_size(s);
size = __get_array_length(s);
return (char*)malloc(size);
}
@ -106,9 +106,9 @@ char* strcat(char* s1, const char* s2) {
int size1;
int size2;
__require_allocated_array(s1);
size1 = __get_array_size(s1);
size1 = __get_array_length(s1);
__require_allocated_array(s2);
size2 = __get_array_size(s2);
size2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION(size2 > size1);
return s1;
}
@ -133,7 +133,7 @@ char* strchr(char* s, int c) throw() {
offset = __infer_nondet_int();
__require_allocated_array(s);
size = __get_array_size(s);
size = __get_array_length(s);
if (nondet)
return 0;
INFER_EXCLUDE_CONDITION(offset < 0 || offset >= size);
@ -167,7 +167,7 @@ int strcmp(const char* s1, const char* s2) {
size_t strlen(const char* s) {
int size;
__require_allocated_array(s);
size = __get_array_size(s);
size = __get_array_length(s);
return size - 1;
}
@ -185,9 +185,9 @@ char* strncat(char* s1, const char* s2, size_t n) {
int size_s1;
int size_s2;
__require_allocated_array(s1);
size_s1 = __get_array_size(s1);
size_s1 = __get_array_length(s1);
__require_allocated_array(s2);
size_s2 = __get_array_size(s2);
size_s2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION((n > size_s1) || (n > size_s2));
return s1;
}
@ -201,9 +201,9 @@ int strncmp(const char* s1, const char* s2, size_t n) {
int res;
res = __infer_nondet_int();
__require_allocated_array(s1);
size_s1 = __get_array_size(s1);
size_s1 = __get_array_length(s1);
__require_allocated_array(s2);
size_s2 = __get_array_size(s2);
size_s2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION((n > size_s1) || (n > size_s2));
return res;
}
@ -216,7 +216,7 @@ char* strncpy(char* s1, const char* s2, size_t n) {
__infer_set_flag("ignore_return",
""); // no warnings if the return value is ignored
__require_allocated_array(s1);
size1 = __get_array_size(s1);
size1 = __get_array_length(s1);
__require_allocated_array(s2);
INFER_EXCLUDE_CONDITION(n > size1);
return s1;
@ -237,7 +237,7 @@ char* strpbrk(char* s1, const char* s2) throw() {
nondet = __infer_nondet_int();
offset = __infer_nondet_int();
__require_allocated_array(s1);
size1 = __get_array_size(s1);
size1 = __get_array_length(s1);
__require_allocated_array(s2);
if (nondet)
return 0;
@ -252,7 +252,7 @@ size_t strspn(const char* s1, const char* s2) {
int res;
res = __infer_nondet_int();
__require_allocated_array(s1);
size_s1 = __get_array_size(s1);
size_s1 = __get_array_length(s1);
__require_allocated_array(s2);
INFER_EXCLUDE_CONDITION(res < 0 || res > size_s1);
return res;
@ -274,7 +274,7 @@ char* strstr(char* s1, const char* s2) throw() {
nondet = __infer_nondet_int();
offset = __infer_nondet_int();
__require_allocated_array(s1);
size1 = __get_array_size(s1);
size1 = __get_array_length(s1);
__require_allocated_array(s2);
if (nondet)
return 0;
@ -300,7 +300,7 @@ unsigned long strtoul(const char* str, char** endptr, int base) {
int offset;
int res;
__require_allocated_array(str);
size = __get_array_size(str);
size = __get_array_length(str);
offset = __infer_nondet_int();
INFER_EXCLUDE_CONDITION(offset < 0 || offset >= size);
if (endptr)
@ -338,7 +338,7 @@ void* memchr(void* s, int c, size_t n) throw() {
nondet = __infer_nondet_int();
offset = __infer_nondet_int();
__require_allocated_array(s);
size = __get_array_size(s);
size = __get_array_length(s);
INFER_EXCLUDE_CONDITION(n > size);
if (nondet)
return 0;
@ -353,9 +353,9 @@ int memcmp(const void* s1, const void* s2, size_t n) {
int size_s1;
int size_s2;
__require_allocated_array(s1);
size_s1 = __get_array_size(s1);
size_s1 = __get_array_length(s1);
__require_allocated_array(s2);
size_s2 = __get_array_size(s2);
size_s2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION((n > size_s1) || (n > size_s2));
return __infer_nondet_int();
}
@ -368,9 +368,9 @@ void* memcpy(void* s1, const void* s2, size_t n) {
__infer_set_flag("ignore_return",
""); // no warnings if the return value is ignored
__require_allocated_array(s1);
size_s1 = __get_array_size(s1);
size_s1 = __get_array_length(s1);
__require_allocated_array(s2);
size_s2 = __get_array_size(s2);
size_s2 = __get_array_length(s2);
INFER_EXCLUDE_CONDITION((n < 0) || (n > size_s1) || (n > size_s2));
return s1;
}
@ -389,7 +389,7 @@ void* memset(void* s, int c, size_t n) {
__infer_set_flag("ignore_return",
""); // no warnings if the return value is ignored
__require_allocated_array(s);
size_s = __get_array_size(s);
size_s = __get_array_length(s);
INFER_EXCLUDE_CONDITION(n > size_s);
return s;
}
@ -434,7 +434,7 @@ char* tmpnam(char* s) {
return NULL;
if (s) {
__require_allocated_array(s);
size = __get_array_size(s);
size = __get_array_length(s);
INFER_EXCLUDE_CONDITION(size < L_tmpnam);
return s;
} else
@ -535,7 +535,7 @@ char* fgets(char* str, int num, FILE* stream) {
if (n > 0) {
__require_allocated_array(str);
size1 = __get_array_size(str);
size1 = __get_array_length(str);
INFER_EXCLUDE_CONDITION(num > size1);
return str;
} else
@ -614,7 +614,7 @@ char* getcwd(char* buffer, size_t size) {
if (n > 0) {
__require_allocated_array(buffer);
size_buf = __get_array_size(buffer);
size_buf = __get_array_length(buffer);
INFER_EXCLUDE_CONDITION(size > size_buf);
return buffer;
} else
@ -753,7 +753,7 @@ void* realloc(void* ptr, size_t size) {
can_enlarge = __infer_nondet_int(); // nondeterministically choose whether the
// current block can be enlarged
if (can_enlarge) {
__set_array_size(ptr, size); // enlarge the block
__set_array_length(ptr, size); // enlarge the block
return ptr;
}
int* newblock = (int*)malloc(size);
@ -1167,7 +1167,7 @@ size_t strlcpy(char* dst, const char* src, size_t size) {
// force dst to be allocated for at least size
__require_allocated_array(dst);
size_dst = __get_array_size(dst);
size_dst = __get_array_length(dst);
INFER_EXCLUDE_CONDITION(size > size_dst);
INFER_EXCLUDE_CONDITION(res > size || res < 0);
@ -1188,7 +1188,7 @@ size_t strlcat(char* dst, const char* src, size_t size) {
// force dst to be allocated for at least size
__require_allocated_array(dst);
size_dst = __get_array_size(dst);
size_dst = __get_array_length(dst);
INFER_EXCLUDE_CONDITION(size > size_dst);
INFER_EXCLUDE_CONDITION(res > size || res < 0);
@ -1533,7 +1533,7 @@ ssize_t read(int fildes, void* buf, size_t nbyte) {
if (nbyte == 0)
return 0;
__require_allocated_array(buf);
INFER_EXCLUDE_CONDITION(__get_array_size(buf) < nbyte);
INFER_EXCLUDE_CONDITION(__get_array_length(buf) < nbyte);
int ret = __infer_nondet_int();
INFER_EXCLUDE_CONDITION(ret < -1 || ret > nbyte);
@ -1665,7 +1665,7 @@ size_t strcspn(const char* s1, const char* s2) {
#if defined __APPLE__ || (defined __USE_XOPEN2K && !defined __USE_GNU)
int strerror_r(int errnum, char* strerrbuf, size_t buflen) {
__require_allocated_array(strerrbuf);
INFER_EXCLUDE_CONDITION(__get_array_size(strerrbuf) < buflen);
INFER_EXCLUDE_CONDITION(__get_array_length(strerrbuf) < buflen);
return __infer_nondet_int();
}
@ -1673,7 +1673,7 @@ int strerror_r(int errnum, char* strerrbuf, size_t buflen) {
ssize_t write(int fildes, const void* buf, size_t nbyte) {
__require_allocated_array(buf);
INFER_EXCLUDE_CONDITION(__get_array_size(buf) < nbyte);
INFER_EXCLUDE_CONDITION(__get_array_length(buf) < nbyte);
int ret = __infer_nondet_int();
INFER_EXCLUDE_CONDITION(ret < -1 || nbyte < ret);
@ -1731,7 +1731,7 @@ void* bsearch(const void* key,
nondet = __infer_nondet_int();
offset = __infer_nondet_int();
__require_allocated_array(base);
base_size = __get_array_size(base);
base_size = __get_array_length(base);
INFER_EXCLUDE_CONDITION(nmemb > base_size);
if (nondet)
return 0;

@ -127,7 +127,7 @@ int CLIBCALL swprintf(wchar_t* restrict s,
...) {
int res;
int size1;
size1 = __get_array_size(s);
size1 = __get_array_length(s);
res = __infer_nondet_int();
INFER_EXCLUDE_CONDITION(res < 0);
return res;

@ -12,7 +12,7 @@
CFStringRef __cf_alloc(CFStringRef);
void __get_array_size(const UInt8);
void __get_array_length(const UInt8);
CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc,
const UInt8* bytes,
@ -24,7 +24,7 @@ CFStringRef CFStringCreateWithBytesNoCopy(CFAllocatorRef alloc,
CFStringRef s = __cf_alloc(c);
if (s) {
if (bytes) {
__get_array_size(bytes);
__get_array_length(bytes);
free(bytes);
}
}

@ -10,7 +10,7 @@
#import "NSString.h"
#import <stdlib.h>
void __get_array_size(const UInt8);
void __get_array_length(const UInt8);
@implementation NSString
@ -41,7 +41,7 @@ void __get_array_size(const UInt8);
freeWhenDone:(BOOL)flag {
if (flag == YES) {
if (bytes) {
__get_array_size(bytes);
__get_array_length(bytes);
free(bytes);
}
}

@ -741,6 +741,10 @@ and struct_typ = {
def_methods: list Procname.t, /** methods defined */
struct_annotations: item_annotation /** annotations */
}
/** statically determined length of an array type, if any */
and static_length = option Int.t
/** dynamically determined length of an array value, if any */
and dynamic_length = option exp
/** types for sil (structured) expressions */
and typ =
| Tvar of Typename.t /** named type */
@ -750,7 +754,7 @@ and typ =
| Tfun of bool /** function type with noreturn attribute */
| Tptr of typ ptr_kind /** pointer type */
| Tstruct of struct_typ /** Type for a structured value */
| Tarray of typ exp /** array type with fixed size */
| Tarray of typ static_length /** array type with statically fixed length */
/** Program expressions. */
and exp =
/** Pure variable: it is not an lvalue */
@ -769,10 +773,24 @@ and exp =
| Lfield of exp Ident.fieldname typ
/** An array index offset: [exp1\[exp2\]] */
| Lindex of exp exp
/** A sizeof expression. [Sizeof typ (Some len)] represents the size of a value of type [typ]
which ends in an extensible array of length [len]. The lengths in [Tarray] record the
statically determined lengths, while the lengths in [Sizeof] record the dynamic lengths. */
| Sizeof of typ (option exp) Subtype.t;
/** A sizeof expression. [Sizeof (Tarray elt (Some static_length)) (Some dynamic_length)]
represents the size of an array value consisting of [dynamic_length] elements of type [elt].
The [dynamic_length], tracked by symbolic execution, may differ from the [static_length]
obtained from the type definition, e.g. when an array is over-allocated. For struct types,
the [dynamic_length] is that of the final extensible array, if any. */
| Sizeof of typ dynamic_length Subtype.t;
/** the element typ of the final extensible array in the given typ, if any */
let rec get_extensible_array_element_typ =
fun
| Tarray typ _ => Some typ
| Tstruct {instance_fields} =>
Option.map_default
(fun (_, fld_typ, _) => get_extensible_array_element_typ fld_typ)
None
(IList.last instance_fields)
| _ => None;
/** Kind of prune instruction */
@ -873,10 +891,10 @@ type inst =
type strexp =
| Eexp of exp inst /** Base case: expression with instrumentation */
| Estruct of (list (Ident.fieldname, strexp)) inst /** C structure */
| Earray of exp (list (exp, strexp)) inst /** Array of given size. */
| Earray of exp (list (exp, strexp)) inst /** Array of given length */
/** There are two conditions imposed / used in the array case.
First, if some index and value pair appears inside an array
in a strexp, then the index is less than the size of the array.
in a strexp, then the index is less than the length of the array.
For instance, x |->[10 | e1: v1] implies that e1 <= 9.
Second, if two indices appear in an array, they should be different.
For instance, x |->[10 | e1: v1, e2: v2] implies that e1 != e2. */
@ -2343,8 +2361,8 @@ and pp_struct_typ pe pp_base f struct_typ =>
}
/** Pretty print a type declaration.
pp_base prints the variable for a declaration, or can be skip to print only the type
pp_size prints the expression for the array size */
and pp_type_decl pe pp_base pp_size f =>
pp_static_len prints the expression for the array length */
and pp_type_decl pe pp_base pp_static_len f =>
fun
| Tvar tname => F.fprintf f "%s %a" (Typename.to_string tname) pp_base ()
| Tint ik => F.fprintf f "%s %a" (ikind_to_string ik) pp_base ()
@ -2354,16 +2372,21 @@ and pp_type_decl pe pp_base pp_size f =>
| Tfun true => F.fprintf f "_fn_noreturn_ %a" pp_base ()
| Tptr ((Tarray _ | Tfun _) as typ) pk => {
let pp_base' fmt () => F.fprintf fmt "(%s%a)" (ptr_kind_string pk) pp_base ();
pp_type_decl pe pp_base' pp_size f typ
pp_type_decl pe pp_base' pp_static_len f typ
}
| Tptr typ pk => {
let pp_base' fmt () => F.fprintf fmt "%s%a" (ptr_kind_string pk) pp_base ();
pp_type_decl pe pp_base' pp_size f typ
pp_type_decl pe pp_base' pp_static_len f typ
}
| Tstruct struct_typ => pp_struct_typ pe pp_base f struct_typ
| Tarray typ size => {
let pp_base' fmt () => F.fprintf fmt "%a[%a]" pp_base () (pp_size pe) size;
pp_type_decl pe pp_base' pp_size f typ
| Tarray typ static_len => {
let pp_array_static_len fmt => (
fun
| Some static_len => pp_static_len pe fmt (Const (Cint static_len))
| None => F.fprintf fmt "_"
);
let pp_base' fmt () => F.fprintf fmt "%a[%a]" pp_base () pp_array_static_len static_len;
pp_type_decl pe pp_base' pp_static_len f typ
}
/** Pretty print a type with all the details, using the C syntax. */
and pp_typ_full pe => pp_type_decl pe (fun _ () => ()) pp_exp_full
@ -2994,10 +3017,9 @@ let rec pp_sexp_env pe0 envo f se => {
F.fprintf f "%a:%a" (Ident.pp_fieldname_latex Latex.Boldface) n (pp_sexp_env pe envo) se;
F.fprintf f "\\{%a\\}%a" (pp_seq_diff pp_diff pe) fel (pp_inst_if_trace pe) inst
}
| Earray size nel inst =>
| Earray len nel inst =>
let pp_diff f (i, se) => F.fprintf f "%a:%a" (pp_exp pe) i (pp_sexp_env pe envo) se;
F.fprintf
f "[%a|%a]%a" (pp_exp pe) size (pp_seq_diff pp_diff pe) nel (pp_inst_if_trace pe) inst
F.fprintf f "[%a|%a]%a" (pp_exp pe) len (pp_seq_diff pp_diff pe) nel (pp_inst_if_trace pe) inst
};
color_post_wrapper changed pe0 f
}
@ -3205,13 +3227,13 @@ let rec strexp_expmap (f: (exp, option inst) => (exp, option inst)) => {
let f_fld_se (fld, se) => (fld, strexp_expmap f se);
Estruct (IList.map f_fld_se fld_se_list) inst
}
| Earray size idx_se_list inst => {
let size' = fe size;
| Earray len idx_se_list inst => {
let len' = fe len;
let f_idx_se (idx, se) => {
let idx' = fe idx;
(idx', strexp_expmap f se)
};
Earray size' (IList.map f_idx_se idx_se_list) inst
Earray len' (IList.map f_idx_se idx_se_list) inst
}
};
@ -3246,9 +3268,9 @@ let rec strexp_instmap (f: inst => inst) strexp =>
| Estruct fld_se_list inst =>
let f_fld_se (fld, se) => (fld, strexp_instmap f se);
Estruct (IList.map f_fld_se fld_se_list) (f inst)
| Earray size idx_se_list inst =>
| Earray len idx_se_list inst =>
let f_idx_se (idx, se) => (idx, strexp_instmap f se);
Earray size (IList.map f_idx_se idx_se_list) (f inst)
Earray len (IList.map f_idx_se idx_se_list) (f inst)
}
and hpara_instmap (f: inst => inst) hpara => {
...hpara,
@ -3449,10 +3471,10 @@ let rec strexp_fpv =
let f (_, se) => strexp_fpv se;
IList.flatten (IList.map f fld_se_list)
}
| Earray size idx_se_list _ => {
let fpv_in_size = exp_fpv size;
| Earray len idx_se_list _ => {
let fpv_in_len = exp_fpv len;
let f (idx, se) => exp_fpv idx @ strexp_fpv se;
fpv_in_size @ IList.flatten (IList.map f idx_se_list)
fpv_in_len @ IList.flatten (IList.map f idx_se_list)
}
and hpred_fpv =
fun
@ -3662,8 +3684,8 @@ let rec strexp_fav_add fav =>
fun
| Eexp e _ => exp_fav_add fav e
| Estruct fld_se_list _ => IList.iter (fun (_, se) => strexp_fav_add fav se) fld_se_list
| Earray size idx_se_list _ => {
exp_fav_add fav size;
| Earray len idx_se_list _ => {
exp_fav_add fav len;
IList.iter
(
fun (e, se) => {
@ -4015,18 +4037,7 @@ let sub_fpv (sub: subst) => IList.flatten (IList.map (fun (_, e) => exp_fpv e) s
/** Substitutions do not contain binders */
let sub_av_add = sub_fav_add;
let rec typ_sub (subst: subst) typ =>
switch typ {
| Tvar _
| Tint _
| Tfloat _
| Tvoid
| Tstruct _
| Tfun _ => typ
| Tptr t' pk => Tptr (typ_sub subst t') pk
| Tarray t e => Tarray (typ_sub subst t) (exp_sub subst e)
}
and exp_sub (subst: subst) e =>
let rec exp_sub (subst: subst) e =>
switch e {
| Var id =>
let rec apply_sub = (
@ -4053,12 +4064,7 @@ and exp_sub (subst: subst) e =>
Cast t e1'
| UnOp op e1 typo =>
let e1' = exp_sub subst e1;
let typo' =
switch typo {
| None => None
| Some typ => Some (typ_sub subst typ)
};
UnOp op e1' typo'
UnOp op e1' typo
| BinOp op e1 e2 =>
let e1' = exp_sub subst e1;
let e2' = exp_sub subst e2;
@ -4066,13 +4072,12 @@ and exp_sub (subst: subst) e =>
| Lvar _ => e
| Lfield e1 fld typ =>
let e1' = exp_sub subst e1;
let typ' = typ_sub subst typ;
Lfield e1' fld typ'
Lfield e1' fld typ
| Lindex e1 e2 =>
let e1' = exp_sub subst e1;
let e2' = exp_sub subst e2;
Lindex e1' e2'
| Sizeof t l s => Sizeof (typ_sub subst t) (Option.map (exp_sub subst) l) s
| Sizeof t l s => Sizeof t (Option.map (exp_sub subst) l) s
};
let instr_sub (subst: subst) instr => {
@ -4082,20 +4087,19 @@ let instr_sub (subst: subst) instr => {
| _ => id
};
let exp_s = exp_sub subst;
let typ_s = typ_sub subst;
switch instr {
| Letderef id e t loc => Letderef (id_s id) (exp_s e) (typ_s t) loc
| Set e1 t e2 loc => Set (exp_s e1) (typ_s t) (exp_s e2) loc
| Letderef id e t loc => Letderef (id_s id) (exp_s e) t loc
| Set e1 t e2 loc => Set (exp_s e1) t (exp_s e2) loc
| Prune cond loc true_branch ik => Prune (exp_s cond) loc true_branch ik
| Call ret_ids e arg_ts loc cf =>
let arg_s (e, t) => (exp_s e, typ_s t);
let arg_s (e, t) => (exp_s e, t);
Call (IList.map id_s ret_ids) (exp_s e) (IList.map arg_s arg_ts) loc cf
| Nullify _ => instr
| Abstract _ => instr
| Remove_temps temps loc => Remove_temps (IList.map id_s temps) loc
| Stackop _ => instr
| Declare_locals ptl loc =>
let pt_s (pv, t) => (pv, typ_s t);
let pt_s (pv, t) => (pv, t);
Declare_locals (IList.map pt_s ptl) loc
}
};
@ -4512,13 +4516,13 @@ let rec strexp_replace_exp epairs =>
let f (fld, se) => (fld, strexp_replace_exp epairs se);
Estruct (IList.map f fsel) inst
}
| Earray size isel inst => {
let size' = exp_replace_exp epairs size;
| Earray len isel inst => {
let len' = exp_replace_exp epairs len;
let f (idx, se) => {
let idx' = exp_replace_exp epairs idx;
(idx', strexp_replace_exp epairs se)
};
Earray size' (IList.map f isel) inst
Earray len' (IList.map f isel) inst
};
let hpred_replace_exp epairs =>

@ -314,6 +314,10 @@ and struct_typ = {
def_methods: list Procname.t, /** methods defined */
struct_annotations: item_annotation /** annotations */
}
/** statically determined length of an array type, if any */
and static_length = option Int.t
/** dynamically determined length of an array value, if any */
and dynamic_length = option exp
/** Types for sil (structured) expressions. */
and typ =
| Tvar of Typename.t /** named type */
@ -323,7 +327,7 @@ and typ =
| Tfun of bool /** function type with noreturn attribute */
| Tptr of typ ptr_kind /** pointer type */
| Tstruct of struct_typ /** Type for a structured value */
| Tarray of typ exp /** array type with fixed size */
| Tarray of typ static_length /** array type with statically fixed length */
/** Program expressions. */
and exp =
/** Pure variable: it is not an lvalue */
@ -342,8 +346,14 @@ and exp =
| Lfield of exp Ident.fieldname typ
/** An array index offset: [exp1\[exp2\]] */
| Lindex of exp exp
/** A sizeof expression */
| Sizeof of typ (option exp) Subtype.t;
/** A sizeof expression. [Sizeof typ (Some len)] represents the size of a value of type [typ]
which ends in an extensible array of length [len]. The length in [Tarray] records the
statically determined length, while the length in [Sizeof] records the dynamic length. */
| Sizeof of typ dynamic_length Subtype.t;
/** the element typ of the final extensible array in the given typ, if any */
let get_extensible_array_element_typ: typ => option typ;
let struct_typ_equal: struct_typ => struct_typ => bool;
@ -528,10 +538,10 @@ let inst_partial_meet: inst => inst => inst;
type strexp =
| Eexp of exp inst /** Base case: expression with instrumentation */
| Estruct of (list (Ident.fieldname, strexp)) inst /** C structure */
| Earray of exp (list (exp, strexp)) inst /** Array of given size. */
| Earray of exp (list (exp, strexp)) inst /** Array of given length */
/** There are two conditions imposed / used in the array case.
First, if some index and value pair appears inside an array
in a strexp, then the index is less than the size of the array.
in a strexp, then the index is less than the length of the array.
For instance, x |->[10 | e1: v1] implies that e1 <= 9.
Second, if two indices appear in an array, they should be different.
For instance, x |->[10 | e1: v1, e2: v2] implies that e1 != e2. */
@ -886,9 +896,9 @@ let pp_typ_full: printenv => F.formatter => typ => unit;
let typ_to_string: typ => string;
/** [pp_type_decl pe pp_base pp_size f typ] pretty prints a type declaration.
/** [pp_type_decl pe pp_base pp_len f typ] pretty prints a type declaration.
pp_base prints the variable for a declaration, or can be skip to print only the type
pp_size prints the expression for the array size */
pp_len prints the expression for the array length */
let pp_type_decl:
printenv =>
(F.formatter => unit => unit) =>

@ -57,9 +57,8 @@ let lookup_java_typ_from_string tenv typ_str => {
| "double" => Some (Sil.Tfloat Sil.FDouble)
| typ_str when String.contains typ_str '[' => {
let stripped_typ = String.sub typ_str 0 (String.length typ_str - 2);
let array_typ_size = Sil.exp_get_undefined false;
switch (loop stripped_typ) {
| Some typ => Some (Sil.Tptr (Sil.Tarray typ array_typ_size) Sil.Pk_pointer)
| Some typ => Some (Sil.Tptr (Sil.Tarray typ None) Sil.Pk_pointer)
| None => None
}
}

@ -903,14 +903,14 @@ let get_cycle root prop =
let visited' = (fst et_src):: visited in
let res = (match get_points_to e with
| None -> path, false
| Some (Sil.Hpointsto(_, Sil.Estruct(fl, _), Sil.Sizeof(te, _, _))) ->
| Some (Sil.Hpointsto (_, Sil.Estruct (fl, _), Sil.Sizeof (te, _, _))) ->
dfs e_root (e, te) ((et_src, f, e):: path) fl visited'
| _ -> path, false (* check for lists *)) in
if snd res then res
else dfs e_root et_src path el' visited') in
L.d_strln "Looking for cycle with root expression: "; Sil.d_hpred root; L.d_strln "";
match root with
| Sil.Hpointsto(e_root, Sil.Estruct(fl, _), Sil.Sizeof(te, _, _)) ->
| Sil.Hpointsto (e_root, Sil.Estruct (fl, _), Sil.Sizeof (te, _, _)) ->
let se_root = Sil.Eexp(e_root, Sil.Inone) in
(* start dfs with empty path and expr pointing to root *)
let (pot_cycle, res) = dfs se_root (se_root, te) [] fl [] in
@ -949,7 +949,7 @@ let get_var_retain_cycle _prop =
| _ -> false in
let is_hpred_block v h =
match h, v with
| Sil.Hpointsto (e, _, Sil.Sizeof(typ, _, _)), Sil.Eexp (e', _)
| Sil.Hpointsto (e, _, Sil.Sizeof (typ, _, _)), Sil.Eexp (e', _)
when Sil.exp_equal e e' && Sil.is_block_type typ -> true
| _, _ -> false in
let find v =

@ -28,7 +28,7 @@ module StrexpMatch : sig
(** create a path from a root and a list of offsets *)
val path_from_exp_offsets : Sil.exp -> Sil.offset list -> path
(** path to the root, size, elements and type of a new_array *)
(** path to the root, length, elements and type of a new_array *)
type strexp_data = path * Sil.strexp * Sil.typ
(** sigma with info about a current array *)
@ -73,7 +73,7 @@ end = struct
(IList.find (fun (f', _, _) ->
Sil.fld_equal f' fld) instance_fields) in
get_strexp_at_syn_offsets se' t' syn_offs'
| Sil.Earray (_, esel, _), Sil.Tarray(t', _), Index ind :: syn_offs' ->
| Sil.Earray (_, esel, _), Sil.Tarray (t', _), Index ind :: syn_offs' ->
let se' = snd (IList.find (fun (i', _) -> Sil.exp_equal i' ind) esel) in
get_strexp_at_syn_offsets se' t' syn_offs'
| _ ->
@ -95,11 +95,11 @@ end = struct
let se_mod = replace_strexp_at_syn_offsets se' t' syn_offs' update in
let fsel' = IList.map (fun (f'', se'') -> if Sil.fld_equal f'' fld then (fld, se_mod) else (f'', se'')) fsel in
Sil.Estruct (fsel', inst)
| Sil.Earray (size, esel, inst), Sil.Tarray (t', _), Index idx :: syn_offs' ->
| Sil.Earray (len, esel, inst), Sil.Tarray (t', _), Index idx :: syn_offs' ->
let se' = snd (IList.find (fun (i', _) -> Sil.exp_equal i' idx) esel) in
let se_mod = replace_strexp_at_syn_offsets se' t' syn_offs' update in
let esel' = IList.map (fun ese -> if Sil.exp_equal (fst ese) idx then (idx, se_mod) else ese) esel in
Sil.Earray (size, esel', inst)
Sil.Earray (len, esel', inst)
| _ -> assert false
(** convert a path into an expression *)
@ -124,7 +124,7 @@ end = struct
let syn_offs = IList.map offset_to_syn_offset offs in
(root, syn_offs)
(** path to the root, size, elements and type of a new_array *)
(** path to the root, len, elements and type of a new_array *)
type strexp_data = path * Sil.strexp * Sil.typ
(** Store hpred using physical equality, and offset list for an array *)
@ -205,13 +205,13 @@ end = struct
let update se' =
let se_in = update se' in
match se', se_in with
| Sil.Earray (size, esel, _), Sil.Earray (_, esel_in, inst2) ->
| Sil.Earray (len, esel, _), Sil.Earray (_, esel_in, inst2) ->
let orig_indices = IList.map fst esel in
let index_is_not_new idx = IList.exists (Sil.exp_equal idx) orig_indices in
let process_index idx =
if index_is_not_new idx then idx else (Sil.array_clean_new_index footprint_part idx) in
let esel_in' = IList.map (fun (idx, se) -> process_index idx, se) esel_in in
Sil.Earray (size, esel_in', inst2)
Sil.Earray (len, esel_in', inst2)
| _, _ -> se_in in
begin
match hpred with
@ -232,9 +232,9 @@ end = struct
let replace_index footprint_part ((sigma, hpred, syn_offs) : t) (index: Sil.exp) (index': Sil.exp) =
let update se' =
match se' with
| Sil.Earray (size, esel, inst) ->
| Sil.Earray (len, esel, inst) ->
let esel' = IList.map (fun (e', se') -> if Sil.exp_equal e' index then (index', se') else (e', se')) esel in
Sil.Earray (size, esel', inst)
Sil.Earray (len, esel', inst)
| _ -> assert false in
let hpred' = hpred_replace_strexp footprint_part hpred syn_offs update in
replace_hpred (sigma, hpred, syn_offs) hpred'
@ -413,11 +413,11 @@ let keep_only_indices
let matched = StrexpMatch.find_path sigma path in
let (_, se, _) = StrexpMatch.get_data matched in
match se with
| Sil.Earray (size, esel, inst) ->
| Sil.Earray (len, esel, inst) ->
let esel', esel_leftover' = IList.partition (fun (e, _) -> IList.exists (Sil.exp_equal e) indices) esel in
if esel_leftover' == [] then (sigma, false)
else begin
let se' = Sil.Earray (size, esel', inst) in
let se' = Sil.Earray (len, esel', inst) in
let sigma' = StrexpMatch.replace_strexp footprint_part matched se' in
(sigma', true)
end
@ -584,9 +584,9 @@ let remove_redundant_elements prop =
remove () (* index unknown can be removed *)
| _ -> true in
let remove_redundant_se fp_part = function
| Sil.Earray (size, esel, inst) ->
| Sil.Earray (len, esel, inst) ->
let esel' = IList.filter (filter_redundant_e_se fp_part) esel in
Sil.Earray (size, esel', inst)
Sil.Earray (len, esel', inst)
| se -> se in
let remove_redundant_hpred fp_part = function
| Sil.Hpointsto (e, se, te) ->

@ -307,8 +307,8 @@ let create_idmap sigma : idmap =
do_exp e typ
| Sil.Estruct (fsel, _), Sil.Tstruct { Sil.instance_fields } ->
do_struct fsel instance_fields
| Sil.Earray (size, esel, _), Sil.Tarray (typ, _) ->
do_se (Sil.Eexp (size, Sil.inst_none)) (Sil.Tint Sil.IULong);
| Sil.Earray (len, esel, _), Sil.Tarray (typ, _) ->
do_se (Sil.Eexp (len, Sil.inst_none)) (Sil.Tint Sil.IULong);
do_array esel typ
| _ ->
L.err "Unmatched sexp: %a : %a@." (Sil.pp_sexp pe) se (Sil.pp_typ_full pe) typ;
@ -415,20 +415,20 @@ let mk_size_name id =
"_size_" ^ string_of_int id
let pp_texp_for_malloc fmt =
let rec handle_arr_size typ = match typ with
let rec handle_arr_len typ = match typ with
| Sil.Tvar _ | Sil.Tint _ | Sil.Tfloat _ | Sil.Tvoid | Sil.Tfun _ ->
typ
| Sil.Tptr (t, pk) ->
Sil.Tptr (handle_arr_size t, pk)
Sil.Tptr (handle_arr_len t, pk)
| Sil.Tstruct struct_typ ->
let instance_fields =
IList.map (fun (f, t, a) -> (f, handle_arr_size t, a)) struct_typ.Sil.instance_fields in
IList.map (fun (f, t, a) -> (f, handle_arr_len t, a)) struct_typ.Sil.instance_fields in
Sil.Tstruct { struct_typ with Sil.instance_fields }
| Sil.Tarray (t, e) ->
Sil.Tarray (handle_arr_size t, e) in
Sil.Tarray (handle_arr_len t, e) in
function
| Sil.Sizeof (typ, _, _) ->
let typ' = handle_arr_size typ in
let typ' = handle_arr_len typ in
F.fprintf fmt "sizeof(%a)" (pp_typ_c pe) typ'
| e -> pp_exp_c pe fmt e

@ -877,6 +877,12 @@ let ident_partial_meet (id1: Ident.t) (id2: Ident.t) =
(** {2 Join and Meet for Exps} *)
let option_partial_join partial_join o1 o2 =
match o1, o2 with
| None, _ -> o2
| _, None -> o1
| Some x1, Some x2 -> partial_join x1 x2
let const_partial_join c1 c2 =
let is_int = function Sil.Cint _ -> true | _ -> false in
if Sil.const_equal c1 c2 then Sil.Const c1
@ -951,35 +957,36 @@ let rec exp_partial_join (e1: Sil.exp) (e2: Sil.exp) : Sil.exp =
let e2'' = exp_partial_join e1' e2' in
Sil.Lindex(e1'', e2'')
| Sil.Sizeof (t1, len1, st1), Sil.Sizeof (t2, len2, st2) ->
Sil.Sizeof (typ_partial_join t1 t2, len_partial_join len1 len2, Sil.Subtype.join st1 st2)
Sil.Sizeof
(typ_partial_join t1 t2, dynamic_length_partial_join len1 len2, Sil.Subtype.join st1 st2)
| _ ->
L.d_str "exp_partial_join no match "; Sil.d_exp e1; L.d_str " "; Sil.d_exp e2; L.d_ln ();
raise IList.Fail
and len_partial_join len1 len2 =
match len1, len2 with
| None, _ -> len2
| _, None -> len1
| Some len1, Some len2 -> Some (size_partial_join len1 len2)
and size_partial_join size1 size2 = match size1, size2 with
and length_partial_join len1 len2 = match len1, len2 with
| Sil.BinOp(Sil.PlusA, e1, Sil.Const c1), Sil.BinOp(Sil.PlusA, e2, Sil.Const c2) ->
let e' = exp_partial_join e1 e2 in
let c' = exp_partial_join (Sil.Const c1) (Sil.Const c2) in
Sil.BinOp (Sil.PlusA, e', c')
| Sil.BinOp(Sil.PlusA, _, _), Sil.BinOp(Sil.PlusA, _, _) ->
Rename.extend size1 size2 Rename.ExtFresh
Rename.extend len1 len2 Rename.ExtFresh
| Sil.Var id1, Sil.Var id2 when Ident.equal id1 id2 ->
size1
| _ -> exp_partial_join size1 size2
len1
| _ -> exp_partial_join len1 len2
and static_length_partial_join l1 l2 =
option_partial_join (fun len1 len2 -> if Sil.Int.eq len1 len2 then Some len1 else None) l1 l2
and dynamic_length_partial_join l1 l2 =
option_partial_join (fun len1 len2 -> Some (length_partial_join len1 len2)) l1 l2
and typ_partial_join t1 t2 = match t1, t2 with
| Sil.Tptr (t1, pk1), Sil.Tptr (t2, pk2) when Sil.ptr_kind_compare pk1 pk2 = 0 ->
Sil.Tptr (typ_partial_join t1 t2, pk1)
| Sil.Tarray (typ1, size1), Sil.Tarray(typ2, size2) ->
| Sil.Tarray (typ1, len1), Sil.Tarray (typ2, len2) ->
let t = typ_partial_join typ1 typ2 in
let size = size_partial_join size1 size2 in
Sil.Tarray (t, size)
let len = static_length_partial_join len1 len2 in
Sil.Tarray (t, len)
| _ when Sil.typ_equal t1 t2 -> t1 (* common case *)
| _ ->
L.d_str "typ_partial_join no match "; Sil.d_typ_full t1; L.d_str " "; Sil.d_typ_full t2; L.d_ln ();
@ -1072,21 +1079,21 @@ let rec strexp_partial_join mode (strexp1: Sil.strexp) (strexp2: Sil.strexp) : S
assert false (* This case should not happen. *)
end in
let rec f_idx_se_list inst size idx_se_list_acc idx_se_list1 idx_se_list2 =
let rec f_idx_se_list inst len idx_se_list_acc idx_se_list1 idx_se_list2 =
match idx_se_list1, idx_se_list2 with
| [], [] -> Sil.Earray (size, IList.rev idx_se_list_acc, inst)
| [], [] -> Sil.Earray (len, IList.rev idx_se_list_acc, inst)
| [], _ | _, [] ->
begin
match mode with
| JoinState.Pre -> (L.d_strln "failure reason 44"; raise IList.Fail)
| JoinState.Post ->
Sil.Earray (size, IList.rev idx_se_list_acc, inst)
Sil.Earray (len, IList.rev idx_se_list_acc, inst)
end
| (idx1, se1):: idx_se_list1', (idx2, se2):: idx_se_list2' ->
let idx = exp_partial_join idx1 idx2 in
let strexp' = strexp_partial_join mode se1 se2 in
let idx_se_list_new = (idx, strexp') :: idx_se_list_acc in
f_idx_se_list inst size idx_se_list_new idx_se_list1' idx_se_list2' in
f_idx_se_list inst len idx_se_list_new idx_se_list1' idx_se_list2' in
match strexp1, strexp2 with
| Sil.Eexp (e1, inst1), Sil.Eexp (e2, inst2) ->
@ -1094,10 +1101,10 @@ let rec strexp_partial_join mode (strexp1: Sil.strexp) (strexp2: Sil.strexp) : S
| Sil.Estruct (fld_se_list1, inst1), Sil.Estruct (fld_se_list2, inst2) ->
let inst = Sil.inst_partial_join inst1 inst2 in
f_fld_se_list inst mode [] fld_se_list1 fld_se_list2
| Sil.Earray (size1, idx_se_list1, inst1), Sil.Earray (size2, idx_se_list2, inst2) ->
let size = size_partial_join size1 size2 in
| Sil.Earray (len1, idx_se_list1, inst1), Sil.Earray (len2, idx_se_list2, inst2) ->
let len = length_partial_join len1 len2 in
let inst = Sil.inst_partial_join inst1 inst2 in
f_idx_se_list inst size [] idx_se_list1 idx_se_list2
f_idx_se_list inst len [] idx_se_list1 idx_se_list2
| _ -> L.d_strln "no match in strexp_partial_join"; raise IList.Fail
let rec strexp_partial_meet (strexp1: Sil.strexp) (strexp2: Sil.strexp) : Sil.strexp =
@ -1130,19 +1137,19 @@ let rec strexp_partial_meet (strexp1: Sil.strexp) (strexp2: Sil.strexp) : Sil.st
let acc_new = (fld1, strexp') :: acc in
f_fld_se_list inst acc_new fld_se_list1' fld_se_list2' in
let rec f_idx_se_list inst size acc idx_se_list1 idx_se_list2 =
let rec f_idx_se_list inst len acc idx_se_list1 idx_se_list2 =
match idx_se_list1, idx_se_list2 with
| [],[] ->
Sil.Earray (size, IList.rev acc, inst)
Sil.Earray (len, IList.rev acc, inst)
| [], _ ->
Sil.Earray (size, construct Rhs acc idx_se_list2, inst)
Sil.Earray (len, construct Rhs acc idx_se_list2, inst)
| _, [] ->
Sil.Earray (size, construct Lhs acc idx_se_list1, inst)
Sil.Earray (len, construct Lhs acc idx_se_list1, inst)
| (idx1, se1):: idx_se_list1', (idx2, se2):: idx_se_list2' ->
let idx = exp_partial_meet idx1 idx2 in
let se' = strexp_partial_meet se1 se2 in
let acc_new = (idx, se') :: acc in
f_idx_se_list inst size acc_new idx_se_list1' idx_se_list2' in
f_idx_se_list inst len acc_new idx_se_list1' idx_se_list2' in
match strexp1, strexp2 with
| Sil.Eexp (e1, inst1), Sil.Eexp (e2, inst2) ->
@ -1150,10 +1157,10 @@ let rec strexp_partial_meet (strexp1: Sil.strexp) (strexp2: Sil.strexp) : Sil.st
| Sil.Estruct (fld_se_list1, inst1), Sil.Estruct (fld_se_list2, inst2) ->
let inst = Sil.inst_partial_meet inst1 inst2 in
f_fld_se_list inst [] fld_se_list1 fld_se_list2
| Sil.Earray (size1, idx_se_list1, inst1), Sil.Earray (size2, idx_se_list2, inst2)
when Sil.exp_equal size1 size2 ->
| Sil.Earray (len1, idx_se_list1, inst1), Sil.Earray (len2, idx_se_list2, inst2)
when Sil.exp_equal len1 len2 ->
let inst = Sil.inst_partial_meet inst1 inst2 in
f_idx_se_list inst size1 [] idx_se_list1 idx_se_list2
f_idx_se_list inst len1 [] idx_se_list1 idx_se_list2
| _ -> (L.d_strln "failure reason 52"; raise IList.Fail)
(** {2 Join and Meet for kind, hpara, hpara_dll} *)
@ -1554,26 +1561,26 @@ let pi_partial_join mode
| Sil.Const _ -> true
(* | Sil.Lvar _ -> true *)
| _ -> false in
let get_array_size prop =
(* find some array size in the prop, to be used as heuritic for upper bound in widening *)
let size_list = ref [] in
let get_array_len prop =
(* find some array length in the prop, to be used as heuritic for upper bound in widening *)
let len_list = ref [] in
let do_hpred = function
| Sil.Hpointsto (_, Sil.Earray (Sil.Const (Sil.Cint n), _, _), _) ->
(if Sil.Int.geq n Sil.Int.one then size_list := n::!size_list)
(if Sil.Int.geq n Sil.Int.one then len_list := n :: !len_list)
| _ -> () in
IList.iter do_hpred (Prop.get_sigma prop);
!size_list in
!len_list in
let bounds =
let bounds1 = get_array_size ep1 in
let bounds2 = get_array_size ep2 in
let bounds1 = get_array_len ep1 in
let bounds2 = get_array_len ep2 in
let bounds_sorted = IList.sort Sil.Int.compare_value (bounds1@bounds2) in
IList.rev (IList.remove_duplicates Sil.Int.compare_value bounds_sorted) in
let widening_atom a =
(* widening heuristic for upper bound: take the size of some array, -2 and -1 *)
(* widening heuristic for upper bound: take the length of some array, -2 and -1 *)
match Prop.atom_exp_le_const a, bounds with
| Some (e, n), size:: _ ->
let first_try = Sil.Int.sub size Sil.Int.one in
let second_try = Sil.Int.sub size Sil.Int.two in
| Some (e, n), len :: _ ->
let first_try = Sil.Int.sub len Sil.Int.one in
let second_try = Sil.Int.sub len Sil.Int.two in
let bound =
if Sil.Int.leq n first_try then
if Sil.Int.leq n second_try then second_try else first_try
@ -1629,14 +1636,14 @@ let pi_partial_join mode
| Some (e, n) ->
if IList.exists (is_stronger_le e n) pi_op then (widening_atom a_res) else Some a_res
end in
let handle_atom_with_widening size p_op pi_op atom_list a =
let handle_atom_with_widening len p_op pi_op atom_list a =
(* find a join for the atom, if it fails apply widening heuristing and try again *)
match join_atom size p_op pi_op a with
match join_atom len p_op pi_op a with
| None ->
(match widening_atom a with
| None -> atom_list
| Some a' ->
(match join_atom size p_op pi_op a' with
(match join_atom len p_op pi_op a' with
| None -> atom_list
| Some a' -> a' :: atom_list))
| Some a' -> a' :: atom_list in

@ -65,7 +65,7 @@ type dotty_node =
(* Dotstruct(coo,e,l,c): struct box for expression e with field list l at coordinate coo and color c *)
| Dotstruct of coordinate * Sil.exp * (Ident.fieldname * Sil.strexp) list * string * Sil.exp
(* Dotarray(coo,e1,e2,l,t,c): array box for expression e1 with field list l at coordinate coo and color c*)
(* e2 is the size and t is the type *)
(* e2 is the len and t is the type *)
| Dotarray of coordinate * Sil.exp * Sil.exp * (Sil.exp * Sil.strexp) list * Sil.typ * string
(* Dotlseg(coo,e1,e2,k,h,c): list box from e1 to e2 at coordinate coo and color c*)
| Dotlseg of coordinate * Sil.exp * Sil.exp * Sil.lseg_kind * Sil.hpred list * string
@ -294,7 +294,7 @@ let rec dotty_mk_node pe sigma =
let n = !dotty_state_count in
incr dotty_state_count;
let do_hpred_lambda exp_color = function
| (Sil.Hpointsto (e, Sil.Earray (e', l, _), Sil.Sizeof (Sil.Tarray(t, _), _, _)), lambda) ->
| (Sil.Hpointsto (e, Sil.Earray (e', l, _), Sil.Sizeof (Sil.Tarray (t, _), _, _)), lambda) ->
incr dotty_state_count; (* increment once more n+1 is the box for the array *)
let e_color_str = color_to_str (exp_color e) in
let e_color_str'= color_to_str (exp_color e') in
@ -1221,13 +1221,13 @@ let rec compute_target_nodes_from_sexp nodes se prop field_lab =
compute_target_nodes_from_sexp nodes se2 prop (Ident.fieldname_to_string fn) @
compute_target_nodes_from_sexp nodes (Sil.Estruct (l', inst)) prop ""
)
| Sil.Earray(size, lie, inst) ->
| Sil.Earray (len, lie, inst) ->
(match lie with
| [] -> []
| (idx, se2):: l' ->
let lab ="["^exp_to_xml_string idx^"]" in
compute_target_nodes_from_sexp nodes se2 prop lab @
compute_target_nodes_from_sexp nodes (Sil.Earray(size, l', inst)) prop ""
compute_target_nodes_from_sexp nodes (Sil.Earray (len, l', inst)) prop ""
)
@ -1291,9 +1291,9 @@ let rec pointsto_contents_to_xml (co: Sil.strexp) : Io_infer.Xml.node =
| Sil.Estruct (fel, _) ->
let f (fld, exp) = Io_infer.Xml.create_tree "struct-field" [("id", Ident.fieldname_to_string fld)] [(pointsto_contents_to_xml exp)] in
Io_infer.Xml.create_tree "struct" [] (IList.map f fel)
| Sil.Earray (size, nel, _) ->
| Sil.Earray (len, nel, _) ->
let f (e, se) = Io_infer.Xml.create_tree "array-element" [("index", exp_to_xml_string e)] [pointsto_contents_to_xml se] in
Io_infer.Xml.create_tree "array" [("size", exp_to_xml_string size)] (IList.map f nel)
Io_infer.Xml.create_tree "array" [("size", exp_to_xml_string len)] (IList.map f nel)
(* Convert an atom to xml in a light version. Namely, the expressions are not fully blown-up into *)
(* xml tree but visualized as strings *)

@ -29,8 +29,8 @@ let stable_sort = List.stable_sort
let tl = List.tl
let rec last = function
| [] -> invalid_arg "IList.last"
| [x] -> x
| [] -> None
| [x] -> Some x
| _ :: xs -> last xs
(** tail-recursive variant of List.fold_right *)

@ -64,8 +64,8 @@ val split : ('a * 'b) list -> 'a list * 'b list
val stable_sort : ('a -> 'a -> int) -> 'a list -> 'a list
val tl : 'a list -> 'a list
(** last element, raises invalid_arg if empty *)
val last : 'a list -> 'a
(** last element, if any *)
val last : 'a list -> 'a option
(* Drops the first n elements from a list. *)
val drop_first : int -> 'a list -> 'a list

@ -101,8 +101,8 @@ let rec strexp_match sexp1 sub vars sexp2 : (Sil.subst * Ident.t list) option =
fsel_match fsel1 sub vars fsel2
| Sil.Estruct _, _ | _, Sil.Estruct _ ->
None
| Sil.Earray (size1, isel1, _), Sil.Earray (size2, isel2, _) ->
(match exp_match size1 sub vars size2 with
| Sil.Earray (len1, isel1, _), Sil.Earray (len2, isel2, _) ->
(match exp_match len1 sub vars len2 with
| Some (sub', vars') -> isel_match isel1 sub' vars' isel2
| None -> None)
@ -497,8 +497,8 @@ let rec generate_todos_from_strexp mode todos sexp1 sexp2 =
else generate_todos_from_fel mode todos fel1 fel2
| Sil.Estruct _, _ ->
None
| Sil.Earray (size1, iel1, _), Sil.Earray (size2, iel2, _) ->
if (not (Sil.exp_equal size1 size2) || IList.length iel1 <> IList.length iel2)
| Sil.Earray (len1, iel1, _), Sil.Earray (len2, iel2, _) ->
if (not (Sil.exp_equal len1 len2) || IList.length iel1 <> IList.length iel2)
then None
else generate_todos_from_iel mode todos iel1 iel2
| Sil.Earray _, _ ->

@ -28,24 +28,24 @@ let execute___builtin_va_arg { Builtin.pdesc; tenv; prop_; path; ret_ids; args;
SymExec.instrs ~mask_errors:true tenv pdesc [instr'] [(prop_, path)]
| _ -> raise (Exceptions.Wrong_argument_number __POS__)
let mk_empty_array size =
Sil.Earray (size, [], Sil.inst_none)
let mk_empty_array len =
Sil.Earray (len, [], Sil.inst_none)
(* Make a rearranged array. As it is rearranged when it appears in a precondition
it requires that the function is called with the array allocated. If not infer
return a null pointer deref *)
let mk_empty_array_rearranged size =
Sil.Earray (size, [], Sil.inst_rearrange true (State.get_loc ()) (State.get_path_pos ()))
let mk_empty_array_rearranged len =
Sil.Earray (len, [], Sil.inst_rearrange true (State.get_loc ()) (State.get_path_pos ()))
let extract_array_type typ =
if (!Config.curr_language = Config.Java) then
match typ with
| Sil.Tptr ( Sil.Tarray (typ', _), _) -> Some typ'
| Sil.Tptr (Sil.Tarray _ as arr, _) -> Some arr
| _ -> None
else
match typ with
| Sil.Tptr (typ', _) | Sil.Tarray (typ', _) ->
Some typ'
| Sil.Tarray _ as arr -> Some arr
| Sil.Tptr (elt, _) -> Some (Sil.Tarray (elt, None))
| _ -> None
(** Return a result from a procedure call. *)
@ -54,8 +54,8 @@ let return_result e prop ret_ids =
| [ret_id] -> Prop.conjoin_eq e (Sil.Var ret_id) prop
| _ -> prop
(* Add an array of typ pointed to by lexp to prop_ if it doesnt exist alread*)
(* Return the new prop and the array size *)
(* Add an array of typ pointed to by lexp to prop_ if it doesn't already exist *)
(* Return the new prop and the array length *)
(* Return None if it fails to add the array *)
let add_array_to_prop pdesc prop_ lexp typ =
let pname = Cfg.Procdesc.get_proc_name pdesc in
@ -66,24 +66,21 @@ let add_array_to_prop pdesc prop_ lexp typ =
| Sil.Hpointsto(e, _, _) -> Sil.exp_equal e n_lexp
| _ -> false) (Prop.get_sigma prop) in
match hpred with
| Sil.Hpointsto(_, Sil.Earray(size, _, _), _) ->
Some (size, prop)
| Sil.Hpointsto(_, Sil.Earray (len, _, _), _) ->
Some (len, prop)
| _ -> None (* e points to something but not an array *)
with Not_found -> (* e is not allocated, so we can add the array *)
let otyp' = (extract_array_type typ) in
match otyp' with
| Some typ' ->
let size = Sil.Var(Ident.create_fresh Ident.kfootprint) in
let s = mk_empty_array_rearranged size in
let hpred =
Prop.mk_ptsto n_lexp s
(Sil.Sizeof (Sil.Tarray (typ', size), None, Sil.Subtype.exact)) in
let sigma = Prop.get_sigma prop in
let sigma_fp = Prop.get_sigma_footprint prop in
let prop'= Prop.replace_sigma (hpred:: sigma) prop in
let prop''= Prop.replace_sigma_footprint (hpred:: sigma_fp) prop' in
let prop''= Prop.normalize prop'' in
Some (size, prop'')
match extract_array_type typ with
| Some arr_typ ->
let len = Sil.Var (Ident.create_fresh Ident.kfootprint) in
let s = mk_empty_array_rearranged len in
let hpred = Prop.mk_ptsto n_lexp s (Sil.Sizeof (arr_typ, Some len, Sil.Subtype.exact)) in
let sigma = Prop.get_sigma prop in
let sigma_fp = Prop.get_sigma_footprint prop in
let prop'= Prop.replace_sigma (hpred:: sigma) prop in
let prop''= Prop.replace_sigma_footprint (hpred:: sigma_fp) prop' in
let prop''= Prop.normalize prop'' in
Some (len, prop'')
| _ -> None
end
@ -97,31 +94,31 @@ let execute___require_allocated_array { Builtin.pdesc; prop_; path; ret_ids; arg
| Some (_, prop) -> [(prop, path)])
| _ -> raise (Exceptions.Wrong_argument_number __POS__)
let execute___get_array_size { Builtin.pdesc; prop_; path; ret_ids; args; }
let execute___get_array_length { Builtin.pdesc; prop_; path; ret_ids; args; }
: Builtin.ret_typ =
match args with
| [(lexp, typ)] when IList.length ret_ids <= 1 ->
(match add_array_to_prop pdesc prop_ lexp typ with
| None -> []
| Some (size, prop) -> [(return_result size prop ret_ids, path)])
| Some (len, prop) -> [(return_result len prop ret_ids, path)])
| _ -> raise (Exceptions.Wrong_argument_number __POS__)
let execute___set_array_size { Builtin.pdesc; prop_; path; ret_ids; args; }
let execute___set_array_length { Builtin.pdesc; prop_; path; ret_ids; args; }
: Builtin.ret_typ =
match args, ret_ids with
| [(lexp, typ); (size, _)], []->
| [(lexp, typ); (len, _)], []->
(match add_array_to_prop pdesc prop_ lexp typ with
| None -> []
| Some (_, prop_a) -> (* Invariant: prop_a has an array pointed to by lexp *)
let pname = Cfg.Procdesc.get_proc_name pdesc in
let n_lexp, prop__ = check_arith_norm_exp pname lexp prop_a in
let n_size, prop = check_arith_norm_exp pname size prop__ in
let n_len, prop = check_arith_norm_exp pname len prop__ in
let hpred, sigma' = IList.partition (function
| Sil.Hpointsto(e, _, _) -> Sil.exp_equal e n_lexp
| _ -> false) (Prop.get_sigma prop) in
(match hpred with
| [Sil.Hpointsto(e, Sil.Earray(_, esel, inst), t)] ->
let hpred' = Sil.Hpointsto (e, Sil.Earray (n_size, esel, inst), t) in
let hpred' = Sil.Hpointsto (e, Sil.Earray (n_len, esel, inst), t) in
let prop' = Prop.replace_sigma (hpred':: sigma') prop in
[(Prop.normalize prop', path)]
| _ -> [])) (* by construction of prop_a this case is impossible *)
@ -162,8 +159,8 @@ let create_type tenv n_lexp typ prop =
let hpred = Prop.mk_ptsto n_lexp sexp texp in
Some hpred
| Sil.Tarray _ ->
let size = Sil.Var(Ident.create_fresh Ident.kfootprint) in
let sexp = mk_empty_array size in
let len = Sil.Var (Ident.create_fresh Ident.kfootprint) in
let sexp = mk_empty_array len in
let texp = Sil.Sizeof (typ, None, Sil.Subtype.subtypes) in
let hpred = Prop.mk_ptsto n_lexp sexp texp in
Some hpred
@ -763,11 +760,9 @@ let execute_alloc mk can_return_null
| Sil.Const _ | Sil.Cast _ | Sil.Lvar _ | Sil.Lfield _ | Sil.Lindex _ -> e
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, _), Some len, _) when Sil.ikind_is_char ik ->
evaluate_char_sizeof len
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, len), None, _) when Sil.ikind_is_char ik ->
evaluate_char_sizeof len
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, Some len), None, _) when Sil.ikind_is_char ik ->
evaluate_char_sizeof (Sil.Const (Sil.Cint len))
| Sil.Sizeof _ -> e in
let handle_sizeof_exp len =
Sil.Sizeof (Sil.Tarray (Sil.Tint Sil.IChar, len), Some len, Sil.Subtype.exact) in
let size_exp, procname = match args with
| [(Sil.Sizeof
(Sil.Tstruct
@ -790,7 +785,8 @@ let execute_alloc mk can_return_null
let n_size_exp, prop = check_arith_norm_exp pname size_exp prop_ in
let n_size_exp' = evaluate_char_sizeof n_size_exp in
Prop.exp_normalize_prop prop n_size_exp', prop in
let cnt_te = handle_sizeof_exp size_exp' in
let cnt_te =
Sil.Sizeof (Sil.Tarray (Sil.Tint Sil.IChar, None), Some size_exp', Sil.Subtype.exact) in
let id_new = Ident.create_fresh Ident.kprimed in
let exp_new = Sil.Var id_new in
let ptsto_new =
@ -989,9 +985,9 @@ let __delete_array = Builtin.register
let __exit = Builtin.register
(* _exit from C library *)
"_exit" execute_exit
let __get_array_size = Builtin.register
(* return the size of the array passed as a parameter *)
"__get_array_size" execute___get_array_size
let __get_array_length = Builtin.register
(* return the length of the array passed as a parameter *)
"__get_array_length" execute___get_array_length
let __require_allocated_array = Builtin.register
(* require the parameter to point to an allocated array *)
"__require_allocated_array" execute___require_allocated_array
@ -1053,9 +1049,9 @@ let __placement_new = Builtin.register
let _ = Builtin.register
(* print a value as seen by the engine *)
"__print_value" execute___print_value
let __set_array_size = Builtin.register
(* set the size of the array passed as a parameter *)
"__set_array_size" execute___set_array_size
let __set_array_length = Builtin.register
(* set the length of the array passed as a parameter *)
"__set_array_length" execute___set_array_length
let __set_autorelease_attribute = Builtin.register
(* set the attribute of the parameter as autorelease *)
"__set_autorelease_attribute" execute___set_autorelease_attribute

@ -15,7 +15,7 @@ val __assert_fail : Procname.t
val __delete : Procname.t
val __delete_array : Procname.t
val __exit : Procname.t
val __get_array_size : Procname.t
val __get_array_length : Procname.t
val __get_type_of : Procname.t
val __infer_fail : Procname.t
val __instanceof : Procname.t (** [__instanceof(val,typ)] implements java's [val instanceof typ] *)
@ -26,7 +26,7 @@ val __new : Procname.t
val __new_array : Procname.t
val __objc_alloc : Procname.t
val __objc_alloc_no_fail : Procname.t
val __set_array_size : Procname.t
val __set_array_length : Procname.t
val __unwrap_exception : Procname.t
val __set_file_attribute : Procname.t
val __set_mem_attribute : Procname.t

@ -456,10 +456,12 @@ let sym_eval abs e =
Sil.Const (Sil.Cclosure { c with captured_vars; })
| Sil.Const _ ->
e
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, l), None, _)
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, _), Some l, _)
when Sil.ikind_is_char ik && !Config.curr_language <> Config.Java ->
when Sil.ikind_is_char ik && !Config.curr_language = Config.Clang ->
eval l
| Sil.Sizeof (Sil.Tarray (Sil.Tint ik, Some l), _, _)
when Sil.ikind_is_char ik && !Config.curr_language = Config.Clang ->
Sil.Const (Sil.Cint l)
| Sil.Sizeof _ ->
e
| Sil.Cast (_, e1) ->
@ -592,53 +594,31 @@ let sym_eval abs e =
(* progress: convert inner +I to +A *)
let e2' = Sil.BinOp (Sil.PlusA, e12, e2) in
eval (Sil.BinOp (Sil.PlusPI, e11, e2'))
| Sil.BinOp
(Sil.PlusA,
(Sil.Sizeof (Sil.Tstruct struct_typ, None, _) as e1),
e2) ->
(* pattern for extensible structs given a struct declatead as struct s { ... t arr[n] ... },
allocation pattern malloc(sizeof(struct s) + k * siezof(t)) turn it into
struct s { ... t arr[n + k] ... } *)
let e1' = eval e1 in
let e2' = eval e2 in
let instance_fields = struct_typ.Sil.instance_fields in
(match IList.rev instance_fields, e2' with
(fname, Sil.Tarray (typ, size), _) :: ltfa,
Sil.BinOp(Sil.Mult, num_elem, Sil.Sizeof (texp, None, st))
when instance_fields != [] && Sil.typ_equal typ texp ->
let size' = Sil.BinOp(Sil.PlusA, size, num_elem) in
let ltfa' = (fname, Sil.Tarray(typ, size'), Sil.item_annotation_empty) :: ltfa in
let struct_typ' =
{ struct_typ with Sil.instance_fields = IList.rev ltfa' } in
Sil.Sizeof (Sil.Tstruct struct_typ', None, st)
| _ -> Sil.BinOp(Sil.PlusA, e1', e2'))
| Sil.BinOp (Sil.PlusA as oplus, e1, e2)
| Sil.BinOp (Sil.PlusPI as oplus, e1, e2) ->
let e1' = eval e1 in
let e2' = eval e2 in
let isPlusA = oplus = Sil.PlusA in
let ominus = if isPlusA then Sil.MinusA else Sil.MinusPI in
let (+++) x y = match y with
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
let (+++) x y = match x, y with
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
| Sil.Const (Sil.Cint i), Sil.Const (Sil.Cint j) -> Sil.Const (Sil.Cint (Sil.Int.add i j))
| _ -> Sil.BinOp (oplus, x, y) in
let (---) x y = match y with
| Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
let (---) x y = match x, y with
| _, Sil.Const (Sil.Cint i) when Sil.Int.iszero i -> x
| Sil.Const (Sil.Cint i), Sil.Const (Sil.Cint j) -> Sil.Const (Sil.Cint (Sil.Int.sub i j))
| _ -> Sil.BinOp (ominus, x, y) in
(* test if the extensible array at the end of [typ] has elements of type [elt] *)
let extensible_array_element_typ_equal elt typ =
Option.map_default (Sil.typ_equal elt) false (Sil.get_extensible_array_element_typ typ) in
begin
match e1', e2' with
| Sil.Sizeof (typ, Some len1, st),
Sil.BinOp (Sil.Mult, len2, Sil.Sizeof (elt, None, _))
when isPlusA
&& (match typ with
| Sil.Tarray (elt2, _) ->
Sil.typ_equal elt elt2
| Sil.Tstruct {instance_fields= _::_ as flds} ->
Sil.typ_equal elt (snd3 (IList.last flds))
| _ ->
false) ->
(* pattern for extensible structs:
sizeof(struct s {... t[l]}) + k * sizeof(elt)) = sizeof(struct s {... t[l + k]}) *)
Sil.Sizeof (typ, Some (len1 +++ len2), st)
(* pattern for arrays and extensible structs:
sizeof(struct s {... t[l]}) + k * sizeof(t)) = sizeof(struct s {... t[l + k]}) *)
| Sil.Sizeof (typ, len1_opt, st), Sil.BinOp (Sil.Mult, len2, Sil.Sizeof (elt, None, _))
when isPlusA && (extensible_array_element_typ_equal elt typ) ->
let len = match len1_opt with Some len1 -> len1 +++ len2 | None -> len2 in
Sil.Sizeof (typ, Some len, st)
| Sil.Const c, _ when iszero_int_float c ->
e2'
| _, Sil.Const c when iszero_int_float c ->
@ -750,10 +730,13 @@ let sym_eval abs e =
| Sil.Const (Sil.Cfloat v), Sil.Const (Sil.Cfloat w) ->
Sil.exp_float (v /.w)
| Sil.Sizeof (Sil.Tarray (elt, _), Some len, _), Sil.Sizeof (elt2, None, _)
| Sil.Sizeof (Sil.Tarray (elt, len), None, _), Sil.Sizeof (elt2, None, _)
(* pattern: sizeof(elt[len]) / sizeof(elt) = len *)
when Sil.typ_equal elt elt2 ->
len
| Sil.Sizeof (Sil.Tarray (elt, Some len), None, _), Sil.Sizeof (elt2, None, _)
(* pattern: sizeof(elt[len]) / sizeof(elt) = len *)
when Sil.typ_equal elt elt2 ->
Sil.Const (Sil.Cint len)
| _ ->
if abs then Sil.exp_get_undefined false else Sil.BinOp (Sil.Div, e1', e2')
end
@ -852,65 +835,10 @@ let exp_normalize sub exp =
if !Config.abs_val >= 1 then sym_eval true exp'
else sym_eval false exp'
let rec texp_normalize sub exp = match exp with
| Sil.Sizeof (typ, len, st) ->
Sil.Sizeof (typ_normalize sub typ, Option.map (exp_normalize sub) len, st)
let texp_normalize sub exp = match exp with
| Sil.Sizeof (typ, len, st) -> Sil.Sizeof (typ, Option.map (exp_normalize sub) len, st)
| _ -> exp_normalize sub exp
(* NOTE: usage of == operator in flds_norm and typ_normalize is intended*)
(* to decrease pressure on ocaml's GC and not allocate new objects if typ_normalize*)
(* doesn't change anything in structure of the type *)
and flds_norm sub fields =
let fld_norm ((f, t, a) as field) =
let t' = typ_normalize sub t in
if t' == t then
field
else
(f, t', a) in
let rec flds_norm_aux fields = match fields with
| [] -> fields
| field :: rest ->
let rest' = flds_norm_aux rest in
let field' = fld_norm field in
if field == field' && rest == rest' then
fields
else
field' :: rest' in
flds_norm_aux fields
and typ_normalize sub typ = match typ with
| Sil.Tvar _
| Sil.Tint _
| Sil.Tfloat _
| Sil.Tvoid
| Sil.Tfun _ ->
typ
| Sil.Tptr (t, pk) ->
let t' = typ_normalize sub t in
if t == t' then
typ
else
Sil.Tptr (t', pk)
| Sil.Tstruct struct_typ ->
let instance_fields = struct_typ.Sil.instance_fields in
let instance_fields' = flds_norm sub instance_fields in
let static_fields = struct_typ.Sil.static_fields in
let static_fields' = flds_norm sub static_fields in
if instance_fields == instance_fields' && static_fields == static_fields' then
typ
else
Sil.Tstruct
{ struct_typ with
Sil.instance_fields = instance_fields';
static_fields = static_fields';
}
| Sil.Tarray (t, e) ->
(* this case is not optimized for less GC allocations since it requires*)
(* to make exp_normalize GC-friendly as well. Profiling showed that it's fine*)
(* to keep this code not optimized *)
Sil.Tarray (typ_normalize sub t, exp_normalize sub e)
let run_with_abs_val_eq_zero f =
let abs_val_old = !Config.abs_val in
Config.abs_val := 0;
@ -1136,12 +1064,12 @@ let rec strexp_normalize sub se =
let fld_cnts'' = IList.sort Sil.fld_strexp_compare fld_cnts' in
Sil.Estruct (fld_cnts'', inst)
end
| Sil.Earray (size, idx_cnts, inst) ->
| Sil.Earray (len, idx_cnts, inst) ->
begin
let size' = exp_normalize_noabs sub size in
let len' = exp_normalize_noabs sub len in
match idx_cnts with
| [] ->
if Sil.exp_equal size size' then se else Sil.Earray (size', idx_cnts, inst)
if Sil.exp_equal len len' then se else Sil.Earray (len', idx_cnts, inst)
| _ ->
let idx_cnts' =
IList.map (fun (idx, cnt) ->
@ -1149,7 +1077,7 @@ let rec strexp_normalize sub se =
idx', strexp_normalize sub cnt) idx_cnts in
let idx_cnts'' =
IList.sort Sil.exp_strexp_compare idx_cnts' in
Sil.Earray (size', idx_cnts'', inst)
Sil.Earray (len', idx_cnts'', inst)
end
(** create a strexp of the given type, populating the structures if [expand_structs] is true *)
@ -1184,8 +1112,11 @@ let rec create_strexp_of_type tenvo struct_init_mode typ len inst =
let flds, _ = IList.fold_right f instance_fields ([], len) in
Sil.Estruct (flds, inst)
)
| Sil.Tarray (_, size), None ->
Sil.Earray (size, [], inst)
| Sil.Tarray (_, len_opt), None ->
let len = match len_opt with
| None -> Sil.exp_get_undefined false
| Some len -> Sil.Const (Sil.Cint len) in
Sil.Earray (len, [], inst)
| Sil.Tarray _, Some len ->
Sil.Earray (len, [], inst)
| Sil.Tvar _, _
@ -1216,8 +1147,8 @@ let mk_ptsto_exp tenvo struct_init_mode (exp, te, expo) inst : Sil.hpred =
mk_ptsto exp strexp te
let replace_array_contents hpred esel = match hpred with
| Sil.Hpointsto (root, Sil.Earray (size, [], inst), te) ->
Sil.Hpointsto (root, Sil.Earray (size, esel, inst), te)
| Sil.Hpointsto (root, Sil.Earray (len, [], inst), te) ->
Sil.Hpointsto (root, Sil.Earray (len, esel, inst), te)
| _ -> assert false
let rec hpred_normalize sub hpred =
@ -2345,39 +2276,20 @@ let rec exp_captured_ren ren = function
| Sil.Var id -> Sil.Var (ident_captured_ren ren id)
| Sil.Const (Sil.Cexn e) -> Sil.Const (Sil.Cexn (exp_captured_ren ren e))
| Sil.Const _ as e -> e
| Sil.Sizeof (t, len, st) ->
Sil.Sizeof (typ_captured_ren ren t, Option.map (exp_captured_ren ren) len, st)
| Sil.Sizeof (t, len, st) -> Sil.Sizeof (t, Option.map (exp_captured_ren ren) len, st)
| Sil.Cast (t, e) -> Sil.Cast (t, exp_captured_ren ren e)
| Sil.UnOp (op, e, topt) ->
let topt' = match topt with
| Some t -> Some (typ_captured_ren ren t)
| None -> None in
Sil.UnOp (op, exp_captured_ren ren e, topt')
| Sil.UnOp (op, e, topt) -> Sil.UnOp (op, exp_captured_ren ren e, topt)
| Sil.BinOp (op, e1, e2) ->
let e1' = exp_captured_ren ren e1 in
let e2' = exp_captured_ren ren e2 in
Sil.BinOp (op, e1', e2')
| Sil.Lvar id -> Sil.Lvar id
| Sil.Lfield (e, fld, typ) -> Sil.Lfield (exp_captured_ren ren e, fld, typ_captured_ren ren typ)
| Sil.Lfield (e, fld, typ) -> Sil.Lfield (exp_captured_ren ren e, fld, typ)
| Sil.Lindex (e1, e2) ->
let e1' = exp_captured_ren ren e1 in
let e2' = exp_captured_ren ren e2 in
Sil.Lindex(e1', e2')
(* Apply a renaming function to a type *)
and typ_captured_ren ren typ = match typ with
| Sil.Tvar _
| Sil.Tint _
| Sil.Tfloat _
| Sil.Tvoid
| Sil.Tstruct _
| Sil.Tfun _ ->
typ
| Sil.Tptr (t', pk) ->
Sil.Tptr (typ_captured_ren ren t', pk)
| Sil.Tarray (t, e) ->
Sil.Tarray (typ_captured_ren ren t, exp_captured_ren ren e)
let atom_captured_ren ren = function
| Sil.Aeq (e1, e2) ->
Sil.Aeq (exp_captured_ren ren e1, exp_captured_ren ren e2)
@ -2390,12 +2302,12 @@ let rec strexp_captured_ren ren = function
| Sil.Estruct (fld_se_list, inst) ->
let f (fld, se) = (fld, strexp_captured_ren ren se) in
Sil.Estruct (IList.map f fld_se_list, inst)
| Sil.Earray (size, idx_se_list, inst) ->
| Sil.Earray (len, idx_se_list, inst) ->
let f (idx, se) =
let idx' = exp_captured_ren ren idx in
(idx', strexp_captured_ren ren se) in
let size' = exp_captured_ren ren size in
Sil.Earray (size', IList.map f idx_se_list, inst)
let len' = exp_captured_ren ren len in
Sil.Earray (len', IList.map f idx_se_list, inst)
and hpred_captured_ren ren = function
| Sil.Hpointsto (base, se, te) ->

@ -380,8 +380,8 @@ end = struct
| Sil.Eexp _ -> ()
| Sil.Estruct (fsel, _) ->
IList.iter (fun (_, se) -> strexp_extract se) fsel
| Sil.Earray (size, isel, _) ->
add_lt_minus1_e size;
| Sil.Earray (len, isel, _) ->
add_lt_minus1_e len;
IList.iter (fun (idx, se) ->
add_lt_minus1_e idx;
strexp_extract se) isel in
@ -909,7 +909,7 @@ module ProverState : sig
val checks : check list ref
type bounds_check = (** type for array bounds checks *)
| BCsize_imply of Sil.exp * Sil.exp * Sil.exp list (** coming from array_size_imply *)
| BClen_imply of Sil.exp * Sil.exp * Sil.exp list (** coming from array_len_imply *)
| BCfrom_pre of Sil.atom (** coming implicitly from preconditions *)
val add_bounds_check : bounds_check -> unit
@ -934,12 +934,12 @@ module ProverState : sig
val d_implication_error : string * (Sil.subst * Sil.subst) * exc_body -> unit
end = struct
type bounds_check =
| BCsize_imply of Sil.exp * Sil.exp * Sil.exp list
| BClen_imply of Sil.exp * Sil.exp * Sil.exp list
| BCfrom_pre of Sil.atom
let implication_lhs = ref Prop.prop_emp
let implication_rhs = ref (Prop.expose Prop.prop_emp)
let fav_in_array_size = ref (Sil.fav_new ()) (* free variables in array size position *)
let fav_in_array_len = ref (Sil.fav_new ()) (* free variables in array len position *)
let bounds_checks = ref [] (* delayed bounds check for arrays *)
let frame_fld = ref []
let missing_fld = ref []
@ -949,12 +949,12 @@ end = struct
let missing_typ = ref []
let checks = ref []
(** free vars in array size position in current part of prop *)
let prop_fav_size prop =
(** free vars in array len position in current strexp part of prop *)
let prop_fav_len prop =
let fav = Sil.fav_new () in
let do_hpred = function
| Sil.Hpointsto (_, Sil.Earray (Sil.Var _ as size, _, _), _) ->
Sil.exp_fav_add fav size
| Sil.Hpointsto (_, Sil.Earray (Sil.Var _ as len, _, _), _) ->
Sil.exp_fav_add fav len
| _ -> () in
IList.iter do_hpred (Prop.get_sigma prop);
fav
@ -963,7 +963,7 @@ end = struct
checks := [];
implication_lhs := lhs;
implication_rhs := rhs;
fav_in_array_size := prop_fav_size rhs;
fav_in_array_len := prop_fav_len rhs;
bounds_checks := [];
frame_fld := [];
frame_typ := [];
@ -993,11 +993,12 @@ end = struct
let add_missing_sigma sigma =
missing_sigma := sigma @ !missing_sigma
(** atom considered array bounds check if it contains vars present in array size position in the pre *)
(** atom considered array bounds check if it contains vars present in array length position in the
pre *)
let atom_is_array_bounds_check atom =
let fav_a = Sil.atom_fav atom in
Prop.atom_is_inequality atom &&
Sil.fav_exists fav_a (fun a -> Sil.fav_mem !fav_in_array_size a)
Sil.fav_exists fav_a (fun a -> Sil.fav_mem !fav_in_array_len a)
let get_bounds_checks () = !bounds_checks
let get_frame_fld () = !frame_fld
@ -1213,18 +1214,18 @@ let path_to_id path =
| None -> Ident.create_fresh Ident.kfootprint
| Some s -> Ident.create_path s
(** Implication for the size of arrays *)
let array_size_imply calc_missing subs size1 size2 indices2 =
match size1, size2 with
(** Implication for the length of arrays *)
let array_len_imply calc_missing subs len1 len2 indices2 =
match len1, len2 with
| _, Sil.Var _
| _, Sil.BinOp (Sil.PlusA, Sil.Var _, _)
| _, Sil.BinOp (Sil.PlusA, _, Sil.Var _)
| Sil.BinOp (Sil.Mult, _, _), _ ->
(try exp_imply calc_missing subs size1 size2 with
(try exp_imply calc_missing subs len1 len2 with
| IMPL_EXC (s, subs', x) ->
raise (IMPL_EXC ("array size:" ^ s, subs', x)))
raise (IMPL_EXC ("array len:" ^ s, subs', x)))
| _ ->
ProverState.add_bounds_check (ProverState.BCsize_imply (size1, size2, indices2));
ProverState.add_bounds_check (ProverState.BClen_imply (len1, len2, indices2));
subs
(** Extend [sub1] and [sub2] to witnesses that each instance of
@ -1252,18 +1253,18 @@ let rec sexp_imply source calc_index_frame calc_missing subs se1 se2 typ2 : subs
d_impl_err ("sexp_imply not implemented", subs, (EXC_FALSE_SEXPS (se1, se2)));
raise (Exceptions.Abduction_case_not_implemented __POS__)
end
| Sil.Earray (size1, esel1, inst1), Sil.Earray (size2, esel2, _) ->
| Sil.Earray (len1, esel1, inst1), Sil.Earray (len2, esel2, _) ->
let indices2 = IList.map fst esel2 in
let subs' = array_size_imply calc_missing subs size1 size2 indices2 in
let subs' = array_len_imply calc_missing subs len1 len2 indices2 in
let subs'', index_frame, index_missing =
array_imply source calc_index_frame calc_missing subs' esel1 esel2 typ2 in
let index_frame_opt = if index_frame != []
then Some (Sil.Earray (size1, index_frame, inst1))
then Some (Sil.Earray (len1, index_frame, inst1))
else None in
let index_missing_opt =
if index_missing != [] &&
(Config.allow_missing_index_in_proc_call || !Config.footprint)
then Some (Sil.Earray (size1, index_missing, inst1))
then Some (Sil.Earray (len1, index_missing, inst1))
else None in
subs'', index_frame_opt, index_missing_opt
| Sil.Eexp (_, inst), Sil.Estruct (fsel, inst') ->
@ -1272,13 +1273,17 @@ let rec sexp_imply source calc_index_frame calc_missing subs se1 se2 typ2 : subs
let g (f, _) = (f, Sil.Eexp (Sil.Var (Ident.create_fresh Ident.knormal), inst)) in
IList.map g fsel in
sexp_imply source calc_index_frame calc_missing subs (Sil.Estruct (fsel', inst')) se2 typ2
| Sil.Eexp _, Sil.Earray (size, _, inst)
| Sil.Estruct _, Sil.Earray (size, _, inst) ->
let se1' = Sil.Earray (size, [(Sil.exp_zero, se1)], inst) in
| Sil.Eexp _, Sil.Earray (len, _, inst)
| Sil.Estruct _, Sil.Earray (len, _, inst) ->
let se1' = Sil.Earray (len, [(Sil.exp_zero, se1)], inst) in
sexp_imply source calc_index_frame calc_missing subs se1' se2 typ2
| Sil.Earray (size, _, _), Sil.Eexp (_, inst) ->
let se2' = Sil.Earray (size, [(Sil.exp_zero, se2)], inst) in
let typ2' = Sil.Tarray (typ2, size) in
| Sil.Earray (len, _, _), Sil.Eexp (_, inst) ->
let se2' = Sil.Earray (len, [(Sil.exp_zero, se2)], inst) in
let typ2' = Sil.Tarray (typ2, None) in
(* In the sexp_imply, struct_imply, array_imply, and sexp_imply_nolhs functions, the typ2
argument is only used by eventually passing its value to Sil.struct_typ_fld, Sil.Lfield,
Sil.struct_typ_fld, or Sil.array_typ_elem. None of these are sensitive to the length field
of Tarray, so forgetting the length of typ2' here is not a problem. *)
sexp_imply source true calc_missing subs se1 se2' typ2' (* calculate index_frame because the rhs is a singleton array *)
| _ ->
d_impl_err ("sexp_imply not implemented", subs, (EXC_FALSE_SEXPS (se1, se2)));
@ -1442,18 +1447,19 @@ let expand_hpred_pointer calc_index_frame hpred : bool * bool * Sil.hpred =
let hpred' = Sil.Hpointsto (e, Sil.Estruct ([(fld, se)], Sil.inst_none), t') in
expand true true hpred'
| Sil.Hpointsto (Sil.Lindex (e, ind), se, t) ->
let clen = Sil.exp_get_undefined false in
let t' = match t with
| Sil.Sizeof (t_, vlen, st) ->
Sil.Sizeof (Sil.Tarray (t_, clen), vlen, st)
| Sil.Sizeof (t_, len, st) -> Sil.Sizeof (Sil.Tarray (t_, None), len, st)
| _ -> raise (Failure "expand_hpred_pointer: Unexpected non-sizeof type in Lindex") in
let hpred' = Sil.Hpointsto (e, Sil.Earray (clen, [(ind, se)], Sil.inst_none), t') in
let len = match t' with
| Sil.Sizeof (_, Some len, _) -> len
| _ -> Sil.exp_get_undefined false in
let hpred' = Sil.Hpointsto (e, Sil.Earray (len, [(ind, se)], Sil.inst_none), t') in
expand true true hpred'
| Sil.Hpointsto (Sil.BinOp (Sil.PlusPI, e1, e2), Sil.Earray (size, esel, inst), t) ->
| Sil.Hpointsto (Sil.BinOp (Sil.PlusPI, e1, e2), Sil.Earray (len, esel, inst), t) ->
let shift_exp e = Sil.BinOp (Sil.PlusA, e, e2) in
let size' = shift_exp size in
let len' = shift_exp len in
let esel' = IList.map (fun (e, se) -> (shift_exp e, se)) esel in
let hpred' = Sil.Hpointsto (e1, Sil.Earray (size', esel', inst), t) in
let hpred' = Sil.Hpointsto (e1, Sil.Earray (len', esel', inst), t) in
expand true calc_index_frame hpred'
| _ -> changed, calc_index_frame, hpred in
expand false calc_index_frame hpred
@ -1687,8 +1693,8 @@ let texp_imply tenv subs texp1 texp2 e1 calc_missing =
else
None, None
(** pre-process implication between a non-array and an array: the non-array is turned into an array of size given by its type
only active in type_size mode *)
(** pre-process implication between a non-array and an array: the non-array is turned into an array
of length given by its type only active in type_size mode *)
let sexp_imply_preprocess se1 texp1 se2 = match se1, texp1, se2 with
| Sil.Eexp (_, inst), Sil.Sizeof _, Sil.Earray _ when Config.type_size ->
let se1' = Sil.Earray (texp1, [(Sil.exp_zero, se1)], inst) in
@ -1941,13 +1947,14 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : (subst2 *
| _ -> None)
| _ -> None in
let mk_constant_string_hpred s = (* create an hpred from a constant string *)
let len = Sil.exp_int (Sil.Int.of_int (1 + String.length s)) in
let len = Sil.Int.of_int (1 + String.length s) in
let root = Sil.Const (Sil.Cstr s) in
let sexp =
let index = Sil.exp_int (Sil.Int.of_int (String.length s)) in
match !Config.curr_language with
| Config.Clang ->
Sil.Earray (len, [(index, Sil.Eexp (Sil.exp_zero, Sil.inst_none))], Sil.inst_none)
Sil.Earray
(Sil.exp_int len, [(index, Sil.Eexp (Sil.exp_zero, Sil.inst_none))], Sil.inst_none)
| Config.Java ->
let mk_fld_sexp s =
let fld = Ident.create_fieldname (Mangled.from_string s) 0 in
@ -1958,7 +1965,7 @@ and sigma_imply tenv calc_index_frame calc_missing subs prop1 sigma2 : (subst2 *
let const_string_texp =
match !Config.curr_language with
| Config.Clang ->
Sil.Sizeof (Sil.Tarray (Sil.Tint Sil.IChar, len), Some len, Sil.Subtype.exact)
Sil.Sizeof (Sil.Tarray (Sil.Tint Sil.IChar, Some len), None, Sil.Subtype.exact)
| Config.Java ->
let object_type =
Typename.TN_csu (Csu.Class Csu.Java, Mangled.from_string "java.lang.String") in
@ -2107,13 +2114,14 @@ let check_array_bounds (sub1, sub2) prop =
let lt_ineq = Prop.mk_inequality (Sil.BinOp(Sil.Le, e', e'')) in
if check_atom prop lt_ineq then check_failed lt_ineq in
let check_bound = function
| ProverState.BCsize_imply (_size1, _size2, _indices2) ->
let size1 = Sil.exp_sub sub1 _size1 in
let size2 = Sil.exp_sub sub2 _size2 in
(* L.d_strln_color Orange "check_bound "; Sil.d_exp size1; L.d_str " "; Sil.d_exp size2; L.d_ln(); *)
let indices_to_check = match size2 with
| _ -> [Sil.BinOp(Sil.PlusA, size2, Sil.exp_minus_one)] (* only check size *) in
IList.iter (fail_if_le size1) indices_to_check
| ProverState.BClen_imply (len1_, len2_, _indices2) ->
let len1 = Sil.exp_sub sub1 len1_ in
let len2 = Sil.exp_sub sub2 len2_ in
(* L.d_strln_color Orange "check_bound ";
Sil.d_exp len1; L.d_str " "; Sil.d_exp len2; L.d_ln(); *)
let indices_to_check = match len2 with
| _ -> [Sil.BinOp(Sil.PlusA, len2, Sil.exp_minus_one)] (* only check len *) in
IList.iter (fail_if_le len1) indices_to_check
| ProverState.BCfrom_pre _atom ->
let atom_neg = Prop.atom_negate (Sil.atom_sub sub2 _atom) in
(* L.d_strln_color Orange "BCFrom_pre"; Sil.d_atom atom_neg; L.d_ln (); *)
@ -2152,8 +2160,10 @@ let check_implication_base pname tenv check_frame_empty calc_missing prop1 prop2
let sigma1' = Prop.sigma_sub sub1 sigma1 in
L.d_ln ();
let prop_for_impl = prepare_prop_for_implication (sub1, sub2) pi1' sigma1' in
imply_pi calc_missing (sub1, sub2) prop_for_impl pi2_nobcheck; (* only deal with pi2 without bound checks *)
check_array_bounds (sub1, sub2) prop_for_impl; (* handle implicit bound checks, plus those from array_size_imply *)
(* only deal with pi2 without bound checks *)
imply_pi calc_missing (sub1, sub2) prop_for_impl pi2_nobcheck;
(* handle implicit bound checks, plus those from array_len_imply *)
check_array_bounds (sub1, sub2) prop_for_impl;
L.d_strln "Result of Abduction";
L.d_increase_indent 1; d_impl (sub1, sub2) (prop1, prop2); L.d_decrease_indent 1; L.d_ln ();
L.d_strln"returning TRUE";

@ -28,23 +28,23 @@ let rec list_rev_and_concat l1 l2 =
| x1:: l1' -> list_rev_and_concat l1' (x1:: l2)
(** Check whether the index is out of bounds.
If the size is - 1, no check is performed.
If the length is - 1, no check is performed.
If the index is provably out of bound, a bound error is given.
If the size is a constant and the index is not provably in bound, a warning is given.
If the length is a constant and the index is not provably in bound, a warning is given.
*)
let check_bad_index pname p size index loc =
let size_is_constant = match size with
let check_bad_index pname p len index loc =
let len_is_constant = match len with
| Sil.Const _ -> true
| _ -> false in
let index_provably_out_of_bound () =
let index_too_large = Prop.mk_inequality (Sil.BinOp(Sil.Le, size, index)) in
let index_negative = Prop.mk_inequality (Sil.BinOp(Sil.Le, index, Sil.exp_minus_one)) in
let index_too_large = Prop.mk_inequality (Sil.BinOp (Sil.Le, len, index)) in
let index_negative = Prop.mk_inequality (Sil.BinOp (Sil.Le, index, Sil.exp_minus_one)) in
(Prover.check_atom p index_too_large) || (Prover.check_atom p index_negative) in
let index_provably_in_bound () =
let size_minus_one = Sil.BinOp(Sil.PlusA, size, Sil.exp_minus_one) in
let index_not_too_large = Prop.mk_inequality (Sil.BinOp(Sil.Le, index, size_minus_one)) in
let len_minus_one = Sil.BinOp(Sil.PlusA, len, Sil.exp_minus_one) in
let index_not_too_large = Prop.mk_inequality (Sil.BinOp(Sil.Le, index, len_minus_one)) in
let index_nonnegative = Prop.mk_inequality (Sil.BinOp(Sil.Le, Sil.exp_zero, index)) in
Prover.check_zero index || (* index 0 always in bound, even when we know nothing about size *)
Prover.check_zero index || (* index 0 always in bound, even when we know nothing about len *)
((Prover.check_atom p index_not_too_large) && (Prover.check_atom p index_nonnegative)) in
let index_has_bounds () =
match Prover.get_bounds p index with
@ -55,17 +55,17 @@ let check_bad_index pname p size index loc =
| _ -> None in
if not (index_provably_in_bound ()) then
begin
let size_const_opt = get_const_opt size in
let len_const_opt = get_const_opt len in
let index_const_opt = get_const_opt index in
if index_provably_out_of_bound () then
let deref_str = Localise.deref_str_array_bound size_const_opt index_const_opt in
let deref_str = Localise.deref_str_array_bound len_const_opt index_const_opt in
let exn =
Exceptions.Array_out_of_bounds_l1
(Errdesc.explain_array_access deref_str p loc, __POS__) in
let pre_opt = State.get_normalized_pre (Abs.abstract_no_symop pname) in
Reporting.log_warning pname ~pre: pre_opt exn
else if size_is_constant then
let deref_str = Localise.deref_str_array_bound size_const_opt index_const_opt in
else if len_is_constant then
let deref_str = Localise.deref_str_array_bound len_const_opt index_const_opt in
let desc = Errdesc.explain_array_access deref_str p loc in
let exn = if index_has_bounds ()
then Exceptions.Array_out_of_bounds_l2 (desc, __POS__)
@ -75,14 +75,14 @@ let check_bad_index pname p size index loc =
end
(** Perform bounds checking *)
let bounds_check pname prop size e =
let bounds_check pname prop len e =
if Config.trace_rearrange then
begin
L.d_str "Bounds check index:"; Sil.d_exp e;
L.d_str " size: "; Sil.d_exp size;
L.d_str " len: "; Sil.d_exp len;
L.d_ln()
end;
check_bad_index pname prop size e
check_bad_index pname prop len e
let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp t
(off: Sil.offset list) inst : Sil.atom list * Sil.strexp * Sil.typ =
@ -112,7 +112,8 @@ let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp
create_struct_values
pname tenv orig_prop footprint_part kind max_stamp t' off' inst in
let se = Sil.Estruct ([(f, se')], inst) in
let replace_typ_of_f (f', t', a') = if Ident.fieldname_equal f f' then (f, res_t', a') else (f', t', a') in
let replace_typ_of_f (f', t', a') =
if Ident.fieldname_equal f f' then (f, res_t', a') else (f', t', a') in
let instance_fields' =
IList.sort Sil.fld_typ_ann_compare (IList.map replace_typ_of_f instance_fields) in
(atoms', se, Sil.Tstruct { struct_typ with Sil.instance_fields = instance_fields'})
@ -121,25 +122,29 @@ let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp
create_struct_values
pname tenv orig_prop footprint_part kind max_stamp t off' inst in
let e' = Sil.array_clean_new_index footprint_part e in
let size = Sil.exp_get_undefined false in
let se = Sil.Earray (size, [(e', se')], inst) in
let res_t = Sil.Tarray (res_t', size) in
(Sil.Aeq(e, e'):: atoms', se, res_t)
| Sil.Tarray(_, size),[] ->
([], Sil.Earray(size, [], inst), t)
| Sil.Tarray(t', size'), (Sil.Off_index e) :: off' ->
bounds_check pname orig_prop size' e (State.get_loc ());
let atoms', se', res_t' =
create_struct_values
pname tenv orig_prop footprint_part kind max_stamp t' off' inst in
let e' = Sil.array_clean_new_index footprint_part e in
let se = Sil.Earray(size', [(e', se')], inst) in
let res_t = Sil.Tarray(res_t', size') in
(Sil.Aeq(e, e'):: atoms', se, res_t)
| Sil.Tarray _, (Sil.Off_fld _) :: _ ->
assert false
let len = Sil.Var (new_id ()) in
let se = Sil.Earray (len, [(e', se')], inst) in
let res_t = Sil.Tarray (res_t', None) in
(Sil.Aeq(e, e') :: atoms', se, res_t)
| Sil.Tarray (t', len_), off ->
let len = match len_ with
| None -> Sil.Var (new_id ())
| Some len -> Sil.Const (Sil.Cint len) in
(match off with
| [] ->
([], Sil.Earray (len, [], inst), t)
| (Sil.Off_index e) :: off' ->
bounds_check pname orig_prop len e (State.get_loc ());
let atoms', se', res_t' =
create_struct_values
pname tenv orig_prop footprint_part kind max_stamp t' off' inst in
let e' = Sil.array_clean_new_index footprint_part e in
let se = Sil.Earray (len, [(e', se')], inst) in
let res_t = Sil.Tarray (res_t', len_) in
(Sil.Aeq(e, e') :: atoms', se, res_t)
| (Sil.Off_fld _) :: _ ->
assert false
)
| Sil.Tint _, [] | Sil.Tfloat _, [] | Sil.Tvoid, [] | Sil.Tfun _, [] | Sil.Tptr _, [] ->
let id = new_id () in
([], Sil.Eexp (Sil.Var id, inst), t)
@ -150,14 +155,13 @@ let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp
let t' = match t with
| Sil.Tptr(t', _) -> t'
| _ -> t in
let size = Sil.Var (new_id ()) in
let len = Sil.Var (new_id ()) in
let atoms', se', res_t' =
create_struct_values
pname tenv orig_prop footprint_part kind max_stamp t' [] inst in
let e' = Sil.array_clean_new_index footprint_part e in
let se = Sil.Earray(size, [(e', se')], inst) in
let res_t = Sil.Tarray(res_t', size) in
let se = Sil.Earray (len, [(e', se')], inst) in
let res_t = Sil.Tarray (res_t', None) in
(Sil.Aeq(e, e'):: atoms', se, res_t)
| Sil.Tint _, _ | Sil.Tfloat _, _ | Sil.Tvoid, _ | Sil.Tfun _, _ | Sil.Tptr _, _ ->
L.d_str "create_struct_values type:"; Sil.d_typ_full t; L.d_str " off: "; Sil.d_offset_list off; L.d_ln();
@ -185,6 +189,9 @@ let rec create_struct_values pname tenv orig_prop footprint_part kind max_stamp
let rec _strexp_extend_values
pname tenv orig_prop footprint_part kind max_stamp
se typ (off : Sil.offset list) inst =
let new_id () =
incr max_stamp;
Ident.create kind !max_stamp in
match off, se, typ with
| [], Sil.Eexp _, _
| [], Sil.Estruct _, _ ->
@ -193,7 +200,7 @@ let rec _strexp_extend_values
let off_new = Sil.Off_index(Sil.exp_zero):: off in
_strexp_extend_values
pname tenv orig_prop footprint_part kind max_stamp se typ off_new inst
| (Sil.Off_fld _):: _, Sil.Earray _, Sil.Tarray _ ->
| (Sil.Off_fld _) :: _, Sil.Earray _, Sil.Tarray _ ->
let off_new = Sil.Off_index(Sil.exp_zero):: off in
_strexp_extend_values
pname tenv orig_prop footprint_part kind max_stamp se typ off_new inst
@ -243,18 +250,17 @@ let rec _strexp_extend_values
| (Sil.Off_index _):: _, Sil.Eexp _, Sil.Tptr _
| (Sil.Off_index _):: _, Sil.Estruct _, Sil.Tstruct _ ->
(* L.d_strln_color Orange "turn into an array"; *)
let size = match se with
| Sil.Eexp (_, Sil.Ialloc) -> Sil.exp_one (* if allocated explicitly, we know size is 1 *)
let len = match se with
| Sil.Eexp (_, Sil.Ialloc) -> Sil.exp_one (* if allocated explicitly, we know len is 1 *)
| _ ->
if Config.type_size then Sil.exp_one (* Sil.Sizeof (typ, Sil.Subtype.exact) *)
else Sil.exp_get_undefined false in
let se_new = Sil.Earray(size, [(Sil.exp_zero, se)], inst) in
let typ_new = Sil.Tarray(typ, size) in
else Sil.Var (new_id ()) in
let se_new = Sil.Earray (len, [(Sil.exp_zero, se)], inst) in
let typ_new = Sil.Tarray (typ, None) in
_strexp_extend_values
pname tenv orig_prop footprint_part kind max_stamp se_new typ_new off inst
| (Sil.Off_index e):: off', Sil.Earray(size, esel, inst_arr), Sil.Tarray(typ', size_for_typ') ->
bounds_check pname orig_prop size e (State.get_loc ());
| (Sil.Off_index e) :: off', Sil.Earray (len, esel, inst_arr), Sil.Tarray (typ', len_for_typ') ->
bounds_check pname orig_prop len e (State.get_loc ());
begin
try
let _, se' = IList.find (fun (e', _) -> Sil.exp_equal e e') esel in
@ -264,15 +270,19 @@ let rec _strexp_extend_values
let replace acc (res_atoms', res_se', res_typ') =
let replace_ise ise = if Sil.exp_equal e (fst ise) then (e, res_se') else ise in
let res_esel' = IList.map replace_ise esel in
if (Sil.typ_equal res_typ' typ') || (IList.length res_esel' = 1)
then (res_atoms', Sil.Earray(size, res_esel', inst_arr), Sil.Tarray(res_typ', size_for_typ')) :: acc
else raise (Exceptions.Bad_footprint __POS__) in
if (Sil.typ_equal res_typ' typ') || (IList.length res_esel' = 1) then
( res_atoms'
, Sil.Earray (len, res_esel', inst_arr)
, Sil.Tarray (res_typ', len_for_typ') )
:: acc
else
raise (Exceptions.Bad_footprint __POS__) in
IList.fold_left replace [] atoms_se_typ_list'
with Not_found ->
array_case_analysis_index pname tenv orig_prop
footprint_part kind max_stamp
size esel
size_for_typ' typ'
len esel
len_for_typ' typ'
e off' inst_arr inst
end
| _, _, _ ->
@ -280,8 +290,8 @@ let rec _strexp_extend_values
and array_case_analysis_index pname tenv orig_prop
footprint_part kind max_stamp
array_size array_cont
typ_array_size typ_cont
array_len array_cont
typ_array_len typ_cont
index off inst_arr inst
=
let check_sound t' =
@ -290,13 +300,13 @@ and array_case_analysis_index pname tenv orig_prop
let index_in_array =
IList.exists (fun (i, _) -> Prover.check_equal Prop.prop_emp index i) array_cont in
let array_is_full =
match array_size with
match array_len with
| Sil.Const (Sil.Cint n') -> Sil.Int.geq (Sil.Int.of_int (IList.length array_cont)) n'
| _ -> false in
if index_in_array then
let array_default = Sil.Earray(array_size, array_cont, inst_arr) in
let typ_default = Sil.Tarray(typ_cont, typ_array_size) in
let array_default = Sil.Earray (array_len, array_cont, inst_arr) in
let typ_default = Sil.Tarray (typ_cont, typ_array_len) in
[([], array_default, typ_default)]
else if !Config.footprint then begin
let atoms, elem_se, elem_typ =
@ -304,8 +314,8 @@ and array_case_analysis_index pname tenv orig_prop
pname tenv orig_prop footprint_part kind max_stamp typ_cont off inst in
check_sound elem_typ;
let cont_new = IList.sort Sil.exp_strexp_compare ((index, elem_se):: array_cont) in
let array_new = Sil.Earray(array_size, cont_new, inst_arr) in
let typ_new = Sil.Tarray(elem_typ, typ_array_size) in
let array_new = Sil.Earray (array_len, cont_new, inst_arr) in
let typ_new = Sil.Tarray (elem_typ, typ_array_len) in
[(atoms, array_new, typ_new)]
end
else begin
@ -317,8 +327,8 @@ and array_case_analysis_index pname tenv orig_prop
pname tenv orig_prop footprint_part kind max_stamp typ_cont off inst in
check_sound elem_typ;
let cont_new = IList.sort Sil.exp_strexp_compare ((index, elem_se):: array_cont) in
let array_new = Sil.Earray(array_size, cont_new, inst_arr) in
let typ_new = Sil.Tarray(elem_typ, typ_array_size) in
let array_new = Sil.Earray (array_len, cont_new, inst_arr) in
let typ_new = Sil.Tarray (elem_typ, typ_array_len) in
[(atoms, array_new, typ_new)]
end in
let rec handle_case acc isel_seen_rev = function
@ -330,10 +340,10 @@ and array_case_analysis_index pname tenv orig_prop
let atoms_se_typ_list' =
IList.fold_left (fun acc' (atoms', se', typ') ->
check_sound typ';
let atoms_new = Sil.Aeq(index, i) :: atoms' in
let atoms_new = Sil.Aeq (index, i) :: atoms' in
let isel_new = list_rev_and_concat isel_seen_rev ((i, se'):: isel_unseen) in
let array_new = Sil.Earray(array_size, isel_new, inst_arr) in
let typ_new = Sil.Tarray(typ', typ_array_size) in
let array_new = Sil.Earray (array_len, isel_new, inst_arr) in
let typ_new = Sil.Tarray (typ', typ_array_len) in
(atoms_new, array_new, typ_new):: acc'
) [] atoms_se_typ_list in
let acc_new = atoms_se_typ_list' :: acc in
@ -927,7 +937,7 @@ let type_at_offset texp off =
(IList.find (fun (f', _, _) -> Ident.fieldname_equal f f') instance_fields) in
strip_offset off' typ'
with Not_found -> None)
| (Sil.Off_index _):: off', Sil.Tarray (typ', _) ->
| (Sil.Off_index _) :: off', Sil.Tarray (typ', _) ->
strip_offset off' typ'
| _ -> None in
match texp with

@ -26,7 +26,7 @@ let tenv_key, summary_key, cfg_key, trace_key, cg_key,
799050016, 579094948, 972393003
(** version of the binary files, to be incremented for each change *)
let version = 24
let version = 25
(** Retry the function while an exception filtered is thrown,

@ -170,11 +170,11 @@ let rec apply_offlist
pp_error();
assert false
| (Sil.Off_index idx):: offlist', Sil.Earray (size, esel, inst1) ->
| (Sil.Off_index idx) :: offlist', Sil.Earray (len, esel, inst1) ->
let nidx = Prop.exp_normalize_prop p idx in
begin
let typ' = Tenv.expand_type tenv typ in
let t', size' = match typ' with Sil.Tarray (t', size') -> (t', size') | _ -> assert false in
let t', len' = match typ' with Sil.Tarray (t', len') -> (t', len') | _ -> assert false in
try
let idx_ese', se' = IList.find (fun ese -> Prover.check_equal p nidx (fst ese)) esel in
let res_e', res_se', res_t', res_pred_insts_op' =
@ -185,8 +185,8 @@ let rec apply_offlist
if Sil.exp_equal idx_ese' (fst ese)
then (idx_ese', res_se')
else ese in
let res_se = Sil.Earray(size, IList.map replace_ese esel, inst1) in
let res_t = Sil.Tarray(res_t', size') in
let res_se = Sil.Earray (len, IList.map replace_ese esel, inst1) in
let res_t = Sil.Tarray (res_t', len') in
(res_e', res_se, res_t, res_pred_insts_op')
with Not_found ->
(* return a nondeterministic value if the index is not found after rearrangement *)
@ -264,9 +264,9 @@ let rec execute_nullify_se = function
| Sil.Estruct (fsel, _) ->
let fsel' = IList.map (fun (fld, se) -> (fld, execute_nullify_se se)) fsel in
Sil.Estruct (fsel', Sil.inst_nullify)
| Sil.Earray (size, esel, _) ->
| Sil.Earray (len, esel, _) ->
let esel' = IList.map (fun (idx, se) -> (idx, execute_nullify_se se)) esel in
Sil.Earray (size, esel', Sil.inst_nullify)
Sil.Earray (len, esel', Sil.inst_nullify)
(** Do pruning for conditional [if (e1 != e2) ] if [positive] is true
and [(if (e1 == e2)] if [positive] is false *)

@ -413,8 +413,8 @@ let rec sexp_set_inst inst = function
Sil.Eexp (e, inst)
| Sil.Estruct (fsel, _) ->
Sil.Estruct ((IList.map (fun (f, se) -> (f, sexp_set_inst inst se)) fsel), inst)
| Sil.Earray (size, esel, _) ->
Sil.Earray (size, IList.map (fun (e, se) -> (e, sexp_set_inst inst se)) esel, inst)
| Sil.Earray (len, esel, _) ->
Sil.Earray (len, IList.map (fun (e, se) -> (e, sexp_set_inst inst se)) esel, inst)
let rec fsel_star_fld fsel1 fsel2 = match fsel1, fsel2 with
| [], fsel2 -> fsel2
@ -447,11 +447,11 @@ and sexp_star_fld se1 se2 : Sil.strexp =
match se1, se2 with
| Sil.Estruct (fsel1, _), Sil.Estruct (fsel2, inst2) ->
Sil.Estruct (fsel_star_fld fsel1 fsel2, inst2)
| Sil.Earray (size1, esel1, _), Sil.Earray (_, esel2, inst2) ->
Sil.Earray (size1, esel_star_fld esel1 esel2, inst2)
| Sil.Eexp (_, inst1), Sil.Earray (size2, esel2, _) ->
| Sil.Earray (len1, esel1, _), Sil.Earray (_, esel2, inst2) ->
Sil.Earray (len1, esel_star_fld esel1 esel2, inst2)
| Sil.Eexp (_, inst1), Sil.Earray (len2, esel2, _) ->
let esel1 = [(Sil.exp_zero, se1)] in
Sil.Earray (size2, esel_star_fld esel1 esel2, inst1)
Sil.Earray (len2, esel_star_fld esel1 esel2, inst1)
| _ ->
L.d_str "cannot star ";
Sil.d_sexp se1; L.d_str " and "; Sil.d_sexp se2;

@ -684,7 +684,7 @@ let var_or_zero_in_init_list tenv e typ ~return_zero:return_zero =
let exp_types = zip lh_exprs lh_types in
IList.map (fun (e, t) ->
IList.flatten (var_or_zero_in_init_list' e t tns)) exp_types
| Sil.Tarray (arrtyp, Sil.Const (Sil.Cint n)) ->
| Sil.Tarray (arrtyp, Some n) ->
let size = Sil.Int.to_int n in
let indices = list_range 0 (size - 1) in
let index_constants =

@ -61,10 +61,10 @@ let pointer_attribute_of_objc_attribute attr_info =
| `OCL_Weak -> Sil.Pk_objc_weak
| `OCL_Autoreleasing -> Sil.Pk_objc_autoreleasing
let rec build_array_type translate_decl tenv type_ptr n =
let rec build_array_type translate_decl tenv type_ptr n_opt =
let array_type = type_ptr_to_sil_type translate_decl tenv type_ptr in
let exp = Sil.exp_int (Sil.Int.of_int64 (Int64.of_int n)) in
Sil.Tarray (array_type, exp)
let len = Option.map (fun n -> Sil.Int.of_int64 (Int64.of_int n)) n_opt in
Sil.Tarray (array_type, len)
and sil_type_of_attr_type translate_decl tenv type_info attr_info =
match type_info.Clang_ast_t.ti_desugared_type with
@ -96,9 +96,9 @@ and sil_type_of_c_type translate_decl tenv c_type =
| IncompleteArrayType (_, type_ptr)
| DependentSizedArrayType (_, type_ptr)
| VariableArrayType (_, type_ptr) ->
build_array_type translate_decl tenv type_ptr (-1)
build_array_type translate_decl tenv type_ptr None
| ConstantArrayType (_, type_ptr, n) ->
build_array_type translate_decl tenv type_ptr n
build_array_type translate_decl tenv type_ptr (Some n)
| FunctionProtoType _
| FunctionNoProtoType _ ->
Sil.Tfun false

@ -124,7 +124,6 @@ module ComplexExpressions = struct
case_not_handled ()
| Sil.Dunop (op, de) ->
Sil.str_unop op ^ dexp_to_string de
| Sil.Dsizeof _ ->
case_not_handled ()
| Sil.Dunknown ->
@ -528,7 +527,7 @@ let typecheck_instr
(typecheck_expr_simple typestate' e' typ TypeOrigin.ONone loc)
typestate'
| Sil.Call ([id], Sil.Const (Sil.Cfun pn), [(array_exp, t)], loc, _)
when Procname.equal pn ModelBuiltins.__get_array_size ->
when Procname.equal pn ModelBuiltins.__get_array_length ->
let (_, ta, _) = typecheck_expr
find_canonical_duplicate
calls_this

@ -84,10 +84,10 @@ let rec inhabit_typ typ cfg env =
try (TypMap.find typ env.cache, env)
with Not_found ->
let inhabit_internal typ env = match typ with
| Sil.Tptr (Sil.Tarray (inner_typ, Sil.Const (Sil.Cint _)), Sil.Pk_pointer) ->
let arr_size = Sil.Const (Sil.Cint (Sil.Int.one)) in
let arr_typ = Sil.Tarray (inner_typ, arr_size) in
inhabit_alloc arr_typ (Some arr_size) typ ModelBuiltins.__new_array env
| Sil.Tptr (Sil.Tarray (inner_typ, Some _), Sil.Pk_pointer) ->
let len = Sil.Const (Sil.Cint (Sil.Int.one)) in
let arr_typ = Sil.Tarray (inner_typ, Some Sil.Int.one) in
inhabit_alloc arr_typ (Some len) typ ModelBuiltins.__new_array env
| Sil.Tptr (typ, Sil.Pk_pointer) as ptr_to_typ ->
(* TODO (t4575417): this case does not work correctly for enums, but they are currently
* broken in Infer anyway (see t4592290) *)

@ -400,8 +400,8 @@ let use_static_final_fields context =
let builtin_new =
Sil.Const (Sil.Cfun ModelBuiltins.__new)
let builtin_get_array_size =
Sil.Const (Sil.Cfun ModelBuiltins.__get_array_size)
let builtin_get_array_length =
Sil.Const (Sil.Cfun ModelBuiltins.__get_array_length)
let create_sil_deref exp typ loc =
let fresh_id = Ident.create_fresh Ident.knormal in
@ -447,7 +447,8 @@ let rec expression context pc expr =
let deref = create_sil_deref sil_ex array_typ_no_ptr loc in
let args = [(sil_ex, type_of_ex)] in
let ret_id = Ident.create_fresh Ident.knormal in
let call_instr = Sil.Call([ret_id], builtin_get_array_size, args, loc, Sil.cf_default) in
let call_instr =
Sil.Call ([ret_id], builtin_get_array_length, args, loc, Sil.cf_default) in
(instrs @ [deref; call_instr], Sil.Var ret_id, type_of_expr)
| JBir.Conv conv ->
let cast_ex = Sil.Cast (JTransType.cast_type conv, sil_ex) in
@ -478,7 +479,7 @@ let rec expression context pc expr =
match binop with
| JBir.ArrayLoad _ ->
(* add an instruction that dereferences the array *)
let array_typ = Sil.Tarray(type_of_expr, Sil.Var (Ident.create_fresh Ident.kprimed)) in
let array_typ = Sil.Tarray (type_of_expr, None) in
let deref_array_instr = create_sil_deref sil_ex1 array_typ loc in
let id = Ident.create_fresh Ident.knormal in
let letderef_instr =
@ -638,17 +639,17 @@ let method_invocation context loc pc var_opt cn ms sil_obj_opt expr_list invoke_
(callee_procname, instrs)
let get_array_size context pc expr_list content_type =
let get_array_length context pc expr_list content_type =
let get_expr_instr expr other_instrs =
let (instrs, sil_size_expr, _) = expression context pc expr in
let (instrs, sil_len_expr, _) = expression context pc expr in
match other_instrs with
| (other_instrs, other_exprs) ->
(instrs@other_instrs, sil_size_expr:: other_exprs) in
let (instrs, sil_size_exprs) = (IList.fold_right get_expr_instr expr_list ([],[])) in
let get_array_type_len sil_size_expr (content_type, _) =
(Sil.Tarray (content_type, sil_size_expr), Some sil_size_expr) in
(instrs @ other_instrs, sil_len_expr :: other_exprs) in
let (instrs, sil_len_exprs) = (IList.fold_right get_expr_instr expr_list ([],[])) in
let get_array_type_len sil_len_expr (content_type, _) =
(Sil.Tarray (content_type, None), Some sil_len_expr) in
let array_type, array_len =
IList.fold_right get_array_type_len sil_size_exprs (content_type, None) in
IList.fold_right get_array_type_len sil_len_exprs (content_type, None) in
let array_size = Sil.Sizeof (array_type, array_len, Sil.Subtype.exact) in
(instrs, array_size)
@ -925,7 +926,7 @@ let rec instruction context pc instr : translation =
let content_type = JTransType.value_type program tenv vt in
let array_type = JTransType.create_array_type content_type (IList.length expr_list) in
let array_name = JContext.set_pvar context var array_type in
let (instrs, array_size) = get_array_size context pc expr_list content_type in
let (instrs, array_size) = get_array_length context pc expr_list content_type in
let call_args = [(array_size, array_type)] in
let ret_id = Ident.create_fresh Ident.knormal in
let call_instr = Sil.Call([ret_id], builtin_new_array, call_args, loc, Sil.cf_default) in

@ -69,8 +69,7 @@ let rec get_named_type vt =
match ot with
| JBasics.TArray vt ->
let content_type = get_named_type vt in
let size = Sil.exp_get_undefined false in
Sil.Tptr (Sil.Tarray (content_type, size), Sil.Pk_pointer) (* unknown size *)
Sil.Tptr (Sil.Tarray (content_type, None), Sil.Pk_pointer)
| JBasics.TClass cn -> Sil.Tptr (Sil.Tvar (typename_of_classname cn), Sil.Pk_pointer)
end
@ -84,8 +83,7 @@ let extract_cn_type_np typ =
let rec create_array_type typ dim =
if dim > 0 then
let content_typ = create_array_type typ (dim - 1) in
let size = Sil.exp_get_undefined false in
Sil.Tptr(Sil.Tarray (content_typ, size), Sil.Pk_pointer)
Sil.Tptr(Sil.Tarray (content_typ, None), Sil.Pk_pointer)
else typ
let extract_cn_no_obj typ =
@ -384,9 +382,7 @@ let is_closeable program tenv typ =
let rec object_type program tenv ot =
match ot with
| JBasics.TClass cn -> get_class_type program tenv cn
| JBasics.TArray at ->
let size = Sil.exp_get_undefined false in
Sil.Tptr (Sil.Tarray (value_type program tenv at, size), Sil.Pk_pointer)
| JBasics.TArray at -> Sil.Tptr (Sil.Tarray (value_type program tenv at, None), Sil.Pk_pointer)
(** translate a value type *)
and value_type program tenv vt =
match vt with
@ -397,9 +393,6 @@ and value_type program tenv vt =
(** Translate object types into Sil.Sizeof expressions *)
let sizeof_of_object_type program tenv ot subtypes =
match object_type program tenv ot with
| Sil.Tptr (Sil.Tarray (vtyp, len), Sil.Pk_pointer) ->
let typ = (Sil.Tarray (vtyp, len)) in
Sil.Sizeof (typ, Some len, subtypes)
| Sil.Tptr (typ, _) ->
Sil.Sizeof (typ, None, subtypes)
| _ ->

@ -36,7 +36,7 @@ let rec trans_typ : LAst.typ -> Sil.typ = function
| Tfloat -> Sil.Tfloat Sil.FFloat
| Tptr tp -> Sil.Tptr (trans_typ tp, Sil.Pk_pointer)
| Tvector (i, tp)
| Tarray (i, tp) -> Sil.Tarray (trans_typ tp, Sil.Const (Sil.Cint (Sil.Int.of_int i)))
| Tarray (i, tp) -> Sil.Tarray (trans_typ tp, Some (Sil.Int.of_int i))
| Tfunc _ -> Sil.Tfun false
| Tlabel -> raise (ImproperTypeError "Tried to generate Sil type from LLVM label type.")
| Tmetadata -> raise (ImproperTypeError "Tried to generate Sil type from LLVM metadata type.")

@ -42,9 +42,8 @@ public class ArrayIndexOutOfBoundsExceptionTest {
public void matchErrors()
throws IOException, InterruptedException, InferException {
String[] methods = {
// TODO (#7651424): re-enable these tests once the translation of arrays is fixed
// "callOutOfBound",
// "missingCheckOnIndex",
"callOutOfBound",
"missingCheckOnIndex",
"arrayIndexOutOfBoundsInCallee",
};
assertThat(

Loading…
Cancel
Save