musl: stdio: check if stream is writable earlier in fputs() and friends
This commit is contained in:
@@ -11,3 +11,21 @@
|
||||
int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *__new, unsigned __flags);
|
||||
# define renameat2 missing_renameat2
|
||||
#endif
|
||||
|
||||
/* When a stream is opened read-only under glibc, fputs() and friends fail with EBADF. However, they
|
||||
* succeed under musl. We rely on the glibc behavior in the code base. The following _check_writable()
|
||||
* functions first check if the passed stream is writable, and refuse to write with EBADF if not. */
|
||||
|
||||
int putc_check_writable(int c, FILE *stream);
|
||||
int putc_unlocked_check_writable(int c, FILE *stream);
|
||||
int fputc_check_writable(int c, FILE *stream);
|
||||
int fputc_unlocked_check_writable(int c, FILE *stream);
|
||||
int fputs_check_writable(const char *s, FILE *stream);
|
||||
int fputs_unlocked_check_writable(const char *s, FILE *stream);
|
||||
|
||||
#define putc putc_check_writable
|
||||
#define putc_unlocked putc_unlocked_check_writable
|
||||
#define fputc fputc_check_writable
|
||||
#define fputc_unlocked fputc_unlocked_check_writable
|
||||
#define fputs fputs_check_writable
|
||||
#define fputs_unlocked fputs_unlocked_check_writable
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdio_ext.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -9,3 +11,37 @@ int missing_renameat2(int __oldfd, const char *__old, int __newfd, const char *_
|
||||
return syscall(__NR_renameat2, __oldfd, __old, __newfd, __new, __flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define DEFINE_PUT(func) \
|
||||
int func##_check_writable(int c, FILE *stream) { \
|
||||
if (!__fwritable(stream)) { \
|
||||
errno = EBADF; \
|
||||
return EOF; \
|
||||
} \
|
||||
\
|
||||
return func(c, stream); \
|
||||
}
|
||||
|
||||
#define DEFINE_FPUTS(func) \
|
||||
int func##_check_writable(const char *s, FILE *stream) { \
|
||||
if (!__fwritable(stream)) { \
|
||||
errno = EBADF; \
|
||||
return EOF; \
|
||||
} \
|
||||
\
|
||||
return func(s, stream); \
|
||||
}
|
||||
|
||||
#undef putc
|
||||
#undef putc_unlocked
|
||||
#undef fputc
|
||||
#undef fputc_unlocked
|
||||
#undef fputs
|
||||
#undef fputs_unlocked
|
||||
|
||||
DEFINE_PUT(putc);
|
||||
DEFINE_PUT(putc_unlocked);
|
||||
DEFINE_PUT(fputc);
|
||||
DEFINE_PUT(fputc_unlocked);
|
||||
DEFINE_FPUTS(fputs);
|
||||
DEFINE_FPUTS(fputs_unlocked);
|
||||
|
||||
@@ -191,6 +191,7 @@ simple_tests += files(
|
||||
'test-specifier.c',
|
||||
'test-stat-util.c',
|
||||
'test-static-destruct.c',
|
||||
'test-stdio.c',
|
||||
'test-strbuf.c',
|
||||
'test-string-util.c',
|
||||
'test-strip-tab-ansi.c',
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "fd-util.h"
|
||||
#include "tests.h"
|
||||
#include "tmpfile-util.h"
|
||||
|
||||
TEST(read_only) {
|
||||
_cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-stdio-read-only-XXXXXX";
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
ASSERT_OK(fd = mkostemp_safe(fn));
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = putc('a', f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = putc_unlocked('a', f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = fputc('a', f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = fputc_unlocked('a', f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = fputs("a", f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
|
||||
ASSERT_NOT_NULL(f = fopen(fn, "r"));
|
||||
ASSERT_ERROR_ERRNO(r = fputs_unlocked("a", f), EBADF);
|
||||
ASSERT_EQ(r, EOF);
|
||||
f = safe_fclose(f);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
Reference in New Issue
Block a user