diff --git a/base/comps/components.toml b/base/comps/components.toml index 368a3b86f41..35ae6afcdb9 100644 --- a/base/comps/components.toml +++ b/base/comps/components.toml @@ -1647,7 +1647,6 @@ includes = ["**/*.comp.toml", "component-check-disablement.toml", "component-min [components.libXp] [components.libXpm] [components.libXpresent] -[components.libabigail] [components.libaccounts-glib] [components.libaccounts-qt] [components.libadwaita] diff --git a/base/comps/libabigail/libabigail.comp.toml b/base/comps/libabigail/libabigail.comp.toml new file mode 100644 index 00000000000..c635d9ffd76 --- /dev/null +++ b/base/comps/libabigail/libabigail.comp.toml @@ -0,0 +1,29 @@ +# The upstream libabigail tarball `libabigail-2.9.tar.xz` (Source0) ships an +# abidiff regression-test fixture set whose two separated-debuginfo files trip +# anti-malware scanning on the AZL RPM-signing pipeline, which rejects +# encrypted / unscannable payloads inside SRPMs: +# +# - tests/data/test-abidiff-exit/PR30329/{old,new}-image/usr/lib/debug/ +# usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6.debug +# Stripped DWARF debuginfo for a pre-built upstream sqlite3 shared +# library, used (with dwz multifile companions) to exercise abidiff +# across separated-debuginfo + dwz layouts. The scanner flags both +# .debug files as "packer_high_entropy:eod". +# +# Replace upstream Source0 with a deterministically-repacked tarball produced +# by base/comps/libabigail/modify_source.sh, which strips the entire +# PR30329/ fixture directory (so nothing in-tree references the missing +# files) and patches tests/test-abidiff-exit.cc to drop the two +# `InOutSpec in_out_specs[]` entries that exercised the fixture, keeping +# `make check` green. The upstream filename is preserved so +# `replace-upstream = true` swaps the entry in place in the Fedora `sources` +# manifest -- no spec edit required. +[components.libabigail] + +[[components.libabigail.source-files]] +filename = "libabigail-2.9.tar.xz" +hash = "526594be5ac79c01406cb33c2cb1b5003f8d2a341dcfb9c007479b89314aa8bea3e58a66605e104d81ab080a1036ff9538e5306c47656af0dc0965f4cbfa0ac0" +hash-type = "SHA512" +origin = { type = "download", uri = "https://azltempstaginglookaside.blob.core.windows.net/repo/pkgs_modified/libabigail/libabigail-2.9.tar.xz/sha512/526594be5ac79c01406cb33c2cb1b5003f8d2a341dcfb9c007479b89314aa8bea3e58a66605e104d81ab080a1036ff9538e5306c47656af0dc0965f4cbfa0ac0/libabigail-2.9.tar.xz" } +replace-upstream = true +replace-reason = "Repacked source tarball with tests/data/test-abidiff-exit/PR30329/ removed (two libsqlite3.so.0.8.6.debug fixtures inside it were flagged as packer_high_entropy:eod by the AZL signing-pipeline AV scanner) and tests/test-abidiff-exit.cc patched to drop the corresponding InOutSpec entries. See modify_source.sh." diff --git a/base/comps/libabigail/modify_source.sh b/base/comps/libabigail/modify_source.sh new file mode 100644 index 00000000000..778905c9777 --- /dev/null +++ b/base/comps/libabigail/modify_source.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash +# +# libabigail: deterministic strip-and-repack of upstream `libabigail-2.9.tar.xz` +# with the PR30329 testsuite fixture set (which trips anti-malware scanning on +# the AZL RPM-signing pipeline) removed, plus the two corresponding entries +# in tests/test-abidiff-exit.cc patched out so `make check` still passes. +# Rationale lives in the comp.toml `replace-reason` field. +# +# Usage: bash base/comps/libabigail/modify_source.sh +# Output: base/build/work/scratch/libabigail/libabigail-2.9.tar.xz (+ .sha512) +# The upstream tarball is cached under a `.upstream` suffix; re-runs reuse it. + +set -euo pipefail + +# Pin umask so the extraction step below produces the same mode bits +# regardless of the caller's umask. With `--no-same-permissions`, tar ANDs +# each entry's mode against `~umask`, so e.g. umask 077 would silently strip +# group/other read bits and change the bytes of the repacked tarball. The +# repack step does not re-assert per-file modes (only owner/group/mtime), so +# this pin is what guarantees a byte-identical output across machines. +umask 022 + +# --- Constants -------------------------------------------------------------- + +readonly COMPONENT="libabigail" +readonly UPSTREAM_VERSION="2.9" +readonly UPSTREAM_FILENAME="${COMPONENT}-${UPSTREAM_VERSION}.tar.xz" +readonly UPSTREAM_TOPDIR="${COMPONENT}-${UPSTREAM_VERSION}" +readonly UPSTREAM_URL="https://mirrors.kernel.org/sourceware/libabigail/${UPSTREAM_FILENAME}" + +readonly UPSTREAM_SHA512="5bdf5ec49a5931a61bf28317b41eee583d6277d00ac621b2d2a97bbc0d816c3662bcfe13a5ac7aeee11c947afb69a5a0a9a8015fcebad09965b45af9b1e23606" + +# Directory (relative to ${UPSTREAM_TOPDIR}) to strip in its entirety. The +# PR30329 fixture set is a libabigail abidiff regression test built around a +# pair of stripped sqlite3 shared libraries + their separated debuginfo + +# dwz-multifile components. The two `libsqlite3.so.0.8.6.debug` separated- +# debuginfo files inside it are flagged as encrypted/unscannable payloads by +# the AV scanner ("packer_high_entropy:eod") in the AZL RPM-signing pipeline. +# We strip the whole PR30329/ directory (not just the two .debug files) so +# nothing in the tarball still references the missing pieces; the two +# corresponding `InOutSpec` entries in tests/test-abidiff-exit.cc are removed +# below so `make check` still passes. +readonly REMOVE_DIRS=( + "tests/data/test-abidiff-exit/PR30329" +) + +# File inside ${UPSTREAM_TOPDIR} to patch, and the marker substring that +# identifies the two `InOutSpec` entries we need to delete from it. Both +# entries reference the PR30329 fixture set we are stripping above. +readonly PATCH_FILE="tests/test-abidiff-exit.cc" +readonly PATCH_MARKER="PR30329" + +# Deterministic-repack mtime: 2020-01-01T00:00:00Z (1577836800). +# Any fixed epoch works; do not change without also bumping the +# `hash` in libabigail.comp.toml. +readonly DETERMINISTIC_MTIME="@1577836800" + +# --- Work directory --------------------------------------------------------- + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)" +WORKDIR="${REPO_ROOT}/base/build/work/scratch/${COMPONENT}" + +mkdir -p "${WORKDIR}" +cd "${WORKDIR}" + +echo "[1/6] Working in ${WORKDIR}" + +# --- Download upstream ------------------------------------------------------ +# +# The upstream tarball is cached under a `.upstream` suffix so that +# the repacked output written at the canonical `${UPSTREAM_FILENAME}` +# path below cannot clobber the cache on re-runs. Treat the cache +# as authoritative only after SHA-512 verification. + +UPSTREAM_CACHE="${WORKDIR}/${UPSTREAM_FILENAME}.upstream" + +if [[ ! -f "${UPSTREAM_CACHE}" ]]; then + echo "[2/6] Downloading ${UPSTREAM_FILENAME} from ${UPSTREAM_URL}" + # `--proto` / `--proto-redir` restrict the initial request *and* any + # redirect target to HTTPS, so a downgrade to plain HTTP is refused. + curl -fsSL --retry 3 \ + --proto '=https' --proto-redir '=https' \ + -o "${UPSTREAM_CACHE}.part" "${UPSTREAM_URL}" + mv "${UPSTREAM_CACHE}.part" "${UPSTREAM_CACHE}" +else + echo "[2/6] Using cached upstream tarball ${UPSTREAM_CACHE}" +fi + +# --- Verify upstream SHA-512 ------------------------------------------------ + +echo "[3/6] Verifying upstream SHA-512" +COMPUTED_UPSTREAM_SHA512="$(sha512sum "${UPSTREAM_CACHE}" | awk '{print $1}')" +if [[ "${COMPUTED_UPSTREAM_SHA512}" != "${UPSTREAM_SHA512}" ]]; then + echo "ERROR: upstream SHA-512 mismatch (cache may be corrupt; delete ${UPSTREAM_CACHE} and re-run)" >&2 + echo " expected: ${UPSTREAM_SHA512}" >&2 + echo " computed: ${COMPUTED_UPSTREAM_SHA512}" >&2 + exit 1 +fi + +# --- Extract + strip -------------------------------------------------------- + +echo "[4/6] Extracting and stripping ${#REMOVE_DIRS[@]} fixture dir(s) from ${UPSTREAM_TOPDIR}" +rm -rf "${WORKDIR}/${UPSTREAM_TOPDIR}" +# `--no-same-owner` / `--no-same-permissions` prevent tar from applying the +# archive's uid/gid/mode bits to the extracted tree. They are already the +# default for non-root users, but explicit hardening makes the script safe +# to run under sudo (where the defaults flip) and defends against any +# setuid/setgid bits or unexpected ownership in the upstream tarball. +# Deterministic owner/group is re-asserted in the repack step below. +tar -C "${WORKDIR}" --no-same-owner --no-same-permissions -xf "${UPSTREAM_CACHE}" +for REMOVE_DIR in "${REMOVE_DIRS[@]}"; do + if [[ ! -d "${WORKDIR}/${UPSTREAM_TOPDIR}/${REMOVE_DIR}" ]]; then + echo "ERROR: expected '${UPSTREAM_TOPDIR}/${REMOVE_DIR}' not present in upstream tarball" >&2 + exit 1 + fi + echo " stripping ${UPSTREAM_TOPDIR}/${REMOVE_DIR}" + rm -rf "${WORKDIR}/${UPSTREAM_TOPDIR}/${REMOVE_DIR}" +done + +# --- Patch testsuite driver ------------------------------------------------- +# +# Remove every `{ ... },` array-initializer block in ${PATCH_FILE} that +# mentions ${PATCH_MARKER}. The testsuite driver in +# tests/test-abidiff-exit.cc declares a hard-coded `InOutSpec in_out_specs[]` +# array; two of its entries reference the PR30329 fixture set we just +# stripped, and would cause `make check` to fail when those fixtures cannot +# be opened. The blocks are delimited by `{` ... `},` with no nested braces, +# so a tiny stateful Python pass is exact and robust. +echo "[5/6] Patching ${PATCH_FILE} to drop entries referencing ${PATCH_MARKER}" +PATCH_TARGET="${WORKDIR}/${UPSTREAM_TOPDIR}/${PATCH_FILE}" +if [[ ! -f "${PATCH_TARGET}" ]]; then + echo "ERROR: expected '${PATCH_FILE}' not present in upstream tarball" >&2 + exit 1 +fi +PRE_COUNT="$(grep -c -F "${PATCH_MARKER}" "${PATCH_TARGET}" || true)" +python3 - "${PATCH_TARGET}" "${PATCH_MARKER}" <<'PY' +import sys, pathlib + +# Operate on bytes (not text) so the rewrite is byte-exact regardless of the +# caller's locale or any newline translation. Path.read_text() / write_text() +# would otherwise default to locale.getencoding() and `newline=None` +# (universal-newline mode), both of which can silently rewrite bytes and +# break deterministic repacking. +path = pathlib.Path(sys.argv[1]) +marker = sys.argv[2].encode("utf-8") +lines = path.read_bytes().splitlines(keepends=True) + +# Scope the edit to the `InOutSpec in_out_specs[] = { ... };` array body. +# Inside that array, individual entries are ` {` ... ` },` blocks with +# no internal `{` / `}` (verified by inspection of upstream 2.9). Outside +# the array we touch nothing, which avoids accidentally eating function +# bodies, struct initializers in other code, etc. +ARRAY_DECL = b"InOutSpec in_out_specs[] =" +start = None +for i, line in enumerate(lines): + if ARRAY_DECL in line: + start = i + break +if start is None: + sys.exit(f"ERROR: {ARRAY_DECL!r} not found in {path}") + +# The opening `{` is the first line at column 0 starting with `{` after +# the declaration; the closing `};` is the first line starting with `};` +# after that. +body_start = next(i for i in range(start, len(lines)) if lines[i].lstrip().startswith(b"{")) +body_end = next(i for i in range(body_start + 1, len(lines)) if lines[i].lstrip().startswith(b"};")) + +prefix = lines[: body_start + 1] +body = lines[body_start + 1 : body_end] +suffix = lines[body_end:] + +out, buf, in_entry = [], [], False +for line in body: + stripped = line.strip() + if not in_entry and stripped == b"{": + in_entry = True + buf = [line] + continue + if in_entry: + buf.append(line) + if stripped == b"},": + block = b"".join(buf) + if marker not in block: + out.extend(buf) + buf = [] + in_entry = False + continue + # Comments / blank lines / preprocessor lines between entries: keep. + out.append(line) + +if in_entry: + sys.exit(f"ERROR: unterminated entry while patching {path}") + +path.write_bytes(b"".join(prefix + out + suffix)) +PY +POST_COUNT="$(grep -c -F "${PATCH_MARKER}" "${PATCH_TARGET}" || true)" +echo " ${PATCH_MARKER} occurrences in ${PATCH_FILE}: ${PRE_COUNT} -> ${POST_COUNT}" +if [[ "${POST_COUNT}" != "0" ]]; then + echo "ERROR: ${PATCH_MARKER} still present in ${PATCH_FILE} after patch" >&2 + exit 1 +fi + +# --- Repack deterministically ----------------------------------------------- + +echo "[6/6] Repacking deterministically as ${UPSTREAM_FILENAME}" +# Deterministic flags: +# --sort=name stable entry order +# --owner=0 --group=0 no host uid/gid leakage +# --numeric-owner force numeric uid/gid +# --mtime=@ fixed mtime +# --format=gnu handles long paths deterministically +# LC_ALL=C pins sort collation so --sort=name is locale-independent. +# xz -9e -T1 picks max compression with single-threaded output (multi-threaded +# xz produces non-deterministic byte streams). The upstream tarball is .xz so +# we re-emit .xz to keep the filename and Source0 unchanged. +MODIFIED_TARBALL="${WORKDIR}/${UPSTREAM_FILENAME}" +rm -f "${MODIFIED_TARBALL}" +LC_ALL=C tar \ + -C "${WORKDIR}" \ + --sort=name \ + --owner=0 --group=0 --numeric-owner \ + --mtime="${DETERMINISTIC_MTIME}" \ + --format=gnu \ + -cf - "${UPSTREAM_TOPDIR}" \ + | xz -9e -T1 -c > "${MODIFIED_TARBALL}" + +MODIFIED_SHA512="$(sha512sum "${MODIFIED_TARBALL}" | awk '{print $1}')" +echo "${MODIFIED_SHA512} ${UPSTREAM_FILENAME}" > "${MODIFIED_TARBALL}.sha512" + +echo +echo "================================================================" +echo "DONE" +echo " modified tarball: ${WORKDIR}/${UPSTREAM_FILENAME}" +echo " SHA512: ${MODIFIED_SHA512}" +echo "================================================================" +echo +echo " To upload the modified tarball to the lookaside:" +echo " az storage blob upload \\" +echo " --auth-mode login \\" +echo " --account-name azltempstaginglookaside \\" +echo " --container-name repo \\" +echo " --name \"pkgs_modified/${COMPONENT}/${UPSTREAM_FILENAME}/sha512/${MODIFIED_SHA512}/${UPSTREAM_FILENAME}\" \\" +echo " --file \"${WORKDIR}/${UPSTREAM_FILENAME}\"" diff --git a/locks/libabigail.lock b/locks/libabigail.lock index c56040b2b5c..5269fec3ade 100644 --- a/locks/libabigail.lock +++ b/locks/libabigail.lock @@ -2,5 +2,5 @@ version = 1 import-commit = 'c90c403e2296469fc2120e6c36d876019dbccdb7' upstream-commit = 'c90c403e2296469fc2120e6c36d876019dbccdb7' -input-fingerprint = 'sha256:5072c84946e03de807482d8749bd233a5b3c48c3a0fe9515d16d992e09e31fec' +input-fingerprint = 'sha256:dcbac274316c99cc77ef32f308472fdba82371f71a53aa8bfcb259c324096b6a' resolution-input-hash = 'sha256:466421704711c4fd3c71f0b2ed715a0e61d49e3e26f3a2637fee755795849c8e' diff --git a/specs/l/libabigail/libabigail.spec b/specs/l/libabigail/libabigail.spec index c7d51554994..e517ae38eb7 100644 --- a/specs/l/libabigail/libabigail.spec +++ b/specs/l/libabigail/libabigail.spec @@ -9,7 +9,7 @@ Name: libabigail Version: 2.9 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Set of ABI analysis tools License: Apache-2.0 WITH LLVM-exception diff --git a/specs/l/libabigail/sources b/specs/l/libabigail/sources index ee4135f100d..9be8806200f 100644 --- a/specs/l/libabigail/sources +++ b/specs/l/libabigail/sources @@ -1 +1 @@ -SHA512 (libabigail-2.9.tar.xz) = 5bdf5ec49a5931a61bf28317b41eee583d6277d00ac621b2d2a97bbc0d816c3662bcfe13a5ac7aeee11c947afb69a5a0a9a8015fcebad09965b45af9b1e23606 +SHA512 (libabigail-2.9.tar.xz) = 526594be5ac79c01406cb33c2cb1b5003f8d2a341dcfb9c007479b89314aa8bea3e58a66605e104d81ab080a1036ff9538e5306c47656af0dc0965f4cbfa0ac0