diff --git a/fs/vfs/fs_pseudofile.c b/fs/vfs/fs_pseudofile.c index 7b091bce740..3e5ecf94ae4 100644 --- a/fs/vfs/fs_pseudofile.c +++ b/fs/vfs/fs_pseudofile.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include #include #include -#include #include "inode/inode.h" #include "fs_heap.h" @@ -170,25 +170,67 @@ static int pseudofile_close(FAR struct file *filep) return OK; } +/* Grow allocation by doubling until it covers the requested size. */ + +static size_t pseudofile_buffersize(FAR struct fs_pseudofile_s *pf, + size_t size) +{ + size_t alloc = 0; + + if (pf->content != NULL) + { + alloc = fs_heap_malloc_size(pf->content); + } + + if (alloc == 0) + { + alloc = 1; + } + + while (alloc < size) + { + if (alloc > (SIZE_MAX >> 1)) + { + return size; + } + + alloc <<= 1; + } + + return alloc; +} + static int pseudofile_expand(FAR struct inode *node, size_t size) { FAR struct fs_pseudofile_s *pf = node->i_private; FAR void *tmp; + size_t oldsize = node->i_size; - if (pf->content && fs_heap_malloc_size(pf->content) >= size) + if (pf->content != NULL && fs_heap_malloc_size(pf->content) >= size) { + if (size > oldsize) + { + memset(pf->content + oldsize, 0, size - oldsize); + } + node->i_size = size; return 0; } - tmp = fs_heap_realloc(pf->content, 1 << LOG2_CEIL(size)); + tmp = fs_heap_realloc(pf->content, pseudofile_buffersize(pf, size)); if (tmp == NULL) { return -ENOMEM; } pf->content = tmp; + + if (size > oldsize) + { + memset(pf->content + oldsize, 0, size - oldsize); + } + node->i_size = size; return 0; } @@ -208,6 +250,12 @@ static ssize_t pseudofile_write(FAR struct file *filep, if (filep->f_oflags & O_APPEND) { + if (buflen > SIZE_MAX - node->i_size) + { + nxmutex_unlock(&pf->lock); + return -EFBIG; + } + ret = pseudofile_expand(node, node->i_size + buflen); if (ret < 0) { @@ -219,6 +267,12 @@ static ssize_t pseudofile_write(FAR struct file *filep, } else { + if (filep->f_pos > SIZE_MAX - buflen) + { + nxmutex_unlock(&pf->lock); + return -EFBIG; + } + ret = pseudofile_expand(node, filep->f_pos + buflen); if (ret < 0) { @@ -393,6 +447,12 @@ static int pseudofile_truncate(FAR struct file *filep, off_t length) return ret; } + if (length < 0) + { + ret = -EINVAL; + goto out; + } + if (length < node->i_size) { FAR void *tmp; @@ -414,8 +474,6 @@ static int pseudofile_truncate(FAR struct file *filep, off_t length) { goto out; } - - memset(pf->content + node->i_size, 0, length - node->i_size); } #ifdef CONFIG_PSEUDOFS_ATTRIBUTES