diff --git a/components/dfs/dfs_v1/filesystems/cromfs/dfs_cromfs.c b/components/dfs/dfs_v1/filesystems/cromfs/dfs_cromfs.c index fee8b8dbd153..ba6a602952d5 100644 --- a/components/dfs/dfs_v1/filesystems/cromfs/dfs_cromfs.c +++ b/components/dfs/dfs_v1/filesystems/cromfs/dfs_cromfs.c @@ -24,10 +24,13 @@ #define CROMFS_PATITION_HEAD_SIZE 256 #define CROMFS_DIRENT_CACHE_SIZE 8 -#define CROMFS_MAGIC "CROMFSMG" +#define CROMFS_MAGIC "CROMFSMG" #define CROMFS_CT_ASSERT(name, x) \ - struct assert_##name {char ary[2 * (x) - 1];} + struct assert_##name \ + { \ + char ary[2 * (x) - 1]; \ + } #define CROMFS_POS_ROOT (0x0UL) #define CROMFS_POS_ERROR (0x1UL) @@ -58,7 +61,7 @@ typedef struct uint32_t file_size; /* file data size */ uint32_t file_origin_size; /* file size before compress */ uint32_t parition_pos; /* offset of data */ - uint8_t name[0]; /* name data */ + uint8_t name[0]; /* name data */ } cromfs_dirent; #define CROMFS_ALIGN_SIZE_BIT 4 @@ -109,27 +112,27 @@ typedef struct /**********************************/ -#define avl_key_t uint32_t -#define AVL_EMPTY (struct cromfs_avl_struct *)0 -#define avl_maxheight 32 -#define heightof(tree) ((tree) == AVL_EMPTY ? 0 : (tree)->avl_height) +#define avl_key_t uint32_t +#define AVL_EMPTY (struct cromfs_avl_struct *)0 +#define avl_maxheight 32 +#define heightof(tree) ((tree) == AVL_EMPTY ? 0 : (tree)->avl_height) struct cromfs_avl_struct { struct cromfs_avl_struct *avl_left; struct cromfs_avl_struct *avl_right; - int avl_height; + int avl_height; avl_key_t avl_key; file_info *fi; }; static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct cromfs_avl_struct **ptree); static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_avl_struct **ptree); -static struct cromfs_avl_struct* cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree); +static struct cromfs_avl_struct *cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree); static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int count) { - for (;count > 0; count--) + for (; count > 0; count--) { struct cromfs_avl_struct **nodeplace = *--nodeplaces_ptr; struct cromfs_avl_struct *node = *nodeplace; @@ -139,8 +142,8 @@ static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int int heightright = heightof(noderight); if (heightright + 1 < heightleft) { - struct cromfs_avl_struct * nodeleftleft = nodeleft->avl_left; - struct cromfs_avl_struct * nodeleftright = nodeleft->avl_right; + struct cromfs_avl_struct *nodeleftleft = nodeleft->avl_left; + struct cromfs_avl_struct *nodeleftright = nodeleft->avl_right; int heightleftright = heightof(nodeleftright); if (heightof(nodeleftleft) >= heightleftright) { @@ -183,8 +186,9 @@ static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int *nodeplace = noderightleft; } } - else { - int height = (heightleftavl_height) { break; @@ -233,9 +237,9 @@ static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct c } else { - struct cromfs_avl_struct *** stack_ptr_to_delete = stack_ptr; - struct cromfs_avl_struct ** nodeplace = &node_to_delete->avl_left; - struct cromfs_avl_struct * node; + struct cromfs_avl_struct ***stack_ptr_to_delete = stack_ptr; + struct cromfs_avl_struct **nodeplace = &node_to_delete->avl_left; + struct cromfs_avl_struct *node; for (;;) { node = *nodeplace; @@ -254,7 +258,7 @@ static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct c *nodeplace_to_delete = node; *stack_ptr_to_delete = &node->avl_left; } - cromfs_avl_rebalance(stack_ptr,stack_count); + cromfs_avl_rebalance(stack_ptr, stack_count); } static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_avl_struct **ptree) @@ -266,7 +270,7 @@ static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_ struct cromfs_avl_struct ***stack_ptr = &stack[0]; /* = &stack[stackcount] */ for (;;) { - struct cromfs_avl_struct * node = *nodeplace; + struct cromfs_avl_struct *node = *nodeplace; if (node == AVL_EMPTY) { break; @@ -286,10 +290,10 @@ static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_ new_node->avl_right = AVL_EMPTY; new_node->avl_height = 1; *nodeplace = new_node; - cromfs_avl_rebalance(stack_ptr,stack_count); + cromfs_avl_rebalance(stack_ptr, stack_count); } -static struct cromfs_avl_struct* cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct* ptree) +static struct cromfs_avl_struct *cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree) { for (;;) { @@ -387,7 +391,7 @@ static uint32_t cromfs_blk_read_bytes(cromfs_info *ci, uint32_t pos, void *buf, start_blk++; if (sector_nr) { - ret_len = rt_device_read(ci->device, start_blk, (char*)buf + off_s, sector_nr); + ret_len = rt_device_read(ci->device, start_blk, (char *)buf + off_s, sector_nr); if (ret_len != sector_nr) { goto end; @@ -401,7 +405,7 @@ static uint32_t cromfs_blk_read_bytes(cromfs_info *ci, uint32_t pos, void *buf, { goto end; } - memcpy((char*)buf + off_s, block_buff, size); + memcpy((char *)buf + off_s, block_buff, size); } ret = size_bak; end: @@ -509,7 +513,7 @@ static int dfs_cromfs_mount(struct dfs_filesystem *fs, unsigned long rwflag, con len = cromfs_read_bytes(ci, 0, &ci->part_info, sizeof ci->part_info); if (len != sizeof ci->part_info || - memcmp(ci->part_info.magic, CROMFS_MAGIC, sizeof ci->part_info.magic) != 0) + memcmp(ci->part_info.magic, CROMFS_MAGIC, sizeof ci->part_info.magic) != 0) { free(ci); return -RT_ERROR; @@ -533,7 +537,7 @@ static int dfs_cromfs_unmount(struct dfs_filesystem *fs) ci = (cromfs_info *)fs->data; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return -RT_ERROR; @@ -569,7 +573,7 @@ static int dfs_cromfs_ioctl(struct dfs_file *file, int cmd, void *args) return -EIO; } -static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int* is_dir, uint32_t *size, uint32_t *osize) +static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int *is_dir, uint32_t *size, uint32_t *osize) { uint32_t cur_size = 0, cur_pos = 0, cur_osize = 0; const char *subpath = NULL, *subpath_end = NULL; @@ -664,12 +668,12 @@ static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int* is_dir, ui return cur_pos; } -static uint32_t dfs_cromfs_lookup(cromfs_info *ci, const char *path, int* is_dir, uint32_t *size, uint32_t *osize) +static uint32_t dfs_cromfs_lookup(cromfs_info *ci, const char *path, int *is_dir, uint32_t *size, uint32_t *osize) { rt_err_t result = RT_EOK; uint32_t ret = 0; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return CROMFS_POS_ERROR; @@ -747,7 +751,7 @@ static int dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count) { int fill_ret = 0; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return 0; @@ -765,7 +769,7 @@ static int dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count) { void *di_mem = NULL; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return 0; @@ -773,7 +777,7 @@ static int dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count) di_mem = cromfs_dirent_cache_get(ci, fi->partition_pos, fi->size); if (di_mem) { - memcpy(buf, (char*)di_mem + file->pos, length); + memcpy(buf, (char *)di_mem + file->pos, length); } rt_mutex_release(&ci->lock); if (!di_mem) @@ -800,7 +804,7 @@ static int dfs_cromfs_lseek(struct dfs_file *file, off_t offset) static file_info *get_file_info(cromfs_info *ci, uint32_t partition_pos, int inc_ref) { - struct cromfs_avl_struct* node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); + struct cromfs_avl_struct *node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); if (node) { @@ -870,7 +874,7 @@ static file_info *inset_file_info(cromfs_info *ci, uint32_t partition_pos, int i static void deref_file_info(cromfs_info *ci, uint32_t partition_pos) { - struct cromfs_avl_struct* node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); + struct cromfs_avl_struct *node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); file_info *fi = NULL; if (node) @@ -907,7 +911,7 @@ static int dfs_cromfs_close(struct dfs_file *file) fs = (struct dfs_filesystem *)file->vnode->fs; ci = (cromfs_info *)fs->data; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return -RT_ERROR; @@ -937,8 +941,7 @@ static int dfs_cromfs_open(struct dfs_file *file) RT_ASSERT(file->vnode->ref_count > 0); if (file->vnode->ref_count > 1) { - if (file->vnode->type == FT_DIRECTORY - && !(file->flags & O_DIRECTORY)) + if (file->vnode->type == FT_DIRECTORY && !(file->flags & O_DIRECTORY)) { return -ENOENT; } @@ -977,7 +980,7 @@ static int dfs_cromfs_open(struct dfs_file *file) file->vnode->type = FT_REGULAR; } - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { ret = -EINTR; @@ -1029,7 +1032,7 @@ static int dfs_cromfs_stat(struct dfs_filesystem *fs, const char *path, struct s st->st_dev = 0; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | - S_IWUSR | S_IWGRP | S_IWOTH; + S_IWUSR | S_IWGRP | S_IWOTH; if (is_dir) { @@ -1074,7 +1077,7 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 return -ENOMEM; } - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { free(dirent); @@ -1103,10 +1106,35 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 for (index = 0; index < count && file->pos < file->vnode->size; index++) { uint32_t name_size = 0; + uint32_t entry_name_size = 0; d = dirp + index; + + /* the entry header itself has to be inside the directory blob we loaded */ + if (file->pos + sizeof(cromfs_dirent) > fi->size) + { + result = -EINVAL; + break; + } + sub_dirent = &dirent[file->pos >> CROMFS_ALIGN_SIZE_BIT]; name = sub_dirent->dirent.name; + entry_name_size = sub_dirent->dirent.name_size; + + /* name_size comes straight from the image, so it has to be validated + * before it is used as a length: + * - d_name holds DIRENT_NAME_MAX bytes, the terminating NUL included; + * - the name must stay inside the directory blob that was just loaded. + * Without these checks a crafted image overflows d_name, and since ls() + * and readdir() keep the dirent on the stack, it overflows the stack of + * the thread listing the directory. + */ + if (entry_name_size >= DIRENT_NAME_MAX || + file->pos + sizeof(cromfs_dirent) + entry_name_size > fi->size) + { + result = -EINVAL; + break; + } /* fill dirent */ if (sub_dirent->dirent.attr == CROMFS_DIRENT_ATTR_DIR) @@ -1118,23 +1146,27 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 d->d_type = DT_REG; } - d->d_namlen = sub_dirent->dirent.name_size; + d->d_namlen = entry_name_size; d->d_reclen = (rt_uint16_t)sizeof(struct dirent); - memcpy(d->d_name, (char *)name, sub_dirent->dirent.name_size); - d->d_name[sub_dirent->dirent.name_size] = '\0'; + memcpy(d->d_name, (char *)name, entry_name_size); + d->d_name[entry_name_size] = '\0'; - name_size = (sub_dirent->dirent.name_size + CROMFS_ALIGN_SIZE_MASK) & ~CROMFS_ALIGN_SIZE_MASK; + name_size = (entry_name_size + CROMFS_ALIGN_SIZE_MASK) & ~CROMFS_ALIGN_SIZE_MASK; /* move to next position */ file->pos += (name_size + sizeof *sub_dirent); } free(dirent); + if (result != RT_EOK) + { + return result; + } + return index * sizeof(struct dirent); } -static const struct dfs_file_ops _crom_fops = -{ +static const struct dfs_file_ops _crom_fops = { dfs_cromfs_open, dfs_cromfs_close, dfs_cromfs_ioctl, @@ -1145,8 +1177,7 @@ static const struct dfs_file_ops _crom_fops = dfs_cromfs_getdents, }; -static const struct dfs_filesystem_ops _cromfs = -{ +static const struct dfs_filesystem_ops _cromfs = { "crom", DFS_FS_FLAG_DEFAULT, &_crom_fops, diff --git a/components/dfs/dfs_v2/filesystems/cromfs/SConscript b/components/dfs/dfs_v2/filesystems/cromfs/SConscript index 1bc4d444cebf..f4f382498dec 100644 --- a/components/dfs/dfs_v2/filesystems/cromfs/SConscript +++ b/components/dfs/dfs_v2/filesystems/cromfs/SConscript @@ -8,4 +8,9 @@ CPPPATH = [cwd] group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS','RT_USING_DFS_CROMFS'], CPPPATH = CPPPATH) +list = os.listdir(cwd) +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + group = group + SConscript(os.path.join(item, 'SConscript')) + Return('group') diff --git a/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c b/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c index d262b19d233c..736b568906e9 100644 --- a/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c +++ b/components/dfs/dfs_v2/filesystems/cromfs/dfs_cromfs.c @@ -30,10 +30,13 @@ #define CROMFS_PATITION_HEAD_SIZE 256 #define CROMFS_DIRENT_CACHE_SIZE 8 -#define CROMFS_MAGIC "CROMFSMG" +#define CROMFS_MAGIC "CROMFSMG" #define CROMFS_CT_ASSERT(name, x) \ - struct assert_##name {char ary[2 * (x) - 1];} + struct assert_##name \ + { \ + char ary[2 * (x) - 1]; \ + } #define CROMFS_POS_ROOT (0x0UL) #define CROMFS_POS_ERROR (0x1UL) @@ -56,8 +59,8 @@ typedef struct enum { - CROMFS_DIRENT_ATTR_FILE = 0x0UL, - CROMFS_DIRENT_ATTR_DIR = 0x1UL, + CROMFS_DIRENT_ATTR_FILE = 0x0UL, + CROMFS_DIRENT_ATTR_DIR = 0x1UL, CROMFS_DIRENT_ATTR_SYMLINK = 0x2UL, }; @@ -68,7 +71,7 @@ typedef struct uint32_t file_size; /* file data size */ uint32_t file_origin_size; /* file size before compress */ uint32_t parition_pos; /* offset of data */ - uint8_t name[0]; /* name data */ + uint8_t name[0]; /* name data */ } cromfs_dirent; #define CROMFS_ALIGN_SIZE_BIT 4 @@ -120,27 +123,27 @@ typedef struct /**********************************/ -#define avl_key_t uint32_t -#define AVL_EMPTY (struct cromfs_avl_struct *)0 -#define avl_maxheight 32 -#define heightof(tree) ((tree) == AVL_EMPTY ? 0 : (tree)->avl_height) +#define avl_key_t uint32_t +#define AVL_EMPTY (struct cromfs_avl_struct *)0 +#define avl_maxheight 32 +#define heightof(tree) ((tree) == AVL_EMPTY ? 0 : (tree)->avl_height) struct cromfs_avl_struct { struct cromfs_avl_struct *avl_left; struct cromfs_avl_struct *avl_right; - int avl_height; + int avl_height; avl_key_t avl_key; file_info *fi; }; static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct cromfs_avl_struct **ptree); static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_avl_struct **ptree); -static struct cromfs_avl_struct* cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree); +static struct cromfs_avl_struct *cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree); static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int count) { - for (;count > 0; count--) + for (; count > 0; count--) { struct cromfs_avl_struct **nodeplace = *--nodeplaces_ptr; struct cromfs_avl_struct *node = *nodeplace; @@ -150,8 +153,8 @@ static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int int heightright = heightof(noderight); if (heightright + 1 < heightleft) { - struct cromfs_avl_struct * nodeleftleft = nodeleft->avl_left; - struct cromfs_avl_struct * nodeleftright = nodeleft->avl_right; + struct cromfs_avl_struct *nodeleftleft = nodeleft->avl_left; + struct cromfs_avl_struct *nodeleftright = nodeleft->avl_right; int heightleftright = heightof(nodeleftright); if (heightof(nodeleftleft) >= heightleftright) { @@ -194,8 +197,9 @@ static void cromfs_avl_rebalance(struct cromfs_avl_struct ***nodeplaces_ptr, int *nodeplace = noderightleft; } } - else { - int height = (heightleftavl_height) { break; @@ -244,9 +248,9 @@ static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct c } else { - struct cromfs_avl_struct *** stack_ptr_to_delete = stack_ptr; - struct cromfs_avl_struct ** nodeplace = &node_to_delete->avl_left; - struct cromfs_avl_struct * node; + struct cromfs_avl_struct ***stack_ptr_to_delete = stack_ptr; + struct cromfs_avl_struct **nodeplace = &node_to_delete->avl_left; + struct cromfs_avl_struct *node; for (;;) { node = *nodeplace; @@ -265,7 +269,7 @@ static void cromfs_avl_remove(struct cromfs_avl_struct *node_to_delete, struct c *nodeplace_to_delete = node; *stack_ptr_to_delete = &node->avl_left; } - cromfs_avl_rebalance(stack_ptr,stack_count); + cromfs_avl_rebalance(stack_ptr, stack_count); } static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_avl_struct **ptree) @@ -277,7 +281,7 @@ static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_ struct cromfs_avl_struct ***stack_ptr = &stack[0]; /* = &stack[stackcount] */ for (;;) { - struct cromfs_avl_struct * node = *nodeplace; + struct cromfs_avl_struct *node = *nodeplace; if (node == AVL_EMPTY) { break; @@ -297,10 +301,10 @@ static void cromfs_avl_insert(struct cromfs_avl_struct *new_node, struct cromfs_ new_node->avl_right = AVL_EMPTY; new_node->avl_height = 1; *nodeplace = new_node; - cromfs_avl_rebalance(stack_ptr,stack_count); + cromfs_avl_rebalance(stack_ptr, stack_count); } -static struct cromfs_avl_struct* cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct* ptree) +static struct cromfs_avl_struct *cromfs_avl_find(avl_key_t key, struct cromfs_avl_struct *ptree) { for (;;) { @@ -412,7 +416,7 @@ static uint32_t cromfs_blk_read_bytes(cromfs_info *ci, uint32_t pos, void *buf, start_blk++; if (sector_nr) { - ret_len = rt_device_read(ci->device, start_blk, (char*)buf + off_s, sector_nr); + ret_len = rt_device_read(ci->device, start_blk, (char *)buf + off_s, sector_nr); if (ret_len != sector_nr) { goto end; @@ -426,7 +430,7 @@ static uint32_t cromfs_blk_read_bytes(cromfs_info *ci, uint32_t pos, void *buf, { goto end; } - memcpy((char*)buf + off_s, block_buff, size); + memcpy((char *)buf + off_s, block_buff, size); } ret = size_bak; end: @@ -509,8 +513,7 @@ static void cromfs_dirent_cache_destroy(cromfs_info *ci) #ifdef RT_USING_PAGECACHE static ssize_t dfs_cromfs_page_read(struct dfs_file *file, struct dfs_page *page); -static struct dfs_aspace_ops dfs_cromfs_aspace_ops = -{ +static struct dfs_aspace_ops dfs_cromfs_aspace_ops = { .read = dfs_cromfs_page_read }; #endif @@ -564,7 +567,7 @@ static int dfs_cromfs_mount(struct dfs_mnt *mnt, unsigned long rwflag, const voi len = cromfs_read_bytes(ci, 0, &ci->part_info, sizeof ci->part_info); if (len != sizeof ci->part_info || - memcmp(ci->part_info.magic, CROMFS_MAGIC, sizeof ci->part_info.magic) != 0) + memcmp(ci->part_info.magic, CROMFS_MAGIC, sizeof ci->part_info.magic) != 0) { free(ci); return -RT_ERROR; @@ -588,7 +591,7 @@ static int dfs_cromfs_unmount(struct dfs_mnt *mnt) ci = (cromfs_info *)mnt->data; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return -RT_ERROR; @@ -624,7 +627,7 @@ static int dfs_cromfs_unmount(struct dfs_mnt *mnt) return RT_EOK; } -static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int* file_type, uint32_t *size, uint32_t *osize) +static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int *file_type, uint32_t *size, uint32_t *osize) { uint32_t cur_size = 0, cur_pos = 0, cur_osize = 0; const char *subpath = NULL, *subpath_end = NULL; @@ -727,12 +730,12 @@ static uint32_t cromfs_lookup(cromfs_info *ci, const char *path, int* file_type, return cur_pos; } -static uint32_t __dfs_cromfs_lookup(cromfs_info *ci, const char *path, int* file_type, uint32_t *size, uint32_t *osize) +static uint32_t __dfs_cromfs_lookup(cromfs_info *ci, const char *path, int *file_type, uint32_t *size, uint32_t *osize) { rt_err_t result = RT_EOK; uint32_t ret = 0; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return CROMFS_POS_ERROR; @@ -808,7 +811,7 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o { int fill_ret = 0; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return 0; @@ -826,7 +829,7 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o { void *di_mem = NULL; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return 0; @@ -834,7 +837,7 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o di_mem = cromfs_dirent_cache_get(ci, fi->partition_pos, fi->size); if (di_mem) { - memcpy(buf, (char*)di_mem + *pos, length); + memcpy(buf, (char *)di_mem + *pos, length); } rt_mutex_release(&ci->lock); if (!di_mem) @@ -851,7 +854,7 @@ static ssize_t dfs_cromfs_read(struct dfs_file *file, void *buf, size_t count, o static file_info *get_file_info(cromfs_info *ci, uint32_t partition_pos, int inc_ref) { - struct cromfs_avl_struct* node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); + struct cromfs_avl_struct *node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); if (node) { @@ -921,7 +924,7 @@ static file_info *inset_file_info(cromfs_info *ci, uint32_t partition_pos, int f static void deref_file_info(cromfs_info *ci, uint32_t partition_pos) { - struct cromfs_avl_struct* node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); + struct cromfs_avl_struct *node = cromfs_avl_find(partition_pos, ci->cromfs_avl_root); file_info *fi = NULL; if (node) @@ -956,7 +959,7 @@ static int dfs_cromfs_close(struct dfs_file *file) fi = (file_info *)file->vnode->data; ci = (cromfs_info *)file->dentry->mnt->data; - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { return -RT_ERROR; @@ -985,8 +988,7 @@ static int dfs_cromfs_open(struct dfs_file *file) RT_ASSERT(file->vnode->ref_count > 0); if (file->vnode->ref_count > 1) { - if (file->vnode->type == FT_DIRECTORY - && !(file->flags & O_DIRECTORY)) + if (file->vnode->type == FT_DIRECTORY && !(file->flags & O_DIRECTORY)) { return -ENOENT; } @@ -1028,7 +1030,7 @@ static int dfs_cromfs_open(struct dfs_file *file) file->vnode->type = FT_REGULAR; } - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { ret = -EINTR; @@ -1087,7 +1089,7 @@ static int dfs_cromfs_stat(struct dfs_dentry *dentry, struct stat *st) st->st_mode |= S_IFDIR; st->st_size = size; } - else if(file_type == CROMFS_DIRENT_ATTR_SYMLINK) + else if (file_type == CROMFS_DIRENT_ATTR_SYMLINK) { st->st_mode &= ~S_IFREG; st->st_mode |= S_IFLNK; @@ -1134,7 +1136,7 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 return -ENOMEM; } - result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); + result = rt_mutex_take(&ci->lock, RT_WAITING_FOREVER); if (result != RT_EOK) { free(dirent); @@ -1163,10 +1165,35 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 for (index = 0; index < count && file->fpos < file->vnode->size; index++) { uint32_t name_size = 0; + uint32_t entry_name_size = 0; d = dirp + index; + + /* the entry header itself has to be inside the directory blob we loaded */ + if (file->fpos + sizeof(cromfs_dirent) > fi->size) + { + result = -EINVAL; + break; + } + sub_dirent = &dirent[file->fpos >> CROMFS_ALIGN_SIZE_BIT]; name = sub_dirent->dirent.name; + entry_name_size = sub_dirent->dirent.name_size; + + /* name_size comes straight from the image, so it has to be validated + * before it is used as a length: + * - d_name holds DIRENT_NAME_MAX bytes, the terminating NUL included; + * - the name must stay inside the directory blob that was just loaded. + * Without these checks a crafted image overflows d_name, and since ls() + * and readdir() keep the dirent on the stack, it overflows the stack of + * the thread listing the directory. + */ + if (entry_name_size >= DIRENT_NAME_MAX || + file->fpos + sizeof(cromfs_dirent) + entry_name_size > fi->size) + { + result = -EINVAL; + break; + } /* fill dirent */ if (sub_dirent->dirent.attr == CROMFS_DIRENT_ATTR_DIR) @@ -1178,22 +1205,27 @@ static int dfs_cromfs_getdents(struct dfs_file *file, struct dirent *dirp, uint3 d->d_type = DT_REG; } - d->d_namlen = sub_dirent->dirent.name_size; + d->d_namlen = entry_name_size; d->d_reclen = (rt_uint16_t)sizeof(struct dirent); - memcpy(d->d_name, (char *)name, sub_dirent->dirent.name_size); - d->d_name[sub_dirent->dirent.name_size] = '\0'; + memcpy(d->d_name, (char *)name, entry_name_size); + d->d_name[entry_name_size] = '\0'; - name_size = (sub_dirent->dirent.name_size + CROMFS_ALIGN_SIZE_MASK) & ~CROMFS_ALIGN_SIZE_MASK; + name_size = (entry_name_size + CROMFS_ALIGN_SIZE_MASK) & ~CROMFS_ALIGN_SIZE_MASK; /* move to next position */ file->fpos += (name_size + sizeof *sub_dirent); } free(dirent); + if (result != RT_EOK) + { + return result; + } + return index * sizeof(struct dirent); } -static struct dfs_vnode *dfs_cromfs_lookup (struct dfs_dentry *dentry) +static struct dfs_vnode *dfs_cromfs_lookup(struct dfs_dentry *dentry) { struct dfs_vnode *vnode = RT_NULL; cromfs_info *ci = NULL; @@ -1344,33 +1376,30 @@ static int dfs_cromfs_readlink(struct dfs_dentry *dentry, char *buf, int len) return -EBADF; } -static const struct dfs_file_ops _crom_fops = -{ - .open = dfs_cromfs_open, - .close = dfs_cromfs_close, - .lseek = generic_dfs_lseek, - .read = dfs_cromfs_read, - .getdents = dfs_cromfs_getdents, +static const struct dfs_file_ops _crom_fops = { + .open = dfs_cromfs_open, + .close = dfs_cromfs_close, + .lseek = generic_dfs_lseek, + .read = dfs_cromfs_read, + .getdents = dfs_cromfs_getdents, }; -static const struct dfs_filesystem_ops _cromfs_ops = -{ - .name = "crom", - .flags = 0, - .default_fops = &_crom_fops, - .mount = dfs_cromfs_mount, - .umount = dfs_cromfs_unmount, +static const struct dfs_filesystem_ops _cromfs_ops = { + .name = "crom", + .flags = 0, + .default_fops = &_crom_fops, + .mount = dfs_cromfs_mount, + .umount = dfs_cromfs_unmount, - .readlink = dfs_cromfs_readlink, + .readlink = dfs_cromfs_readlink, - .stat = dfs_cromfs_stat, - .lookup = dfs_cromfs_lookup, - .free_vnode = dfs_cromfs_free_vnode + .stat = dfs_cromfs_stat, + .lookup = dfs_cromfs_lookup, + .free_vnode = dfs_cromfs_free_vnode }; -static struct dfs_filesystem_type _cromfs = -{ - .fs_ops = &_cromfs_ops, +static struct dfs_filesystem_type _cromfs = { + .fs_ops = &_cromfs_ops, }; int dfs_cromfs_init(void) diff --git a/components/dfs/dfs_v2/filesystems/cromfs/utest/Kconfig b/components/dfs/dfs_v2/filesystems/cromfs/utest/Kconfig new file mode 100644 index 000000000000..ab8d3b61d5e6 --- /dev/null +++ b/components/dfs/dfs_v2/filesystems/cromfs/utest/Kconfig @@ -0,0 +1,13 @@ +menu "cromfs Testcase" + +config RT_UTEST_DFS_CROMFS + bool "cromfs dirent name bound test" + default n + depends on RT_USING_DFS_V2 && RT_USING_DFS_CROMFS + select RT_USING_DFS_TMPFS + help + Mount a crafted cromfs image from memory and check that a directory + entry whose name_size does not fit into struct dirent::d_name is + rejected instead of being copied. + +endmenu diff --git a/components/dfs/dfs_v2/filesystems/cromfs/utest/SConscript b/components/dfs/dfs_v2/filesystems/cromfs/utest/SConscript new file mode 100644 index 000000000000..60683c9d9765 --- /dev/null +++ b/components/dfs/dfs_v2/filesystems/cromfs/utest/SConscript @@ -0,0 +1,13 @@ +Import('rtconfig') +from building import * + +cwd = GetCurrentDir() +src = [] +CPPPATH = [cwd] + +if GetDepend(['RT_UTEST_DFS_CROMFS']): + src += ['tc_cromfs_dirent.c'] + +group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/dfs/dfs_v2/filesystems/cromfs/utest/tc_cromfs_dirent.c b/components/dfs/dfs_v2/filesystems/cromfs/utest/tc_cromfs_dirent.c new file mode 100644 index 000000000000..542a3d1fda56 --- /dev/null +++ b/components/dfs/dfs_v2/filesystems/cromfs/utest/tc_cromfs_dirent.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2006-2026, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-10-21 Yunlk the first version + */ + +/** + * Test Case Name: cromfs Directory Name Bound Test (components.dfs.v2.cromfs.dirent_name) + * + * Test Objectives: + * - Verify that dfs_cromfs_getdents() refuses a directory entry whose name_size + * does not fit into struct dirent::d_name instead of copying it. + * - Verify that a well formed image is still listed normally, so the check does + * not reject valid entries. + * + * Test Scenarios: + * - A cromfs image is built in RAM and mounted through the data pointer of + * dfs_mount(), the way bsp/k230 and bsp/cvitek mount their root filesystem. + * That keeps the test independent of any block device and of zlib, because + * listing a directory never decompresses file data. + * 1. **Control image:** one entry with name_size = 2 ("oo"). getdents must + * return a positive byte count and d_name must hold that name. + * 2. **Oversized name:** one entry with name_size = 512, larger than + * DIRENT_NAME_MAX (256). getdents must return -EINVAL and must not write the + * name into the caller's buffer. + * + * Verification Metrics: + * - Control: return value > 0, d_namlen == 2, d_name == "oo". + * - Oversized: return value == -EINVAL, caller buffer left untouched. + * + * Dependencies: + * - Software configuration + * - `RT_USING_UTEST` and `RT_USING_UTESTCASES` must be enabled. + * - `RT-Thread Utestcases` -> `Kernel Components` -> `DFS` -> `cromfs dirent + * name bound test` must be enabled (`RT_UTEST_DFS_CROMFS`). + * - `RT_USING_DFS_V2` and `RT_USING_DFS_CROMFS` must be enabled, and a tmpfs is + * used as the parent filesystem of the mount point (`RT_USING_DFS_TMPFS`). + * - Hardware requirements + * - None (verified on bsp/qemu-vexpress-a9). + * - Run the test case from the msh prompt: + * `utest_run components.dfs.v2.cromfs.dirent_name` + * + * Expected Results: + * - The console prints the return value of both listings. + * - Final Output: `[ PASSED ] [ result ] testcase (components.dfs.v2.cromfs.dirent_name)` + */ + +#include +#include +#include +#include +#include +#include +#include "utest.h" + +#ifdef RT_UTEST_DFS_CROMFS + +#define TEST_MNT_ROOT "/" +#define TEST_MNT_OK "/crom_ok" +#define TEST_MNT_EVIL "/crom_evil" + +#define TEST_IMG_SIZE 1024 +#define TEST_DIR_POS 256 /* CROMFS_PATITION_HEAD_SIZE */ +#define TEST_DIRENT_HDR 16 /* sizeof of the on-image dirent header */ +#define TEST_ALIGN 16 +#define TEST_NAME_MAX_ENTRY 512 /* deliberately larger than DIRENT_NAME_MAX */ +#define TEST_MOUNT_FAILED (-0x7fffffff) + +/* mirrors partition_head_data */ +typedef struct +{ + rt_uint8_t magic[8]; + rt_uint32_t version; + rt_uint32_t partition_attr; + rt_uint32_t partition_size; + rt_uint32_t root_dir_pos; + rt_uint32_t root_dir_size; +} test_partition_head; + +/* mirrors cromfs_dirent without the trailing name */ +typedef struct +{ + rt_uint16_t attr; + rt_uint16_t name_size; + rt_uint32_t file_size; + rt_uint32_t file_origin_size; + rt_uint32_t partition_pos; +} test_dirent_head; + +static rt_uint8_t _image[TEST_IMG_SIZE]; +static rt_bool_t _root_ready; + +#define TEST_ALIGN_UP(x) (((x) + TEST_ALIGN - 1) & ~(TEST_ALIGN - 1)) + +static void _build_image(rt_uint16_t name_size, char name_byte) +{ + test_partition_head *head; + test_dirent_head *entry; + + rt_memset(_image, 0, sizeof(_image)); + + head = (test_partition_head *)_image; + rt_memcpy(head->magic, "CROMFSMG", 8); + head->version = 0; + head->partition_attr = 0; + head->partition_size = sizeof(_image); + head->root_dir_pos = TEST_DIR_POS; + head->root_dir_size = TEST_DIRENT_HDR + TEST_ALIGN_UP(name_size); + + entry = (test_dirent_head *)(_image + TEST_DIR_POS); + entry->attr = 0; /* CROMFS_DIRENT_ATTR_FILE */ + entry->name_size = name_size; + entry->file_size = 0; + entry->partition_pos = 0; + + rt_memset(_image + TEST_DIR_POS + TEST_DIRENT_HDR, name_byte, name_size); +} + +/* mount the image at the given mount point and list its root directory once */ +static int _list_image(const char *mnt_point, struct dirent *dent, rt_size_t dent_size) +{ + struct dfs_file file; + int ret; + + ret = dfs_mount(RT_NULL, mnt_point, "crom", 0, _image); + if (ret != 0) + { + LOG_E("mount of %s failed: %d", mnt_point, ret); + return TEST_MOUNT_FAILED; + } + + rt_memset(&file, 0, sizeof(file)); + ret = dfs_file_open(&file, mnt_point, O_DIRECTORY, 0); + if (ret == 0) + { + ret = dfs_file_getdents(&file, dent, dent_size); + dfs_file_close(&file); + } + else + { + LOG_E("open of %s failed: %d", mnt_point, ret); + } + + return ret; +} + +static void cromfs_dirent_name(void) +{ + struct dirent dent; + int ret; + + /* a well formed entry must still be listed */ + _build_image(2, 'o'); + rt_memset(&dent, 0, sizeof(dent)); + ret = _list_image(TEST_MNT_OK, &dent, sizeof(dent)); + LOG_I("control image -> ret=%d, d_namlen=%d, name=\"%s\" (expect >0 / 2 / oo)", + ret, (int)dent.d_namlen, dent.d_name); + uassert_true(ret > 0); + uassert_int_equal(dent.d_namlen, 2); + + /* a name larger than d_name must be rejected, not copied */ + _build_image(TEST_NAME_MAX_ENTRY, 'A'); + rt_memset(&dent, 0, sizeof(dent)); + ret = _list_image(TEST_MNT_EVIL, &dent, sizeof(dent)); + LOG_I("oversized name -> ret=%d, d_namlen=%d (expect %d / 0)", + ret, (int)dent.d_namlen, -EINVAL); + uassert_true(ret != TEST_MOUNT_FAILED); + uassert_int_equal(ret, -EINVAL); + uassert_int_equal(dent.d_namlen, 0); +} + +static rt_err_t utest_tc_init(void) +{ + /* the mount point has to exist in some parent filesystem */ + if (!_root_ready) + { + if (dfs_mount(RT_NULL, TEST_MNT_ROOT, "tmp", 0, RT_NULL) == 0) + { + _root_ready = RT_TRUE; + } + else + { + /* someone else already provided a root filesystem */ + _root_ready = RT_TRUE; + } + } + + mkdir(TEST_MNT_OK, 0777); + mkdir(TEST_MNT_EVIL, 0777); + + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + dfs_unmount(TEST_MNT_OK); + dfs_unmount(TEST_MNT_EVIL); + + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(cromfs_dirent_name); +} +UTEST_TC_EXPORT(testcase, "components.dfs.v2.cromfs.dirent_name", + utest_tc_init, utest_tc_cleanup, 10); + +#endif /* RT_UTEST_DFS_CROMFS */ diff --git a/components/utilities/utest/Kconfig b/components/utilities/utest/Kconfig index 10781a43315b..8df61e0d778c 100644 --- a/components/utilities/utest/Kconfig +++ b/components/utilities/utest/Kconfig @@ -26,6 +26,7 @@ menu "RT-Thread Utestcases" rsource "../../../components/dfs/utest/Kconfig" rsource "../../../components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig" + rsource "../../../components/dfs/dfs_v2/filesystems/cromfs/utest/Kconfig" rsource "../../../components/libc/compilers/common/utest/Kconfig" rsource "../../../components/libc/cplusplus/utest/Kconfig" rsource "../../../components/lwp/utest/Kconfig"