Precede struct memfd_luo_ser with struct kho_ser_hdr using DECLARE_KHO_SER. Later memfd features can be appended under new feature indices without breaking compatibility between adjacent kernels. Allocate both the memfd_luo_ser feature header and the kho_vmalloc feature header in the same preserved folio. On restore and retrieve, validate incoming required features before accessing payload fields. Signed-off-by: Tarun Sahu --- include/linux/kho/abi/memfd.h | 7 ++-- mm/memfd_luo.c | 67 ++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/include/linux/kho/abi/memfd.h b/include/linux/kho/abi/memfd.h index b0597c438973..27f8880ee593 100644 --- a/include/linux/kho/abi/memfd.h +++ b/include/linux/kho/abi/memfd.h @@ -83,6 +83,8 @@ static_assert(!IS_ENABLED(CONFIG_64BIT) || * @nr_folios: Number of folios in the folios array. * @folios_fhdr: Pointer to KHO vmalloc descriptor for the array of * struct memfd_luo_folio_ser. + * + * Preceded by &struct kho_ser_hdr. */ struct memfd_luo_ser { u64 pos; @@ -93,9 +95,8 @@ struct memfd_luo_ser { DECLARE_KHOSER_PTR(folios_fhdr, DECLARE_KHO_SER_HDR(kho_vmalloc, *)); }; -static_assert(sizeof(struct memfd_luo_ser) == 40); -static_assert(!IS_ENABLED(CONFIG_64BIT) || - __alignof__(struct memfd_luo_ser) == 8); +/* Register memfd_luo_ser with KHO serialization accessors */ +DECLARE_KHO_SER(memfd_luo_ser, 40, 0, 0); /* The compatibility string for memfd file handler */ #define MEMFD_LUO_FH_COMPATIBLE "memfd-v3" diff --git a/mm/memfd_luo.c b/mm/memfd_luo.c index 74c1ff5877c3..94510f73a606 100644 --- a/mm/memfd_luo.c +++ b/mm/memfd_luo.c @@ -256,16 +256,21 @@ static void memfd_luo_unpreserve_folios(DECLARE_KHO_SER_HDR(kho_vmalloc, *folios static size_t memfd_luo_ser_size(void) { - return sizeof(struct memfd_luo_ser) + kho_ser_size(kho_vmalloc); + return kho_ser_size(memfd_luo_ser) + kho_ser_size(kho_vmalloc); } -static struct memfd_luo_ser *memfd_luo_ser_init(void *mem) +static struct memfd_luo_ser * +memfd_luo_ser_init(DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr)) { DECLARE_KHO_SER_HDR(kho_vmalloc, *folios_fhdr); - struct memfd_luo_ser *ser = mem; + struct memfd_luo_ser *ser; + + kho_ser_init(fhdr, memfd_luo_ser); + folios_fhdr = (void *)fhdr + kho_ser_size(memfd_luo_ser); - folios_fhdr = mem + sizeof(struct memfd_luo_ser); + ser = kho_ser_payload(fhdr, memfd_luo_ser); KHOSER_STORE_PTR(ser->folios_fhdr, folios_fhdr); + return ser; } @@ -273,6 +278,7 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) { struct inode *inode = file_inode(args->file); struct memfd_luo_folio_ser *folios_ser; + DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr); struct memfd_luo_ser *ser; int err = 0, seals; u64 inode_size; @@ -304,12 +310,12 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) } /* Allocate the main serialization structure in preserved memory */ - ser = kho_alloc_preserve(memfd_luo_ser_size()); - if (IS_ERR(ser)) { - err = PTR_ERR(ser); + fhdr = kho_alloc_preserve(memfd_luo_ser_size()); + if (IS_ERR(fhdr)) { + err = PTR_ERR(fhdr); goto err_unlock; } - ser = memfd_luo_ser_init(ser); + ser = memfd_luo_ser_init(fhdr); ser->pos = args->file->f_pos; ser->size = inode_size; @@ -323,12 +329,12 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) inode_unlock(inode); args->private_data = folios_ser; - args->serialized_data = virt_to_phys(ser); + args->serialized_data = virt_to_phys(fhdr); return 0; err_free_ser: - kho_unpreserve_free(ser); + kho_unpreserve_free(fhdr); err_unlock: shmem_freeze(inode, false); inode_unlock(inode); @@ -337,12 +343,14 @@ static int memfd_luo_preserve(struct liveupdate_file_op_args *args) static int memfd_luo_freeze(struct liveupdate_file_op_args *args) { + DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr); struct memfd_luo_ser *ser; if (WARN_ON_ONCE(!args->serialized_data)) return -EINVAL; - ser = phys_to_virt(args->serialized_data); + fhdr = phys_to_virt(args->serialized_data); + ser = kho_ser_payload(fhdr, memfd_luo_ser); /* * The pos might have changed since prepare. Everything else stays the @@ -356,6 +364,7 @@ static int memfd_luo_freeze(struct liveupdate_file_op_args *args) static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args) { struct inode *inode = file_inode(args->file); + DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr); struct memfd_luo_ser *ser; if (WARN_ON_ONCE(!args->serialized_data)) @@ -364,13 +373,14 @@ static void memfd_luo_unpreserve(struct liveupdate_file_op_args *args) inode_lock(inode); shmem_freeze(inode, false); - ser = phys_to_virt(args->serialized_data); + fhdr = phys_to_virt(args->serialized_data); + ser = kho_ser_payload(fhdr, memfd_luo_ser); memfd_luo_unpreserve_folios(KHOSER_LOAD_PTR(ser->folios_fhdr), args->private_data, ser->nr_folios); - kho_unpreserve_free(ser); + kho_unpreserve_free(fhdr); inode_unlock(inode); } @@ -402,6 +412,7 @@ static void memfd_luo_discard_folios(const struct memfd_luo_folio_ser *folios_se static void memfd_luo_finish(struct liveupdate_file_op_args *args) { struct memfd_luo_folio_ser *folios_ser; + DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr); struct memfd_luo_ser *ser; /* @@ -412,10 +423,16 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args) if (args->retrieve_status) return; - ser = phys_to_virt(args->serialized_data); - if (!ser) + if (WARN_ON_ONCE(!args->serialized_data)) return; + fhdr = phys_to_virt(args->serialized_data); + if (kho_ser_validate(fhdr, memfd_luo_ser)) { + pr_warn("invalid feature header, folios not discarded\n"); + goto out; + } + + ser = kho_ser_payload(fhdr, memfd_luo_ser); if (ser->nr_folios) { folios_ser = kho_restore_vmalloc(KHOSER_LOAD_PTR(ser->folios_fhdr)); if (!folios_ser) @@ -426,7 +443,7 @@ static void memfd_luo_finish(struct liveupdate_file_op_args *args) } out: - kho_restore_free(ser); + kho_restore_free(fhdr); } static int memfd_luo_retrieve_folios(struct file *file, @@ -533,14 +550,24 @@ static int memfd_luo_retrieve_folios(struct file *file, static int memfd_luo_retrieve(struct liveupdate_file_op_args *args) { struct memfd_luo_folio_ser *folios_ser; + DECLARE_KHO_SER_HDR(memfd_luo_ser, *fhdr); struct memfd_luo_ser *ser; struct file *file; int err; - ser = phys_to_virt(args->serialized_data); - if (!ser) + if (WARN_ON_ONCE(!args->serialized_data)) return -EINVAL; + fhdr = phys_to_virt(args->serialized_data); + err = kho_ser_validate(fhdr, memfd_luo_ser); + if (err) { + pr_err("memfd_luo_ser feature validation failed: %pe\n", + ERR_PTR(err)); + goto free_ser; + } + + ser = kho_ser_payload(fhdr, memfd_luo_ser); + /* Make sure the file only has seals supported by this version. */ if (ser->seals & ~MEMFD_LUO_ALL_SEALS) { err = -EOPNOTSUPP; @@ -581,14 +608,14 @@ static int memfd_luo_retrieve(struct liveupdate_file_op_args *args) } args->file = file; - kho_restore_free(ser); + kho_restore_free(fhdr); return 0; put_file: fput(file); free_ser: - kho_restore_free(ser); + kho_restore_free(fhdr); return err; } -- 2.56.0.506.g506a7eabc5-goog