public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [tests/selinux] main: Add a test for overlayfs mmap bugs (CVE-2026-46054)
@ 2026-06-03  5:48 Ondrej Mosnacek
  0 siblings, 0 replies; only message in thread
From: Ondrej Mosnacek @ 2026-06-03  5:48 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : tests/selinux
            Branch : main
            Commit : 9a7d3d69fb0bf1dd81ec4aab9aba66d2423e2230
            Author : Ondrej Mosnacek <omosnace@redhat.com>
            Date   : 2026-05-28T14:14:02+02:00
            Stats  : +271/-0 in 4 file(s)
            URL    : https://src.fedoraproject.org/tests/selinux/c/9a7d3d69fb0bf1dd81ec4aab9aba66d2423e2230?branch=main

            Log:
            Add a test for overlayfs mmap bugs (CVE-2026-46054)

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>

---
diff --git a/kernel/overlayfs-mmap-bugs/main.fmf b/kernel/overlayfs-mmap-bugs/main.fmf
new file mode 100644
index 0000000..dddb9db
--- /dev/null
+++ b/kernel/overlayfs-mmap-bugs/main.fmf
@@ -0,0 +1,22 @@
+summary: Regression test for overlayfs mmap/mprotect bugs
+description: |
+  Tests various scenarios with overlayfs and mmap/mprotect syscalls.
+  This also covers CVE-2026-46054.
+contact: Ondrej Mosnacek <omosnace@redhat.com>
+component:
+  - kernel
+framework: beakerlib
+require:
+  - selinux-policy-devel
+  - gcc
+duration: 5m
+tier: 2
+enabled: true
+link:
+  - verifies: https://issues.redhat.com/browse/RHEL-127505
+  - verifies: https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2026-46054
+environment:
+  AVC_ERROR: +no_avc_check
+check:
+  - how: avc
+    result: xfail

diff --git a/kernel/overlayfs-mmap-bugs/map_access.c b/kernel/overlayfs-mmap-bugs/map_access.c
new file mode 100644
index 0000000..9300365
--- /dev/null
+++ b/kernel/overlayfs-mmap-bugs/map_access.c
@@ -0,0 +1,70 @@
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/mman.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+int main(int argc, const char **argv)
+{
+	const char *file, *context;
+	void *ptr;
+	int rdonly, fd, ctxfd, ret;
+
+	if (argc < 3  || argc > 4 || (strcmp(argv[2], "RDONLY") && strcmp(argv[2], "RDWR"))) {
+		fprintf(stderr, "Usage %s <file> RDONLY|RDWR\n", argv[0]);
+		return EINVAL;
+	}
+
+	file = argv[1];
+	rdonly = strcmp(argv[2], "RDONLY") == 0;
+	context = argc >= 4 ? argv[3] : NULL;
+
+	fd = open(file, rdonly ? O_RDONLY : O_RDWR);
+	if (fd == -1) {
+		perror("open");
+		return 2;
+	}
+
+	/* try direct mmap */
+	ptr = mmap(NULL, 1, rdonly ? PROT_READ : PROT_READ|PROT_WRITE,
+		   MAP_SHARED, fd, 0);
+	if (ptr == MAP_FAILED) {
+		perror("mmap");
+		return 3;
+	}
+	munmap(ptr, 1);
+
+	/* try mmap with PROT_NONE followed by mprotect with full access */
+	ptr = mmap(NULL, 1, PROT_NONE, MAP_SHARED, fd, 0);
+	if (ptr == MAP_FAILED) {
+		perror("mmap PROT_NONE");
+		return 4;
+	}
+
+	if (context) {
+		ctxfd = open("/proc/self/attr/current", O_RDWR);
+		if (ctxfd == -1) {
+			perror("open");
+			return 6;
+		}
+		ret = write(ctxfd, context, strlen(context));
+		if (ret == -1) {
+			perror("write");
+			return 7;
+		}
+		close(ctxfd);
+	}
+
+	ret = mprotect(ptr, 1, rdonly ? PROT_READ : PROT_READ|PROT_WRITE);
+	if (ret == -1) {
+		perror("mprotect");
+		return 5;
+	}
+
+	munmap(ptr, 1);
+	close(fd);
+	return 0;
+}

