public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] add support for -Wmismatched-dealloc
@ 2020-12-08 22:52 Martin Sebor
  2020-12-09  0:07 ` Joseph Myers
  0 siblings, 1 reply; 27+ messages in thread
From: Martin Sebor @ 2020-12-08 22:52 UTC (permalink / raw)
  To: GNU C Library

[-- Attachment #1: Type: text/plain, Size: 2569 bytes --]

To help detect common kinds of memory (and other resource) management
bugs, GCC 11 adds support for the detection of mismatched calls to
allocation and deallocation functions.  At each call site to a known
deallocation function GCC checks the set of allocation functions
the former can be paired with and, if the two don't match, issues
a -Wmismatched-dealloc warning (something similar happens in C++
for mismatched calls to new and delete).  GCC also uses the same
mechanism to detect attempts to deallocate objects not allocated
by any allocation function (or pointers past the first byte into
allocated objects) by -Wfree-nonheap-object.

This support is enabled for built-in functions like malloc and free.
To extend it beyond those, GCC extends attribute malloc to designate
a deallocation function to which pointers returned from the allocation
function may be passed to deallocate the allocated objects.  Another,
optional argument designates the positional argument to which
the pointer must be passed.

The attached change is the first step in enabling this extended support
for Glibc.  It enables GCC to diagnose mismatched calls such as in
tst-popen.c1:

   FILE *f = popen ("foo", "r");
   ...
   fclose (f);

   warning: ‘fclose’ called on pointer returned from a mismatched 
allocation function [-Wmismatched-dealloc]
      83 |     fclose (f);
         |     ^~~~~~~~~~
   note: returned from ‘popen’
      81 |     FILE *f = popen ("foo", "r");
         |               ^~~~~~~~~~~~~~~~~~

Since <stdio.h> doesn't declare free() and realloc() the patch uses
__builtin_free and __builtin_realloc as the deallocators.  This isn't
pretty but I couldn't think of a more elegant way to do it.  (I also
missed this bit in the initial implementation of the attribute so
current trunk doesn't handle the __builtin_xxx forms.  I submitted
a patch to handle it just today, but to avoid delays, I post this
solution for comments ahead of its acceptance).

Since the deallocation function referenced by the malloc attribute
must have been declared, the changes require rearranging the order
of the declarations a bit.

I tested the changes by rebuilding Glibc on x86_64 and by verifying
that warnings are issued where appropriate for my little test program
but not much else.  Neither set of changes (either in GCC or in Glibc)
impacts code generation and should make no difference in testing
(I didn't see any.)

If there is support for this approach I'll work on extending it to
other headers, hopefully before the upcoming Glibc release.

Thanks
Martin

[-- Attachment #2: glibc-attr-malloc-stdio.diff --]
[-- Type: text/x-patch, Size: 8382 bytes --]

diff --git a/libio/stdio.h b/libio/stdio.h
index 59f2ee01ab..f7ee14a9fd 100644
--- a/libio/stdio.h
+++ b/libio/stdio.h
@@ -165,22 +165,62 @@ extern int renameat2 (int __oldfd, const char *__old, int __newfd,
 		      const char *__new, unsigned int __flags) __THROW;
 #endif
 
+/* Close STREAM.
+
+   This function is a possible cancellation point and therefore not
+   marked with __THROW.  */
+extern int fclose (FILE *__stream);
+
+/* This function is a possible cancellation point and therefore not
+   marked with __THROW.  It's paired with fclose as a deallocator,
+   and (in a subsequent redeclarations) also with itself.  */
+extern FILE *freopen (const char *__restrict __filename,
+		      const char *__restrict __modes,
+		      FILE *__restrict __stream) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1);
+
+#ifdef __USE_LARGEFILE64
+extern FILE *freopen64 (const char *__restrict __filename,
+			const char *__restrict __modes,
+			FILE *__restrict __stream) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3);
+extern FILE *freopen64 (const char *__restrict __filename,
+			const char *__restrict __modes,
+			FILE *__restrict __stream) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc (freopen64, 3);
+extern FILE *fopen64 (const char *__restrict __filename,
+		      const char *__restrict __modes) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc (freopen64, 3);
+
+#  define __attr_dealloc_freopen64 __attr_dealloc (freopen64, 3)
+#else
+#  define __attr_dealloc_freopen64 /* empty */
+#endif
+
 /* Create a temporary file and open it read/write.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 #ifndef __USE_FILE_OFFSET64
-extern FILE *tmpfile (void) __wur;
+extern FILE *tmpfile (void) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #else
 # ifdef __REDIRECT
-extern FILE *__REDIRECT (tmpfile, (void), tmpfile64) __wur;
+extern FILE *__REDIRECT (tmpfile, (void), tmpfile64) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 # else
 #  define tmpfile tmpfile64
 # endif
 #endif
 
 #ifdef __USE_LARGEFILE64
-extern FILE *tmpfile64 (void) __wur;
+extern FILE *tmpfile64 (void) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #endif
 
 /* Generate a temporary filename.  */
@@ -201,16 +241,21 @@ extern char *tmpnam_r (char __s[L_tmpnam]) __THROW __wur;
    If not and if DIR is not NULL, that value is checked.  If that fails,
    P_tmpdir is tried and finally "/tmp".  The storage for the filename
    is allocated by `malloc'.  */
