elf: Fix glibc-hwcaps priorities with cache flags mismatches [BZ #27046]
authorFlorian Weimer <fweimer@redhat.com>
Fri, 25 Jun 2021 06:02:30 +0000 (08:02 +0200)
committerFlorian Weimer <fweimer@redhat.com>
Fri, 25 Jun 2021 06:02:33 +0000 (08:02 +0200)
If lib->flags (in the cache) did not match GLRO (dl_correct_cache_id),
searching for further glibc-hwcaps entries did not happen, and it
was possible that the best glibc-hwcaps was not found.  By accident,
this causes a test failure for elf/tst-glibc-hwcaps-prepend-cache
on armv7l.

This commit changes the cache lookup logic to continue searching
if (a) no match has been found, (b) a named glibc-hwcaps match
has been found(), or (c) non-glibc-hwcaps match has been found
and the entry flags and cache default flags do not match.

_DL_CACHE_DEFAULT_ID is used instead of GLRO (dl_correct_cache_id)
because the latter is only written once on i386 if loading
of libc.so.5 libraries is selected, so GLRO (dl_correct_cache_id)
should probably removed in a future change.

Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
elf/dl-cache.c

index 32f3bef5ead040791eb4baad02568ad3e573d1d7..2b8da8650d0a96e55dc3e9bc350fe48117db0a17 100644 (file)
@@ -269,81 +269,77 @@ search_cache (const char *string_table, uint32_t string_table_size,
              if (_dl_cache_check_flags (flags)
                  && _dl_cache_verify_ptr (lib->value, string_table_size))
                {
-                 if (best == NULL || flags == GLRO (dl_correct_cache_id))
-                   {
-                     /* Named/extension hwcaps get slightly different
-                        treatment: We keep searching for a better
-                        match.  */
-                     bool named_hwcap = false;
+                 /* Named/extension hwcaps get slightly different
+                    treatment: We keep searching for a better
+                    match.  */
+                 bool named_hwcap = false;
 
-                     if (entry_size >= sizeof (struct file_entry_new))
-                       {
-                         /* The entry is large enough to include
-                            HWCAP data.  Check it.  */
-                         struct file_entry_new *libnew
-                           = (struct file_entry_new *) lib;
+                 if (entry_size >= sizeof (struct file_entry_new))
+                   {
+                     /* The entry is large enough to include
+                        HWCAP data.  Check it.  */
+                     struct file_entry_new *libnew
+                       = (struct file_entry_new *) lib;
 
 #ifdef SHARED
-                         named_hwcap = dl_cache_hwcap_extension (libnew);
-                         if (named_hwcap
-                             && !dl_cache_hwcap_isa_level_compatible (libnew))
-                           continue;
+                     named_hwcap = dl_cache_hwcap_extension (libnew);
+                     if (named_hwcap
+                         && !dl_cache_hwcap_isa_level_compatible (libnew))
+                       continue;
 #endif
 
-                         /* The entries with named/extension hwcaps
-                            have been exhausted.  Return the best
-                            match encountered so far if there is
-                            one.  */
-                         if (!named_hwcap && best != NULL)
-                           break;
+                     /* The entries with named/extension hwcaps have
+                        been exhausted (they are listed before all
+                        other entries).  Return the best match
+                        encountered so far if there is one.  */
+                     if (!named_hwcap && best != NULL)
+                       break;
 
-                         if ((libnew->hwcap & hwcap_exclude) && !named_hwcap)
-                           continue;
-                         if (GLRO (dl_osversion)
-                             && libnew->osversion > GLRO (dl_osversion))
-                           continue;
-                         if (_DL_PLATFORMS_COUNT
-                             && (libnew->hwcap & _DL_HWCAP_PLATFORM) != 0
-                             && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
-                                 != platform))
-                           continue;
+                     if ((libnew->hwcap & hwcap_exclude) && !named_hwcap)
+                       continue;
+                     if (GLRO (dl_osversion)
+                         && libnew->osversion > GLRO (dl_osversion))
+                       continue;
+                     if (_DL_PLATFORMS_COUNT
+                         && (libnew->hwcap & _DL_HWCAP_PLATFORM) != 0
+                         && ((libnew->hwcap & _DL_HWCAP_PLATFORM)
+                             != platform))
+                       continue;
 
 #ifdef SHARED
-                         /* For named hwcaps, determine the priority
-                            and see if beats what has been found so
-                            far.  */
-                         if (named_hwcap)
-                           {
-                             uint32_t entry_priority
-                               = glibc_hwcaps_priority (libnew->hwcap);
-                             if (entry_priority == 0)
-                               /* Not usable at all.  Skip.  */
-                               continue;
-                             else if (best == NULL
-                                      || entry_priority < best_priority)
-                               /* This entry is of higher priority
-                                  than the previous one, or it is the
-                                  first entry.  */
-                               best_priority = entry_priority;
-                             else
-                               /* An entry has already been found,
-                                  but it is a better match.  */
-                               continue;
-                           }
-#endif /* SHARED */
+                     /* For named hwcaps, determine the priority and
+                        see if beats what has been found so far.  */
+                     if (named_hwcap)
+                       {
+                         uint32_t entry_priority
+                           = glibc_hwcaps_priority (libnew->hwcap);
+                         if (entry_priority == 0)
+                           /* Not usable at all.  Skip.  */
+                           continue;
+                         else if (best == NULL
+                                  || entry_priority < best_priority)
+                           /* This entry is of higher priority
+                              than the previous one, or it is the
+                              first entry.  */
+                           best_priority = entry_priority;
+                         else
+                           /* An entry has already been found,
+                              but it is a better match.  */
+                           continue;
                        }
+#endif /* SHARED */
+                   }
 
-                     best = string_table + lib->value;
+                 best = string_table + lib->value;
 
-                     if (flags == GLRO (dl_correct_cache_id)
-                         && !named_hwcap)
-                       /* We've found an exact match for the shared
-                          object and no general `ELF' release.  Stop
-                          searching, but not if a named (extension)
-                          hwcap is used.  In this case, an entry with
-                          a higher priority may come up later.  */
-                       break;
-                   }
+                 if (!named_hwcap && flags == _DL_CACHE_DEFAULT_ID)
+                   /* With named hwcaps, we need to keep searching to
+                      see if we find a better match.  A better match
+                      is also possible if the flags of the current
+                      entry do not match the expected cache flags.
+                      But if the flags match, no better entry will be
+                      found.  */
+                   break;
                }
            }
          while (++middle <= right);