diff --git a/zircon/kernel/vm/include/vm/vm_object.h b/zircon/kernel/vm/include/vm/vm_object.h
index 7283185ba0531b41fda574df7f63e661d2034d13..65ec8dc2d837b060375fe3be02602460c57aa7ee 100644
--- a/zircon/kernel/vm/include/vm/vm_object.h
+++ b/zircon/kernel/vm/include/vm/vm_object.h
@@ -36,6 +36,10 @@ public:
     virtual void OnOneChild() = 0;
 };
 
+typedef struct vm_lock : fbl::RefCounted<struct vm_lock> {
+    DECLARE_MUTEX(struct vm_lock) lock;
+} vm_lock_t;
+
 // The base vm object that holds a range of bytes of data
 //
 // Can be created without mapping and used as a container of data, or mappable
@@ -256,10 +260,7 @@ public:
     virtual void DetachSource() {}
 
 protected:
-    // private constructor (use Create())
-    explicit VmObject(fbl::RefPtr<VmObject> parent);
-    VmObject()
-        : VmObject(nullptr) {}
+    VmObject(fbl::RefPtr<VmObject> parent, fbl::RefPtr<vm_lock_t> root_lock);
 
     // private destructor, only called from refptr
     virtual ~VmObject();
@@ -281,15 +282,11 @@ protected:
     // magic value
     fbl::Canary<fbl::magic("VMO_")> canary_;
 
-    // members
-
-    // declare a local mutex and default to pointing at it
-    // if constructed with a parent vmo, point lock_ at the parent's lock
-private:
-    DECLARE_MUTEX(VmObject) local_lock_;
-
-protected:
+    // The lock which protects this class. All VmObjects in a clone tree
+    // share the same lock.
     Lock<Mutex>& lock_;
+    // Pointer to the actual lock.
+    fbl::RefPtr<vm_lock_t> lock_ptr_;
 
     // list of every mapping
     fbl::DoublyLinkedList<VmMapping*> mapping_list_ TA_GUARDED(lock_);
diff --git a/zircon/kernel/vm/include/vm/vm_object_paged.h b/zircon/kernel/vm/include/vm/vm_object_paged.h
index f8ba70b079cd79ac658e85eb9b9e00238527c032..32a88890605a23988aabd3144cbecc4b309a92c6 100644
--- a/zircon/kernel/vm/include/vm/vm_object_paged.h
+++ b/zircon/kernel/vm/include/vm/vm_object_paged.h
@@ -141,7 +141,12 @@ private:
     // private constructor (use Create())
     VmObjectPaged(
         uint32_t options, uint32_t pmm_alloc_flags, uint64_t size,
-        fbl::RefPtr<VmObject> parent, fbl::RefPtr<PageSource> page_source);
+        fbl::RefPtr<VmObject> parent, fbl::RefPtr<vm_lock_t> root_lock,
+        fbl::RefPtr<PageSource> page_source);
+
+    static zx_status_t CreateCommon(uint32_t pmm_alloc_flags,
+                                    uint32_t options,
+                                    uint64_t size, fbl::RefPtr<VmObject>* vmo);
 
     // private destructor, only called from refptr
     ~VmObjectPaged() override;
diff --git a/zircon/kernel/vm/include/vm/vm_object_physical.h b/zircon/kernel/vm/include/vm/vm_object_physical.h
index b12c48e5bdca2dd2df3c4e6d0df913d7aa1d49e2..280543c19383498986bc50ab1db102c1025f6276 100644
--- a/zircon/kernel/vm/include/vm/vm_object_physical.h
+++ b/zircon/kernel/vm/include/vm/vm_object_physical.h
@@ -46,7 +46,7 @@ public:
 
 private:
     // private constructor (use Create())
-    VmObjectPhysical(paddr_t base, uint64_t size);
+    VmObjectPhysical(fbl::RefPtr<vm_lock_t> lock, paddr_t base, uint64_t size);
 
     // private destructor, only called from refptr
     ~VmObjectPhysical() override;
diff --git a/zircon/kernel/vm/vm_object.cpp b/zircon/kernel/vm/vm_object.cpp
index a390712a2de2d8c8d4d1139c036fd65e027dea6a..6d7fa6414d1ec456500d628c44e2ea8b6fa6952b 100644
--- a/zircon/kernel/vm/vm_object.cpp
+++ b/zircon/kernel/vm/vm_object.cpp
@@ -29,10 +29,13 @@
 
 VmObject::GlobalList VmObject::all_vmos_ = {};
 
