[LTP] [PATCH v1] security/dirtyc0w_shmem: Add new test for CVE-2022-2590

David Hildenbrand david@redhat.com
Mon Nov 21 15:59:28 CET 2022


On 18.11.22 14:29, Cyril Hrubis wrote:
> Hi!

Hi Cyril,

thanks for your review!

[...]

>> +
>> +/*
>> + * CVE-2022-2590
>> + *
>> + * This is a regression test for a write race that allows unprivileged programs
>> + * to change readonly files located on tmpfs/shmem on the system.
>> + *
>> + * Fixed by:
>> + *
>> + *   commit 5535be3099717646781ce1540cf725965d680e7b
>> + *   Author: David Hildenbrand <david@redhat.com>
>> + *   Date:   Tue Aug 9 22:56:40 2022 +0200
>> + *
>> + *   mm/gup: fix FOLL_FORCE COW security issue and remove FOLL_COW
>> + */
> 
> This is not a proper documentation comment. We do have a system in place
> that picks up ascii-doc formatted documentation comments and builds a
> documentation based on that.
> 
> The documentation comment has to start with:
> 
> /*\
>   * [Description]
>   *
> 
> 
> Also the CVE and kernel commit are picked from tags when documentation
> is build, so they do not need to be part of the documentation comment.
> 

I used dirtyc0w.c as orientation, which probably wasn't a good idea.

I'll use a simple

/*\
  * [Description]
  *
  * This is a regression test for a write race that allowed unprivileged programs
  * to change readonly files located on tmpfs/shmem on the system using
  * userfaultfd "minor fault handling" (CVE-2022-2590).
  */

>> +#include "config.h"
>> +
>> +#include <pthread.h>
>> +#include <unistd.h>
>> +#include <sys/stat.h>
>> +#include <string.h>
>> +#include <stdlib.h>
>> +#include <stdbool.h>
>> +#include <pwd.h>
>> +
>> +#include "tst_test.h"
>> +
>> +#define TMP_DIR "tmp_dirtyc0w_shmem"
>> +#define TEST_FILE TMP_DIR"/testfile"
>> +#define STR "this is not a test\n"
>> +
>> +static uid_t nobody_uid;
>> +static gid_t nobody_gid;
>> +static bool child_early_exit;
> 
> Anything that is changed from signal handler should be volatile
> otherwise it may end up optimized out.

Ack.

> 
>> +static void sighandler(int sig)
>> +{
>> +	if (sig == SIGCHLD) {
>> +		child_early_exit = true;
>> +		return;
>> +	}
>> +
>> +	_exit(0);
>> +}
>> +
>> +static void setup(void)
>> +{
>> +	struct passwd *pw;
>> +
>> +	umask(0);
>> +
>> +	pw = SAFE_GETPWNAM("nobody");
>> +
>> +	nobody_uid = pw->pw_uid;
>> +	nobody_gid = pw->pw_gid;
>> +
>> +	SAFE_MKDIR(TMP_DIR, 0664);
>> +	SAFE_MOUNT(TMP_DIR, TMP_DIR, "tmpfs", 0, NULL);
>> +}
>> +
>> +static void dirtyc0w_shmem_test(void)
>> +{
>> +	bool failed = false;
>> +	int fd, pid;
>> +	char c;
>> +
>> +	fd = SAFE_OPEN(TEST_FILE, O_WRONLY|O_CREAT|O_EXCL, 0444);
>> +	SAFE_WRITE(SAFE_WRITE_ALL, fd, STR, sizeof(STR)-1);
>> +	SAFE_CLOSE(fd);
> 
> SAFE_FILE_PRINTF() ?

AFAIU, that would mean

SAFE_FILE_PRINTF(TEST_FILE, "%s", STR);
SAFE_CHMOD(TEST_FILE, 0444);

I'll give that a churn.

> 
>> +	pid = SAFE_FORK();
>> +	if (!pid) {
>> +		SAFE_SETGID(nobody_gid);
>> +		SAFE_SETUID(nobody_uid);
>> +		SAFE_EXECLP("dirtyc0w_shmem_child", "dirtyc0w_shmem_child", NULL);
>> +	}
>> +
>> +	TST_CHECKPOINT_WAIT(0);
>> +
>> +	SAFE_SIGNAL(SIGCHLD, sighandler);
>> +	do {
>> +		usleep(100000);
>> +
>> +		SAFE_FILE_SCANF(TEST_FILE, "%c", &c);
>> +
>> +		if (c != 't') {
>> +			failed = true;
>> +			break;
>> +		}
>> +	} while (tst_remaining_runtime() && !child_early_exit);
>> +	SAFE_SIGNAL(SIGCHLD, SIG_DFL);
>> +
>> +	SAFE_KILL(pid, SIGUSR1);
>> +	tst_reap_children();
>> +	SAFE_UNLINK(TEST_FILE);
>> +
>> +	if (child_early_exit)
>> +		tst_res(TINFO, "Early child process exit");
> 
> This will potentionally trigger library detection for buggy tests. A
> test has to report a result in all situations.
> 
> I suppose that this will only happen if child process did exit with
> TBROK or TCONF. If that is the case it should be fine.

Yes, exactly. The child process will only quit in case it fails with
TBROK / TCONF. In all other cases, it will simply run until we zap it.

> 
>> +	else if (failed)
>> +		tst_res(TFAIL, "Bug reproduced!");
>> +	else
>> +		tst_res(TPASS, "Bug not reproduced");
>> +}
>> +
>> +static void cleanup(void)
>> +{
>> +	SAFE_UMOUNT(TMP_DIR);
>> +}
>> +
>> +static struct tst_test test = {
>> +	.needs_checkpoints = 1,
>> +	.forks_child = 1,
>> +	.needs_root = 1,
> 
> We are missing .needs_tmpdir flag here, otherwise the test will create
> the the TMPDIR in PWD which may fail in certain setups.

Thanks!


-- 
Thanks,

David / dhildenb



More information about the ltp mailing list