git: 4fbd6e0e3ca8 - stable/14 - libc: fix access mode tests in fmemopen(3)

From: Ed Maste <emaste_at_FreeBSD.org>
Date: Tue, 29 Oct 2024 19:11:57 UTC
The branch stable/14 has been updated by emaste:

URL: https://cgit.FreeBSD.org/src/commit/?id=4fbd6e0e3ca8e69d2d3789ecda6e4dd76c34e06a

commit 4fbd6e0e3ca8e69d2d3789ecda6e4dd76c34e06a
Author:     Ed Maste <emaste@FreeBSD.org>
AuthorDate: 2024-10-23 13:41:51 +0000
Commit:     Ed Maste <emaste@FreeBSD.org>
CommitDate: 2024-10-29 19:11:38 +0000

    libc: fix access mode tests in fmemopen(3)
    
    Previously a stream opened as read-only could be written to.  Add a test
    case for the fix.
    
    Also correct another incorrect access mode check that worked by
    accident, and improve the tests for that.
    
    PR:             281953
    Reported by:    Erkki Moorits, fuz
    Reviewed by:    fuz, khng (earlier)
    Sponsored by:   The FreeBSD Foundation
    Differential Revision: https://reviews.freebsd.org/D47265
    
    (cherry picked from commit 0953460ce149e6f384aafbcb1e6213dfbf8f6a16)
    (cherry picked from commit 6b9f7133aba44189d9625c352bc2c2a59baf18ef)
---
 lib/libc/stdio/fmemopen.c             | 10 +++++-----
 lib/libc/tests/stdio/fmemopen2_test.c | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/lib/libc/stdio/fmemopen.c b/lib/libc/stdio/fmemopen.c
index 2f835a34951b..dc323921d93a 100644
--- a/lib/libc/stdio/fmemopen.c
+++ b/lib/libc/stdio/fmemopen.c
@@ -74,10 +74,9 @@ fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
 	}
 
 	/*
-	 * There's no point in requiring an automatically allocated buffer
-	 * in write-only mode.
+	 * An automatically allocated buffer is only allowed in read-write mode.
 	 */
-	if (!(flags & O_RDWR) && buf == NULL) {
+	if ((flags & O_ACCMODE) != O_RDWR && buf == NULL) {
 		errno = EINVAL;
 		return (NULL);
 	}
@@ -136,9 +135,10 @@ fmemopen(void * __restrict buf, size_t size, const char * __restrict mode)
 		break;
 	}
 
+	/* Disable read in O_WRONLY mode, and write in O_RDONLY mode. */
 	f = funopen(ck,
-	    flags & O_WRONLY ? NULL : fmemopen_read, 
-	    flags & O_RDONLY ? NULL : fmemopen_write,
+	    (flags & O_ACCMODE) == O_WRONLY ? NULL : fmemopen_read,
+	    (flags & O_ACCMODE) == O_RDONLY ? NULL : fmemopen_write,
 	    fmemopen_seek, fmemopen_close);
 
 	if (f == NULL) {
diff --git a/lib/libc/tests/stdio/fmemopen2_test.c b/lib/libc/tests/stdio/fmemopen2_test.c
index 2e1b9ea917a5..a558ff3515e9 100644
--- a/lib/libc/tests/stdio/fmemopen2_test.c
+++ b/lib/libc/tests/stdio/fmemopen2_test.c
@@ -132,9 +132,11 @@ ATF_TC_BODY(test_autoalloc, tc)
 	/* Open a FILE * using a wrong mode */
 	fp = fmemopen(NULL, 512, "r");
 	ATF_REQUIRE(fp == NULL);
+	ATF_REQUIRE(errno == EINVAL);
 
 	fp = fmemopen(NULL, 512, "w");
 	ATF_REQUIRE(fp == NULL);
+	ATF_REQUIRE(errno == EINVAL);
 }
 
 ATF_TC_WITHOUT_HEAD(test_data_length);
@@ -271,6 +273,36 @@ ATF_TC_BODY(test_size_0, tc)
 	ATF_REQUIRE(errno == EINVAL);
 }
 
+/*
+ * PR281953 - ensure we cannot write in read-only only mode, and cannot read in
+ * write-only mode.
+ */
+ATF_TC_WITHOUT_HEAD(test_rdonly_wronly);
+ATF_TC_BODY(test_rdonly_wronly, tc)
+{
+	FILE *fp;
+	char buf[16];
+	char buf_orig[16] = "input data";
+	char buf_write[16] = "write";
+	size_t sz;
+
+	memcpy(buf, buf_orig, sizeof(buf));
+	fp = fmemopen(buf, sizeof(buf), "r");
+	ATF_REQUIRE(fp != NULL);
+	sz = fwrite(buf_write, 1, strlen(buf_write), fp);
+	ATF_REQUIRE(sz == 0);
+	ATF_REQUIRE(errno == EBADF);
+	ATF_REQUIRE(memcmp(buf, buf_orig, sizeof(buf)) == 0);
+	fclose(fp);
+
+	fp = fmemopen(buf_orig, sizeof(buf), "w");
+	ATF_REQUIRE(fp != NULL);
+	sz = fread(buf, sizeof(buf), 1, fp);
+	ATF_REQUIRE(sz == 0);
+	ATF_REQUIRE(errno == EBADF);
+	fclose(fp);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
@@ -280,6 +312,7 @@ ATF_TP_ADD_TCS(tp)
 	ATF_TP_ADD_TC(tp, test_binary);
 	ATF_TP_ADD_TC(tp, test_append_binary_pos);
 	ATF_TP_ADD_TC(tp, test_size_0);
+	ATF_TP_ADD_TC(tp, test_rdonly_wronly);
 
 	return (atf_no_error());
 }