-VmObject::VmObject(fbl::RefPtr<VmObject> parent)
-    : lock_(parent ? parent->lock_ref() : local_lock_),
-      parent_(ktl::move(parent)) {
+VmObject::VmObject(fbl::RefPtr<VmObject> parent, fbl::RefPtr<vm_lock_t> lock_ptr)
+    : lock_(lock_ptr->lock), lock_ptr_(ktl::move(lock_ptr)), parent_(ktl::move(parent)) {
     LTRACEF("%p\n", this);
+
+    if (parent_) {
+        DEBUG_ASSERT(lock_ptr_ == parent_->lock_ptr_);
+    }
 }
 
 VmObject::~VmObject() {
diff --git a/zircon/kernel/vm/vm_object_paged.cpp b/zircon/kernel/vm/vm_object_paged.cpp
index 0c84b2ad5e33e0eb7f5e8041b1ac0de730b4b1a9..bbcd18b4ca0df72ca30411461a73be381d8ba1a4 100644
--- a/zircon/kernel/vm/vm_object_paged.cpp
+++ b/zircon/kernel/vm/vm_object_paged.cpp
@@ -68,8 +68,9 @@ zx_status_t RoundSize(uint64_t size, uint64_t* out_size) {
 
 VmObjectPaged::VmObjectPaged(
     uint32_t options, uint32_t pmm_alloc_flags, uint64_t size,
-    fbl::RefPtr<VmObject> parent, fbl::RefPtr<PageSource> page_source)
-    : VmObject(ktl::move(parent)),
+    fbl::RefPtr<VmObject> parent, fbl::RefPtr<vm_lock_t> root_lock,
+    fbl::RefPtr<PageSource> page_source)
+    : VmObject(ktl::move(parent), ktl::move(root_lock)),
       options_(options),
       size_(size),
       pmm_alloc_flags_(pmm_alloc_flags),
@@ -112,23 +113,23 @@ VmObjectPaged::~VmObjectPaged() {
     pmm_free(&list);
 }
 
-zx_status_t VmObjectPaged::Create(uint32_t pmm_alloc_flags,
-                                  uint32_t options,
-                                  uint64_t size, fbl::RefPtr<VmObject>* obj) {
+zx_status_t VmObjectPaged::CreateCommon(uint32_t pmm_alloc_flags,
+                                        uint32_t options,
+                                        uint64_t size, fbl::RefPtr<VmObject>* obj) {
     // make sure size is page aligned
     zx_status_t status = RoundSize(size, &size);
     if (status != ZX_OK) {
         return status;
     }
 
-    if (options & kContiguous) {
-        // Force callers to use CreateContiguous() instead.
-        return ZX_ERR_INVALID_ARGS;
+    fbl::AllocChecker ac;
+    auto lock = fbl::AdoptRef<vm_lock_t>(new (&ac) vm_lock_t);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
     }
 
-    fbl::AllocChecker ac;
     auto vmo = fbl::AdoptRef<VmObject>(
-        new (&ac) VmObjectPaged(options, pmm_alloc_flags, size, nullptr, nullptr));
+        new (&ac) VmObjectPaged(options, pmm_alloc_flags, size, nullptr, ktl::move(lock), nullptr));
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
@@ -138,6 +139,17 @@ zx_status_t VmObjectPaged::Create(uint32_t pmm_alloc_flags,
     return ZX_OK;
 }
 
+zx_status_t VmObjectPaged::Create(uint32_t pmm_alloc_flags,
+                                  uint32_t options,
+                                  uint64_t size, fbl::RefPtr<VmObject>* obj) {
+    if (options & kContiguous) {
+        // Force callers to use CreateContiguous() instead.
+        return ZX_ERR_INVALID_ARGS;
+    }
+
+    return CreateCommon(pmm_alloc_flags, options, size, obj);
+}
+
 zx_status_t VmObjectPaged::CreateContiguous(uint32_t pmm_alloc_flags, uint64_t size,
                                             uint8_t alignment_log2, fbl::RefPtr<VmObject>* obj) {
     DEBUG_ASSERT(alignment_log2 < sizeof(uint64_t) * 8);
@@ -147,11 +159,10 @@ zx_status_t VmObjectPaged::CreateContiguous(uint32_t pmm_alloc_flags, uint64_t s
         return status;
     }
 
-    fbl::AllocChecker ac;
-    auto vmo = fbl::AdoptRef<VmObject>(
-        new (&ac) VmObjectPaged(kContiguous, pmm_alloc_flags, size, nullptr, nullptr));
-    if (!ac.check()) {
-        return ZX_ERR_NO_MEMORY;
+    fbl::RefPtr<VmObject> vmo;
+    status = CreateCommon(pmm_alloc_flags, kContiguous, size, &vmo);
+    if (status != ZX_OK) {
+        return status;
     }
 
     if (size == 0) {
@@ -209,7 +220,7 @@ zx_status_t VmObjectPaged::CreateFromWiredPages(const void* data, size_t size, b
     LTRACEF("data %p, size %zu\n", data, size);
 
     fbl::RefPtr<VmObject> vmo;
-    zx_status_t status = Create(PMM_ALLOC_FLAG_ANY, 0, size, &vmo);
+    zx_status_t status = CreateCommon(PMM_ALLOC_FLAG_ANY, 0, size, &vmo);
     if (status != ZX_OK) {
         return status;
     }
@@ -272,8 +283,13 @@ zx_status_t VmObjectPaged::CreateExternal(fbl::RefPtr<PageSource> src, uint32_t
     }
 
     fbl::AllocChecker ac;
+    auto lock = fbl::AdoptRef<vm_lock_t>(new (&ac) vm_lock_t);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+
     auto vmo = fbl::AdoptRef<VmObject>(new (&ac) VmObjectPaged(
-            options, PMM_ALLOC_FLAG_ANY, size, nullptr, ktl::move(src)));
+            options, PMM_ALLOC_FLAG_ANY, size, nullptr, ktl::move(lock), ktl::move(src)));
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
@@ -302,7 +318,7 @@ zx_status_t VmObjectPaged::CreateCowClone(bool resizable, uint64_t offset, uint6
     // been released because we share the lock and the vmo's dtor may acquire it
     fbl::AllocChecker ac;
     auto vmo = fbl::AdoptRef<VmObjectPaged>(new (&ac) VmObjectPaged(
-        options, pmm_alloc_flags_, size, fbl::WrapRefPtr(this), nullptr));
+        options, pmm_alloc_flags_, size, fbl::WrapRefPtr(this), lock_ptr_, nullptr));
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }
diff --git a/zircon/kernel/vm/vm_object_physical.cpp b/zircon/kernel/vm/vm_object_physical.cpp
index aabc8ea9912f12dd2ee838a63dd966c1cecb10e1..1897912c8bf87c7705dc9bce76b8ecc0a492e1be 100644
--- a/zircon/kernel/vm/vm_object_physical.cpp
+++ b/zircon/kernel/vm/vm_object_physical.cpp
@@ -22,8 +22,8 @@
 
 #define LOCAL_TRACE MAX(VM_GLOBAL_TRACE, 0)
 
-VmObjectPhysical::VmObjectPhysical(paddr_t base, uint64_t size)
-    : size_(size), base_(base) {
+VmObjectPhysical::VmObjectPhysical(fbl::RefPtr<vm_lock_t> lock, paddr_t base, uint64_t size)
+    : VmObject(nullptr, ktl::move(lock)), size_(size), base_(base) {
     LTRACEF("%p, size %#" PRIx64 "\n", this, size_);
 
     DEBUG_ASSERT(IS_PAGE_ALIGNED(size_));
@@ -50,7 +50,12 @@ zx_status_t VmObjectPhysical::Create(paddr_t base, uint64_t size, fbl::RefPtr<Vm
     }
 
     fbl::AllocChecker ac;
-    auto vmo = fbl::AdoptRef<VmObject>(new (&ac) VmObjectPhysical(base, size));
+    auto lock = fbl::AdoptRef<vm_lock_t>(new (&ac) vm_lock_t);
+    if (!ac.check()) {
+        return ZX_ERR_NO_MEMORY;
+    }
+
+    auto vmo = fbl::AdoptRef<VmObject>(new (&ac) VmObjectPhysical(ktl::move(lock), base, size));
     if (!ac.check()) {
         return ZX_ERR_NO_MEMORY;
     }