Patch taken from: https://github.com/mshinwell/ocaml/commits/4.02-block-bounds diff --git a/asmcomp/cmmgen.ml b/asmcomp/cmmgen.ml index 01eff9c..b498b58 100644 --- a/asmcomp/cmmgen.ml +++ b/asmcomp/cmmgen.ml @@ -22,6 +22,13 @@ open Clambda open Cmm open Cmx_format +let do_check_field_access = true +(* + match try Some (Sys.getenv "BOUNDS") with Not_found -> None with + | None | Some "" -> false + | Some _ -> true +*) + (* Local binding of complex expressions *) let bind name arg fn = @@ -494,6 +501,35 @@ let get_tag ptr = let get_size ptr = Cop(Clsr, [header ptr; Cconst_int 10]) +(* Bounds checks upon field access, for debugging the compiler *) + +let check_field_access ptr field_index if_success = + if not do_check_field_access then + if_success + else + let field_index = Cconst_int field_index in + (* If [ptr] points at an infix header, we need to move it back to the "main" + [Closure_tag] header. *) + let ptr = + Cifthenelse (Cop (Ccmpi Cne, [get_tag ptr; Cconst_int Obj.infix_tag]), + ptr, + Cop (Csuba, [ptr; + Cop (Cmuli, [get_size ptr (* == Infix_offset_val(ptr) *); + Cconst_int size_addr])])) + in + let not_too_small = Cop (Ccmpi Cge, [field_index; Cconst_int 0]) in + let not_too_big = Cop (Ccmpi Clt, [field_index; get_size ptr]) in + let failure = + Cop (Cextcall ("caml_field_access_out_of_bounds_error", typ_addr, false, + Debuginfo.none), + [ptr; field_index]) + in + Cifthenelse (not_too_small, + Cifthenelse (not_too_big, + if_success, + failure), + failure) + (* Array indexing *) let log2_size_addr = Misc.log2 size_addr @@ -1550,13 +1586,18 @@ and transl_prim_1 p arg dbg = return_unit(remove_unit (transl arg)) (* Heap operations *) | Pfield n -> - get_field (transl arg) n + let ptr = transl arg in + let body = get_field ptr n in + check_field_access ptr n body | Pfloatfield n -> let ptr = transl arg in - box_float( - Cop(Cload Double_u, - [if n = 0 then ptr - else Cop(Cadda, [ptr; Cconst_int(n * size_float)])])) + let body = + box_float( + Cop(Cload Double_u, + [if n = 0 then ptr + else Cop(Cadda, [ptr; Cconst_int(n * size_float)])])) + in + check_field_access ptr n body | Pint_as_pointer -> Cop(Cadda, [transl arg; Cconst_int (-1)]) (* Exceptions *) @@ -1649,20 +1690,25 @@ and transl_prim_1 p arg dbg = and transl_prim_2 p arg1 arg2 dbg = match p with (* Heap operations *) - Psetfield(n, ptr) -> - if ptr then - return_unit(Cop(Cextcall("caml_modify", typ_void, false,Debuginfo.none), - [field_address (transl arg1) n; transl arg2])) - else - return_unit(set_field (transl arg1) n (transl arg2)) + Psetfield(n, is_ptr) -> + let ptr = transl arg1 in + let body = + if is_ptr then + Cop(Cextcall("caml_modify", typ_void, false,Debuginfo.none), + [field_address ptr n; transl arg2]) + else + set_field ptr n (transl arg2) + in + check_field_access ptr n (return_unit body) | Psetfloatfield n -> let ptr = transl arg1 in - return_unit( + let body = Cop(Cstore Double_u, [if n = 0 then ptr else Cop(Cadda, [ptr; Cconst_int(n * size_float)]); - transl_unbox_float arg2])) - + transl_unbox_float arg2]) + in + check_field_access ptr n (return_unit body) (* Boolean operations *) | Psequand -> Cifthenelse(test_bool(transl arg1), transl arg2, Cconst_int 1) diff --git a/asmrun/fail.c b/asmrun/fail.c index cb2c1cb..4f67c74 100644 --- a/asmrun/fail.c +++ b/asmrun/fail.c @@ -15,6 +15,7 @@ #include #include +#include #include "alloc.h" #include "fail.h" #include "io.h" @@ -180,3 +181,20 @@ int caml_is_special_exception(value exn) { || exn == (value) caml_exn_Assert_failure || exn == (value) caml_exn_Undefined_recursive_module; } + +void caml_field_access_out_of_bounds_error(value v_block, intnat index) +{ + assert(Is_block(v_block)); + fprintf(stderr, "Fatal error: out-of-bounds access to field %ld ", index); + fprintf(stderr, "of block at %p (%s, size %ld, tag %d)\n", + (void*) v_block, + Is_young(v_block) ? "in minor heap" + : Is_in_heap(v_block) ? "in major heap" + : Is_in_value_area(v_block) ? "in static data" + : "out-of-heap", + (long) Wosize_val(v_block), (int) Tag_val(v_block)); + fflush(stderr); + /* This error may have occurred in places where it is not reasonable to + attempt to continue. */ + abort(); +}