aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2023-08-21 21:13:19 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2023-08-21 21:13:19 -0400
commit4325c82736d9e8a14b312fd1558e2788b69278cd (patch)
tree8e53b80ff5ae389d5c0e2540b85e54fbbeac04b2 /gcc
parentfe97f09a0caeff2a22cc41b26bf08692bff8686d (diff)
downloadgcc-4325c82736d9e8a14b312fd1558e2788b69278cd.zip
gcc-4325c82736d9e8a14b312fd1558e2788b69278cd.tar.gz
gcc-4325c82736d9e8a14b312fd1558e2788b69278cd.tar.bz2
analyzer: add kf_fopen
Add checking to -fanalyzer that both params of calls to "fopen" are valid null-terminated strings. gcc/analyzer/ChangeLog: * kf.cc (class kf_fopen): New. (register_known_functions): Register it. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/fopen-1.c: New test. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/analyzer/kf.cc28
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/fopen-1.c66
2 files changed, 94 insertions, 0 deletions
diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc
index 6b2db86..1601cf1 100644
--- a/gcc/analyzer/kf.cc
+++ b/gcc/analyzer/kf.cc
@@ -420,6 +420,33 @@ kf_error::impl_call_pre (const call_details &cd) const
model->check_for_null_terminated_string_arg (cd, fmt_arg_idx);
}
+/* Handler for fopen.
+ FILE *fopen (const char *filename, const char *mode);
+ See e.g. https://en.cppreference.com/w/c/io/fopen
+ https://www.man7.org/linux/man-pages/man3/fopen.3.html
+ https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 */
+
+class kf_fopen : public known_function
+{
+public:
+ bool matches_call_types_p (const call_details &cd) const final override
+ {
+ return (cd.num_args () == 2
+ && cd.arg_is_pointer_p (0)
+ && cd.arg_is_pointer_p (1));
+ }
+
+ void impl_call_pre (const call_details &cd) const final override
+ {
+ cd.check_for_null_terminated_string_arg (0);
+ cd.check_for_null_terminated_string_arg (1);
+ cd.set_any_lhs_with_defaults ();
+
+ /* fopen's mode param is effectively a mini-DSL, but there are various
+ non-standard extensions, so we don't bother to check it. */
+ }
+};
+
/* Handler for "free", after sm-handling.
If the ptr points to an underlying heap region, delete the region,
@@ -1422,6 +1449,7 @@ register_known_functions (known_function_manager &kfm)
/* Known POSIX functions, and some non-standard extensions. */
{
+ kfm.add ("fopen", make_unique<kf_fopen> ());
kfm.add ("putenv", make_unique<kf_putenv> ());
register_known_fd_functions (kfm);
diff --git a/gcc/testsuite/gcc.dg/analyzer/fopen-1.c b/gcc/testsuite/gcc.dg/analyzer/fopen-1.c
new file mode 100644
index 0000000..e5b00e9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/fopen-1.c
@@ -0,0 +1,66 @@
+typedef struct FILE FILE;
+FILE *fopen (const char *pathname, const char *mode);
+#define NULL ((void *)0)
+
+FILE *
+test_passthrough (const char *pathname, const char *mode)
+{
+ return fopen (pathname, mode);
+}
+
+FILE *
+test_null_pathname (const char *pathname, const char *mode)
+{
+ return fopen (NULL, mode);
+}
+
+FILE *
+test_null_mode (const char *pathname)
+{
+ return fopen (pathname, NULL);
+}
+
+FILE *
+test_simple_r (void)
+{
+ return fopen ("foo.txt", "r");
+}
+
+FILE *
+test_swapped_args (void)
+{
+ return fopen ("r", "foo.txt"); /* TODO: would be nice to detect this. */
+}
+
+FILE *
+test_unterminated_pathname (const char *mode)
+{
+ char buf[3] = "abc";
+ return fopen (buf, mode); /* { dg-warning "stack-based buffer over-read" } */
+ /* { dg-message "while looking for null terminator for argument 1 \\('&buf'\\) of 'fopen'..." "event" { target *-*-* } .-1 } */
+}
+
+FILE *
+test_unterminated_mode (const char *filename)
+{
+ char buf[3] = "abc";
+ return fopen (filename, buf); /* { dg-warning "stack-based buffer over-read" } */
+ /* { dg-message "while looking for null terminator for argument 2 \\('&buf'\\) of 'fopen'..." "event" { target *-*-* } .-1 } */
+}
+
+FILE *
+test_uninitialized_pathname (const char *mode)
+{
+ char buf[10];
+ return fopen (buf, mode); /* { dg-warning "use of uninitialized value 'buf\\\[0\\\]'" } */
+ /* { dg-message "while looking for null terminator for argument 1 \\('&buf'\\) of 'fopen'..." "event" { target *-*-* } .-1 } */
+}
+
+FILE *
+test_uninitialized_mode (const char *filename)
+{
+ char buf[10];
+ return fopen (filename, buf); /* { dg-warning "use of uninitialized value 'buf\\\[0\\\]'" } */
+ /* { dg-message "while looking for null terminator for argument 2 \\('&buf'\\) of 'fopen'..." "event" { target *-*-* } .-1 } */
+}
+