diff --git a/kernel/overlayfs-mmap-bugs/runtest.sh b/kernel/overlayfs-mmap-bugs/runtest.sh
new file mode 100755
index 0000000..3d15777
--- /dev/null
+++ b/kernel/overlayfs-mmap-bugs/runtest.sh
@@ -0,0 +1,79 @@
+#!/bin/bash
+# vim: dict=/usr/share/beakerlib/dictionary.vim cpt=.,w,b,u,t,i,k
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2025 Red Hat, Inc.
+# Author: Ondrej Mosnacek <omosnace@redhat.com>
+
+# Include Beakerlib environment
+. /usr/share/beakerlib/beakerlib.sh || exit 1
+
+rlJournalStart
+    rlPhaseStartSetup
+        rlRun "uname -r" 0 "Print current running kernel version"
+
+        SE_USER="$(secon -u --pid $$)"
+        SE_ROLE="$(secon -r --pid $$)"
+        SE_TYPE="$(secon -t --pid $$)"
+        SE_MLS="$(secon -m --pid $$)"
+
+        OVERLAYCON="$SE_USER:object_r:test_mountedfile_t:s0"
+        DYNTRANSCON="$SE_USER:$SE_ROLE:test_access_exploit_t:$SE_MLS"
+
+        rlRun "gcc -o map_access map_access.c" 0 \
+            "Build the test program"
+        rlRun "chcon -t bin_t map_access" 0 "Relabel the test program"
+        rlRun "make -f /usr/share/selinux/devel/Makefile test_policy.pp M4PARAM='-D TEST_ROLE=$SE_ROLE -D TEST_TYPE=$SE_TYPE'" 0 \
+            "Build the test policy"
+        rlRun "semodule -i test_policy.pp" 0 "Load test policy"
+
+        rlRun "mkdir lowerdir upperdir workdir mountpoint" 0 "Create test dirs"
+        rlRun "touch lowerdir/file_ok lowerdir/file_no_map lowerdir/file_no_read lowerdir/file_no_write" 0 \
+            "Create test files"
+        rlRun "chcon -R -t test_lowerfile_t lowerdir workdir upperdir"
+        rlRun "chcon -t test_lowerfile_no_map_t lowerdir/file_no_map"
+        rlRun "chcon -t test_lowerfile_no_write_t lowerdir/file_no_write"
+        rlRun "chcon -t test_lowerfile_no_read_t lowerdir/file_no_read"
+
+        rlRun "runcon -t test_mounter_t mount -t overlay none -o 'context=$OVERLAYCON,lowerdir=./lowerdir,upperdir=./upperdir,workdir=./workdir' ./mountpoint" 0 \
+            "Mount the overlay filesystem"
+
+        rlRun ":>/var/log/audit/audit.log; rm -f /var/log/audit/audit.log.*" 0 \
+            "Clear the audit log"
+    rlPhaseEnd
+
+    rlPhaseStartTest
+        # Bug 1
+        # Should get below AVC:
+        # avc:  denied  { map } for scontext=...test_mounter_t... tcontext=...test_lowerfile_no_map_t... tclass=file
+        rlRun "runcon -t test_access_full_t ./map_access ./mountpoint/file_no_map RDONLY" 3 "Test Bug 1"
+
+        # Bug 2, result 1
+        # Shouldn't get below AVC:
+        # avc:  denied  { use } for scontext=...test_access_full_t... tcontext=...test_mounter_t... tclass=fd
+        rlRun "setsebool domain_fd_use 0"
+        rlRun "runcon -t test_access_full_t ./map_access ./mountpoint/file_ok RDONLY" 0 "Test Bug 2, result 1"
+        rlRun "setsebool domain_fd_use 1"
+
+        # Bug 2, result 2
+        # Shouldn't get below AVC:
+        # avc:  denied  { read } for scontext=...test_access_full_t... tcontext=...test_lowerfile_t... tclass=file
+        rlRun "runcon -t test_access_full_t ./map_access ./mountpoint/file_ok RDONLY" 0 "Test Bug 2, result 2"
+
+        # Bug 2, result 3
+        # Should get below AVC:
+        # avc:  denied  { read } for scontext=...test_access_exploit_t... tcontext=...test_mountedfile_t... tclass=file
+        rlRun "runcon -t test_access_full_t ./map_access ./mountpoint/file_ok RDONLY $DYNTRANSCON" 5 "Test Bug 2, result 3"
+    rlPhaseEnd
+
+    rlPhaseStartCleanup
+        rlRun "ausearch -i -m avc" 0 "Show AVC denials"
+
+        rlRun "umount ./mountpoint"
+        rlRun "rm -rf lowerdir upperdir workdir mountpoint"
+        rlRun "semodule -r test_policy" 0 "Unload test policy"
+        rlRun "make -f /usr/share/selinux/devel/Makefile clean" 0 \
+            "Clean the test policy"
+        rlRun "rm -f map_access" 0 "Remove the test program"
+    rlPhaseEnd
+rlJournalPrintText
+rlJournalEnd

