Package: dpkg-dev
Version: 1.18.17
Severity: serious
Tags: patch

Hi!

Commit a927295c93fb7a17742441aa863aaffcf4a351b5 introduced a severe
regression related to ignoring the order of the found shared library
pathnames, very carefully curated over time in the find_library()
function. Which I already mentioned as my biggest gut-feeling concern
at the time (for which I should have known better and push back instead
of succumbing to the unpleasant pressure at the timeā€¦).

This was reported on IRC by Helmut Grohne as being detected on one of
the marvelous rebootstrap runs. An easy reproducer is to install on a
clean amd64 system libc6-amd64:i386, and build anything that depends
on libc6. The binary generated might contain a wrong and unsatisifiable
dependency, because it might emit libc6-amd64 (w/o the arch-qualifier,
which does not exist on amd64) instead of just libc6.

I've prepared the most minimal patch I could come up with up to now,
attached, which fixes that specific problem. But I've not given it
much further testing yet. So I'm a bit hesitant to push this into
1.18.24, because it has the potential to break havoc. :( Of course
there's always the other option of reverting the /usr-merge change
altogetherā€¦

Thanks,
Guillem
From aa1ad5755490cb90a309f2d3daf6539d0b4762cb Mon Sep 17 00:00:00 2001
From: Guillem Jover <guil...@debian.org>
Date: Sun, 23 Apr 2017 04:51:58 +0200
Subject: [PATCH] dpkg-shlibdeps: Preserve the order when scanning
 symbols/shlibs files

Regression introduced in commit a927295c93fb7a17742441aa863aaffcf4a351b5.

The code was getting all the possible shared library pathnames for the
wanted SONAME, but was not preserving the order carefully constructed
in find_library(), so we were overwriting symbols/shlibs information
when parsing multiple entries, and selecting the symbols/shlibs files
randomly based on the perl hash order.

This causes regressions when multiple packages provides the same
SONAME on different directories. An example would be libc6:amd64
and libc6-amd64:i386.

Closes: #
Reported-by: Helmut Grohne <hel...@subdivi.de>
---
 scripts/dpkg-shlibdeps.pl | 74 ++++++++++++++++++++++++++++++++---------------
 1 file changed, 51 insertions(+), 23 deletions(-)

diff --git a/scripts/dpkg-shlibdeps.pl b/scripts/dpkg-shlibdeps.pl
index 0978f33b9..b314202f1 100755
--- a/scripts/dpkg-shlibdeps.pl
+++ b/scripts/dpkg-shlibdeps.pl
@@ -191,6 +191,7 @@ foreach my $file (keys %exec) {
     # Load symbols files for all needed libraries (identified by SONAME)
     my %libfiles;
     my %altlibfiles;
+    my %soname_libs;
     my %soname_notfound;
     my %alt_soname;
     foreach my $soname (@sonames) {
@@ -209,6 +210,11 @@ foreach my $file (keys %exec) {
 	    }
 	    next;
 	}
+
+	# Track shared libraries for a given SONAME.
+	push @{$soname_libs{$soname}}, @libs;
+
+	# Track shared libraries for package mapping.
 	foreach my $lib (@libs) {
 	    $libfiles{$lib} = $soname;
 	    my $reallib = realpath($lib);
@@ -222,9 +228,11 @@ foreach my $file (keys %exec) {
     my $symfile = Dpkg::Shlibs::SymbolFile->new();
     my $dumplibs_wo_symfile = Dpkg::Shlibs::Objdump->new();
     my @soname_wo_symfile;
-    foreach my $lib (keys %libfiles) {
-	my $soname = $libfiles{$lib};
+    SONAME: foreach my $soname (@sonames) {
+      # Select the first good entry from the ordered list that we got from
+      # find_library(), and skip to the next SONAME.
 
+      foreach my $lib (@{$soname_libs{$soname}}) {
 	if (none { $_ ne '' } @{$file2pkg->{$lib}}) {
 	    # The path of the library as calculated is not the
 	    # official path of a packaged file, try to fallback on
@@ -244,6 +252,7 @@ foreach my $file (keys %exec) {
 	}
 
 	# Load symbols/shlibs files from packages providing libraries
+	my $missing_wanted_shlibs_info = 0;
 	foreach my $pkg (@{$file2pkg->{$lib}}) {
 	    my $symfile_path;
             my $haslocaldep = 0;
@@ -273,6 +282,9 @@ foreach my $file (keys %exec) {
 		my $minver = $symfile->get_smallest_version($soname) || '';
 		update_dependency_version($dep, $minver);
 		debug(2, " Minimal version of ($dep) initialized with ($minver)");
+
+		# Found a symbols file for the SONAME.
+		next SONAME;
 	    } else {
 		# No symbol file found, fall back to standard shlibs
                 debug(1, "Using shlibs+objdump for $soname (file $lib)");
@@ -284,31 +296,47 @@ foreach my $file (keys %exec) {
 		    $alt_soname{$id} = $soname;
 		}
 		push @soname_wo_symfile, $soname;
+
 		# Only try to generate a dependency for libraries with a SONAME
-		if ($libobj->is_public_library() and not
-		    add_shlibs_dep($soname, $pkg, $lib)) {
-		    # This failure is fairly new, try to be kind by
-		    # ignoring as many cases that can be safely ignored
-		    my $ignore = 0;
-		    # 1/ when the lib and the binary are in the same
-		    # package
-		    my $root_file = guess_pkg_root_dir($file);
-		    my $root_lib = guess_pkg_root_dir($lib);
-		    $ignore++ if defined $root_file and defined $root_lib
-			and check_files_are_the_same($root_file, $root_lib);
-		    # 2/ when the lib is not versioned and can't be
-		    # handled by shlibs
-		    $ignore++ unless scalar(split_soname($soname));
-		    # 3/ when we have been asked to do so
-		    $ignore++ if $ignore_missing_info;
-		    error(g_('no dependency information found for %s ' .
-		             "(used by %s)\n" .
-		             'Hint: check if the library actually comes ' .
-		             'from a package.'), $lib, $file)
-		        unless $ignore;
+		if (not $libobj->is_public_library()) {
+		    debug(1, "Skipping shlibs+objdump info for private library $lib");
+		    next;
 		}
+
+		# If we found a shlibs file for the SONAME, skip to the next.
+		next SONAME if add_shlibs_dep($soname, $pkg, $lib);
+
+		$missing_wanted_shlibs_info = 1;
+
+		debug(1, "No shlibs+objdump info available, trying next package for $lib");
 	    }
 	}
+
+	next if not $missing_wanted_shlibs_info;
+
+        # We will only reach this point, if we have found no symbols nor
+        # shlibs files for the given SONAME.
+
+        # This failure is fairly new, try to be kind by
+        # ignoring as many cases that can be safely ignored
+        my $ignore = 0;
+        # 1/ when the lib and the binary are in the same
+        # package
+        my $root_file = guess_pkg_root_dir($file);
+        my $root_lib = guess_pkg_root_dir($lib);
+        $ignore++ if defined $root_file and defined $root_lib
+            and check_files_are_the_same($root_file, $root_lib);
+        # 2/ when the lib is not versioned and can't be
+        # handled by shlibs
+        $ignore++ unless scalar(split_soname($soname));
+        # 3/ when we have been asked to do so
+        $ignore++ if $ignore_missing_info;
+        error(g_('no dependency information found for %s ' .
+                 "(used by %s)\n" .
+                 'Hint: check if the library actually comes ' .
+                 'from a package.'), $lib, $file)
+            unless $ignore;
+      }
     }
 
     # Scan all undefined symbols of the binary and resolve to a
-- 
2.12.2.816.g2cccc81164

Reply via email to