summaryrefslogtreecommitdiff
path: root/poky/meta/lib
diff options
context:
space:
mode:
Diffstat (limited to 'poky/meta/lib')
-rw-r--r--poky/meta/lib/oe/cve_check.py83
-rw-r--r--poky/meta/lib/oe/overlayfs.py43
-rw-r--r--poky/meta/lib/oe/packagedata.py6
-rw-r--r--poky/meta/lib/oeqa/runtime/cases/date.py9
-rw-r--r--poky/meta/lib/oeqa/sdk/buildtools-cases/README2
-rw-r--r--poky/meta/lib/oeqa/sdk/buildtools-cases/build.py23
-rw-r--r--poky/meta/lib/oeqa/sdk/buildtools-cases/sanity.py22
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/fitimage.py21
-rw-r--r--poky/meta/lib/oeqa/selftest/cases/overlayfs.py171
9 files changed, 365 insertions, 15 deletions
diff --git a/poky/meta/lib/oe/cve_check.py b/poky/meta/lib/oe/cve_check.py
index a1d7c292a..0302beeb4 100644
--- a/poky/meta/lib/oe/cve_check.py
+++ b/poky/meta/lib/oe/cve_check.py
@@ -63,3 +63,86 @@ def _cmpkey(release, patch_l, pre_l, pre_v):
else:
_pre = float(pre_v) if pre_v else float('-inf')
return _release, _patch, _pre
+
+
+def get_patched_cves(d):
+ """
+ Get patches that solve CVEs using the "CVE: " tag.
+ """
+
+ import re
+ import oe.patch
+
+ pn = d.getVar("PN")
+ cve_match = re.compile("CVE:( CVE\-\d{4}\-\d+)+")
+
+ # Matches the last "CVE-YYYY-ID" in the file name, also if written
+ # in lowercase. Possible to have multiple CVE IDs in a single
+ # file name, but only the last one will be detected from the file name.
+ # However, patch files contents addressing multiple CVE IDs are supported
+ # (cve_match regular expression)
+
+ cve_file_name_match = re.compile(".*([Cc][Vv][Ee]\-\d{4}\-\d+)")
+
+ patched_cves = set()
+ bb.debug(2, "Looking for patches that solves CVEs for %s" % pn)
+ for url in oe.patch.src_patches(d):
+ patch_file = bb.fetch.decodeurl(url)[2]
+
+ if not os.path.isfile(patch_file):
+ bb.error("File Not found: %s" % patch_file)
+ raise FileNotFoundError
+
+ # Check patch file name for CVE ID
+ fname_match = cve_file_name_match.search(patch_file)
+ if fname_match:
+ cve = fname_match.group(1).upper()
+ patched_cves.add(cve)
+ bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file))
+
+ with open(patch_file, "r", encoding="utf-8") as f:
+ try:
+ patch_text = f.read()
+ except UnicodeDecodeError:
+ bb.debug(1, "Failed to read patch %s using UTF-8 encoding"
+ " trying with iso8859-1" % patch_file)
+ f.close()
+ with open(patch_file, "r", encoding="iso8859-1") as f:
+ patch_text = f.read()
+
+ # Search for one or more "CVE: " lines
+ text_match = False
+ for match in cve_match.finditer(patch_text):
+ # Get only the CVEs without the "CVE: " tag
+ cves = patch_text[match.start()+5:match.end()]
+ for cve in cves.split():
+ bb.debug(2, "Patch %s solves %s" % (patch_file, cve))
+ patched_cves.add(cve)
+ text_match = True
+
+ if not fname_match and not text_match:
+ bb.debug(2, "Patch %s doesn't solve CVEs" % patch_file)
+
+ return patched_cves
+
+
+def get_cpe_ids(cve_product, version):
+ """
+ Get list of CPE identifiers for the given product and version
+ """
+
+ version = version.split("+git")[0]
+
+ cpe_ids = []
+ for product in cve_product.split():
+ # CVE_PRODUCT in recipes may include vendor information for CPE identifiers. If not,
+ # use wildcard for vendor.
+ if ":" in product:
+ vendor, product = product.split(":", 1)
+ else:
+ vendor = "*"
+
+ cpe_id = f'cpe:2.3:a:{vendor}:{product}:{version}:*:*:*:*:*:*:*'
+ cpe_ids.append(cpe_id)
+
+ return cpe_ids
diff --git a/poky/meta/lib/oe/overlayfs.py b/poky/meta/lib/oe/overlayfs.py
new file mode 100644
index 000000000..21ef71050
--- /dev/null
+++ b/poky/meta/lib/oe/overlayfs.py
@@ -0,0 +1,43 @@
+#
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# This file contains common functions for overlayfs and its QA check
+
+# this function is based on https://github.com/systemd/systemd/blob/main/src/basic/unit-name.c
+def escapeSystemdUnitName(path):
+ escapeMap = {
+ '/': '-',
+ '-': "\\x2d",
+ '\\': "\\x5d"
+ }
+ return "".join([escapeMap.get(c, c) for c in path.strip('/')])
+
+def strForBash(s):
+ return s.replace('\\', '\\\\')
+
+def mountUnitName(unit):
+ return escapeSystemdUnitName(unit) + '.mount'
+
+def helperUnitName(unit):
+ return escapeSystemdUnitName(unit) + '-create-upper-dir.service'
+
+def unitFileList(d):
+ fileList = []
+ overlayMountPoints = d.getVarFlags("OVERLAYFS_MOUNT_POINT")
+
+ if not overlayMountPoints:
+ bb.fatal("A recipe uses overlayfs class but there is no OVERLAYFS_MOUNT_POINT set in your MACHINE configuration")
+
+ # check that we have required mount points set first
+ requiredMountPoints = d.getVarFlags('OVERLAYFS_WRITABLE_PATHS')
+ for mountPoint in requiredMountPoints:
+ if mountPoint not in overlayMountPoints:
+ bb.fatal("Missing required mount point for OVERLAYFS_MOUNT_POINT[%s] in your MACHINE configuration" % mountPoint)
+
+ for mountPoint in overlayMountPoints:
+ for path in d.getVarFlag('OVERLAYFS_WRITABLE_PATHS', mountPoint).split():
+ fileList.append(mountUnitName(path))
+ fileList.append(helperUnitName(path))
+
+ return fileList
+
diff --git a/poky/meta/lib/oe/packagedata.py b/poky/meta/lib/oe/packagedata.py
index 22261d271..0b17897e4 100644
--- a/poky/meta/lib/oe/packagedata.py
+++ b/poky/meta/lib/oe/packagedata.py
@@ -45,14 +45,14 @@ def read_pkgdata(pn, d):
return read_pkgdatafile(fn)
#
-# Collapse FOO_pkg variables into FOO
+# Collapse FOO:pkg variables into FOO
#
def read_subpkgdata_dict(pkg, d):
ret = {}
subd = read_pkgdatafile(get_subpkgedata_fn(pkg, d))
for var in subd:
- newvar = var.replace("_" + pkg, "")
- if newvar == var and var + "_" + pkg in subd:
+ newvar = var.replace(":" + pkg, "")
+ if newvar == var and var + ":" + pkg in subd:
continue
ret[newvar] = subd[var]
return ret
diff --git a/poky/meta/lib/oeqa/runtime/cases/date.py b/poky/meta/lib/oeqa/runtime/cases/date.py
index e14322911..bd6537400 100644
--- a/poky/meta/lib/oeqa/runtime/cases/date.py
+++ b/poky/meta/lib/oeqa/runtime/cases/date.py
@@ -28,14 +28,13 @@ class DateTest(OERuntimeTestCase):
self.assertEqual(status, 0, msg=msg)
oldDate = output
- sampleDate = '"2016-08-09 10:00:00"'
- (status, output) = self.target.run("date -s %s" % sampleDate)
+ sampleTimestamp = 1488800000
+ (status, output) = self.target.run("date -s @%d" % sampleTimestamp)
self.assertEqual(status, 0, msg='Date set failed, output: %s' % output)
- (status, output) = self.target.run("date -R")
- p = re.match('Tue, 09 Aug 2016 10:00:.. \+0000', output)
+ (status, output) = self.target.run('date +"%s"')
msg = 'The date was not set correctly, output: %s' % output
- self.assertTrue(p, msg=msg)
+ self.assertTrue(int(output) - sampleTimestamp < 300, msg=msg)
(status, output) = self.target.run('date -s "%s"' % oldDate)
msg = 'Failed to reset date, output: %s' % output
diff --git a/poky/meta/lib/oeqa/sdk/buildtools-cases/README b/poky/meta/lib/oeqa/sdk/buildtools-cases/README
new file mode 100644
index 000000000..d4f20faa9
--- /dev/null
+++ b/poky/meta/lib/oeqa/sdk/buildtools-cases/README
@@ -0,0 +1,2 @@
+These test cases are used by buildtools-tarball, and are not used by the testsdk
+class.
diff --git a/poky/meta/lib/oeqa/sdk/buildtools-cases/build.py b/poky/meta/lib/oeqa/sdk/buildtools-cases/build.py
new file mode 100644
index 000000000..5a17ab98c
--- /dev/null
+++ b/poky/meta/lib/oeqa/sdk/buildtools-cases/build.py
@@ -0,0 +1,23 @@
+#
+# SPDX-License-Identifier: MIT
+#
+
+import os, tempfile
+from oeqa.sdk.case import OESDKTestCase
+from oeqa.utils.subprocesstweak import errors_have_output
+errors_have_output()
+
+class BuildTests(OESDKTestCase):
+ """
+ Verify that bitbake can build virtual/libc inside the buildtools.
+ """
+ def test_libc(self):
+ with tempfile.TemporaryDirectory(prefix='bitbake-build-', dir=self.tc.sdk_dir) as testdir:
+ corebase = self.td['COREBASE']
+
+ self._run('. %s/oe-init-build-env %s' % (corebase, testdir))
+ with open(os.path.join(testdir, 'conf', 'local.conf'), 'ta') as conf:
+ conf.write('\n')
+ conf.write('DL_DIR = "%s"\n' % self.td['DL_DIR'])
+
+ self._run('. %s/oe-init-build-env %s && bitbake virtual/libc' % (corebase, testdir))
diff --git a/poky/meta/lib/oeqa/sdk/buildtools-cases/sanity.py b/poky/meta/lib/oeqa/sdk/buildtools-cases/sanity.py
new file mode 100644
index 000000000..64baaa8f8
--- /dev/null
+++ b/poky/meta/lib/oeqa/sdk/buildtools-cases/sanity.py
@@ -0,0 +1,22 @@
+#
+# SPDX-License-Identifier: MIT
+#
+
+import shutil
+import os.path
+from oeqa.sdk.case import OESDKTestCase
+
+class SanityTests(OESDKTestCase):
+ def test_tools(self):
+ """
+ Test that wget and tar come from the buildtools, not the host. This
+ verifies that the buildtools have installed correctly. We can't check
+ for gcc as that is only installed by buildtools-extended.
+ """
+ for command in ("tar", "wget"):
+ # Canonicalise the SDK root
+ sdk_base = os.path.realpath(self.tc.sdk_dir)
+ # Canonicalise the location of this command
+ tool_path = os.path.realpath(self._run("command -v %s" % command).strip())
+ # Assert that the tool was found inside the SDK root
+ self.assertEquals(os.path.commonprefix((sdk_base, tool_path)), sdk_base)
diff --git a/poky/meta/lib/oeqa/selftest/cases/fitimage.py b/poky/meta/lib/oeqa/selftest/cases/fitimage.py
index 815ee48c0..184c8778d 100644
--- a/poky/meta/lib/oeqa/selftest/cases/fitimage.py
+++ b/poky/meta/lib/oeqa/selftest/cases/fitimage.py
@@ -114,7 +114,8 @@ KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
-UBOOT_SIGN_KEYNAME = "oe-selftest"
+UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
+UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
"""
@@ -173,11 +174,11 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
reqsigvalues_image = {
'algo': '"sha256,rsa2048"',
- 'key-name-hint': '"oe-selftest"',
+ 'key-name-hint': '"img-oe-selftest"',
}
reqsigvalues_config = {
'algo': '"sha256,rsa2048"',
- 'key-name-hint': '"oe-selftest"',
+ 'key-name-hint': '"cfg-oe-selftest"',
'sign-images': '"kernel", "fdt"',
}
@@ -215,7 +216,10 @@ UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart comment'"
self.assertIn('conf-am335x-boneblack.dtb', signed_sections)
for signed_section, values in signed_sections.items():
value = values.get('Sign algo', None)
- self.assertEqual(value, 'sha256,rsa2048:oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
+ if signed_section.startswith("conf"):
+ self.assertEqual(value, 'sha256,rsa2048:cfg-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
+ else:
+ self.assertEqual(value, 'sha256,rsa2048:img-oe-selftest', 'Signature algorithm for %s not expected value' % signed_section)
value = values.get('Sign value', None)
self.assertEqual(len(value), 512, 'Signature value for section %s not expected length' % signed_section)
@@ -266,7 +270,8 @@ KERNEL_CLASSES = " kernel-fitimage"
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
-UBOOT_SIGN_KEYNAME = "oe-selftest"
+UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
+UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
"""
self.write_config(config)
@@ -348,7 +353,8 @@ KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
-UBOOT_SIGN_KEYNAME = "oe-selftest"
+UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
+UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
UBOOT_MKIMAGE_SIGN_ARGS = "-c 'a smart U-Boot comment'"
"""
@@ -592,7 +598,8 @@ KERNEL_CLASSES = " kernel-fitimage test-mkimage-wrapper "
UBOOT_SIGN_ENABLE = "1"
FIT_GENERATE_KEYS = "1"
UBOOT_SIGN_KEYDIR = "${TOPDIR}/signing-keys"
-UBOOT_SIGN_KEYNAME = "kernel-oe-selftest"
+UBOOT_SIGN_IMG_KEYNAME = "img-oe-selftest"
+UBOOT_SIGN_KEYNAME = "cfg-oe-selftest"
FIT_SIGN_INDIVIDUAL = "1"
"""
self.write_config(config)
diff --git a/poky/meta/lib/oeqa/selftest/cases/overlayfs.py b/poky/meta/lib/oeqa/selftest/cases/overlayfs.py
new file mode 100644
index 000000000..0184d5249
--- /dev/null
+++ b/poky/meta/lib/oeqa/selftest/cases/overlayfs.py
@@ -0,0 +1,171 @@
+#
+# SPDX-License-Identifier: MIT
+#
+
+from oeqa.selftest.case import OESelftestTestCase
+from oeqa.utils.commands import runCmd, bitbake, get_bb_var, runqemu
+
+class OverlayFSTests(OESelftestTestCase):
+ """Overlayfs class usage tests"""
+
+ def getline(self, res, line):
+ for l in res.output.split('\n'):
+ if line in l:
+ return l
+
+ def add_overlay_conf_to_machine(self):
+ machine_inc = """
+OVERLAYFS_MOUNT_POINT[mnt-overlay] = "/mnt/overlay"
+"""
+ self.set_machine_config(machine_inc)
+
+ def test_distro_features_missing(self):
+ """
+ Summary: Check that required DISTRO_FEATURES are set
+ Expected: Fail when either systemd or overlayfs are not in DISTRO_FEATURES
+ Author: Vyacheslav Yurkov <uvv.mail@gmail.com>
+ """
+
+ config = """
+IMAGE_INSTALL:append = " overlayfs-user"
+"""
+ overlayfs_recipe_append = """
+inherit overlayfs
+"""
+ self.write_config(config)
+ self.add_overlay_conf_to_machine()
+ self.write_recipeinc('overlayfs-user', overlayfs_recipe_append)
+
+ res = bitbake('core-image-minimal', ignore_status=True)
+ line = self.getline(res, "overlayfs-user was skipped: missing required distro features")
+ self.assertTrue("overlayfs" in res.output, msg=res.output)
+ self.assertTrue("systemd" in res.output, msg=res.output)
+ self.assertTrue("ERROR: Required build target 'core-image-minimal' has no buildable providers." in res.output, msg=res.output)
+
+ def test_not_all_units_installed(self):
+ """
+ Summary: Test QA check that we have required mount units in the image
+ Expected: Fail because mount unit for overlay partition is not installed
+ Author: Vyacheslav Yurkov <uvv.mail@gmail.com>
+ """
+
+ config = """
+IMAGE_INSTALL:append = " overlayfs-user"
+DISTRO_FEATURES += "systemd overlayfs"
+"""
+
+ self.write_config(config)
+ self.add_overlay_conf_to_machine()
+
+ res = bitbake('core-image-minimal', ignore_status=True)
+ line = self.getline(res, "Unit name mnt-overlay.mount not found in systemd unit directories")
+ self.assertTrue(line and line.startswith("WARNING:"), msg=res.output)
+ line = self.getline(res, "Not all mount units are installed by the BSP")
+ self.assertTrue(line and line.startswith("ERROR:"), msg=res.output)
+
+ def test_mount_unit_not_set(self):
+ """
+ Summary: Test whether mount unit was set properly
+ Expected: Fail because mount unit was not set
+ Author: Vyacheslav Yurkov <uvv.mail@gmail.com>
+ """
+
+ config = """
+IMAGE_INSTALL:append = " overlayfs-user"
+DISTRO_FEATURES += "systemd overlayfs"
+"""
+
+ self.write_config(config)
+
+ res = bitbake('core-image-minimal', ignore_status=True)
+ line = self.getline(res, "A recipe uses overlayfs class but there is no OVERLAYFS_MOUNT_POINT set in your MACHINE configuration")
+ self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output)
+
+ def test_wrong_mount_unit_set(self):
+ """
+ Summary: Test whether mount unit was set properly
+ Expected: Fail because not the correct flag used for mount unit
+ Author: Vyacheslav Yurkov <uvv.mail@gmail.com>
+ """
+
+ config = """
+IMAGE_INSTALL:append = " overlayfs-user"
+DISTRO_FEATURES += "systemd overlayfs"
+"""
+
+ wrong_machine_config = """
+OVERLAYFS_MOUNT_POINT[usr-share-overlay] = "/usr/share/overlay"
+"""
+
+ self.write_config(config)
+ self.set_machine_config(wrong_machine_config)
+
+ res = bitbake('core-image-minimal', ignore_status=True)
+ line = self.getline(res, "Missing required mount point for OVERLAYFS_MOUNT_POINT[mnt-overlay] in your MACHINE configuration")
+ self.assertTrue(line and line.startswith("Parsing recipes...ERROR:"), msg=res.output)
+
+ def test_correct_image(self):
+ """
+ Summary: Check that we can create an image when all parameters are
+ set correctly
+ Expected: Image is created successfully
+ Author: Vyacheslav Yurkov <uvv.mail@gmail.com>
+ """
+
+ config = """
+IMAGE_INSTALL:append = " overlayfs-user systemd-machine-units"
+DISTRO_FEATURES += "systemd overlayfs"
+
+# Use systemd as init manager
+VIRTUAL-RUNTIME_init_manager = "systemd"
+
+# enable overlayfs in the kernel
+KERNEL_EXTRA_FEATURES:append = " features/overlayfs/overlayfs.scc"
+"""
+
+ systemd_machine_unit_append = """
+SYSTEMD_SERVICE:${PN} += " \
+ mnt-overlay.mount \
+"
+
+do_install:append() {
+ install -d ${D}${systemd_system_unitdir}
+ cat <<EOT > ${D}${systemd_system_unitdir}/mnt-overlay.mount
+[Unit]
+Description=Tmpfs directory
+DefaultDependencies=no
+
+[Mount]
+What=tmpfs
+Where=/mnt/overlay
+Type=tmpfs
+Options=mode=1777,strictatime,nosuid,nodev
+
+[Install]
+WantedBy=multi-user.target
+EOT
+}
+
+"""
+
+ self.write_config(config)
+ self.add_overlay_conf_to_machine()
+ self.write_recipeinc('systemd-machine-units', systemd_machine_unit_append)
+
+ bitbake('core-image-minimal')
+
+ def getline_qemu(out, line):
+ for l in out.split('\n'):
+ if line in l:
+ return l
+
+ with runqemu('core-image-minimal') as qemu:
+ # Check that we have /mnt/overlay fs mounted as tmpfs and
+ # /usr/share/my-application as an overlay (see overlayfs-user recipe)
+ status, output = qemu.run_serial("/bin/mount -t tmpfs,overlay")
+
+ line = getline_qemu(output, "on /mnt/overlay")
+ self.assertTrue(line and line.startswith("tmpfs"), msg=output)
+
+ line = getline_qemu(output, "upperdir=/mnt/overlay/upper/usr/share/my-application")
+ self.assertTrue(line and line.startswith("overlay"), msg=output)