Patchwork [CONSOLIDATED,PULL,19/26] insane.bbclass: add QA tests for unsafe references to exec_prefix

login
register
mail settings
Submitter Saul Wold
Date Dec. 22, 2011, 7:55 a.m.
Message ID <d4299e7c7d0c584cc0b1a0663111a9b328a73266.1324540323.git.sgw@linux.intel.com>
Download mbox | patch
Permalink /patch/17443/
State New
Headers show

Comments

Saul Wold - Dec. 22, 2011, 7:55 a.m.
From: Scott Garman <scott.a.garman@intel.com>

Files under exec_prefix (commonly /usr) may not be available during
system recovery. exec_prefix may also be kept on a separate partition
that is mounted late in the boot process.

This QA test throws an warning if a binary in base_[bindir|sbindir|libdir]
is dynamically linked to a file under exec_prefix. The intention is to
turn this into an error in the near future.

It also checks executable non-binaries (e.g, shell scripts) in the above
base directories with a simple grep test to look for references to
exec_prefix. This test only produces a warning, since false positives
are likely.

This fixes [YOCTO #1008]

Signed-off-by: Scott Garman <scott.a.garman@intel.com>
---
 meta/classes/insane.bbclass |  111 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 109 insertions(+), 2 deletions(-)
Phil Blundell - Dec. 22, 2011, 9:02 a.m.
On Wed, 2011-12-21 at 23:55 -0800, Saul Wold wrote:
> +		exec_prefix = d.getVar('exec_prefix', True)
> +		sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
> +		sysroot_path_usr = sysroot_path + exec_prefix
> +
> +		try:
> +			ldd_output = sub.check_output(["prelink-rtld", "--root", sysroot_path, path])
> +		except sub.CalledProcessError as e:
> +			if e.returncode != 127:
> +				error_msg = pn + ": prelink-rtld aborted when processing %s" % path
> +				package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
> +				return False
> +			else:
> +				# Sometimes this is done deliberately (e.g, e2fsprogs), so only warn
> +				bb.warn("%s has missing library dependencies" % path)
> +				return
> +		if sysroot_path_usr in ldd_output:
> +			error_msg = pn + ": %s links to something under exec_prefix" % path
> +			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
> +			error_msg = "ldd reports: %s" % ldd_output
> +			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
> +			return False

Is that going to do the right thing if ${prefix} == ${exec_prefix}?
It's not obvious to me that it will handle that case correctly.

p.
Scott Garman - Dec. 22, 2011, 4:16 p.m.
On 12/22/2011 01:02 AM, Phil Blundell wrote:
> On Wed, 2011-12-21 at 23:55 -0800, Saul Wold wrote:
>> +		exec_prefix = d.getVar('exec_prefix', True)
>> +		sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
>> +		sysroot_path_usr = sysroot_path + exec_prefix
>> +
>> +		try:
>> +			ldd_output = sub.check_output(["prelink-rtld", "--root", sysroot_path, path])
>> +		except sub.CalledProcessError as e:
>> +			if e.returncode != 127:
>> +				error_msg = pn + ": prelink-rtld aborted when processing %s" % path
>> +				package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
>> +				return False
>> +			else:
>> +				# Sometimes this is done deliberately (e.g, e2fsprogs), so only warn
>> +				bb.warn("%s has missing library dependencies" % path)
>> +				return
>> +		if sysroot_path_usr in ldd_output:
>> +			error_msg = pn + ": %s links to something under exec_prefix" % path
>> +			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
>> +			error_msg = "ldd reports: %s" % ldd_output
>> +			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
>> +			return False
>
> Is that going to do the right thing if ${prefix} == ${exec_prefix}?
> It's not obvious to me that it will handle that case correctly.

The function unsafe_references_skippable checks if exec_prefix == "" and 
also if base_bindir == bindir and base_sbindir == sbindir and 
base_libdir == libdir. Would that cover this case, or should this check 
be made explicit?

Scott

Patch

diff --git a/meta/classes/insane.bbclass b/meta/classes/insane.bbclass
index 5726e69..b8d4507 100644
--- a/meta/classes/insane.bbclass
+++ b/meta/classes/insane.bbclass
@@ -11,6 +11,10 @@ 
 #  -Check if packages contains .debug directories or .so files
 #   where they should be in -dev or -dbg
 #  -Check if config.log contains traces to broken autoconf tests
+#  -Ensure that binaries in base_[bindir|sbindir|libdir] do not link
+#   into exec_prefix
+#  -Check that scripts in base_[bindir|sbindir|libdir] do not reference
+#   files under exec_prefix
 
 
 #
@@ -19,9 +23,14 @@ 
 # The package.bbclass can help us here.
 #
 inherit package
-PACKAGE_DEPENDS += "pax-utils-native desktop-file-utils-native"
+PACKAGE_DEPENDS += "pax-utils-native desktop-file-utils-native ${QADEPENDS}"
 PACKAGEFUNCS += " do_package_qa "
 
+# unsafe-references-in-binaries requires prelink-rtld from
+# prelink-native, but we don't want this DEPENDS for -native builds
+QADEPENDS = "prelink-native"
+QADEPENDS_virtclass-native = ""
+QADEPENDS_virtclass-nativesdk = ""
 
 #
 # dictionary for elf headers
@@ -100,7 +109,7 @@  def package_qa_get_machine_dict():
 
 
 # Currently not being used by default "desktop"
-WARN_QA ?= "ldflags useless-rpaths rpaths"
+WARN_QA ?= "ldflags useless-rpaths rpaths unsafe-references-in-binaries unsafe-references-in-scripts"
 ERROR_QA ?= "dev-so debug-deps dev-deps debug-files arch la2 pkgconfig la perms"
 
 def package_qa_clean_path(path,d):
@@ -201,6 +210,104 @@  def package_qa_check_perm(path,name,d, elf, messages):
     """
     return
 
+QAPATHTEST[unsafe-references-in-binaries] = "package_qa_check_unsafe_references_in_binaries"
+def package_qa_check_unsafe_references_in_binaries(path, name, d, elf, messages):
+	"""
+	Ensure binaries in base_[bindir|sbindir|libdir] do not link to files under exec_prefix
+	"""
+	if unsafe_references_skippable(path, name, d):
+		return
+
+	if elf:
+		import subprocess as sub
+		pn = d.getVar('PN', True)
+
+		exec_prefix = d.getVar('exec_prefix', True)
+		sysroot_path = d.getVar('STAGING_DIR_TARGET', True)
+		sysroot_path_usr = sysroot_path + exec_prefix
+
+		try:
+			ldd_output = sub.check_output(["prelink-rtld", "--root", sysroot_path, path])
+		except sub.CalledProcessError as e:
+			if e.returncode != 127:
+				error_msg = pn + ": prelink-rtld aborted when processing %s" % path
+				package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
+				return False
+			else:
+				# Sometimes this is done deliberately (e.g, e2fsprogs), so only warn
+				bb.warn("%s has missing library dependencies" % path)
+				return
+		if sysroot_path_usr in ldd_output:
+			error_msg = pn + ": %s links to something under exec_prefix" % path
+			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
+			error_msg = "ldd reports: %s" % ldd_output
+			package_qa_handle_error("unsafe-references-in-binaries", error_msg, d)
+			return False
+
+QAPATHTEST[unsafe-references-in-scripts] = "package_qa_check_unsafe_references_in_scripts"
+def package_qa_check_unsafe_references_in_scripts(path, name, d, elf, messages):
+	"""
+	Warn if scripts in base_[bindir|sbindir|libdir] reference files under exec_prefix
+	"""
+	if unsafe_references_skippable(path, name, d):
+		return
+
+	if not elf:
+		import stat
+		pn = d.getVar('PN', True)
+
+		# Ensure we're checking an executable script
+		statinfo = os.stat(path)
+		if bool(statinfo.st_mode & stat.S_IXUSR):
+			# grep shell scripts for possible references to /exec_prefix/
+			exec_prefix = d.getVar('exec_prefix', True)
+			statement = "grep -e '%s/' %s > /dev/null" % (exec_prefix, path)
+			if os.system(statement) == 0:
+				error_msg = pn + ": Found a reference to %s/ in %s" % (exec_prefix, path)
+				package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
+				error_msg = "Shell scripts in base_bindir and base_sbindir should not reference anything in exec_prefix"
+				package_qa_handle_error("unsafe-references-in-scripts", error_msg, d)
+
+def unsafe_references_skippable(path, name, d):
+	if bb.data.inherits_class('native', d) or bb.data.inherits_class('nativesdk', d):
+		return True
+
+	if "-dbg" in name or "-dev" in name:
+		return True
+
+	# Other package names to skip:
+	if name.startswith("kernel-module-"):
+		return True
+
+	# Skip symlinks
+	if os.path.islink(path):
+		return True
+
+	# Skip unusual rootfs layouts which make these tests irrelevant
+	exec_prefix = d.getVar('exec_prefix', True)
+	if exec_prefix == "":
+		return True
+
+	pkgdest = d.getVar('PKGDEST', True)
+	pkgdest = pkgdest + "/" + name
+	pkgdest = os.path.abspath(pkgdest)
+	base_bindir = pkgdest + d.getVar('base_bindir', True)
+	base_sbindir = pkgdest + d.getVar('base_sbindir', True)
+	base_libdir = pkgdest + d.getVar('base_libdir', True)
+	bindir = pkgdest + d.getVar('bindir', True)
+	sbindir = pkgdest + d.getVar('sbindir', True)
+	libdir = pkgdest + d.getVar('libdir', True)
+
+	if base_bindir == bindir and base_sbindir == sbindir and base_libdir == libdir:
+		return True
+
+	# Skip files not in base_[bindir|sbindir|libdir]
+	path = os.path.abspath(path)
+	if not (base_bindir in path or base_sbindir in path or base_libdir in path):
+		return True
+
+	return False
+
 QAPATHTEST[arch] = "package_qa_check_arch"
 def package_qa_check_arch(path,name,d, elf, messages):
     """