aboutsummaryrefslogtreecommitdiff
path: root/bfd/bfd.c
diff options
context:
space:
mode:
authorTom Tromey <tom@tromey.com>2024-02-12 18:06:56 -0700
committerTom Tromey <tom@tromey.com>2024-04-16 14:01:43 -0600
commitbacc61fd3e6fd61a59fb59bcc657be17a381520d (patch)
tree62c1fb63d1e326c4a8f921b7a5a16819626920ef /bfd/bfd.c
parent6732c57eeea61d72ad1046fae1dd0d00920150e1 (diff)
downloadgdb-bacc61fd3e6fd61a59fb59bcc657be17a381520d.zip
gdb-bacc61fd3e6fd61a59fb59bcc657be17a381520d.tar.gz
gdb-bacc61fd3e6fd61a59fb59bcc657be17a381520d.tar.bz2
Thread-safety improvements for bfd_check_format_matches
A gdb bug found that bfd_check_format_matches has some data races when called from multiple threads. In particular, it changes the BFD error handler, which is a global. It also has a local static variable ("in_check_format") that is used for recursion detection. And, finally, it may emit warnings to the per-xvec warning array, which is a global. This patch removes all the races here. The first part of patch is to change _bfd_error_handler to directly handle the needs of bfd_check_format_matches. This way, the error handler does not need to be changed. This change lets us use the new per-thread global (error_handler_messages, replacing error_handler_bfd) to also remove the need for in_check_format -- a single variable suffices. Finally, the global per-xvec array is replaced with a new type that holds the error messages. The outermost such type is stack-allocated in bfd_check_format_matches. I tested this using the binutils test suite. I also built gdb with thread sanitizer and ran the test case that was noted as failing. Finally, Alan sent me the test file that caused the addition of the xvec warning code in the first place, and I confirmed that "nm-new" has the same behavior on this file both before and after this patch. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31264 Co-Authored-By: Alan Modra <amodra@gmail.com>
Diffstat (limited to 'bfd/bfd.c')
-rw-r--r--bfd/bfd.c142
1 files changed, 127 insertions, 15 deletions
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 8fd86f6..ace2f67 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -1565,10 +1565,91 @@ err_sprintf (void *stream, const char *fmt, ...)
return total;
}
-/* Communicate the bfd processed by bfd_check_format_matches to the
- error handling function error_handler_sprintf. */
+/*
+INTERNAL
+.{* Cached _bfd_check_format messages are put in this. *}
+.struct per_xvec_message
+.{
+. struct per_xvec_message *next;
+. char message[];
+.};
+.
+.{* A list of per_xvec_message objects. The targ field indicates
+. which xvec this list holds; PER_XVEC_NO_TARGET is only set for the
+. root of the list and indicates that the entry isn't yet used. The
+. abfd field is only needed in the root entry of the list. *}
+.struct per_xvec_messages
+.{
+. bfd *abfd;
+. const bfd_target *targ;
+. struct per_xvec_message *messages;
+. struct per_xvec_messages *next;
+.};
+.
+.#define PER_XVEC_NO_TARGET ((const bfd_target *) -1)
+*/
+
+/* Helper function to find or allocate the correct per-xvec object
+ when emitting a message. */
+
+static struct per_xvec_message *
+_bfd_per_xvec_warn (struct per_xvec_messages *messages, size_t alloc)
+{
+ const bfd_target *targ = messages->abfd->xvec;
+
+ struct per_xvec_messages *prev = NULL;
+ struct per_xvec_messages *iter = messages;
+
+ if (iter->targ == PER_XVEC_NO_TARGET)
+ iter->targ = targ;
+ else
+ for (; iter != NULL; iter = iter->next)
+ {
+ if (iter->targ == targ)
+ break;
+ prev = iter;
+ }
+
+ if (iter == NULL)
+ {
+ iter = bfd_malloc (sizeof (*iter));
+ if (iter == NULL)
+ return NULL;
+ iter->abfd = messages->abfd;
+ iter->targ = targ;
+ iter->messages = NULL;
+ iter->next = NULL;
+ prev->next = iter;
+ }
+
+ struct per_xvec_message **m = &iter->messages;
+ int count = 0;
+ while (*m)
+ {
+ m = &(*m)->next;
+ count++;
+ }
+ /* Anti-fuzzer measure. Don't cache more than 5 messages. */
+ if (count < 5)
+ {
+ *m = bfd_malloc (sizeof (**m) + alloc);
+ if (*m != NULL)
+ (*m)->next = NULL;
+ }
+ return *m;
+}
-static bfd *error_handler_bfd;
+/* Communicate the error-message container processed by
+ bfd_check_format_matches to the error handling function
+ error_handler_sprintf. When non-NULL, _bfd_error_handler will call
+ error_handler_sprintf; when NULL, _bfd_error_internal will be used
+ instead. */
+
+static TLS struct per_xvec_messages *error_handler_messages;
+
+/* A special value for error_handler_messages that indicates that the
+ error should simply be ignored. */
+#define IGNORE_ERROR_MESSAGES ((struct per_xvec_messages *) -1)
/* An error handler that prints to a string, then dups that string to
a per-xvec cache. */
@@ -1585,12 +1666,12 @@ error_handler_sprintf (const char *fmt, va_list ap)
_bfd_print (err_sprintf, &error_stream, fmt, ap);
size_t len = error_stream.ptr - error_buf;
- struct per_xvec_message **warn
- = _bfd_per_xvec_warn (error_handler_bfd->xvec, len + 1);
- if (*warn)
+ struct per_xvec_message *warn
+ = _bfd_per_xvec_warn (error_handler_messages, len + 1);
+ if (warn)
{
- memcpy ((*warn)->message, error_buf, len);
- (*warn)->message[len] = 0;
+ memcpy (warn->message, error_buf, len);
+ warn->message[len] = 0;
}
}
@@ -1628,7 +1709,14 @@ _bfd_error_handler (const char *fmt, ...)
va_list ap;
va_start (ap, fmt);
- _bfd_error_internal (fmt, ap);
+ if (error_handler_messages == IGNORE_ERROR_MESSAGES)
+ {
+ /* Nothing. */
+ }
+ else if (error_handler_messages != NULL)
+ error_handler_sprintf (fmt, ap);
+ else
+ _bfd_error_internal (fmt, ap);
va_end (ap);
}
@@ -1659,18 +1747,42 @@ INTERNAL_FUNCTION
_bfd_set_error_handler_caching
SYNOPSIS
- bfd_error_handler_type _bfd_set_error_handler_caching (bfd *);
+ struct per_xvec_messages *_bfd_set_error_handler_caching (struct per_xvec_messages *);
DESCRIPTION
Set the BFD error handler function to one that stores messages
- to the per_xvec_warn array. Returns the previous function.
+ to the per_xvec_messages object. Returns the previous object
+ to which messages are stored. Note that two sequential calls
+ to this with a non-NULL argument will cause output to be
+ dropped, rather than gathered.
*/
-bfd_error_handler_type
-_bfd_set_error_handler_caching (bfd *abfd)
+struct per_xvec_messages *
+_bfd_set_error_handler_caching (struct per_xvec_messages *messages)
+{
+ struct per_xvec_messages *old = error_handler_messages;
+ if (old == NULL)
+ error_handler_messages = messages;
+ else
+ error_handler_messages = IGNORE_ERROR_MESSAGES;
+ return old;
+}
+
+/*
+INTERNAL_FUNCTION
+ _bfd_restore_error_handler_caching
+
+SYNOPSIS
+ void _bfd_restore_error_handler_caching (struct per_xvec_messages *);
+
+DESCRIPTION
+ Reset the BFD error handler object to an earlier value.
+*/
+
+void
+_bfd_restore_error_handler_caching (struct per_xvec_messages *old)
{
- error_handler_bfd = abfd;
- return bfd_set_error_handler (error_handler_sprintf);
+ error_handler_messages = old;
}
/*