git: ce6a0c776b70 - main - tarfs: Fix issues revealed by static analysis and testing.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 09 Feb 2023 17:41:38 UTC
The branch main has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=ce6a0c776b702f063d4f200de34bfeaddcbb3cb7
commit ce6a0c776b702f063d4f200de34bfeaddcbb3cb7
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-02-09 17:35:28 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-02-09 17:35:47 +0000
tarfs: Fix issues revealed by static analysis and testing.
* tarfs_alloc_mount(): Remove an unnecessary null check (CID 1504505) and an unused variable.
* tarfs_alloc_one(): Verify that the file size is not negative (CID 1504506). While there, also validate the mode, owner and group.
* tarfs_vget(), tarfs_zio_init(): Explicitly ignore return value from getnewvnode(), which cannot fail (CID 1504508)
* tarfs_lookup_path(): Fix a case where a specially-crafted tarball could trigger a null pointer dereference by first descending into, and then backing out of, a previously unknown directory. (CID 1504515)
* mktar: Construct a tarball that triggers the aforementioned null pointer dereference.
Reported by: Coverity
Sponsored by: Juniper Networks, Inc.
Sponsored by: Klara, Inc.
Reviewed by: imp, kib
Differential Revision: https://reviews.freebsd.org/D38463
---
sys/fs/tarfs/tarfs_io.c | 2 +-
sys/fs/tarfs/tarfs_vfsops.c | 88 +++++++++++++++++++++++++++------------------
sys/fs/tarfs/tarfs_vnops.c | 2 +-
tests/sys/fs/tarfs/mktar.c | 43 ++++++++++++++++++++--
4 files changed, 96 insertions(+), 39 deletions(-)
diff --git a/sys/fs/tarfs/tarfs_io.c b/sys/fs/tarfs/tarfs_io.c
index c185de8beef1..8837681ac5f0 100644
--- a/sys/fs/tarfs/tarfs_io.c
+++ b/sys/fs/tarfs/tarfs_io.c
@@ -630,7 +630,7 @@ tarfs_zio_init(struct tarfs_mount *tmp, off_t i, off_t o)
zio->idx[zio->curidx].o = zio->opos = o;
tmp->zio = zio;
TARFS_DPF(ALLOC, "%s: allocated zio index\n", __func__);
- getnewvnode("tarfsz", tmp->vfs, &tarfs_znodeops, &zvp);
+ (void)getnewvnode("tarfsz", tmp->vfs, &tarfs_znodeops, &zvp);
zvp->v_data = zio;
zvp->v_type = VREG;
zvp->v_mount = tmp->vfs;
diff --git a/sys/fs/tarfs/tarfs_vfsops.c b/sys/fs/tarfs/tarfs_vfsops.c
index a45f005a2bd1..138a57c22e7f 100644
--- a/sys/fs/tarfs/tarfs_vfsops.c
+++ b/sys/fs/tarfs/tarfs_vfsops.c
@@ -289,7 +289,7 @@ tarfs_lookup_path(struct tarfs_mount *tmp, char *name, size_t namelen,
char **endp, char **sepp, struct tarfs_node **retparent,
struct tarfs_node **retnode, boolean_t create_dirs)
{
- struct componentname cn;
+ struct componentname cn = { };
struct tarfs_node *parent, *tnp;
char *sep;
size_t len;
@@ -304,8 +304,6 @@ tarfs_lookup_path(struct tarfs_mount *tmp, char *name, size_t namelen,
if (tnp == NULL)
panic("%s: root node not yet created", __func__);
- bzero(&cn, sizeof(cn));
-
TARFS_DPF(LOOKUP, "%s: Full path: %.*s\n", __func__, (int)namelen,
name);
@@ -329,24 +327,21 @@ tarfs_lookup_path(struct tarfs_mount *tmp, char *name, size_t namelen,
/* nothing */ ;
/* check for . and .. */
- if (name[0] == '.' && len <= 2) {
- if (len == 1) {
- /* . */
- name += len;
- namelen -= len;
- continue;
- } else if (name[1] == '.') {
- /* .. */
- if (tnp == tmp->root) {
- error = EINVAL;
- break;
- }
- tnp = tnp->parent;
- parent = tnp->parent;
- name += len;
- namelen -= len;
- continue;
+ if (name[0] == '.' && len == 1) {
+ name += len;
+ namelen -= len;
+ continue;
+ }
+ if (name[0] == '.' && name[1] == '.' && len == 2) {
+ if (tnp == tmp->root) {
+ error = EINVAL;
+ break;
}
+ tnp = parent;
+ parent = tnp->parent;
+ name += len;
+ namelen -= len;
+ continue;
}
/* create parent if necessary */
@@ -441,6 +436,7 @@ tarfs_alloc_one(struct tarfs_mount *tmp, off_t *blknump)
struct sbuf *namebuf = NULL;
char *exthdr = NULL, *name = NULL, *link = NULL;
off_t blknum = *blknump;
+ int64_t num;
int endmarker = 0;
char *namep, *sep;
struct tarfs_node *parent, *tnp;
@@ -511,10 +507,41 @@ again:
}
/* get standard attributes */
- mode = tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode));
- uid = tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid));
- gid = tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid));
- sz = tarfs_str2int64(hdrp->size, sizeof(hdrp->size));
+ num = tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode));
+ if (num < 0 || num > ALLPERMS) {
+ TARFS_DPF(ALLOC, "%s: invalid file mode at %zu\n",
+ __func__, TARFS_BLOCKSIZE * (blknum - 1));
+ mode = S_IRUSR;
+ } else {
+ mode = num;
+ }
+ num = tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid));
+ if (num < 0 || num > UID_MAX) {
+ TARFS_DPF(ALLOC, "%s: UID out of range at %zu\n",
+ __func__, TARFS_BLOCKSIZE * (blknum - 1));
+ uid = tmp->root->uid;
+ mode &= ~S_ISUID;
+ } else {
+ uid = num;
+ }
+ num = tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid));
+ if (num < 0 || num > GID_MAX) {
+ TARFS_DPF(ALLOC, "%s: GID out of range at %zu\n",
+ __func__, TARFS_BLOCKSIZE * (blknum - 1));
+ gid = tmp->root->gid;
+ mode &= ~S_ISGID;
+ } else {
+ gid = num;
+ }
+ num = tarfs_str2int64(hdrp->size, sizeof(hdrp->size));
+ if (num < 0) {
+ TARFS_DPF(ALLOC, "%s: negative size at %zu\n",
+ __func__, TARFS_BLOCKSIZE * (blknum - 1));
+ error = EINVAL;
+ goto bad;
+ } else {
+ sz = num;
+ }
mtime = tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime));
rdev = NODEV;
TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__,
@@ -777,7 +804,6 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
{
struct vattr va;
struct thread *td = curthread;
- char *fullpath;
struct tarfs_mount *tmp;
struct tarfs_node *root;
off_t blknum;
@@ -788,7 +814,6 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
ASSERT_VOP_LOCKED(vp, __func__);
tmp = NULL;
- fullpath = NULL;
TARFS_DPF(ALLOC, "%s: Allocating tarfs mount structure for vp %p\n",
__func__, vp);
@@ -802,8 +827,7 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
mtime = va.va_mtime.tv_sec;
/* Allocate and initialize tarfs mount structure */
- tmp = (struct tarfs_mount *)malloc(sizeof(struct tarfs_mount),
- M_TARFSMNT, M_WAITOK | M_ZERO);
+ tmp = malloc(sizeof(*tmp), M_TARFSMNT, M_WAITOK | M_ZERO);
TARFS_DPF(ALLOC, "%s: Allocated mount structure\n", __func__);
mp->mnt_data = tmp;
@@ -848,9 +872,7 @@ tarfs_alloc_mount(struct mount *mp, struct vnode *vp,
return (0);
bad:
- if (tmp != NULL)
- tarfs_free_mount(tmp);
- free(fullpath, M_TEMP);
+ tarfs_free_mount(tmp);
return (error);
}
@@ -1104,9 +1126,7 @@ tarfs_vget(struct mount *mp, ino_t ino, int lkflags, struct vnode **vpp)
if (tnp == NULL)
return (ENOENT);
- error = getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp);
- if (error != 0)
- goto bad;
+ (void)getnewvnode("tarfs", mp, &tarfs_vnodeops, &vp);
TARFS_DPF(FS, "%s: allocated vnode\n", __func__);
vp->v_data = tnp;
vp->v_type = tnp->type;
diff --git a/sys/fs/tarfs/tarfs_vnops.c b/sys/fs/tarfs/tarfs_vnops.c
index 266002bca7b2..99ff39d41271 100644
--- a/sys/fs/tarfs/tarfs_vnops.c
+++ b/sys/fs/tarfs/tarfs_vnops.c
@@ -250,7 +250,7 @@ tarfs_lookup(struct vop_cachedlookup_args *ap)
static int
tarfs_readdir(struct vop_readdir_args *ap)
{
- struct dirent cde;
+ struct dirent cde = { };
struct tarfs_node *current, *tnp;
struct vnode *vp;
struct uio *uio;
diff --git a/tests/sys/fs/tarfs/mktar.c b/tests/sys/fs/tarfs/mktar.c
index e1b1183af114..9b3d7910a12c 100644
--- a/tests/sys/fs/tarfs/mktar.c
+++ b/tests/sys/fs/tarfs/mktar.c
@@ -41,6 +41,8 @@
#define PROGNAME "mktar"
#define SUBDIRNAME "directory"
+#define EMPTYDIRNAME "empty"
+#define NORMALFILENAME "file"
#define SPARSEFILENAME "sparse_file"
#define HARDLINKNAME "hard_link"
#define SHORTLINKNAME "short_link"
@@ -61,6 +63,25 @@ static void verbose(const char *fmt, ...)
fprintf(stderr, "\n");
}
+static void
+mknormalfile(const char *filename, mode_t mode)
+{
+ char buf[512];
+ ssize_t res;
+ int fd;
+
+ if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
+ err(1, "%s", filename);
+ for (unsigned int i = 0; i < sizeof(buf); i++)
+ buf[i] = 32 + i % 64;
+ res = write(fd, buf, sizeof(buf));
+ if (res < 0)
+ err(1, "%s", filename);
+ if (res != sizeof(buf))
+ errx(1, "%s: short write", filename);
+ close(fd);
+}
+
static void
mksparsefile(const char *filename, mode_t mode)
{
@@ -68,7 +89,7 @@ mksparsefile(const char *filename, mode_t mode)
ssize_t res;
int fd;
- if ((fd = open(filename, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0)
+ if ((fd = open(filename, O_RDWR|O_CREAT|O_EXCL, mode)) < 0)
err(1, "%s", filename);
for (unsigned int i = 33; i <= 126; i++) {
memset(buf, i, sizeof(buf));
@@ -106,6 +127,15 @@ mktar(void)
if (mkdir(SUBDIRNAME, 0755) != 0)
err(1, "%s", SUBDIRNAME);
+ /* create a second subdirectory which will remain empty */
+ verbose("mkdir %s", EMPTYDIRNAME);
+ if (mkdir(EMPTYDIRNAME, 0755) != 0)
+ err(1, "%s", EMPTYDIRNAME);
+
+ /* create a normal file */
+ verbose("creating %s", NORMALFILENAME);
+ mknormalfile(NORMALFILENAME, 0644);
+
/* create a sparse file */
verbose("creating %s", SPARSEFILENAME);
mksparsefile(SPARSEFILENAME, 0644);
@@ -198,7 +228,12 @@ main(int argc, char *argv[])
#if 0
"--options", "zstd:frame-per-file",
#endif
- ".",
+ "./" EMPTYDIRNAME "/../" NORMALFILENAME,
+ "./" SPARSEFILENAME,
+ "./" HARDLINKNAME,
+ "./" SHORTLINKNAME,
+ "./" SUBDIRNAME,
+ "./" LONGLINKNAME,
NULL);
err(1, "execlp()");
}
@@ -222,7 +257,9 @@ main(int argc, char *argv[])
(void)unlink(HARDLINKNAME);
verbose("rm %s", SPARSEFILENAME);
(void)unlink(SPARSEFILENAME);
- verbose("rm %s", SUBDIRNAME);
+ verbose("rmdir %s", EMPTYDIRNAME);
+ (void)rmdir(EMPTYDIRNAME);
+ verbose("rmdir %s", SUBDIRNAME);
(void)rmdir(SUBDIRNAME);
verbose("cd -");
exit(0);