Linux Test Project
 help / color / mirror / Atom feed
* [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write()
@ 2025-07-16  7:28 Florian Schmaus via ltp
  2025-07-17 16:08 ` Wei Gao via ltp
  2025-07-25 13:11 ` Cyril Hrubis
  0 siblings, 2 replies; 3+ messages in thread
From: Florian Schmaus via ltp @ 2025-07-16  7:28 UTC (permalink / raw)
  To: ltp

The sigrelse01 test would invoke write(fd, msg, MAXMESG), where
MAXMESG=512. However, msg is often as short as "ready", i.e., 6
bytes (5 bytes + \0).

This mismatch causes write() to read additional bytes outside the *msg
buffer and write everything to the file descriptor. For example, the
strace output of sigrelese01 contains the following:

    write(6, "ready\0Unable to tell child to go"..., 512)

Fix the out-of-bounds read in sigrelse01 by invoking write() with the
correct number of bytes to (read and) write by using strlen(). There
is one case where sigrelese01 invoked write_pipe() not passing a
string: when the child sends sig_array to its parent process. We
convert this case from write_pipe() to write() using the proper
arguments. After doing so, the memcpy() of sig_array is no longer
required.

We identified this issue on a CHERI [1] system, which provides
fine-grained memory protection through architectural
capabilities. Unlike traditional MMU-based protection, which would not
detect this specific out-of-bounds access, CHERI precisely bounds
memory regions. In sigrelse01's case, CHERI correctly identified that
the 6-byte buffer containing "ready" was being overread. Consequently,
this out-of-bounds read during the write() syscall would cause the
Linux kernel to return -EFAULT, revealing this hidden bug.

1: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/

Signed-off-by: Florian Schmaus <florian.schmaus@codasip.com>
---

Changes in v2:
    - remove unnecessary '\n' in tst_resm

 testcases/kernel/syscalls/sigrelse/sigrelse01.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/testcases/kernel/syscalls/sigrelse/sigrelse01.c b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
index 95754212053e..68d69c3ef5e7 100644
--- a/testcases/kernel/syscalls/sigrelse/sigrelse01.c
+++ b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
@@ -486,12 +486,14 @@ static void child(void)
 	 * then PASS, otherwise FAIL.
 	 */
 
-	if (exit_val == EXIT_OK) {
-		(void)memcpy(note, (char *)sig_array, sizeof(sig_array));
-	}
-
 	/* send note to parent and exit */
-	if (write_pipe(pipe_fd[1], note) < 0) {
+	if (exit_val == EXIT_OK) {
+		if (write(pipe_fd[1], sig_array, sizeof(sig_array)) < 0) {
+			tst_resm(TBROK, "write() pipe failed. error:%d %s.", errno, strerror(errno));
+			exit(WRITE_BROK);
+		}
+	}
+	else if (write_pipe(pipe_fd[1], note) < 0) {
 		/*
 		 * write_pipe() failed.  Set exit value to WRITE_BROK to let
 		 * parent know what happened
@@ -622,7 +624,7 @@ static int write_pipe(int fd, char *msg)
 	printf("write_pipe: pid=%d, sending %s.\n", getpid(), msg);
 #endif
 
-	if (write(fd, msg, MAXMESG) < 0) {
+	if (write(fd, msg, strlen(msg) + 1) < 0) {
 		(void)sprintf(mesg, "write() pipe failed. error:%d %s.",
 			      errno, strerror(errno));
 
-- 
2.49.1


-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write()
  2025-07-16  7:28 [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write() Florian Schmaus via ltp
@ 2025-07-17 16:08 ` Wei Gao via ltp
  2025-07-25 13:11 ` Cyril Hrubis
  1 sibling, 0 replies; 3+ messages in thread
From: Wei Gao via ltp @ 2025-07-17 16:08 UTC (permalink / raw)
  To: Florian Schmaus; +Cc: ltp

On Wed, Jul 16, 2025 at 09:28:46AM +0200, Florian Schmaus via ltp wrote:
> The sigrelse01 test would invoke write(fd, msg, MAXMESG), where
> MAXMESG=512. However, msg is often as short as "ready", i.e., 6
> bytes (5 bytes + \0).
> 
> This mismatch causes write() to read additional bytes outside the *msg
> buffer and write everything to the file descriptor. For example, the
> strace output of sigrelese01 contains the following:
> 
>     write(6, "ready\0Unable to tell child to go"..., 512)
> 
> Fix the out-of-bounds read in sigrelse01 by invoking write() with the
> correct number of bytes to (read and) write by using strlen(). There
> is one case where sigrelese01 invoked write_pipe() not passing a
> string: when the child sends sig_array to its parent process. We
> convert this case from write_pipe() to write() using the proper
> arguments. After doing so, the memcpy() of sig_array is no longer
> required.
> 
> We identified this issue on a CHERI [1] system, which provides
> fine-grained memory protection through architectural
> capabilities. Unlike traditional MMU-based protection, which would not
> detect this specific out-of-bounds access, CHERI precisely bounds
> memory regions. In sigrelse01's case, CHERI correctly identified that
> the 6-byte buffer containing "ready" was being overread. Consequently,
> this out-of-bounds read during the write() syscall would cause the
> Linux kernel to return -EFAULT, revealing this hidden bug.
> 
> 1: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/
> 
> Signed-off-by: Florian Schmaus <florian.schmaus@codasip.com>
> ---
> 
> Changes in v2:
>     - remove unnecessary '\n' in tst_resm
> 
>  testcases/kernel/syscalls/sigrelse/sigrelse01.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/testcases/kernel/syscalls/sigrelse/sigrelse01.c b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> index 95754212053e..68d69c3ef5e7 100644
> --- a/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> +++ b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> @@ -486,12 +486,14 @@ static void child(void)
>  	 * then PASS, otherwise FAIL.
>  	 */
>  
> -	if (exit_val == EXIT_OK) {
> -		(void)memcpy(note, (char *)sig_array, sizeof(sig_array));
> -	}
> -
>  	/* send note to parent and exit */
> -	if (write_pipe(pipe_fd[1], note) < 0) {
> +	if (exit_val == EXIT_OK) {
> +		if (write(pipe_fd[1], sig_array, sizeof(sig_array)) < 0) {
> +			tst_resm(TBROK, "write() pipe failed. error:%d %s.", errno, strerror(errno));
> +			exit(WRITE_BROK);
> +		}
> +	}
> +	else if (write_pipe(pipe_fd[1], note) < 0) {
>  		/*
>  		 * write_pipe() failed.  Set exit value to WRITE_BROK to let
>  		 * parent know what happened
> @@ -622,7 +624,7 @@ static int write_pipe(int fd, char *msg)
>  	printf("write_pipe: pid=%d, sending %s.\n", getpid(), msg);
>  #endif
>  
> -	if (write(fd, msg, MAXMESG) < 0) {
> +	if (write(fd, msg, strlen(msg) + 1) < 0) {
>  		(void)sprintf(mesg, "write() pipe failed. error:%d %s.",
>  			      errno, strerror(errno));
>  
Thanks for your update. I don't see any obvious mistakes.
Reviewed-by: Wei Gao <wegao@suse.com>
> -- 
> 2.49.1
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write()
  2025-07-16  7:28 [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write() Florian Schmaus via ltp
  2025-07-17 16:08 ` Wei Gao via ltp
@ 2025-07-25 13:11 ` Cyril Hrubis
  1 sibling, 0 replies; 3+ messages in thread
From: Cyril Hrubis @ 2025-07-25 13:11 UTC (permalink / raw)
  To: Florian Schmaus; +Cc: ltp

Hi!
> Signed-off-by: Florian Schmaus <florian.schmaus@codasip.com>
> ---
> 
> Changes in v2:
>     - remove unnecessary '\n' in tst_resm
> 
>  testcases/kernel/syscalls/sigrelse/sigrelse01.c | 14 ++++++++------
>  1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/testcases/kernel/syscalls/sigrelse/sigrelse01.c b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> index 95754212053e..68d69c3ef5e7 100644
> --- a/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> +++ b/testcases/kernel/syscalls/sigrelse/sigrelse01.c
> @@ -486,12 +486,14 @@ static void child(void)
>  	 * then PASS, otherwise FAIL.
>  	 */
>  
> -	if (exit_val == EXIT_OK) {
> -		(void)memcpy(note, (char *)sig_array, sizeof(sig_array));
> -	}
> -
>  	/* send note to parent and exit */
> -	if (write_pipe(pipe_fd[1], note) < 0) {
> +	if (exit_val == EXIT_OK) {
> +		if (write(pipe_fd[1], sig_array, sizeof(sig_array)) < 0) {
> +			tst_resm(TBROK, "write() pipe failed. error:%d %s.", errno, strerror(errno));
                                     ^
				   use | TERRNO instead of printing the
				   errno manually here.
> +			exit(WRITE_BROK);
> +		}
> +	}
> +	else if (write_pipe(pipe_fd[1], note) < 0) {

We follow LKML coding style so the proper way to write this is:

	} else if (...) {
		...
	}


Other than these two minor things the patch looks fine.

-- 
Cyril Hrubis
chrubis@suse.cz

-- 
Mailing list info: https://lists.linux.it/listinfo/ltp

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-07-25 13:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-16  7:28 [LTP] [PATCH v2] sigrelse01: Fix out-of-bounds read when invoking write() Florian Schmaus via ltp
2025-07-17 16:08 ` Wei Gao via ltp
2025-07-25 13:11 ` Cyril Hrubis

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