diff --git a/kernel/overlayfs-mmap-bugs/test_policy.te b/kernel/overlayfs-mmap-bugs/test_policy.te
new file mode 100644
index 0000000..5260f28
--- /dev/null
+++ b/kernel/overlayfs-mmap-bugs/test_policy.te
@@ -0,0 +1,100 @@
+policy_module(test_policy,1.0.0)
+
+type test_lowerfile_t;
+files_type(test_lowerfile_t)
+
+type test_lowerfile_no_map_t;
+files_type(test_lowerfile_no_map_t)
+
+type test_lowerfile_no_write_t;
+files_type(test_lowerfile_no_write_t)
+
+type test_lowerfile_no_read_t;
+files_type(test_lowerfile_no_read_t)
+
+type test_mountedfile_t;
+files_type(test_mountedfile_t)
+
+type test_mounter_t;
+domain_type(test_mounter_t)
+
+allow test_mounter_t self:capability { sys_admin dac_read_search dac_override };
+
+# test_mounter_t has full access to test_lowerfile_t
+manage_dirs_pattern(test_mounter_t, test_lowerfile_t, test_lowerfile_t)
+manage_files_pattern(test_mounter_t, test_lowerfile_t, test_lowerfile_t)
+manage_chr_files_pattern(test_mounter_t, test_lowerfile_t, test_lowerfile_t)
+allow test_mounter_t test_lowerfile_t:file map;
+
+# test_mounter_t can't map test_lowerfile_t
+rw_files_pattern(test_mounter_t, test_lowerfile_no_map_t, test_lowerfile_no_map_t)
+
+# test_mounter_t can't write test_lowerfile_no_write_t
+read_files_pattern(test_mounter_t, test_lowerfile_no_write_t, test_lowerfile_no_write_t)
+allow test_mounter_t test_lowerfile_no_write_t:file map;
+
+# test_mounter_t can't read test_lowerfile_no_read_t
+write_files_pattern(test_mounter_t, test_lowerfile_no_read_t, test_lowerfile_no_read_t)
+allow test_mounter_t test_lowerfile_no_read_t:file map;
+
+allow test_mounter_t test_mountedfile_t:dir { getattr setattr };
+allow test_mounter_t test_mountedfile_t:filesystem { relabelfrom relabelto mount };
+
+kernel_read_system_state(test_mounter_t)
+kernel_read_proc_symlinks(test_mounter_t)
+kernel_request_load_module(test_mounter_t)
+kernel_search_proc(test_mounter_t)
+
+fs_getattr_xattr_fs(test_mounter_t)
+fs_relabelfrom_xattr_fs(test_mounter_t)
+
+mount_entry_type(test_mounter_t)
+mount_rw_pid_files(test_mounter_t)
+
+selinux_getattr_fs(test_mounter_t)
+
+files_mounton_all_mountpoints(test_mounter_t)
+
+# Domain with full mountedfile access
+type test_access_full_t;
+domain_type(test_access_full_t)
+
+manage_dirs_pattern(test_access_full_t, test_mountedfile_t, test_mountedfile_t)
+manage_files_pattern(test_access_full_t, test_mountedfile_t, test_mountedfile_t)
+allow test_access_full_t test_mountedfile_t:file map;
+
+corecmd_bin_entry_type(test_access_full_t)
+
+# Domain with lowerfile access, but no mountedfile access (exploiting Bug 2)
+type test_access_exploit_t;
+domain_type(test_access_exploit_t)
+
+manage_files_pattern(test_access_exploit_t, test_lowerfile_t, test_lowerfile_t)
+
+corecmd_bin_entry_type(test_access_exploit_t)
+
+# for dyntransition test_access_full_t -> test_access_exploit_t
+allow test_access_full_t self:process { setcurrent };
+allow test_access_full_t test_access_exploit_t:process { dyntransition };
+
+attribute test_domain;
+typeattribute test_mounter_t test_domain;
+typeattribute test_access_full_t test_domain;
+typeattribute test_access_exploit_t test_domain;
+
+require {
+	type TEST_TYPE;
+	role TEST_ROLE;
+}
+allow TEST_TYPE test_domain:process transition;
+role TEST_ROLE types test_domain;
+
+allow test_domain TEST_TYPE:fd use;
+allow test_domain TEST_TYPE:fifo_file rw_inherited_fifo_file_perms;
+allow test_domain TEST_TYPE:process { sigchld };
+
+files_search_tmp(test_domain)
+
+term_use_all_terms(test_domain)
+
+userdom_search_user_tmp_dirs(test_domain)

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-06-03  5:48 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-06-03  5:48 [tests/selinux] main: Add a test for overlayfs mmap bugs (CVE-2026-46054) Ondrej Mosnacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox