[clang] Warn when unique objects might be duplicated in shared libraries (PR #117622)
Hans Wennborg via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 29 10:22:46 PST 2025
================
@@ -0,0 +1,187 @@
+/**
+ * When building shared libraries, hidden objects which are defined in header
+ * files will be duplicated, with one copy in each shared library. If the object
+ * was meant to be globally unique (one copy per program), this can cause very
+ * subtle bugs. This file contains tests for the -Wunique-object-duplication
+ * warning, which is meant to detect this.
+ *
+ * Roughly, an object might be incorrectly duplicated if:
+ * - Is defined in a header (so it might appear in multiple TUs), and
+ * - Has external linkage (otherwise it's supposed to be duplicated), and
+ * - Has hidden visibility (or else the dynamic linker will handle it)
+ *
+ * Duplication becomes an issue only if one of the following is true:
+ * - The object is mutable (the copies won't be in sync), or
+ * - Its initialization may has side effects (it may now run more than once), or
+ * - The value of its address is used.
+ *
+ * Currently, we only detect the first two, and only warn on effectful
+ * initialization if we're certain there are side effects. Warning if the
+ * address is taken is prone to false positives, so we don't warn for now.
+ *
+ * The check is also disabled on Windows for now, since it uses
+ * dllimport/dllexport instead of visibility.
+ */
+
+#define HIDDEN __attribute__((visibility("hidden")))
+#define DEFAULT __attribute__((visibility("default")))
+
+// Helper functions
+constexpr int init_constexpr(int x) { return x; };
+extern double init_dynamic(int);
+
+/******************************************************************************
+ * Case one: Static local variables in an externally-visible function
+ ******************************************************************************/
+namespace StaticLocalTest {
+
+inline void has_static_locals_external() {
+ // Mutable
+ static int disallowedStatic1 = 0; // hidden-warning {{'disallowedStatic1' is mutable, has hidden visibility, and external linkage; it may be duplicated when built into a shared library}}
+ // Initialization might run more than once
+ static const double disallowedStatic2 = disallowedStatic1++; // hidden-warning {{'disallowedStatic2' has hidden visibility, and external linkage; its initialization may run more than once when built into a shared library}}
+
+ // OK, because immutable and compile-time-initialized
+ static constexpr int allowedStatic1 = 0;
+ static const float allowedStatic2 = 1;
+ static constexpr int allowedStatic3 = init_constexpr(2);
+ static const int allowedStatic4 = init_constexpr(3);
+}
+
+// Don't warn for non-inline functions, since they can't (legally) appear
+// in more than one TU in the first place.
+void has_static_locals_non_inline() {
+ // Mutable
+ static int allowedStatic1 = 0;
+ // Initialization might run more than once
+ static const double allowedStatic2 = allowedStatic1++;
+}
+
+// Everything in this function is OK because the function is TU-local
+static void has_static_locals_internal() {
+ static int allowedStatic1 = 0;
+ static double allowedStatic2 = init_dynamic(2);
+ static char allowedStatic3 = []() { return allowedStatic1++; }();
+
+ static constexpr int allowedStatic4 = 0;
+ static const float allowedStatic5 = 1;
+ static constexpr int allowedStatic6 = init_constexpr(2);
+ static const int allowedStatic7 = init_constexpr(3);
----------------
zmodem wrote:
These are immutable, so wouldn't warn anyway, right? I'd consider skipping them here then.
https://github.com/llvm/llvm-project/pull/117622
More information about the cfe-commits
mailing list