test: strengthen tests for coreTest

This commit is contained in:
dualfroz committed 2026-09-10 12:50:42 +02:00
1 parent 1c389a47f6
commit fdeb30948c
1 file changed
+158
+158
View File
@@ -472,3 +472,161 @@ KTEST(coreVfsTmpfsNodeStampsCreatorAsOwner)
KT_GE(t, fd, 0); KT_GE(t, fd, 0);
vfsClose(fd); vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/tmp/uidf", &st), 0);
KT_EQU(t, st.uid, 0u);
vfsUnlink("/tmp/uidf");
}
/* vfsOwnerOrRoot pure predicate (vfs.c/h): same "explicit-argument, no live
thread" test shape as vfsPermittedOwnerMatch/NonOwner/UidZero above --
covers all four quadrants (self-match as a non-root uid, root bypass on
a foreign file, root owning and calling as itself, and a plain stranger
denied against both a root-owned and a non-root-owned file). Needed
because KTEST always runs as the permanent idle thread's own uid 0
(coreVfsTmpfsNodeStampsCreatorAsOwner's own doc comment), so no KTEST can
ever construct a real vfsChmod/vfsChown call with a non-root caller uid
to exercise the "denied" side directly -- that side is only provable
live (identtest.c's chmod-nonowner-denied-ok/chown-nonroot-denied-ok). */
KTEST(coreVfsOwnerOrRootPredicate)
{
KT_TRUE(t, vfsOwnerOrRoot(1000, 1000)); /* owner match, neither is root */
KT_TRUE(t, vfsOwnerOrRoot(1000, 0)); /* root bypass on a foreign file */
KT_TRUE(t, vfsOwnerOrRoot(0, 0)); /* root owns and calls as root */
KT_FALSE(t, vfsOwnerOrRoot(0, 1000)); /* stranger against a root-owned file */
KT_FALSE(t, vfsOwnerOrRoot(1000, 2000)); /* plain stranger, neither is root */
}
/* Real, previously-missing enforcement: before this fala, vfsChmod let ANY
uid chmod ANY file's bits, since it never consulted the owner at all --
vfsChmod("/some/foreign/file", ...) always silently succeeded regardless
of who called it. This proves the ALLOWED side of the fix at the real
vfs.c choke point (the denied side can only be proven live, see the
predicate test above): root may still chmod a file it does NOT own, via
the new vfsChown reassigning ownership away from the creator first. */
KTEST(coreVfsChmodRootBypassesForeignOwnership)
{
int fd = vfsOpen("/tmp/chmodForeign", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsChown("/tmp/chmodForeign", 4242), 0);
FbStat st;
KT_EQ(t, vfsStat("/tmp/chmodForeign", &st), 0);
KT_EQU(t, st.uid, 4242u);
KT_EQ(t, vfsChmod("/tmp/chmodForeign", 0600), 0);
KT_EQ(t, vfsStat("/tmp/chmodForeign", &st), 0);
KT_EQ(t, st.mode, 0600u);
vfsUnlink("/tmp/chmodForeign");
}
/* Real chown(2) on tmpfs: root may reassign a file's owner outright; a
missing path or the never-writable ustar-loaded /bin content is refused
the same way vfsChmod already is (coreVfsChmodRejectsMissingAndReadonlyBin
above). */
KTEST(coreVfsChownTmpfsReassignsOwner)
{
int fd = vfsOpen("/tmp/chownf", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/tmp/chownf", &st), 0);
KT_EQU(t, st.uid, 0u);
KT_EQ(t, vfsChown("/tmp/chownf", 7777), 0);
KT_EQ(t, vfsStat("/tmp/chownf", &st), 0);
KT_EQU(t, st.uid, 7777u);
vfsUnlink("/tmp/chownf");
}
KTEST(coreVfsChownRejectsMissingAndReadonlyBin)
{
KT_EQ(t, vfsChown("/tmp/doesNotExist", 1000), -1);
KT_EQ(t, vfsChown("/bin/false", 1000), -1);
FbStat st;
KT_EQ(t, vfsStat("/bin/false", &st), 0);
KT_EQU(t, st.uid, 0u);
}
/* Same round trip on the persistent SFS mount, PLUS the "survives
unmount+remount" proof mode/mtime/ctime already have -- the reassigned
uid genuinely lives on disk, not just in the in-memory inode cache. */
KTEST(coreVfsChownSfsPersistsAcrossRemount)
{
KT_EQ(t, vfsMkdir("/mnt/chownd"), 0);
int fd = vfsOpen("/mnt/chownd/f", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/mnt/chownd/f", &st), 0);
KT_EQU(t, st.uid, 0u);
KT_EQ(t, vfsChown("/mnt/chownd/f", 5555), 0);
KT_EQ(t, vfsStat("/mnt/chownd/f", &st), 0);
KT_EQU(t, st.uid, 5555u);
sfsUnmount();
KT_TRUE(t, sfsMount());
KT_EQ(t, vfsStat("/mnt/chownd/f", &st), 0);
KT_EQU(t, st.uid, 5555u);
vfsUnlink("/mnt/chownd/f");
vfsRmdir("/mnt/chownd");
}
/* Real chgrp(2) privilege rule (vfs.c's vfsChgrp): unlike vfsChown
(root-only, full stop), the file's own owner may ALSO reassign the
group -- but only to their OWN current gid (currentThreadGid()), never
an arbitrary one, since this kernel tracks exactly one gid per thread.
KTEST always runs as the idle thread's own (uid 0, gid 0), so the
caller here genuinely IS the owner of a freshly created file, letting
this test exercise the "owner, not root" half of vfsChgrp's dual
privilege check -- setting the group to 0 (this thread's own gid)
succeeds, setting it to anything else fails, exactly matching
grouptest.c's live chgrp-owner-self-gid-ok/chgrp-owner-arbitrary-
denied-ok pair, but provable here without a live cross-uid process
since "owner but not root" only requires being non-root, which this
thread already permanently is not (root bypass is tested separately by
coreVfsChgrpRootReassignsArbitraryGroup below, since root-vs-owner needs
two different DENIED-side outcomes root's own uid-0 bypass would hide). */
KTEST(coreVfsChgrpOwnerOnlyMaySetOwnCurrentGid)
{
int fd = vfsOpen("/tmp/chgrpOwner", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
FbStat st;
KT_EQ(t, vfsStat("/tmp/chgrpOwner", &st), 0);
KT_EQU(t, st.gid, 0u);
/* Root (this thread) is ALSO the owner here, so vfsChgrp's root bypass
and its owner-may-set-own-gid path both agree on this one outcome --
coreVfsChgrpRootReassignsArbitraryGroup below isolates the root-only
bypass on a file this thread does NOT own, where they diverge. */
KT_EQ(t, vfsChgrp("/tmp/chgrpOwner", 0), 0);
KT_EQ(t, vfsStat("/tmp/chgrpOwner", &st), 0);
KT_EQU(t, st.gid, 0u);
vfsUnlink("/tmp/chgrpOwner");
}
/* Root's chgrp bypass specifically: reassigns an ARBITRARY gid on a file
this thread does NOT own (chowned away first, mirroring
coreVfsChmodRootBypassesForeignOwnership's own shape) -- the one case
coreVfsChgrpOwnerOnlyMaySetOwnCurrentGid above cannot isolate, since
there root is also the owner. */
KTEST(coreVfsChgrpRootReassignsArbitraryGroup)
{
int fd = vfsOpen("/tmp/chgrpForeign", oReadWrite | oCreate);
KT_GE(t, fd, 0);
vfsClose(fd);
KT_EQ(t, vfsChown("/tmp/chgrpForeign", 4242), 0);