diff --git a/src/kernel_services/analysis/destructors.ml b/src/kernel_services/analysis/destructors.ml
index 6e9b5379c30b37d53e7593165422781bf20e4da1..bf2949535759afab05435ca2840746b3aede3b5b 100644
--- a/src/kernel_services/analysis/destructors.ml
+++ b/src/kernel_services/analysis/destructors.ml
@@ -162,12 +162,7 @@ class vis flag = object(self)
     Cil.DoChildrenPost post
 
   method! vstmt_aux s =
-    let inspect_closed_blocks b =
-      (* blocks are sorted from innermost to outermost. The fold_left
-         will give us the list in appropriate order for add_destructors
-         which expects variable from oldest to newest.
-      *)
-      let vars = List.fold_left (fun acc b -> b.blocals @ acc) [] b in
+    let insert_destructors vars =
       let has_destructors, stmts = add_destructors vars in
       if has_destructors then begin
         flag:=true;
@@ -184,6 +179,29 @@ class vis flag = object(self)
       end;
       Cil.SkipChildren
     in
+    let vars_from_blocks blocks =
+      List.fold_left (fun acc b -> b.blocals @ acc) [] blocks
+    in
+    let vars_from_edge s succ =
+      let closed_blocks = Kernel_function.blocks_closed_by_edge s succ in
+      (* blocks are sorted from innermost to outermost. The fold_left
+         will give us the list in appropriate order for add_destructors
+         which expects variable from oldest to newest.
+      *)
+      let current_block = Kernel_function.common_block s succ in
+      let vars = vars_from_blocks closed_blocks in
+      (* for the common block, we have to check whether we are backjumping
+         over some definitions in the middle of the block, that is
+         definitions that dominate s but not its successor.
+      *)
+      let is_backjump_var v =
+        v.vdefined &&
+        let def = Cil.find_def_stmt current_block v in
+        Dominators.dominates def s && not (Dominators.dominates def succ)
+      in
+      let current_vars = List.filter is_backjump_var current_block.blocals in
+      current_vars @ vars
+    in
     let abort_if_non_trivial_type kind v =
       if Cil.hasAttribute Cabs2cil.frama_c_destructor v.vattr then
         Kernel.abort
@@ -208,10 +226,17 @@ class vis flag = object(self)
     let inspect_local_vars kind b s lv =
       List.iter (check_def_domination kind b s) lv
     in
+    let inspect_var_current_block kind b s succ v =
+      if v.vdefined then begin
+        let def = Cil.find_def_stmt b v in
+        if Kernel_function.is_between b s def succ then
+          (* we are forward-jumping over def *)
+          abort_if_non_trivial_type kind v
+      end else abort_if_non_trivial_type kind v
+    in
     let treat_jump_close s =
       match s.succs with
-      | [ succ ] ->
-        inspect_closed_blocks (Kernel_function.blocks_closed_by_edge s succ)
+      | [ succ ] -> insert_destructors (vars_from_edge s succ)
       | _ ->
         Kernel.fatal ~current:true
           "%a in function %a is expected to have a single successor"
@@ -221,21 +246,11 @@ class vis flag = object(self)
     let treat_succ_open kind s succ =
       (* The jump must not bypass a vla initialization in the opened blocks. *)
       let blocks = Kernel_function.blocks_opened_by_edge s succ in
-      if blocks <> []
-      then List.iter (fun b -> inspect_local_vars kind b succ b.blocals) blocks
-      else begin
-        (* If there is no opened block, check that the jump does not bypass a
-           vla initialization in the destination block. [s] is in this block. *)
-        let block = Kernel_function.find_enclosing_block succ in
-        (* Does the definition of variable [v] dominates the statement [s]? *)
-        let dominate_s v =
-          v.vdefined && Dominators.dominates (Cil.find_def_stmt block v) s
-        in
-        (* Only consider variables defined after statement [s]. *)
-        let lvs = List.filter (fun v -> not (dominate_s v)) block.blocals in
-        (* Check that they are not defined before statement [succ]. *)
-        inspect_local_vars kind block succ lvs
-      end
+      let current_block = Kernel_function.common_block s succ in
+      List.iter (fun b -> inspect_local_vars kind b succ b.blocals) blocks;
+      List.iter
+        (inspect_var_current_block kind current_block s succ)
+        current_block.blocals
     in
     let treat_jump_open k s = List.iter (treat_succ_open k s) s.succs in
     match s.skind with