-extern char *tempnam (const char *__dir, const char *__pfx)
-     __THROW __attribute_malloc__ __wur;
-#endif
-
-
-/* Close STREAM.
+extern char *tempnam (const char *__dir, const char *__pfx) __wur
+  __attribute_malloc__
+# ifdef __has_builtin
+/* Reference the builtins since the declaration of neither function need be
+   in scope at this point.  */
+#   if __has_builtin (__builtin_free)
+      __attr_dealloc (__builtin_free, 1)
+#   endif
+#   if __has_builtin (__builtin_realloc)
+      __attr_dealloc (__builtin_realloc, 1)
+#   endif
+#  endif
+# endif
+  __THROW;
 
-   This function is a possible cancellation point and therefore not
-   marked with __THROW.  */
-extern int fclose (FILE *__stream);
 /* Flush STREAM, or all streams if STREAM is NULL.
 
    This function is a possible cancellation point and therefore not
@@ -244,39 +289,40 @@ extern int fcloseall (void);
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 extern FILE *fopen (const char *__restrict __filename,
-		    const char *__restrict __modes) __wur;
+		    const char *__restrict __modes) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 /* Open a file, replacing an existing stream with it.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
 extern FILE *freopen (const char *__restrict __filename,
 		      const char *__restrict __modes,
-		      FILE *__restrict __stream) __wur;
+		      FILE *__restrict __stream) __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #else
 # ifdef __REDIRECT
 extern FILE *__REDIRECT (fopen, (const char *__restrict __filename,
 				 const char *__restrict __modes), fopen64)
-  __wur;
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64 __wur;
 extern FILE *__REDIRECT (freopen, (const char *__restrict __filename,
 				   const char *__restrict __modes,
 				   FILE *__restrict __stream), freopen64)
-  __wur;
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+   __attr_dealloc_freopen64 __wur;
 # else
 #  define fopen fopen64
 #  define freopen freopen64
 # endif
 #endif
-#ifdef __USE_LARGEFILE64
-extern FILE *fopen64 (const char *__restrict __filename,
-		      const char *__restrict __modes) __wur;
-extern FILE *freopen64 (const char *__restrict __filename,
-			const char *__restrict __modes,
-			FILE *__restrict __stream) __wur;
-#endif
 
 #ifdef	__USE_POSIX
 /* Create a new stream that refers to an existing system file descriptor.  */
-extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
+extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #endif
 
 #ifdef	__USE_GNU
@@ -284,18 +330,24 @@ extern FILE *fdopen (int __fd, const char *__modes) __THROW __wur;
    and uses the given functions for input and output.  */
 extern FILE *fopencookie (void *__restrict __magic_cookie,
 			  const char *__restrict __modes,
-			  cookie_io_functions_t __io_funcs) __THROW __wur;
+			  cookie_io_functions_t __io_funcs) __THROW __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #endif
 
 #if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
 /* Create a new stream that refers to a memory buffer.  */
 extern FILE *fmemopen (void *__s, size_t __len, const char *__modes)
-  __THROW __wur;
+  __THROW __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 
 /* Open a stream that writes into a malloc'd buffer that is expanded as
    necessary.  *BUFLOC and *SIZELOC are updated with the buffer's location
    and the number of characters written on fflush or fclose.  */
-extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __THROW __wur;
+extern FILE *open_memstream (char **__bufloc, size_t *__sizeloc) __THROW __wur
+  __attribute_malloc__ __attr_dealloc (fclose, 1) __attr_dealloc (freopen, 3)
+  __attr_dealloc_freopen64;
 #endif
 
 
@@ -792,17 +844,17 @@ extern int fileno_unlocked (FILE *__stream) __THROW __wur;
 
 
 #ifdef __USE_POSIX2
-/* Create a new stream connected to a pipe running the given command.
+/* Close a stream opened by popen and return the status of its child.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern FILE *popen (const char *__command, const char *__modes) __wur;
-
-/* Close a stream opened by popen and return the status of its child.
+extern int pclose (FILE *__stream);
+/* Create a new stream connected to a pipe running the given command.
 
    This function is a possible cancellation point and therefore not
    marked with __THROW.  */
-extern int pclose (FILE *__stream);
+extern FILE *popen (const char *__command, const char *__modes) __wur
+  __attribute_malloc__ __attr_dealloc (pclose, 1);
 #endif
 
 
diff --git a/libio/tst-popen1.c b/libio/tst-popen1.c
index bae6615b9b..9f36b20090 100644
--- a/libio/tst-popen1.c
+++ b/libio/tst-popen1.c
@@ -21,7 +21,7 @@ do_test (void)
 	  res = 1;
 	}
 
-      fclose (fp);
+      pclose (fp);
     }
 
   fp = popen ("echo hello", "re");
@@ -39,7 +39,7 @@ do_test (void)
 	  res = 1;
 	}
 
-      fclose (fp);
+      pclose (fp);
     }
 
   return res;

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

end of thread, other threads:[~2021-05-16 21:25 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-12-08 22:52 [PATCH] add support for -Wmismatched-dealloc Martin Sebor
2020-12-09  0:07 ` Joseph Myers
2020-12-12  2:25   ` Martin Sebor
2020-12-14 21:39     ` Martin Sebor
2020-12-14 22:16       ` Florian Weimer
2020-12-15  1:01       ` Joseph Myers
2020-12-15 16:52         ` Martin Sebor
2020-12-27 23:13           ` Martin Sebor
2021-01-04 15:56             ` Ping: " Martin Sebor
2021-01-04 16:07             ` Florian Weimer
2021-01-04 16:18               ` Martin Sebor
2021-01-04 16:57                 ` Florian Weimer
2021-01-04 23:18                   ` Martin Sebor
2021-01-10 20:42                     ` Ping: " Martin Sebor
2021-01-11  9:13                     ` Florian Weimer
2021-01-12  0:00                       ` Martin Sebor
2021-01-12  0:01                       ` Martin Sebor
2021-01-12  8:59                         ` Florian Weimer
2021-01-19  1:08                           ` Martin Sebor
2021-01-19 16:54                           ` David Malcolm
2021-01-22 21:26                         ` DJ Delorie
2021-01-25 10:56                         ` Florian Weimer
2021-01-25 11:31                         ` Florian Weimer
2021-04-23  0:00                         ` Martin Sebor
2021-05-06 23:54                           ` Martin Sebor
2021-05-13 21:49                             ` Martin Sebor
2021-05-16 21:25                               ` Martin Sebor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).