From b40d106bdcf77a7ba1219090c9d16add131b910e Mon Sep 17 00:00:00 2001 From: shell Date: Mon, 23 Oct 2023 17:24:28 +0800 Subject: [PATCH] [dfs] fixup: file_mmap and page cache Including cache maintenance, potential dereference of null pointer and the use-after-free issue in page cache Signed-off-by: shell --- components/dfs/dfs_v2/src/dfs_file_mmap.c | 52 +++++++++++++---------- components/dfs/dfs_v2/src/dfs_pcache.c | 19 +++++++++ 2 files changed, 48 insertions(+), 23 deletions(-) diff --git a/components/dfs/dfs_v2/src/dfs_file_mmap.c b/components/dfs/dfs_v2/src/dfs_file_mmap.c index 6e356ef405..fe34593f4c 100644 --- a/components/dfs/dfs_v2/src/dfs_file_mmap.c +++ b/components/dfs/dfs_v2/src/dfs_file_mmap.c @@ -272,7 +272,7 @@ rt_err_t on_varea_shrink(struct rt_varea *varea, void *new_vaddr, rt_size_t size rm_start = varea_start + size; rm_end = varea_start + varea->size; } - else if (varea_start < (char *)new_vaddr) + else { rm_start = varea_start; rm_end = new_vaddr; @@ -293,6 +293,7 @@ rt_err_t on_varea_expand(struct rt_varea *varea, void *new_vaddr, rt_size_t size rt_err_t on_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t unmap_len, struct rt_varea *subset) { + rt_err_t rc; struct dfs_file *file = dfs_mem_obj_get_file(existed->mem_obj); if (file) @@ -307,12 +308,15 @@ rt_err_t on_varea_split(struct rt_varea *existed, void *unmap_start, rt_size_t u LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } - unmap_pages(existed, unmap_start, (char *)unmap_start + unmap_len); + rc = unmap_pages(existed, unmap_start, (char *)unmap_start + unmap_len); + if (!rc) + { + rc = unmap_pages(existed, subset->start, (char *)subset->start + subset->size); + if (!rc) + on_varea_open(subset); + } - subset->data = existed->data; - rt_atomic_add(&(file->ref_count), 1); - - return RT_EOK; + return rc; } else { @@ -338,8 +342,7 @@ rt_err_t on_varea_merge(struct rt_varea *merge_to, struct rt_varea *merge_from) } dfs_aspace_unmap(file, merge_from); - merge_from->data = RT_NULL; - rt_atomic_sub(&(file->ref_count), 1); + on_varea_close(merge_from); return RT_EOK; } @@ -406,28 +409,31 @@ int dfs_file_mmap(struct dfs_file *file, struct dfs_mmap2_args *mmap2) LOG_I("mmap2 args addr: %p length: 0x%x prot: %d flags: 0x%x pgoffset: 0x%x", mmap2->addr, mmap2->length, mmap2->prot, mmap2->flags, mmap2->pgoffset); - if (file && file->vnode && file->vnode->aspace) + if (file && file->vnode) { - /* create a va area in user space (lwp) */ - rt_varea_t varea = dfs_map_user_varea_data(mmap2, file); - if (varea) + if (file->vnode->aspace) { - mmap2->ret = varea->start; - LOG_I("%s varea: %p", __func__, varea); - LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", - varea->start, varea->size, varea->offset, varea->attr, varea->flag); - LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); - ret = RT_EOK; + /* create a va area in user space (lwp) */ + rt_varea_t varea = dfs_map_user_varea_data(mmap2, file); + if (varea) + { + mmap2->ret = varea->start; + LOG_I("%s varea: %p", __func__, varea); + LOG_I("varea start: %p size: 0x%x offset: 0x%x attr: 0x%x flag: 0x%x", + varea->start, varea->size, varea->offset, varea->attr, varea->flag); + LOG_I("file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); + ret = RT_EOK; + } + else + { + ret = -ENOMEM; + } } else { - ret = -ENOMEM; + LOG_E("File mapping is not supported, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); } } - else if (file->vnode->aspace == RT_NULL) - { - LOG_E("File mapping is not supported, file: %s%s", file->dentry->mnt->fullpath, file->dentry->pathname); - } return ret; } diff --git a/components/dfs/dfs_v2/src/dfs_pcache.c b/components/dfs/dfs_v2/src/dfs_pcache.c index 0a8037c9c2..920ddd1108 100644 --- a/components/dfs/dfs_v2/src/dfs_pcache.c +++ b/components/dfs/dfs_v2/src/dfs_pcache.c @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2023-05-05 RTT Implement mnt in dfs v2.0 + * 2023-10-23 Shell fix synchronization of data to icache */ #include "dfs_pcache.h" @@ -15,6 +16,8 @@ #include #include +#include + #ifdef RT_USING_PAGECACHE #define DBG_TAG "dfs.pcache" @@ -1270,6 +1273,21 @@ void *dfs_aspace_mmap(struct dfs_file *file, struct rt_varea *varea, void *vaddr int err = rt_varea_map_range(varea, vaddr, pg_paddr, page->size); if (err == RT_EOK) { + /** + * Note: While the page is mapped into user area, the data writing into the page + * is not guaranteed to be visible for machines with the *weak* memory model and + * those Harvard architecture (especially for those ARM64) cores for their + * out-of-order pipelines of data buffer. Besides if the instruction cache in the + * L1 memory system is a VIPT cache, there are chances to have the alias matching + * entry if we reuse the same page frame and map it into the same virtual address + * of the previous one. + * + * That's why we have to do synchronization and cleanup manually to ensure that + * fetching of the next instruction can see the coherent data with the data cache, + * TLB, MMU, main memory, and all the other observers in the computer system. + */ + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, vaddr, ARCH_PAGE_SIZE); + ret = page->page; map->varea = varea; dfs_aspace_lock(aspace); @@ -1358,6 +1376,7 @@ int dfs_aspace_page_unmap(struct dfs_file *file, struct rt_varea *varea, void *v { rt_list_t *node, *tmp; struct dfs_mmap *map; + rt_varea_unmap_page(varea, vaddr); node = page->mmap_head.next;