diff mbox series

PR libgcc/60790: Avoid IFUNC resolver access to uninitialized data

Message ID 8d5057e2-b0c5-342b-e0f7-9a54b7fdc061@redhat.com
State New
Headers show
Series PR libgcc/60790: Avoid IFUNC resolver access to uninitialized data | expand

Commit Message

Florian Weimer March 29, 2018, 2 p.m. UTC
This patch performs lazy initialization of the relevant CPUID feature 
register value.  It will needlessly invoke the CPUID determination code 
on architectures which lack CPUID support or support for the feature 
register, but I think it's not worth to avoid the complexity for that.

I verified manually that the CMPXCHG16B implementation is still selected 
for a 128-bit load after the change.  I don't know how to write an 
automated test for that.

Thanks,
Florian

Comments

Jeff Law May 22, 2018, 10:48 p.m. UTC | #1
On 03/29/2018 08:00 AM, Florian Weimer wrote:
> This patch performs lazy initialization of the relevant CPUID feature
> register value.  It will needlessly invoke the CPUID determination code
> on architectures which lack CPUID support or support for the feature
> register, but I think it's not worth to avoid the complexity for that.
> 
> I verified manually that the CMPXCHG16B implementation is still selected
> for a 128-bit load after the change.  I don't know how to write an
> automated test for that.
> 
> Thanks,
> Florian
> 
> pr60790.patch
> 
> 
> Index: libatomic/ChangeLog
> ===================================================================
> --- libatomic/ChangeLog	(revision 258952)
> +++ libatomic/ChangeLog	(working copy)
> @@ -1,3 +1,18 @@
> +2018-03-29  Florian Weimer  <fweimer@tor.usersys.redhat.com>
> +
> +	PR libgcc/60790
> +	x86: Do not assume ELF constructors run before IFUNC resolvers.
> +	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
> +	Remove declarations.
> +	(__libat_feat1, __libat_feat1_init): Declare.
> +	(FEAT1_REGISTER): Define.
> +	(load_feat1): New function.
> +	(IFUNC_COND_1): Adjust.
> +	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
> +	(init_cpuid): Remove definitions.
> +	(__libat_feat1): New variable.
> +	(__libat_feat1_init): New function.
OK.

Do you have write access to the GCC repository?  If not I can commit for
you.

jeff
Florian Weimer May 23, 2018, 11:15 a.m. UTC | #2
On 05/23/2018 12:48 AM, Jeff Law wrote:
> OK.

Thanks.  I will let it sit in trunk for a while and then consider 
backporting it to release branches.

> Do you have write access to the GCC repository?  If not I can commit for
> you.

I have write access.  I take this as a hint to contribute more 
regularly. 8-)

Florian
Florian Weimer Oct. 9, 2018, 7:27 p.m. UTC | #3
* Jeff Law:

> On 03/29/2018 08:00 AM, Florian Weimer wrote:
>> This patch performs lazy initialization of the relevant CPUID feature
>> register value.  It will needlessly invoke the CPUID determination code
>> on architectures which lack CPUID support or support for the feature
>> register, but I think it's not worth to avoid the complexity for that.
>> 
>> I verified manually that the CMPXCHG16B implementation is still selected
>> for a 128-bit load after the change.  I don't know how to write an
>> automated test for that.
>> 
>> Thanks,
>> Florian
>> 
>> pr60790.patch
>> 
>> 
>> Index: libatomic/ChangeLog
>> ===================================================================
>> --- libatomic/ChangeLog	(revision 258952)
>> +++ libatomic/ChangeLog	(working copy)
>> @@ -1,3 +1,18 @@
>> +2018-03-29  Florian Weimer  <fweimer@tor.usersys.redhat.com>
>> +
>> +	PR libgcc/60790
>> +	x86: Do not assume ELF constructors run before IFUNC resolvers.
>> +	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
>> +	Remove declarations.
>> +	(__libat_feat1, __libat_feat1_init): Declare.
>> +	(FEAT1_REGISTER): Define.
>> +	(load_feat1): New function.
>> +	(IFUNC_COND_1): Adjust.
>> +	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
>> +	(init_cpuid): Remove definitions.
>> +	(__libat_feat1): New variable.
>> +	(__libat_feat1_init): New function.
> OK.

Here is the backport to gcc-8-branch.

libatomic/ChangeLog

	PR libgcc/60790
	x86: Do not assume ELF constructors run before IFUNC resolvers.
	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
	Remove declarations.
	(__libat_feat1, __libat_feat1_init): Declare.
	(FEAT1_REGISTER): Define.
	(load_feat1): New function.
	(IFUNC_COND_1): Adjust.
	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
	(init_cpuid): Remove definitions.
	(__libat_feat1): New variable.
	(__libat_feat1_init): New function.

Index: libatomic/config/x86/host-config.h
===================================================================
--- libatomic/config/x86/host-config.h	(revision 264990)
+++ libatomic/config/x86/host-config.h	(working copy)
@@ -25,13 +25,39 @@
 #if HAVE_IFUNC
 #include <cpuid.h>
 
-extern unsigned int libat_feat1_ecx HIDDEN;
-extern unsigned int libat_feat1_edx HIDDEN;
+#ifdef __x86_64__
+# define FEAT1_REGISTER ecx
+#else
+# define FEAT1_REGISTER edx
+#endif
 
+/* Value of the CPUID feature register FEAT1_REGISTER for the cmpxchg
+   bit for IFUNC_COND1 below.  */
+extern unsigned int __libat_feat1 HIDDEN;
+
+/* Initialize libat_feat1 and return its value.  */
+unsigned int __libat_feat1_init (void) HIDDEN;
+
+/* Return the value of the relevant feature register for the relevant
+   cmpxchg bit, or 0 if there is no CPUID support.  */
+static inline unsigned int
+__attribute__ ((const))
+load_feat1 (void)
+{
+  /* See the store in __libat_feat1_init.  */
+  unsigned int feat1 = __atomic_load_n (&__libat_feat1, __ATOMIC_RELAXED);
+  if (feat1 == 0)
+    /* Assume that initialization has not happened yet.  This may get
+       called repeatedly if the CPU does not have any feature bits at
+       all.  */
+    feat1 = __libat_feat1_init ();
+  return feat1;
+}
+
 #ifdef __x86_64__
-# define IFUNC_COND_1	(libat_feat1_ecx & bit_CMPXCHG16B)
+# define IFUNC_COND_1	(load_feat1 () & bit_CMPXCHG16B)
 #else
-# define IFUNC_COND_1	(libat_feat1_edx & bit_CMPXCHG8B)
+# define IFUNC_COND_1	(load_feat1 () & bit_CMPXCHG8B)
 #endif
 
 #ifdef __x86_64__
Index: libatomic/config/x86/init.c
===================================================================
--- libatomic/config/x86/init.c	(revision 264990)
+++ libatomic/config/x86/init.c	(working copy)
@@ -26,13 +26,17 @@
 
 #if HAVE_IFUNC
 
-unsigned int libat_feat1_ecx, libat_feat1_edx;
+unsigned int __libat_feat1;
 
-static void __attribute__((constructor))
-init_cpuid (void)
+unsigned int
+__libat_feat1_init (void)
 {
-  unsigned int eax, ebx;
-  __get_cpuid (1, &eax, &ebx, &libat_feat1_ecx, &libat_feat1_edx);
+  unsigned int eax, ebx, ecx, edx;
+  FEAT1_REGISTER = 0;
+  __get_cpuid (1, &eax, &ebx, &ecx, &edx);
+  /* See the load in load_feat1.  */
+  __atomic_store_n (&__libat_feat1, FEAT1_REGISTER, __ATOMIC_RELAXED);
+  return FEAT1_REGISTER;
 }
 
 #endif /* HAVE_IFUNC */
Index: .
===================================================================
--- .	(revision 264990)
+++ .	(working copy)

