From nobody Wed Mar 06 16:24:30 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4Tqd722qRZz5Csd3; Wed, 6 Mar 2024 16:24:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4Tqd72208Rz4fWG; Wed, 6 Mar 2024 16:24:30 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1709742270; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=gcRFJfkHpc9eFzn2459OTlv2kv0Kf8teT3QXc/xbbzw=; b=xhLSxcpf9jPEevOXEgIQwfRzv718fySqW2HvKyuZ9NVyA2T2O3rgojjmfCiGo4pI6i7G5s aeLsyiwoX9HlrZ0Rqj4U6Q/J9CrFJckLkFL3tqMLf/EmIkoQKkG/QW+v3/0ZcBbmlT5DTl 4c3EMNFShJjs8v4xyMdYdpvOR8SFyBD2Ud6hSwuQyZKWzkA6D8MTfReLbsPcgJnHAv36d+ +UYZny44PkhZYAO88KbMJG5EeE8kfbl3NHlBJpW6y89TMrk1UiKgb3LlB+803dCMumULz8 GKTRobeDMWynIQtJU4NSYB+EHV/6dJsrvXhYnqd/eHtaqRfZnMfbWMPYFJ9EmA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1709742270; a=rsa-sha256; cv=none; b=Ps+yMtSLP+YCYvSeCAisxCp8TkziU94HiYzERbIY0OibAcQvY5i42KYWPEJotlq9V+6+i+ zglwiwkMr7ogBlY1V7bvfqlep6pYNQn8L8FFhed1f/1bmLt6mI2zWGvsNmpNA0olkSi/Zv +9ldqnHv2A3pU5uREMBJ232k94NE9QUS4TgYIyVj2/UrQE2DeV9rZzD8YDAzSMZVeoBn0s FYQTDkiEBHCXh6SNqQhryqIclGREwQ05yRptvbjWbsCQf8uuwRdTB2iLCi99/F8nfDzRuD j2V5YqzoWPUz6rAM4xMOvXTJhXrLN5kyZ88jHI3SOrzhABIIG6XV4Td5yGkdJA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1709742270; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=gcRFJfkHpc9eFzn2459OTlv2kv0Kf8teT3QXc/xbbzw=; b=d/lLEbhaWEseTAnFs9QIlNHJRoEbFDUl89DSG8RZrrru5SCGkJ02F2tw5eucBCLYn1WtCd BeJ/bLEp0ThhzK+I2K5PPiSsAQHayTBkD3/qamDEW1jfq31yzeztV7BY+4/0JpKdQiDUH1 4oAZG1EvUSZmlU8P8BStzySV3XHdAqNEAzuxAFhZBZKRrVEjg2LizEZ1l7I8wsL1pIfeIP cU+fTDHyQnsV5XpCmZXUmPs7lMJfn69njfQcAranFZNdSmPYzqowv+RddPLuuJFnyUt5AZ nozpgkz+ZMBOXCzD4fPF8eC8KDNgIo5YD58gW6OWtdtujK0O6r0enXrdzIt1FA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4Tqd721J4ZzxY5; Wed, 6 Mar 2024 16:24:30 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 426GOUUU025068; Wed, 6 Mar 2024 16:24:30 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 426GOUX9025065; Wed, 6 Mar 2024 16:24:30 GMT (envelope-from git) Date: Wed, 6 Mar 2024 16:24:30 GMT Message-Id: <202403061624.426GOUX9025065@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling =?utf-8?Q?Sm=C3=B8rgrav?= Subject: git: 8427d94ce056 - main - tarfs: Improve validation of numeric fields. List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 8427d94ce05682abb6c75e2a27c8c497962c0dc5 Auto-Submitted: auto-generated The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=8427d94ce05682abb6c75e2a27c8c497962c0dc5 commit 8427d94ce05682abb6c75e2a27c8c497962c0dc5 Author: Dag-Erling Smørgrav AuthorDate: 2024-03-06 16:13:51 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2024-03-06 16:13:51 +0000 tarfs: Improve validation of numeric fields. MFC after: 3 days Sponsored by: Juniper Networks, Inc. Sponsored by: Klara, Inc. Reviewed by: sjg, allanjude Differential Revision: https://reviews.freebsd.org/D44166 --- sys/fs/tarfs/tarfs_vfsops.c | 143 ++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 66 deletions(-) diff --git a/sys/fs/tarfs/tarfs_vfsops.c b/sys/fs/tarfs/tarfs_vfsops.c index e45503373793..df8ad240d032 100644 --- a/sys/fs/tarfs/tarfs_vfsops.c +++ b/sys/fs/tarfs/tarfs_vfsops.c @@ -113,58 +113,46 @@ static const char *tarfs_opts[] = { }; /* - * Reads a len-width signed octal number from strp. Returns the value. - * XXX Does not report errors. + * Reads a len-width signed octal number from strp. Returns 0 on success + * and non-zero on error. */ -static int64_t -tarfs_str2octal(const char *strp, size_t len) +static int +tarfs_str2octal(const char *strp, size_t len, int64_t *num) { int64_t val; size_t idx; int sign; - /* - * Skip leading spaces or tabs. - * XXX why? POSIX requires numeric fields to be 0-padded. - */ - for (idx = 0; idx < len; idx++) - if (strp[idx] != ' ' && strp[idx] != '\t') - break; - - if (idx == len) - return (0); - + idx = 0; if (strp[idx] == '-') { sign = -1; idx++; - } else + } else { sign = 1; + } val = 0; - for (; idx < len; idx++) { + for (; idx < len && strp[idx] != '\0' && strp[idx] != ' '; idx++) { if (strp[idx] < '0' || strp[idx] > '7') - break; + return (EINVAL); val <<= 3; - val += (strp[idx] - '0'); - - /* Truncate on overflow */ - if (val > INT64_MAX / 8) { - val = INT64_MAX; - break; - } + val += strp[idx] - '0'; + if (val > INT64_MAX / 8) + return (ERANGE); } - return (sign > 0) ? val : -val; + *num = val * sign; + return (0); } /* * Reads a len-byte extended numeric value from strp. The first byte has * bit 7 set to indicate the format; the remaining 7 bits + the (len - 1) * bytes that follow form a big-endian signed two's complement binary - * number. Returns the value. XXX Does not report errors. + * number. Returns 0 on success and non-zero on error; */ -static int64_t -tarfs_str2base256(const char *strp, size_t len) +static int +tarfs_str2base256(const char *strp, size_t len, int64_t *num) { int64_t val; size_t idx; @@ -183,37 +171,27 @@ tarfs_str2base256(const char *strp, size_t len) for (idx = 1; idx < len; idx++) { val <<= 8; val |= (0xff & (int64_t)strp[idx]); - - /* Truncate on overflow and underflow */ - if (val > INT64_MAX / 256) { - val = INT64_MAX; - break; - } else if (val < INT64_MAX / 256) { - val = INT64_MIN; - break; - } + if (val > INT64_MAX / 256 || val < INT64_MIN / 256) + return (ERANGE); } - return (val); + *num = val; + return (0); } /* * Read a len-byte numeric field from strp. If bit 7 of the first byte it * set, assume an extended numeric value (signed two's complement); * otherwise, assume a signed octal value. - * - * XXX practically no error checking or handling */ -static int64_t -tarfs_str2int64(const char *strp, size_t len) +static int +tarfs_str2int64(const char *strp, size_t len, int64_t *num) { - if (len < 1) - return (0); - + return (EINVAL); if ((strp[0] & 0x80) != 0) - return (tarfs_str2base256(strp, len)); - return (tarfs_str2octal(strp, len)); + return (tarfs_str2base256(strp, len, num)); + return (tarfs_str2octal(strp, len, num)); } /* @@ -521,42 +499,47 @@ again: } /* get standard attributes */ - num = tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode)); - if (num < 0 || num > (S_IFMT|ALLPERMS)) { + if (tarfs_str2int64(hdrp->mode, sizeof(hdrp->mode), &num) != 0 || + num < 0 || num > (S_IFMT|ALLPERMS)) { TARFS_DPF(ALLOC, "%s: invalid file mode at %zu\n", __func__, TARFS_BLOCKSIZE * (blknum - 1)); mode = S_IRUSR; } else { mode = num & ALLPERMS; } - 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", + if (tarfs_str2int64(hdrp->uid, sizeof(hdrp->uid), &num) != 0 || + num < 0 || num > UID_MAX) { + TARFS_DPF(ALLOC, "%s: invalid UID 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", + if (tarfs_str2int64(hdrp->gid, sizeof(hdrp->gid), &num) != 0 || + num < 0 || num > GID_MAX) { + TARFS_DPF(ALLOC, "%s: invalid GID 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", + if (tarfs_str2int64(hdrp->size, sizeof(hdrp->size), &num) != 0 || + num < 0) { + TARFS_DPF(ALLOC, "%s: invalid size at %zu\n", + __func__, TARFS_BLOCKSIZE * (blknum - 1)); + error = EINVAL; + goto bad; + } + sz = num; + if (tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime), &num) != 0) { + TARFS_DPF(ALLOC, "%s: invalid modification time at %zu\n", __func__, TARFS_BLOCKSIZE * (blknum - 1)); error = EINVAL; goto bad; - } else { - sz = num; } - mtime = tarfs_str2int64(hdrp->mtime, sizeof(hdrp->mtime)); + mtime = num; rdev = NODEV; TARFS_DPF(ALLOC, "%s: [%c] %zu @%jd %o %d:%d\n", __func__, hdrp->typeflag[0], sz, (intmax_t)mtime, mode, uid, gid); @@ -772,16 +755,44 @@ again: parent, &tnp); break; case TAR_TYPE_BLOCK: - major = tarfs_str2int64(hdrp->major, sizeof(hdrp->major)); - minor = tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor)); + if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 || + num < 0 || num > INT_MAX) { + TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n", + __func__, (int)namelen, name); + error = EINVAL; + goto bad; + } + major = num; + if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 || + num < 0 || num > INT_MAX) { + TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n", + __func__, (int)namelen, name); + error = EINVAL; + goto bad; + } + minor = num; rdev = makedev(major, minor); error = tarfs_alloc_node(tmp, namep, sep - namep, VBLK, 0, 0, mtime, uid, gid, mode, flags, NULL, rdev, parent, &tnp); break; case TAR_TYPE_CHAR: - major = tarfs_str2int64(hdrp->major, sizeof(hdrp->major)); - minor = tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor)); + if (tarfs_str2int64(hdrp->major, sizeof(hdrp->major), &num) != 0 || + num < 0 || num > INT_MAX) { + TARFS_DPF(ALLOC, "%s: %.*s: invalid device major\n", + __func__, (int)namelen, name); + error = EINVAL; + goto bad; + } + major = num; + if (tarfs_str2int64(hdrp->minor, sizeof(hdrp->minor), &num) != 0 || + num < 0 || num > INT_MAX) { + TARFS_DPF(ALLOC, "%s: %.*s: invalid device minor\n", + __func__, (int)namelen, name); + error = EINVAL; + goto bad; + } + minor = num; rdev = makedev(major, minor); error = tarfs_alloc_node(tmp, namep, sep - namep, VCHR, 0, 0, mtime, uid, gid, mode, flags, NULL, rdev,