[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] careadlinkat: fix GCC 10 workaround
From: |
Bruno Haible |
Subject: |
Re: [PATCH] careadlinkat: fix GCC 10 workaround |
Date: |
Mon, 17 Aug 2020 19:10:19 +0200 |
User-agent: |
KMail/5.1.3 (Linux/4.4.0-186-generic; KDE/5.18.0; x86_64; ; ) |
Paul Eggert wrote:
> What's wrong with avoiding the problem via GCC_LINT?
Some people, especially those that build distros, want to get warnings about
the exact code that they ship in the distro, not about some different code.
> I suppose you could add -Wno-return-local-addr to your CFLAGS, but I don't
> recommend this as it's normally a useful warning.
Right.
How about this modification? It splits the function into two functions, and
unless the first function is inlined, the warning disappears.
- No use of GCC_LINT.
- The runtime cost is smaller: It still uses a stack-allocated buffer.
Just an additional function call.
diff --git a/lib/careadlinkat.c b/lib/careadlinkat.c
index 1aa0436..67ea7d7 100644
--- a/lib/careadlinkat.c
+++ b/lib/careadlinkat.c
@@ -60,45 +60,24 @@
If successful, return the buffer address; otherwise return NULL and
set errno. */
-char *
-careadlinkat (int fd, char const *filename,
- char *buffer, size_t buffer_size,
- struct allocator const *alloc,
- ssize_t (*preadlinkat) (int, char const *, char *, size_t))
+/* A broken gcc -Wreturn-local-addr would cry wolf about returning the address
+ of a stack-allocated buffer, if this function gets inlined. See:
+ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95044
+ When the GCC bug is fixed this workaround should be limited to the broken
+ GCC versions. */
+#if _GL_GNUC_PREREQ (10, 1) && ! defined __clang__
+__attribute__ ((__noinline__))
+#endif
+static char *
+careadlinkat_internal (int fd, char const *filename,
+ char *buffer, size_t buffer_size,
+ struct allocator const *alloc,
+ ssize_t (*preadlinkat) (int, char const *, char *,
size_t))
{
char *buf;
size_t buf_size;
size_t buf_size_max =
SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
- char stack_buf[1024];
-
-#if (defined GCC_LINT || defined lint) && _GL_GNUC_PREREQ (10, 1)
- /* Pacify preadlinkat without creating a pointer to the stack
- that a broken gcc -Wreturn-local-addr would cry wolf about. See:
- https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95044
- This workaround differs from the mainline code, but
- no other way to pacify GCC 10.1.0 is known; even an explicit
- #pragma does not pacify GCC. When the GCC bug is fixed this
- workaround should be limited to the broken GCC versions. */
-# define WORK_AROUND_GCC_BUG_95044
-#endif
-
- if (! alloc)
- alloc = &stdlib_allocator;
-
- if (!buffer)
- {
-#ifdef WORK_AROUND_GCC_BUG_95044
- buffer = alloc->allocate (sizeof stack_buf);
-#else
- /* Allocate the initial buffer on the stack. This way, in the
- common case of a symlink of small size, we get away with a
- single small malloc() instead of a big malloc() followed by a
- shrinking realloc(). */
- buffer = stack_buf;
-#endif
- buffer_size = sizeof stack_buf;
- }
buf = buffer;
buf_size = buffer_size;
@@ -130,15 +109,6 @@ careadlinkat (int fd, char const *filename,
{
buf[link_size++] = '\0';
- if (buf == stack_buf)
- {
- char *b = alloc->allocate (link_size);
- buf_size = link_size;
- if (! b)
- break;
- return memcpy (b, buf, link_size);
- }
-
if (link_size < buf_size && buf != buffer && alloc->reallocate)
{
/* Shrink BUF before returning it. */
@@ -172,3 +142,44 @@ careadlinkat (int fd, char const *filename,
errno = ENOMEM;
return NULL;
}
+
+char *
+careadlinkat (int fd, char const *filename,
+ char *buffer, size_t buffer_size,
+ struct allocator const *alloc,
+ ssize_t (*preadlinkat) (int, char const *, char *, size_t))
+{
+ char stack_buf[1024];
+
+ if (! alloc)
+ alloc = &stdlib_allocator;
+
+ if (!buffer)
+ {
+ /* Allocate the initial buffer on the stack. This way, in the
+ common case of a symlink of small size, we get away with a
+ single small malloc() instead of a big malloc() followed by a
+ shrinking realloc(). */
+ buffer = stack_buf;
+ buffer_size = sizeof stack_buf;
+ }
+
+ char *buf = careadlinkat_internal (fd, filename, buffer, buffer_size, alloc,
+ preadlinkat);
+
+ if (buf == stack_buf)
+ {
+ size_t link_size = strlen (buf) + 1;
+ char *b = alloc->allocate (link_size);
+ if (b)
+ return memcpy (b, buf, link_size);
+ else
+ {
+ if (alloc->die)
+ alloc->die (link_size);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+ return buf;
+}