Property changes on: .
Florian Weimer March 19, 2019, 3 p.m. UTC | #4
* Florian Weimer:

> * Jeff Law:
>
>> On 03/29/2018 08:00 AM, Florian Weimer wrote:
>>> This patch performs lazy initialization of the relevant CPUID feature
>>> register value.  It will needlessly invoke the CPUID determination code
>>> on architectures which lack CPUID support or support for the feature
>>> register, but I think it's not worth to avoid the complexity for that.
>>> 
>>> I verified manually that the CMPXCHG16B implementation is still selected
>>> for a 128-bit load after the change.  I don't know how to write an
>>> automated test for that.
>>> 
>>> Thanks,
>>> Florian
>>> 
>>> pr60790.patch
>>> 
>>> 
>>> Index: libatomic/ChangeLog
>>> ===================================================================
>>> --- libatomic/ChangeLog	(revision 258952)
>>> +++ libatomic/ChangeLog	(working copy)
>>> @@ -1,3 +1,18 @@
>>> +2018-03-29  Florian Weimer  <fweimer@tor.usersys.redhat.com>
>>> +
>>> +	PR libgcc/60790
>>> +	x86: Do not assume ELF constructors run before IFUNC resolvers.
>>> +	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
>>> +	Remove declarations.
>>> +	(__libat_feat1, __libat_feat1_init): Declare.
>>> +	(FEAT1_REGISTER): Define.
>>> +	(load_feat1): New function.
>>> +	(IFUNC_COND_1): Adjust.
>>> +	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
>>> +	(init_cpuid): Remove definitions.
>>> +	(__libat_feat1): New variable.
>>> +	(__libat_feat1_init): New function.
>> OK.
>
> Here is the backport to gcc-8-branch.

Ping?  <https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00524.html>
Jeff Law March 19, 2019, 8:47 p.m. UTC | #5
On 3/19/19 9:00 AM, Florian Weimer wrote:
> * Florian Weimer:
> 
>> * Jeff Law:
>>
>>> On 03/29/2018 08:00 AM, Florian Weimer wrote:
>>>> This patch performs lazy initialization of the relevant CPUID feature
>>>> register value.  It will needlessly invoke the CPUID determination code
>>>> on architectures which lack CPUID support or support for the feature
>>>> register, but I think it's not worth to avoid the complexity for that.
>>>>
>>>> I verified manually that the CMPXCHG16B implementation is still selected
>>>> for a 128-bit load after the change.  I don't know how to write an
>>>> automated test for that.
>>>>
>>>> Thanks,
>>>> Florian
>>>>
>>>> pr60790.patch
>>>>
>>>>
>>>> Index: libatomic/ChangeLog
>>>> ===================================================================
>>>> --- libatomic/ChangeLog	(revision 258952)
>>>> +++ libatomic/ChangeLog	(working copy)
>>>> @@ -1,3 +1,18 @@
>>>> +2018-03-29  Florian Weimer  <fweimer@tor.usersys.redhat.com>
>>>> +
>>>> +	PR libgcc/60790
>>>> +	x86: Do not assume ELF constructors run before IFUNC resolvers.
>>>> +	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
>>>> +	Remove declarations.
>>>> +	(__libat_feat1, __libat_feat1_init): Declare.
>>>> +	(FEAT1_REGISTER): Define.
>>>> +	(load_feat1): New function.
>>>> +	(IFUNC_COND_1): Adjust.
>>>> +	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
>>>> +	(init_cpuid): Remove definitions.
>>>> +	(__libat_feat1): New variable.
>>>> +	(__libat_feat1_init): New function.
>>> OK.
>>
>> Here is the backport to gcc-8-branch.
> 
> Ping?  <https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00524.html>
OK for the branch as well.  I didn't know you were waiting on me :-)
jeff
diff mbox series

Patch

