[LTP] [PATCH] syscalls/mknod03: Convert to new API
rbm@suse.com
rbm@suse.com
Thu Mar 20 14:51:23 CET 2025
Hello Andrea!
Thanks for reviewing, I'll send a v2 addressing your points later :)
- Ricardo.
On Thu Mar 20, 2025 at 9:42 AM -03, Andrea Cervesato wrote:
> Hi Ricardo,
>
> On 3/19/25 18:37, Ricardo B. Marlière via ltp wrote:
>> From: Ricardo B. Marlière <rbm@suse.com>
>>
>> Signed-off-by: Ricardo B. Marlière <rbm@suse.com>
>> ---
>> testcases/kernel/syscalls/mknod/mknod03.c | 320 +++++-------------------------
>> 1 file changed, 50 insertions(+), 270 deletions(-)
>>
>> diff --git a/testcases/kernel/syscalls/mknod/mknod03.c b/testcases/kernel/syscalls/mknod/mknod03.c
>> index 7ecadb5b37c3ab7eded90aa8a6d1e27f07236b1f..204df593969e0d5145aec479d4edc2aafe46ee66 100644
>> --- a/testcases/kernel/syscalls/mknod/mknod03.c
>> +++ b/testcases/kernel/syscalls/mknod/mknod03.c
>> @@ -1,296 +1,76 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> /*
>> - *
>> - * Copyright (c) International Business Machines Corp., 2001
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License as published by
>> - * the Free Software Foundation; either version 2 of the License, or
>> - * (at your option) any later version.
>> - *
>> - * This program is distributed in the hope that it will be useful,
>> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
>> - * the GNU General Public License for more details.
>> - *
>> - * You should have received a copy of the GNU General Public License
>> - * along with this program; if not, write to the Free Software
>> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
>> + * Copyright (c) International Business Machines Corp., 2001
>> + * 07/2001 Ported by Wayne Boyer
>> + * Copyright (c) 2025 SUSE LLC Ricardo B. Marlière <rbm@suse.com>
>> */
>>
>> -/*
>> - * Test Name: mknod03
>> - *
>> - * Test Description:
>> - * Verify that mknod(2) succeeds when used to create a filesystem
>> - * node with set group-ID bit set on a directory with set group-ID bit set.
>> - * The node created should have set group-ID bit set and its gid should be
>> - * equal to the effective gid of the process.
>> - *
>> - * Expected Result:
>> - * mknod() should return value 0 on success and node created should have
>> - * set group-ID bit set, its gid should be equal to the effective gid of
>> - * the process.
>> - *
>> - * Algorithm:
>> - * Setup:
>> - * Setup signal handling.
>> - * Create temporary directory.
>> - * Pause for SIGUSR1 if option specified.
>> - *
>> - * Test:
>> - * Loop if the proper options are given.
>> - * Execute system call
>> - * Check return code, if system call failed (return=-1)
>> - * Log the errno and Issue a FAIL message.
>> - * Otherwise,
>> - * Verify the Functionality of system call
>> - * if successful,
>> - * Issue Functionality-Pass message.
>> - * Otherwise,
>> - * Issue Functionality-Fail message.
>> - * Cleanup:
>> - * Print errno log and/or timing stats if options given
>> - * Delete the temporary directory created.
>> - *
>> - * Usage: <for command-line>
>> - * mknod03 [-c n] [-f] [-i n] [-I x] [-P x] [-t]
>> - * where, -c n : Run n copies concurrently.
>> - * -f : Turn off functionality Testing.
>> - * -i n : Execute test n times.
>> - * -I x : Execute test for x seconds.
>> - * -P x : Pause for x seconds between iterations.
>> - * -t : Turn on syscall timing.
>> - *
>> - * HISTORY
>> - * 07/2001 Ported by Wayne Boyer
>> - *
>> - * RESTRICTIONS:
>> - * This test should be run by 'super-user' (root) only.
>> - *
>> +/*\
>> + * Verify that mknod succeeds when used to create a filesystem node with
>> + * set-group-ID bit set on a directory with set-group-ID bit set. The node
>> + * created should have set-group-ID bit set and its gid should be equal to
>> + * the "nobody" gid.
>> */
>>
>> -#include <stdio.h>
>> -#include <stdlib.h>
>> -#include <unistd.h>
>> -#include <errno.h>
>> -#include <string.h>
>> -#include <signal.h>
>> +#include "tst_uid.h"
>> #include <pwd.h>
>> -#include <sys/types.h>
>> -#include <sys/stat.h>
>> -
>> -#include "test.h"
>> -#include "safe_macros.h"
>> -
>> -#define LTPUSER "nobody"
>> -#define MODE_RWX S_IFIFO | S_IRWXU | S_IRWXG | S_IRWXO
>> -#define MODE_SGID S_IFIFO | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO
>> -#define DIR_TEMP "testdir_3"
>> -#define TNODE "tnode_%d"
>> -
>> -struct stat buf; /* struct. to hold stat(2) o/p contents */
>> -struct passwd *user1; /* struct. to hold getpwnam(3) o/p contents */
>> -
>> -char *TCID = "mknod03";
>> -int TST_TOTAL = 1;
>> -char node_name[PATH_MAX]; /* buffer to hold node name created */
>> +#include "tst_test.h"
>>
>> -gid_t group1_gid, group2_gid, mygid; /* user and process group id's */
>> -uid_t save_myuid, user1_uid; /* user and process user id's */
>> -pid_t mypid; /* process id */
>> +#define MODE_RWX 0777
>> +#define MODE_SGID (S_ISGID | 0777)
>> +#define TST_DIR "tst_dir"
>> +#define TST_NODE TST_DIR "/tst_node"
>>
>> -void setup(); /* setup function for the test */
>> -void cleanup(); /* cleanup function for the test */
>> +static uid_t orig_uid, nobody_uid;
>> +static gid_t nobody_gid, free_gid;
>>
>> -int main(int ac, char **av)
>> +void run(void)
>> {
>> - int lc;
>> - int fflag;
>> + struct stat buf;
>>
>> - tst_parse_opts(ac, av, NULL, NULL);
>> + SAFE_MKDIR(TST_DIR, MODE_RWX);
>> + SAFE_CHOWN(TST_DIR, nobody_uid, free_gid);
>> + SAFE_CHMOD(TST_DIR, MODE_SGID);
>>
>> - setup();
>> + SAFE_STAT(TST_DIR, &buf);
>>
>> - for (lc = 0; TEST_LOOPING(lc); lc++) {
>> + if (!(buf.st_mode & S_ISGID))
>> + tst_brk(TBROK, "%s: setgid bit not set", TST_DIR);
>>
>> - tst_count = 0;
>> -
>> - /*
>> - * Attempt to create a filesystem node with group id (sgid)
>> - * bit set on a directory with group id (sgid) bit set
>> - * such that, the node created by mknod(2) should have
>> - * group id (sgid) bit set and node's gid should be equal
>> - * to that of effective gid of the process.
>> - */
>> - TEST(mknod(node_name, MODE_SGID, 0));
>> -
>> - /* Check return code from mknod(2) */
>> - if (TEST_RETURN == -1) {
>> - tst_resm(TFAIL, "mknod(%s, %#o, 0) failed, errno=%d : "
>> - "%s", node_name, MODE_SGID, TEST_ERRNO,
>> - strerror(TEST_ERRNO));
>> - continue;
>> - }
>> - /* Set the functionality flag */
>> - fflag = 1;
>> -
>> - /* Check for node's creation */
>> - if (stat(node_name, &buf) < 0) {
>> - tst_resm(TFAIL, "stat() of %s failed, errno:%d",
>> - node_name, TEST_ERRNO);
>> - /* unset functionality flag */
>> - fflag = 0;
>> - }
>> -
>> - /*
>> - * Skip S_ISGID check
>> - * 0fa3ecd87848 ("Fix up non-directory creation in SGID directories")
>> - * clears S_ISGID for files created by non-group members
>> - */
>> -
>> - /* Verify group ID */
>> - if (buf.st_gid != group2_gid) {
>> - tst_resm(TFAIL, "%s: Incorrect group",
>> - node_name);
>> - /* unset flag as functionality fails */
>> - fflag = 0;
>> - }
>> - if (fflag) {
>> - tst_resm(TPASS, "Functionality of mknod(%s, "
>> - "%#o, 0) successful",
>> - node_name, MODE_SGID);
>> - }
>> -
>> - /* Remove the node for the next go `round */
>> - if (unlink(node_name) == -1) {
>> - tst_resm(TWARN, "unlink(%s) failed, errno:%d %s",
>> - node_name, errno, strerror(errno));
>> - }
>> - }
>> + SAFE_SETGID(nobody_gid);
>> + SAFE_SETREUID(-1, nobody_uid);
>>
>> - /* Change the directory back to temporary directory */
>> - SAFE_CHDIR(cleanup, "..");
>> + SAFE_MKNOD(TST_NODE, MODE_SGID, 0);
>> + SAFE_STAT(TST_NODE, &buf);
> The base directory can be created inside the setup like it was before,
> so in the run() function we only have the basic mknod() check.
>>
>> /*
>> - * Invoke cleanup() to delete the test directories created
>> - * in the setup() and exit main().
>> + * CVE 2018-13405 (privilege escalation using setgid bit) has its
>> + * own test, skip setgid check here
>> */
>> - cleanup();
>> -
>> - tst_exit();
>> -}
>> -
>> -/*
>> - * setup(void) - performs all ONE TIME setup for this test.
>> - * Exit the test program on receipt of unexpected signals.
>> - * Create a temporary directory used to hold test directories created
>> - * and change the directory to it.
>> - * Verify that pid of process executing the test is root.
>> - * Create a test directory on temporary directory and set the ownership
>> - * of test directory to guest user and process, change mode permissions
>> - * to set group id bit on it.
>> - * Set the effective uid/gid of the process to that of guest user.
>> - */
>> -void setup(void)
>> -{
>> - tst_require_root();
>> -
>> - /* Capture unexpected signals */
>> - tst_sig(NOFORK, DEF_HANDLER, cleanup);
>> -
>> - TEST_PAUSE;
>> -
>> - /* Make a temp dir and cd to it */
>> - tst_tmpdir();
>> -
>> - /* fix permissions on the tmpdir */
>> - if (chmod(".", 0711) != 0) {
>> - tst_brkm(TBROK, cleanup, "chmod() failed");
>> + if (buf.st_gid != free_gid) {
>> + tst_res(TFAIL, "%s: incorrect group", TST_NODE);
>> + return;
>> }
> If we add return here we won't be able to cleanup the testing folder.
> Also we only need:
>
> TST_EXP_EQ_LI(buf.st_gid, free_gid);
>
>>
>> - /* Save the real user id of the current test process */
>> - save_myuid = getuid();
>> - /* Save the process id of the current test process */
>> - mypid = getpid();
>> -
>> - /* Get the node name to be created in the test */
>> - sprintf(node_name, TNODE, mypid);
>> + tst_res(TPASS, "mknod(%s, %#o, 0): success", TST_NODE, MODE_SGID);
>>
>> - /* Get the uid/gid of ltpuser user */
>> - if ((user1 = getpwnam(LTPUSER)) == NULL) {
>> - tst_brkm(TBROK, cleanup, "%s not in /etc/passwd", LTPUSER);
>> - }
>> - user1_uid = user1->pw_uid;
>> - group1_gid = user1->pw_gid;
>> -
>> - /* Get the effective group id of the test process */
>> - group2_gid = getegid();
>> -
>> - /*
>> - * Create a test directory under temporary directory with the
>> - * specified mode permissions, with uid/gid set to that of guest
>> - * user and the test process.
>> - */
>> - SAFE_MKDIR(cleanup, DIR_TEMP, MODE_RWX);
>> - SAFE_CHOWN(cleanup, DIR_TEMP, user1_uid, group2_gid);
>> - SAFE_CHMOD(cleanup, DIR_TEMP, MODE_SGID);
>> -
>> - /*
>> - * Verify that test directory created with expected permission modes
>> - * and ownerships.
>> - */
>> - SAFE_STAT(cleanup, DIR_TEMP, &buf);
>> -
>> - /* Verify modes of test directory */
>> - if (!(buf.st_mode & S_ISGID)) {
>> - tst_brkm(TBROK, cleanup,
>> - "%s: Incorrect modes, setgid bit not set", DIR_TEMP);
>> - }
>> -
>> - /* Verify group ID of test directory */
>> - if (buf.st_gid != group2_gid) {
>> - tst_brkm(TBROK, cleanup, "%s: Incorrect group", DIR_TEMP);
>> - }
>> -
>> - /*
>> - * Set the effective group id and user id of the test process
>> - * to that of guest user (nobody)
>> - */
>> - SAFE_SETGID(cleanup, group1_gid);
>> - if (setreuid(-1, user1_uid) < 0) {
>> - tst_brkm(TBROK, cleanup,
>> - "Unable to set process uid to that of ltp user");
>> - }
>> -
>> - /* Save the real group ID of the current process */
>> - mygid = getgid();
>> -
>> - /* Change directory to DIR_TEMP */
>> - SAFE_CHDIR(cleanup, DIR_TEMP);
>> + SAFE_SETREUID(-1, orig_uid);
> This is not needed if we move the folder and user configuration inside
> setup().
>> + tst_purge_dir(tst_tmpdir_path());
> Then we only need SAFE_UNLINK(TST_NODE).
>> }
>>
>> -/*
>> - * cleanup() - Performs all ONE TIME cleanup for this test at
>> - * completion or premature exit.
>> - * Print test timing stats and errno log if test executed with options.
>> - * Restore the real/effective user id of the process changed during
>> - * setup().
>> - * Remove temporary directory and sub-directories/files under it
>> - * created during setup().
>> - * Exit the test program with normal exit code.
>> - */
>> -void cleanup(void)
>> +void setup(void)
>> {
>> + struct passwd *ltpuser = SAFE_GETPWNAM("nobody");
>>
>> - /*
>> - * Restore the effective uid of the process changed in the
>> - * setup().
>> - */
>> - if (setreuid(-1, save_myuid) < 0) {
>> - tst_brkm(TBROK, NULL,
>> - "resetting process real/effective uid failed");
>> - }
>> -
>> - tst_rmdir();
>> -
>> + orig_uid = getuid();
>> + nobody_uid = ltpuser->pw_uid;
>> + nobody_gid = ltpuser->pw_gid;
>> + free_gid = tst_get_free_gid(nobody_gid);
>> }
>> +
>> +static struct tst_test test = {
>> + .setup = setup,
>> + .test_all = run,
>> + .needs_root = 1,
>> + .needs_tmpdir = 1,
>> +};
>>
>> ---
>> base-commit: ae279276535fc9ca3117a12c4bba0029d792904e
>> change-id: 20250319-conversions-mknod-cd8cb407d24d
>>
>> Best regards,
> Kind regards,
> Andrea Cervesato
More information about the ltp
mailing list