{
        int ret;
        uint32_t num_devices, num_bos, num_objects;
-       uint64_t priv_size, priv_offset = 0;
+       uint64_t priv_size, priv_offset = 0, bo_priv_offset;
 
        if (!args->devices || !args->bos || !args->priv_data)
                return -EINVAL;
        if (ret)
                goto exit_unlock;
 
-       ret = criu_checkpoint_bos(p, num_bos, (uint8_t __user *)args->bos,
-                           (uint8_t __user *)args->priv_data, &priv_offset);
-       if (ret)
-               goto exit_unlock;
+       /* Leave room for BOs in the private data. They need to be restored
+        * before events, but we checkpoint them last to simplify the error
+        * handling.
+        */
+       bo_priv_offset = priv_offset;
+       priv_offset += num_bos * sizeof(struct kfd_criu_bo_priv_data);
 
        if (num_objects) {
                ret = kfd_criu_checkpoint_queues(p, (uint8_t __user *)args->priv_data,
                                                 &priv_offset);
                if (ret)
-                       goto close_bo_fds;
+                       goto exit_unlock;
 
                ret = kfd_criu_checkpoint_events(p, (uint8_t __user *)args->priv_data,
                                                 &priv_offset);
                if (ret)
-                       goto close_bo_fds;
+                       goto exit_unlock;
 
                ret = kfd_criu_checkpoint_svm(p, (uint8_t __user *)args->priv_data, &priv_offset);
                if (ret)
-                       goto close_bo_fds;
+                       goto exit_unlock;
        }
 
-close_bo_fds:
-       if (ret) {
-               /* If IOCTL returns err, user assumes all FDs opened in criu_dump_bos are closed */
-               uint32_t i;
-               struct kfd_criu_bo_bucket *bo_buckets = (struct kfd_criu_bo_bucket *) args->bos;
-
-               for (i = 0; i < num_bos; i++) {
-                       if (bo_buckets[i].alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM)
-                               close_fd(bo_buckets[i].dmabuf_fd);
-               }
-       }
+       /* This must be the last thing in this function that can fail.
+        * Otherwise we leak dmabuf file descriptors.
+        */
+       ret = criu_checkpoint_bos(p, num_bos, (uint8_t __user *)args->bos,
+                          (uint8_t __user *)args->priv_data, &bo_priv_offset);
 
 exit_unlock:
        mutex_unlock(&p->mutex);