Index: libatomic/ChangeLog
===================================================================
--- libatomic/ChangeLog	(revision 258952)
+++ libatomic/ChangeLog	(working copy)
@@ -1,3 +1,18 @@ 
+2018-03-29  Florian Weimer  <fweimer@tor.usersys.redhat.com>
+
+	PR libgcc/60790
+	x86: Do not assume ELF constructors run before IFUNC resolvers.
+	* config/x86/host-config.h (libat_feat1_ecx, libat_feat1_edx):
+	Remove declarations.
+	(__libat_feat1, __libat_feat1_init): Declare.
+	(FEAT1_REGISTER): Define.
+	(load_feat1): New function.
+	(IFUNC_COND_1): Adjust.
+	* config/x86/init.c (libat_feat1_ecx, libat_feat1_edx)
+	(init_cpuid): Remove definitions.
+	(__libat_feat1): New variable.
+	(__libat_feat1_init): New function.
+
 2018-03-09  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>
 
 	* config/s390/exch_n.c: New file.
Index: libatomic/config/x86/host-config.h
===================================================================
--- libatomic/config/x86/host-config.h	(revision 258952)
+++ libatomic/config/x86/host-config.h	(working copy)
@@ -25,13 +25,39 @@ 
 #if HAVE_IFUNC
 #include <cpuid.h>
 
-extern unsigned int libat_feat1_ecx HIDDEN;
-extern unsigned int libat_feat1_edx HIDDEN;
+#ifdef __x86_64__
+# define FEAT1_REGISTER ecx
+#else
+# define FEAT1_REGISTER edx
+#endif
 
+/* Value of the CPUID feature FEAT1_REGISTER for the cmpxchg bit for
+   IFUNC_COND1 below.  */
+extern unsigned int __libat_feat1 HIDDEN;
+
+/* Initialize libat_feat1 and return its value.  */
+unsigned int __libat_feat1_init (void) HIDDEN;
+
+/* Return the value of the relevant feature register for the relevant
+   cmpxchg bit, or 0 if there is no CPUID support.  */
+static inline unsigned int
+__attribute__ ((const))
+load_feat1 (void)
+{
+  /* Synchronizes with the store in __libat_feat1_init.  */
+  unsigned int feat1 = __atomic_load_n (&__libat_feat1, __ATOMIC_RELAXED);
+  if (feat1 == 0)
+    /* Assume that initialization has not happened yet.  This may get
+       called repeatedly if the CPU does not have any feature bits at
+       all.  */
+    feat1 = __libat_feat1_init ();
+  return feat1;
+}
+
 #ifdef __x86_64__
-# define IFUNC_COND_1	(libat_feat1_ecx & bit_CMPXCHG16B)
+# define IFUNC_COND_1	(load_feat1 () & bit_CMPXCHG16B)
 #else
-# define IFUNC_COND_1	(libat_feat1_edx & bit_CMPXCHG8B)
+# define IFUNC_COND_1	(load_feat1 () & bit_CMPXCHG8B)
 #endif
 
 #ifdef __x86_64__
Index: libatomic/config/x86/init.c
===================================================================
--- libatomic/config/x86/init.c	(revision 258952)
+++ libatomic/config/x86/init.c	(working copy)
@@ -26,13 +26,17 @@ 
 
 #if HAVE_IFUNC
 
-unsigned int libat_feat1_ecx, libat_feat1_edx;
+unsigned int __libat_feat1;
 
-static void __attribute__((constructor))
-init_cpuid (void)
+unsigned int
+__libat_feat1_init (void)
 {
-  unsigned int eax, ebx;
-  __get_cpuid (1, &eax, &ebx, &libat_feat1_ecx, &libat_feat1_edx);
+  unsigned int eax, ebx, ecx, edx;
+  FEAT1_REGISTER = 0;
+  __get_cpuid (1, &eax, &ebx, &ecx, &edx);
+  /* Synchronizes with the load in load_feat1.  */
+  __atomic_store_n (&__libat_feat1, FEAT1_REGISTER, __ATOMIC_RELAXED);
+  return FEAT1_REGISTER;
 }
 
 #endif /* HAVE_IFUNC */