diff --git a/_CoqProject b/_CoqProject
index 2bb663e11adec753a1436c12c8be1a48e2da3d7a..1619de757060b8a50ccc77e5366844dfedfb60e9 100644
--- a/_CoqProject
+++ b/_CoqProject
@@ -124,7 +124,7 @@ theories/heap_lang/lib/atomic_heap.v
 theories/heap_lang/lib/increment.v
 theories/heap_lang/lib/diverge.v
 theories/heap_lang/lib/arith.v
-theories/heap_lang/lib/array_copy.v
+theories/heap_lang/lib/array_tools.v
 theories/proofmode/base.v
 theories/proofmode/tokens.v
 theories/proofmode/coq_tactics.v
diff --git a/theories/heap_lang/lib/array_copy.v b/theories/heap_lang/lib/array_tools.v
similarity index 65%
rename from theories/heap_lang/lib/array_copy.v
rename to theories/heap_lang/lib/array_tools.v
index 1125d22bc496ae33af00267d42c04562bd9310e5..364c806d40ebfd99d70afc81161e86847a6f7332 100644
--- a/theories/heap_lang/lib/array_copy.v
+++ b/theories/heap_lang/lib/array_tools.v
@@ -4,18 +4,28 @@ From iris.heap_lang Require Export lang array.
 From iris.heap_lang Require Import proofmode notation.
 Set Default Proof Using "Type".
 
-(** Implement [array_copy_to], a function which copies to an array in-place.
+(** Provides some array utilities:
 
-Using [array_copy_to] we also implement [array_clone], which allocates a fresh
-array and copies to it. *)
+* [array_free], to deallocate an entire array in one go.
+* [array_copy_to], a function which copies to an array in-place.
+* Using [array_copy_to] we also implement [array_clone], which allocates a fresh
+array and copies to it.
 
-Definition array_copy_to: val :=
+*)
+
+Definition array_free : val :=
+  rec: "freeN" "ptr" "n" :=
+    if: "n" ≤ #0 then #()
+    else Free "ptr";;
+         "freeN" ("ptr" +â‚— #1) ("n" - #1).
+
+Definition array_copy_to : val :=
   rec: "array_copy_to" "dst" "src" "n" :=
     if: "n" ≤ #0 then #()
     else "dst" <- !"src";;
          "array_copy_to" ("dst" +â‚— #1) ("src" +â‚— #1) ("n" - #1).
 
-Definition array_clone: val :=
+Definition array_clone : val :=
   λ: "src" "n",
     let: "dst" := AllocN "n" #() in
     array_copy_to "dst" "src" "n";;
@@ -24,7 +34,25 @@ Definition array_clone: val :=
 Section proof.
   Context `{!heapG Σ}.
 
-  Theorem twp_array_copy_to stk E (dst src: loc) vdst vsrc q (n:Z) :
+  Lemma twp_array_free s E l vs (n : Z) :
+    n = length vs →
+    [[{ l ↦∗ vs }]] array_free #l #n @ s; E [[{ RET #(); True }]].
+  Proof.
+    iIntros (Hlen Φ) "Hl HΦ".
+    iInduction vs as [|v vs] "IH" forall (l n Hlen); subst n; wp_rec; wp_pures.
+    { iApply "HΦ". done. }
+    iDestruct (array_cons with "Hl") as "[Hv Hl]".
+    wp_free. wp_pures. iApply ("IH" with "[] Hl"); eauto with lia.
+  Qed.
+  Lemma wp_array_free s E l vs (n : Z) :
+    n = length vs →
+    {{{ l ↦∗ vs }}} array_free #l #n @ s; E {{{ RET #(); True }}}.
+  Proof.
+    iIntros (? Φ) "H HΦ". iApply (twp_wp_step with "HΦ").
+    iApply (twp_array_free with "H"); [auto..|]; iIntros "H HΦ". by iApply "HΦ".
+  Qed.
+
+  Theorem twp_array_copy_to stk E (dst src : loc) vdst vsrc q (n : Z) :
     Z.of_nat (length vdst) = n → Z.of_nat (length vsrc) = n →
     [[{ dst ↦∗ vdst ∗ src ↦∗{q} vsrc }]]
       array_copy_to #dst #src #n @ stk; E
@@ -41,7 +69,7 @@ Section proof.
     iIntros "[Hvdst Hvsrc]".
     iApply "HΦ"; by iFrame.
   Qed.
-  Theorem wp_array_copy_to stk E (dst src: loc) vdst vsrc q (n:Z) :
+  Theorem wp_array_copy_to stk E (dst src : loc) vdst vsrc q (n : Z) :
     Z.of_nat (length vdst) = n → Z.of_nat (length vsrc) = n →
     {{{ dst ↦∗ vdst ∗ src ↦∗{q} vsrc }}}
       array_copy_to #dst #src #n @ stk; E