@@ -254,7 +269,8 @@ class vis flag = object(self)
     | Switch _ -> treat_jump_open "switch" s; Cil.DoChildren
     (* jump outside of the function: all currently opened blocks are closed. *)
     | Return _ | Throw _ ->
-      inspect_closed_blocks (Kernel_function.find_all_enclosing_blocks s)
+      insert_destructors
+        (vars_from_blocks (Kernel_function.find_all_enclosing_blocks s))
     (* no jump yet, visit children *)
     | _ -> Cil.DoChildren
 
diff --git a/src/kernel_services/ast_data/kernel_function.ml b/src/kernel_services/ast_data/kernel_function.ml
index 8e876e4d23bfee17053055c823884a4565903dd8..35ee82a3805dd515db5b4823bf4bbe908e088a8c 100644
--- a/src/kernel_services/ast_data/kernel_function.ml
+++ b/src/kernel_services/ast_data/kernel_function.ml
@@ -211,6 +211,94 @@ let find_all_enclosing_blocks s =
    let table = compute () in
   let (_,_,b) = Datatype.Int.Hashtbl.find table s.sid in b
 
+let find_stmt_in_block b s =
+  let has_stmt l =
+    List.exists (fun (s',_,_,_,_) -> Cil_datatype.Stmt.equal s s') l
+  in
+  let rec aux = function
+    | [] ->
+      Kernel.fatal "statement %a is not inside block@\n%a"
+        Cil_printer.pp_stmt s Cil_printer.pp_block b
+    | s' :: _ when Cil_datatype.Stmt.equal s s' -> s'
+    | { skind = UnspecifiedSequence l } as s':: _ when has_stmt l -> s'
+    | _ :: l -> aux l
+  in aux b.bstmts
+
+let find_stmt_of_block outer inner =
+  let rec is_stmt_of_block s =
+    match s.skind with
+    | Block b -> b == inner
+    | If (_,b1,b2,_) -> b1 == inner || b2 == inner
+    | Switch (_,b,_,_) -> b == inner
+    | Loop (_,b,_,_,_) -> b == inner
+    | UnspecifiedSequence l -> is_stmt_of_unspecified l
+    | TryCatch (b, l, _) ->
+      b == inner || List.exists (fun (_,b) -> b == inner) l
+    | TryFinally (b1,b2,_) -> b1 == inner || b2 == inner
+    | TryExcept (b1,_,b2,_) -> b1 == inner || b2 == inner
+    | _ -> false
+  and is_stmt_of_unspecified l =
+    List.exists (fun (s,_,_,_,_) -> is_stmt_of_block s) l
+  in
+  try
+    List.find is_stmt_of_block outer.bstmts
+  with Not_found ->
+    Kernel.fatal "inner block@\n%a@\nis not a direct child of outer block@\n%a"
+      Cil_printer.pp_block inner Cil_printer.pp_block outer
+
+let find_enclosing_stmt_in_block b s =
+  let blocks = find_all_enclosing_blocks s in
+  let rec aux prev l =
+    match l, prev with
+    | [], _ ->
+      Kernel.fatal "statement %a is not part of block@\n%a"
+        Cil_printer.pp_stmt s Cil_printer.pp_block b
+    | b' :: _, None when b' == b -> find_stmt_in_block b s
+    | b' :: _, Some prev when b' == b -> find_stmt_of_block b prev
+    | b' :: l, _ -> aux (Some b') l
+  in
+  aux None blocks
+
+let is_between b s1 s2 s3 =
+  let s1 = find_enclosing_stmt_in_block b s1 in
+  let s2 = find_enclosing_stmt_in_block b s2 in
+  let s3 = find_enclosing_stmt_in_block b s3 in
+  let rec aux has_s1 l =
+    match l with
+    | [] ->
+      Kernel.fatal
+        "Unexpected end of block while looking for %a"
+        Cil_printer.pp_stmt s3
+    | s :: l when Cil_datatype.Stmt.equal s s1 -> aux true l
+    | s :: _ when Cil_datatype.Stmt.equal s s2 -> has_s1
+    | s :: _ when Cil_datatype.Stmt.equal s s3 -> false
+    | _ :: l -> aux has_s1 l
+  in
+  aux false b.bstmts
+
+let common_block s1 s2 =
+  let kf1 = find_englobing_kf s1 in
+  let kf2 = find_englobing_kf s2 in
+  if not (equal kf1 kf2) then
+    Kernel.fatal
+      "cannot find a common block for statements occurring \
+       in two distinct functions";
+  let b1 = find_all_enclosing_blocks s1 in
+  let b2 = find_all_enclosing_blocks s2 in
+  let rec aux last l1 l2 =
+    match l1,l2 with
+    | [], _ | _, [] -> last
+    | b1 :: l1, b2 :: l2 when b1 == b2 -> aux b1 l1 l2
+    | _ :: _, _ :: _ -> last
+  in
+  match List.rev b1, List.rev b2 with
+  | [], _ | _, [] ->
+    Kernel.fatal "Statement not contained in any block"
+  | b1 :: l1, b2 :: l2 when b1 == b2 -> aux b1 l1 l2
+  | _ :: _, _ :: _ ->
+    Kernel.fatal
+      "Statements do not share their function body as outermost common block"
+
 let () = Globals.find_all_enclosing_blocks := find_all_enclosing_blocks
 
 let stmt_in_loop kf stmt =
diff --git a/src/kernel_services/ast_data/kernel_function.mli b/src/kernel_services/ast_data/kernel_function.mli
index 18c1dac1287a83abf9be394c92e837802c8c22c9..dc999bb6dc809590ce5b9f4c0c9c638eaecbf75c 100644
--- a/src/kernel_services/ast_data/kernel_function.mli
+++ b/src/kernel_services/ast_data/kernel_function.mli
@@ -108,8 +108,16 @@ val blocks_opened_by_edge: stmt -> stmt -> block list
       @raise Invalid_argument if [s2] is not a successor of [s1] in the cfg.
       @since Magnesium-20151001 *)
 
+val common_block: stmt -> stmt -> block
+(** [common_block s1 s2] returns the innermost block that contains
+    both [s1] and [s2], provided the statements belong to the same function.
+    raises a fatal error if this is not the case.
+
+    @since Frama-C+dev
+*)
+
 val stmt_in_loop: t -> stmt -> bool
-  (** [stmt_in_loop kf stmt] is [true] iff [stmt] strictly 
+  (** [stmt_in_loop kf stmt] is [true] iff [stmt] strictly
       occurs in a loop of [kf].
       @since Oxygen-20120901 *)
 
@@ -135,6 +143,27 @@ val var_is_in_scope: stmt -> varinfo -> bool
 
       @since Frama-C+dev *)
 
+val find_enclosing_stmt_in_block: block -> stmt -> stmt
+  (** [find_enclosing_stmt_in_block b s] returns the statements [s']
+      inside [b.bstmts] that contains [s]. It might be [s] itself, but also
+      an inner block (recursively) containing [s].
+
+      @raise AbortFatal if [b] is not equal to [find_enclosing_block s]
+      @since Frama-C+dev
+  *)
+
+val is_between: block -> stmt -> stmt -> stmt -> bool
+(** [is_between b s1 s2 s3] returns [true] if the statement [s2] appears
+    between [s1] and [s3] inside the [b.bstmts] list. All three statements
+    must actually occur in [b.bstmts], either directly or indirectly
+    (see {!Kernel_function.find_enclosing_stmt_in_block}).
+
+    @raise AbortFatal if pre-conditions are not met.
+
+    @since Frama-C+dev
+*)
+
+
 (* ************************************************************************* *)
 (** {2 Checkers} *)
 (* ************************************************************************* *)
diff --git a/tests/syntax/oracle/vla_goto_same_block_above.res.oracle b/tests/syntax/oracle/vla_goto_same_block_above.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..9d5bd2d88c817afd6daf3fab6976ca2c35b17ddc
--- /dev/null
+++ b/tests/syntax/oracle/vla_goto_same_block_above.res.oracle
@@ -0,0 +1,64 @@
+[kernel] Parsing tests/syntax/vla_goto_same_block_above.i (no preprocessing)
+/* Generated by Frama-C */
+int volatile nondet;
+/*@ assigns \nothing;
+    frees p; */
+ __attribute__((__FC_BUILTIN__)) void __fc_vla_free(void *p);
+
+/*@ assigns \result;
+    assigns \result \from \nothing;
+    allocates \result; */
+ __attribute__((__FC_BUILTIN__)) void *__fc_vla_alloc(unsigned int size);
+
+int main(void)
+{
+  int __retres;
+  unsigned int __lengthof_vla;
+  int i = 42;
+  toto: ;
+  /*@ assert alloca_bounds: 0 < sizeof(char) * i ≤ 4294967295; */ ;
+  __lengthof_vla = (unsigned int)i;
+  char *vla = __fc_vla_alloc(sizeof(char) * __lengthof_vla);
+  if (nondet) {
+    __fc_vla_free((void *)vla);
+    goto toto;
+  }
+  __retres = 0;
+  __fc_vla_free((void *)vla);
+  return __retres;
+}
+
+int f(void)
+{
+  int __retres;
+  unsigned int __lengthof_vla;
+  int i = 42;
+  if (nondet) toto: ;
+  /*@ assert alloca_bounds: 0 < sizeof(char) * i ≤ 4294967295; */ ;
+  __lengthof_vla = (unsigned int)i;
+  char *vla = __fc_vla_alloc(sizeof(char) * __lengthof_vla);
+  if (nondet) {
+    __fc_vla_free((void *)vla);
+    goto toto;
+  }
+  __retres = 0;
+  __fc_vla_free((void *)vla);
+  return __retres;
+}
+
+int g(void)
+{
+  int __retres;
+  unsigned int __lengthof_vla;
+  int i = 42;
+  /*@ assert alloca_bounds: 0 < sizeof(char) * i ≤ 4294967295; */ ;
+  __lengthof_vla = (unsigned int)i;
+  char *vla = __fc_vla_alloc(sizeof(char) * __lengthof_vla);
+  if (nondet) toto: ;
+  if (nondet) goto toto;
+  __retres = 0;
+  __fc_vla_free((void *)vla);
+  return __retres;
+}
+
+
diff --git a/tests/syntax/oracle/vla_goto_same_block_below.res.oracle b/tests/syntax/oracle/vla_goto_same_block_below.res.oracle
new file mode 100644
index 0000000000000000000000000000000000000000..b61b6e96000ef532c750da0b271ca5ad84ddcfd8
--- /dev/null
+++ b/tests/syntax/oracle/vla_goto_same_block_below.res.oracle
@@ -0,0 +1,3 @@
+[kernel] Parsing tests/syntax/vla_goto_same_block_below.i (no preprocessing)
+[kernel] User Error: tests/syntax/vla_goto_same_block_below.i:6, cannot jump from goto statement bypassing initialization of variable vla, declared at tests/syntax/vla_goto_same_block_below.i:9
+[kernel] Frama-C aborted: invalid user input.
diff --git a/tests/syntax/vla_goto_same_block_above.i b/tests/syntax/vla_goto_same_block_above.i
new file mode 100644
index 0000000000000000000000000000000000000000..11704dbc2a72a5efeb51ac7ae4ac0b0cc237b9b9
--- /dev/null
+++ b/tests/syntax/vla_goto_same_block_above.i
@@ -0,0 +1,24 @@
+volatile int nondet ;
+int main() {
+  int i = 42 ;
+  toto : ;
+  char vla[i] ;
+  if (nondet) goto toto ;
+  return 0 ;
+}
+
+int f() {
+  int i = 42 ;
+  if (nondet) { toto : ; }
+  char vla[i] ;
+  if (nondet) goto toto ;
+  return 0 ;
+}
+
+int g() {
+  int i = 42 ;
+  char vla[i] ;
+  if (nondet) { toto : ; }
+  if (nondet) goto toto ;
+  return 0 ;
+}
diff --git a/tests/syntax/vla_goto_same_block_below.i b/tests/syntax/vla_goto_same_block_below.i
new file mode 100644
index 0000000000000000000000000000000000000000..3e163f3bb8227dafc50fa189d2842831938ebf39
--- /dev/null
+++ b/tests/syntax/vla_goto_same_block_below.i
@@ -0,0 +1,15 @@
+volatile int nondet ;
+
+int main() {
+
+int i = 42;
+if (nondet) goto toto; // KO: we are jumping over
+                       // a constructor/destructor definition
+
+char vla[i];
+
+toto: ;
+
+return 0;
+
+}