/* Prototypes for a few program-wide used functions. */
extern void *xmalloc (size_t n)
- __attribute_malloc__ __attribute_alloc_size__ ((1));
+ __attribute_malloc__ __attribute_alloc_size__ ((1)) __attr_dealloc_free;
extern void *xcalloc (size_t n, size_t s)
- __attribute_malloc__ __attribute_alloc_size__ ((1, 2));
+ __attribute_malloc__ __attribute_alloc_size__ ((1, 2)) __attr_dealloc_free;
extern void *xrealloc (void *o, size_t n)
- __attribute_malloc__ __attribute_alloc_size__ ((2));
-extern char *xstrdup (const char *) __attribute_malloc__;
+ __attribute_malloc__ __attribute_alloc_size__ ((2)) __attr_dealloc_free;
+extern char *xstrdup (const char *) __attribute_malloc__ __attr_dealloc_free;
#endif /* xmalloc.h */
bug-ungetc2 bug-ftell bug-ungetc3 bug-ungetc4 tst-fopenloc2 \
tst-memstream1 tst-memstream2 tst-memstream3 tst-memstream4 \
tst-wmemstream1 tst-wmemstream2 tst-wmemstream3 tst-wmemstream4 \
- bug-memstream1 bug-wmemstream1 \
+ tst-wmemstream5 bug-memstream1 bug-wmemstream1 \
tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \
tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
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);
+
+#undef __attr_dealloc_fclose
+#define __attr_dealloc_fclose __attr_dealloc (fclose, 1)
+
/* 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)
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
#else
# ifdef __REDIRECT
-extern FILE *__REDIRECT (tmpfile, (void), tmpfile64) __wur;
+extern FILE *__REDIRECT (tmpfile, (void), tmpfile64)
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
# else
# define tmpfile tmpfile64
# endif
#endif
#ifdef __USE_LARGEFILE64
-extern FILE *tmpfile64 (void) __wur;
+extern FILE *tmpfile64 (void)
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
#endif
/* Generate a temporary filename. */
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;
+ __THROW __attribute_malloc__ __wur __attr_dealloc_free;
#endif
-
-/* Close STREAM.
-
- 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
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)
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
/* Open a file, replacing an existing stream with it.
This function is a possible cancellation point and therefore not
# ifdef __REDIRECT
extern FILE *__REDIRECT (fopen, (const char *__restrict __filename,
const char *__restrict __modes), fopen64)
- __wur;
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
extern FILE *__REDIRECT (freopen, (const char *__restrict __filename,
const char *__restrict __modes,
FILE *__restrict __stream), freopen64)
#endif
#ifdef __USE_LARGEFILE64
extern FILE *fopen64 (const char *__restrict __filename,
- const char *__restrict __modes) __wur;
+ const char *__restrict __modes)
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
extern FILE *freopen64 (const char *__restrict __filename,
const char *__restrict __modes,
FILE *__restrict __stream) __wur;
#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
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
#endif
#ifdef __USE_GNU
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
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
#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 __attribute_malloc__ __attr_dealloc_fclose __wur;
/* 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
+ __attribute_malloc__ __attr_dealloc_fclose __wur;
+
+#ifdef _WCHAR_H
+/* Like OPEN_MEMSTREAM, but the stream is wide oriented and produces
+ a wide character string. Declared here only to add attribute malloc
+ and only if <wchar.h> has been previously #included. */
+extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW
+ __attribute_malloc__ __attr_dealloc_fclose;
+# endif
#endif
-
/* If BUF is NULL, make STREAM unbuffered.
Else make it use buffer BUF, of size BUFSIZ. */
extern void setbuf (FILE *__restrict __stream, char *__restrict __buf) __THROW;
#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;
+extern int pclose (FILE *__stream);
-/* Close a stream opened by popen and return the status of its child.
+/* 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)
+ __attribute_malloc__ __attr_dealloc (pclose, 1) __wur;
+
#endif
fclose (f);
}
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Force an error to detect incorrectly making freopen a deallocator
+ for its last argument via attribute malloc. The function closes
+ the stream without deallocating it so either the argument or
+ the pointer returned from the function (but not both) can be passed
+ to fclose. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+/* Verify that freopen returns stream. */
+static void
+do_test_return_stream (void)
+{
+ FILE *f1 = fopen (name, "r");
+ if (f1 == NULL)
+ FAIL_EXIT1 ("fopen: %m");
+
+ FILE *f2 = freopen (name, "r+", f1);
+ if (f2 == NULL)
+ FAIL_EXIT1 ("freopen: %m");
+
+ /* Verify that freopen isn't declared with the no-argument attribute
+ malloc (which could let GCC fold the inequality to false). */
+ if (f1 != f2)
+ FAIL_EXIT1 ("freopen returned a different stream");
+
+ /* This shouldn't trigger -Wmismatched-dealloc. */
+ fclose (f1);
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Pop -Wmismatched-dealloc set to error above. */
+# pragma GCC diagnostic pop
+#endif
+
/* Test for BZ#21398, where it tries to freopen stdio after the close
of its file descriptor. */
static void
{
do_test_basic ();
do_test_bz21398 ();
+ do_test_return_stream ();
return 0;
}
res = 1;
}
- fclose (fp);
+ pclose (fp);
}
fp = popen ("echo hello", "re");
res = 1;
}
- fclose (fp);
+ pclose (fp);
}
return res;
#include <wchar.h>
+extern int fclose (FILE*);
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Verify that calling fclose on the result of open_wmemstream doesn't
+ trigger GCC -Wmismatched-dealloc with fclose forward-declared and
+ without <stdio.h> included first (it is included later, in.
+ "tst-memstream1.c"). */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+int test_open_wmemstream_no_stdio (void)
+{
+ {
+ wchar_t *buf;
+ size_t size;
+ FILE *f = open_wmemstream (&buf, &size);
+ fclose (f);
+ }
+
+ {
+ FILE* (*pf)(wchar_t**, size_t*) = open_wmemstream;
+ wchar_t *buf;
+ size_t size;
+ FILE *f = pf (&buf, &size);
+ fclose (f);
+ }
+ return 0;
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Restore -Wmismatched-dealloc setting. */
+# pragma GCC diagnostic pop
+#endif
+
#define CHAR_T wchar_t
#define W(o) L##o
#define OPEN_MEMSTREAM open_wmemstream
--- /dev/null
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <wchar.h>
+
+extern int fclose (FILE*);
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Verify that calling fclose on the result of open_wmemstream doesn't
+ trigger GCC -Wmismatched-dealloc with fclose forward-declared and
+ without <stdio.h> included in the same translation unit. */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic error "-Wmismatched-dealloc"
+#endif
+
+static int
+do_test (void)
+{
+ {
+ wchar_t *buf;
+ size_t size;
+ FILE *f = open_wmemstream (&buf, &size);
+ fclose (f);
+ }
+
+ {
+ FILE* (*pf)(wchar_t**, size_t*) = open_wmemstream;
+ wchar_t *buf;
+ size_t size;
+ FILE *f = pf (&buf, &size);
+ fclose (f);
+ }
+
+ return 0;
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Restore -Wmismatched-dealloc setting. */
+# pragma GCC diagnostic pop
+#endif
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
the same pointer that was passed to it, aliasing needs to be allowed
between objects pointed by the old and new pointers. */
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
-__THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2, 3));
+ __THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2, 3))
+ __attr_dealloc_free;
/* Free a block allocated by `malloc', `realloc' or `calloc'. */
extern void free (void *__ptr) __THROW;
/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */
extern void *memalign (size_t __alignment, size_t __size)
-__THROW __attribute_malloc__ __attribute_alloc_size__ ((2)) __wur;
+ __THROW __attribute_malloc__ __attribute_alloc_size__ ((2)) __wur
+ __attr_dealloc_free;
/* Allocate SIZE bytes on a page boundary. */
extern void *valloc (size_t __size) __THROW __attribute_malloc__
- __attribute_alloc_size__ ((1)) __wur;
+ __attribute_alloc_size__ ((1)) __wur __attr_dealloc_free;
/* Equivalent to valloc(minimum-page-that-holds(n)), that is, round up
__size to nearest pagesize. */
-extern void *pvalloc (size_t __size) __THROW __attribute_malloc__ __wur;
+extern void *pvalloc (size_t __size) __THROW __attribute_malloc__
+ __wur __attr_dealloc_free;
/* Underlying allocation function; successive calls should return
contiguous pieces of memory. */
# define __attr_access_none(argno)
#endif
+#if __GNUC_PREREQ (11, 0)
+/* Designates dealloc as a function to call to deallocate objects
+ allocated by the declared function. */
+# define __attr_dealloc(dealloc, argno) \
+ __attribute__ ((__malloc__ (dealloc, argno)))
+# define __attr_dealloc_free __attr_dealloc (__builtin_free, 1)
+#else
+# define __attr_dealloc(dealloc, argno)
+# define __attr_dealloc_free
+#endif
+
/* Specify that a function such as setjmp or vfork may return
twice. */
#if __GNUC_PREREQ (4, 1)
tst-makecontext-align test-bz22786 tst-strtod-nan-sign \
tst-swapcontext1 tst-setcontext4 tst-setcontext5 \
tst-setcontext6 tst-setcontext7 tst-setcontext8 \
- tst-setcontext9 tst-bz20544 tst-canon-bz26341
+ tst-setcontext9 tst-bz20544 tst-canon-bz26341 \
+ tst-realpath
tests-internal := tst-strtod1i tst-strtod3 tst-strtod4 tst-strtod5i \
tst-tls-atexit tst-tls-atexit-nodelete
extern void *realloc (void *__ptr, size_t __size)
__THROW __attribute_warn_unused_result__ __attribute_alloc_size__ ((2));
+/* Free a block allocated by `malloc', `realloc' or `calloc'. */
+extern void free (void *__ptr) __THROW;
+
#ifdef __USE_MISC
/* Re-allocate the previously allocated block in PTR, making the new
block large enough for NMEMB elements of SIZE bytes each. */
between objects pointed by the old and new pointers. */
extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
__THROW __attribute_warn_unused_result__
- __attribute_alloc_size__ ((2, 3));
-#endif
+ __attribute_alloc_size__ ((2, 3))
+ __attr_dealloc_free;
-/* Free a block allocated by `malloc', `realloc' or `calloc'. */
-extern void free (void *__ptr) __THROW;
+/* Add reallocarray as its own deallocator. */
+extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
+ __THROW __attr_dealloc (reallocarray, 1);
+#endif
#ifdef __USE_MISC
# include <alloca.h>
/* Return a malloc'd string containing the canonical absolute name of the
existing named file. */
extern char *canonicalize_file_name (const char *__name)
- __THROW __nonnull ((1)) __wur;
+ __THROW __nonnull ((1)) __attribute_malloc__
+ __attr_dealloc_free __wur;
#endif
#if defined __USE_MISC || defined __USE_XOPEN_EXTENDED
--- /dev/null
+/* Test to verify that realpath() doesn't cause false positives due
+ to GCC attribute malloc.
+
+ Test failure exposes the presence of the attribute in the following
+ declaration:
+
+ __attribute__ ((__malloc__ (free, 1))) char*
+ realpath (const char *, char *);
+
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Turn GCC -Wmismatched-dealloc warnings into errors to expose false
+ positives. */
+# pragma GCC diagnostic push
+# pragma GCC diagnostic error "-Wmismatched-dealloc"
+
+/* Associate dealloc as the only deallocator suitable for pointers
+ returned from alloc.
+ GCC automatically disables inlining of allocator and deallocator
+ functions marked with the argument form of attribute malloc but
+ it doesn't hurt to disable it explicitly. */
+__attribute ((noipa)) void dealloc (void *);
+__attribute ((malloc (dealloc, 1))) char* alloc (void);
+#endif
+
+void dealloc (void *p)
+{
+ free (p);
+}
+
+char* alloc (void)
+{
+ return (char *)malloc (8);
+}
+
+static int
+do_test (void)
+{
+ char *resolved_path = alloc ();
+ char *ret = realpath ("/", resolved_path);
+ dealloc (ret);
+
+ resolved_path = alloc ();
+ ret = realpath ("/", resolved_path);
+ dealloc (resolved_path);
+
+ /* The following should emit a warning (but doesn't with GCC 11):
+ resolved_path = alloc ();
+ ret = realpath ("/", resolved_path);
+ free (ret); // expect -Wmismatched-dealloc
+ */
+
+ return 0;
+}
+
+#if defined __GNUC__ && __GNUC__ >= 11
+/* Restore -Wmismatched-dealloc setting. */
+# pragma GCC diagnostic pop
+#endif
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
size_t __n, locale_t __loc) __THROW;
/* Duplicate S, returning an identical malloc'd string. */
-extern wchar_t *wcsdup (const wchar_t *__s) __THROW __attribute_malloc__;
+extern wchar_t *wcsdup (const wchar_t *__s) __THROW
+ __attribute_malloc__ __attr_dealloc_free;
#endif
/* Find the first occurrence of WC in WCS. */
/* Wide character I/O functions. */
#if defined __USE_XOPEN2K8 || __GLIBC_USE (LIB_EXT2)
+# ifndef __attr_dealloc_fclose
+# if defined __has_builtin
+# if __has_builtin (__builtin_fclose)
+/* If the attribute macro hasn't been defined yet (by <stdio.h>) and
+ fclose is a built-in, use it. */
+# define __attr_dealloc_fclose __attr_dealloc (__builtin_fclose, 1)
+# endif
+# endif
+# endif
+# ifndef __attr_dealloc_fclose
+# define __attr_dealloc_fclose /* empty */
+# endif
+
/* Like OPEN_MEMSTREAM, but the stream is wide oriented and produces
a wide character string. */
-extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW;
+extern __FILE *open_wmemstream (wchar_t **__bufloc, size_t *__sizeloc) __THROW
+ __attribute_malloc__ __attr_dealloc_fclose;
#endif
#if defined __USE_ISOC95 || defined __USE_UNIX98