[LTP] [PATCH 4/4] fanotify24: Test open for write of executable files with pre-content watch

Amir Goldstein amir73il@gmail.com
Mon Feb 10 16:13:16 CET 2025


Watching pre-content events should allow opening an executable file for
write and executing a file that is open for write.

We have an existing test where the exectable file is not watched by
pre-content events.

We add a new test case, where the executable file is watched for
FAN_PRE_ACCESS pre-content event and access is allowed.

In the former case (not watched), execution should fail with ETXTBSY and
in the latter case (per-content watched) execution should succeed.

When allowing access events, we allow for multiple events, because
read() may generate more than a single access event.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 .../kernel/syscalls/fanotify/fanotify24.c     | 43 ++++++++++++++++++-
 1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/testcases/kernel/syscalls/fanotify/fanotify24.c b/testcases/kernel/syscalls/fanotify/fanotify24.c
index a7aa2e052..537773d52 100644
--- a/testcases/kernel/syscalls/fanotify/fanotify24.c
+++ b/testcases/kernel/syscalls/fanotify/fanotify24.c
@@ -144,6 +144,15 @@ static struct tcase {
 		{
 		}
 	},
+	{
+		"inode mark, FAN_PRE_ACCESS event allowed",
+		INIT_FANOTIFY_MARK_TYPE(INODE),
+		FAN_PRE_ACCESS, 1,
+		{
+			/* This allows multiple FAN_PRE_ACCESS events */
+			{FAN_PRE_ACCESS, FAN_ALLOW},
+		}
+	},
 };
 
 static int expected_errno(unsigned int response)
@@ -206,6 +215,21 @@ static void generate_events(struct tcase *tc)
 	exp_errno = expected_errno(event->response);
 	event++;
 
+	/*
+	 * If execve() is allowed by permission events, check if executing a
+	 * file that open for write is allowed.
+	 * HSM needs to be able to write to file during pre-content event, so it
+	 * requires that a file being executed can be open for write, which also
+	 * means that a file open for write can be executed.
+	 * Therefore, ETXTBSY is to be expected when file is not being watched
+	 * at all or being watched but not with pre-content events in mask.
+	 */
+	if (!exp_errno) {
+		fd = SAFE_OPEN(FILE_EXEC_PATH, O_RDWR);
+		if (!tc->event_count)
+			exp_errno = ETXTBSY;
+	}
+
 	exp_ret = exp_errno ? -1 : 0;
 	errno = 0;
 	if (execve(FILE_EXEC_PATH, argv, environ) != exp_ret || errno != exp_errno) {
@@ -214,6 +238,9 @@ static void generate_events(struct tcase *tc)
 	} else if (errno == exp_errno) {
 		tst_res(TINFO, "execve() got errno %d as expected", errno);
 	}
+
+	if (fd >= 0)
+		SAFE_CLOSE(fd);
 }
 
 static void child_handler(int tmp)
@@ -309,8 +336,8 @@ static void test_fanotify(unsigned int n)
 	/*
 	 * Process events
 	 *
-	 * tc->count + 1 is to accommodate for checking the child process
-	 * return value
+	 * tc->count + 1 is to let read() wait for child process to exit
+	 * and to accomodate for extra access events
 	 */
 	while (test_num < tc->event_count + 1 && fd_notify != -1) {
 		struct fanotify_event_metadata *event;
@@ -319,6 +346,7 @@ static void test_fanotify(unsigned int n)
 			/* Get more events */
 			ret = read(fd_notify, event_buf + len,
 				   EVENT_BUF_LEN - len);
+			/* Received SIGCHLD */
 			if (fd_notify == -1)
 				break;
 			if (ret < 0) {
@@ -329,6 +357,17 @@ static void test_fanotify(unsigned int n)
 			len += ret;
 		}
 
+		/*
+		 * If we got an event after the last event and the last event was
+		 * allowed then assume this is another event of the same type.
+		 * This is to accomodate for the fact that a single read() may
+		 * generate an unknown number of access permission events if they
+		 * are allowed.
+		 */
+		if (test_num > 0 && test_num == tc->event_count &&
+		    event_set[test_num-1].response == FAN_ALLOW)
+			test_num--;
+
 		event = (struct fanotify_event_metadata *)&event_buf[i];
 		/* Permission events cannot be merged, so the event mask
 		 * reported should exactly match the event mask within the
-- 
2.34.1



More information about the ltp mailing list