livepatching: add automation for livepatch packages. (#3369)

This commit is contained in:
Pawel Winogrodzki 2022-09-28 15:20:31 -07:00 коммит произвёл GitHub
Родитель 7d10a93897
Коммит 6518af92a9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 840 добавлений и 34 удалений

Просмотреть файл

@ -1,13 +1,16 @@
Summary: Kpatch tooling
Name: kpatch
Version: 0.9.6
Release: 2%{?dist}
Release: 4%{?dist}
License: GPLv2
Vendor: Microsoft Corporation
Distribution: Mariner
Group: System Environment/Base
URL: https://github.com/dynup/kpatch
Source0: %{url}/archive/refs/tags/v%{version}.tar.gz#/%{name}-%{version}.tar.gz
Patch0: mariner_update.patch
ExclusiveArch: x86_64
BuildRequires: binutils
BuildRequires: dnf-utils
@ -17,7 +20,8 @@ BuildRequires: gcc
BuildRequires: glibc-devel
BuildRequires: kernel-headers
ExclusiveArch: x86_64
Requires: binutils
Requires: gawk
%description
Kpatch is a Linux dynamic kernel patching infrastructure which allows you to patch
@ -31,9 +35,11 @@ It gives more control over uptime without sacrificing security or stability.
Summary: Tools for building livepatches with kpatch.
Group: Development/Tools
Requires: mariner-release
Requires: numactl-devel
Requires: openssl
Requires: patch
Requires: perl
Requires: rpm-build
Requires: wget
@ -41,7 +47,7 @@ Requires: wget
%{summary}
%prep
%autosetup
%autosetup -p1
%build
%make_build
@ -72,6 +78,12 @@ rm -rf %{buildroot}%{_mandir}
%{_bindir}/kpatch-build
%changelog
* Mon Aug 01 2022 Pawel Winogrodzki <pawelwi@microsoft.com> - 0.9.6-4
- Adding missing dependency on "awk" and "binutils".
* Thu Jun 30 2022 Pawel Winogrodzki <pawelwi@microsoft.com> - 0.9.6-3
- Add Mariner-specific steps to 'kpatch-build'.
* Fri Jun 17 2022 Jon Slobodzian <joslobo@microsoft.com> - 0.9.6-2
- Fix ARM64 build break (exclusive to AMD64)

Просмотреть файл

@ -0,0 +1,139 @@
From 4f97bfb7590a10b6e8b267dc1f595a57a8d9659f Mon Sep 17 00:00:00 2001
From: Pawel Winogrodzki <pawelwi@microsoft.com>
Date: Wed, 15 Jun 2022 10:51:00 -0700
Subject: [PATCH] Adjustments for CBL-Mariner.
---
kpatch-build/kpatch-build | 61 ++++++++++++++++++++-------------------
1 file changed, 31 insertions(+), 30 deletions(-)
diff --git a/kpatch-build/kpatch-build b/kpatch-build/kpatch-build
index 634095d..7766e0d 100755
--- a/kpatch-build/kpatch-build
+++ b/kpatch-build/kpatch-build
@@ -205,21 +205,6 @@ rhel_kernel_version_gte() {
[ "${ARCHVERSION}" = "$(echo -e "${ARCHVERSION}\\n$1" | sort -rV | head -n1)" ]
}
-# klp.arch relocations were supported prior to v5.8
-# and prior to 4.18.0-240.el8
-use_klp_arch()
-{
- if kernel_is_rhel; then
- ! rhel_kernel_version_gte 4.18.0-240.el8
- else
- ! kernel_version_gte 5.8.0
- fi
-}
-
-rhel_kernel_version_gte() {
- [ "${ARCHVERSION}" = "$(echo -e "${ARCHVERSION}\\n$1" | sort -rV | head -n1)" ]
-}
-
# klp.arch relocations were supported prior to v5.8
# and prior to 4.18.0-284.el8
use_klp_arch()
@@ -638,13 +623,23 @@ if [[ $DEBUG -eq 1 ]] || [[ $DEBUG -ge 3 ]]; then
set -o xtrace
fi
+# Don't check external file.
+# shellcheck disable=SC1090
+if [[ -z "$USERSRCDIR" ]] && [[ -f "$RELEASE_FILE" ]]; then
+ source "$RELEASE_FILE"
+ DISTRO="$ID"
+fi
+
if [[ -n "$SRCRPM" ]]; then
if [[ -n "$ARCHVERSION" ]]; then
warn "--archversion is incompatible with --sourcerpm"
exit 1
fi
rpmname="$(basename "$SRCRPM")"
- ARCHVERSION="${rpmname%.src.rpm}.$(uname -m)"
+ ARCHVERSION="${rpmname%.src.rpm}"
+ if [[ "$DISTRO" != mariner ]]; then
+ ARCHVERSION="${ARCHVERSION}.$(uname -m)"
+ fi
ARCHVERSION="${ARCHVERSION#kernel-}"
ARCHVERSION="${ARCHVERSION#alt-}"
fi
@@ -699,20 +694,15 @@ fi
KVER="${ARCHVERSION%%-*}"
if [[ "$ARCHVERSION" =~ - ]]; then
KREL="${ARCHVERSION##*-}"
- KREL="${KREL%.*}"
+ if [[ "$DISTRO" != mariner ]]; then
+ KREL="${KREL%.*}"
+ fi
fi
[[ "$ARCHVERSION" =~ .el7a. ]] && ALT="-alt"
[[ -z "$TARGETS" ]] && TARGETS="vmlinux modules"
-# Don't check external file.
-# shellcheck disable=SC1090
-if [[ -z "$USERSRCDIR" ]] && [[ -f "$RELEASE_FILE" ]]; then
- source "$RELEASE_FILE"
- DISTRO="$ID"
-fi
-
-if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]]; then
+if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = openEuler ]] || [[ "$DISTRO" = mariner ]]; then
[[ -z "$VMLINUX" ]] && VMLINUX="/usr/lib/debug/lib/modules/$ARCHVERSION/vmlinux"
[[ -e "$VMLINUX" ]] || die "kernel-debuginfo-$ARCHVERSION not installed"
@@ -748,9 +738,8 @@ elif [[ -n "$OOT_MODULE" ]]; then
fi
elif [[ -e "$KERNEL_SRCDIR"/.config ]] && [[ -e "$VERSIONFILE" ]] && [[ "$(cat "$VERSIONFILE")" = "$ARCHVERSION" ]]; then
echo "Using cache at $KERNEL_SRCDIR"
-
else
- if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]]; then
+ if [[ "$DISTRO" = fedora ]] || [[ "$DISTRO" = rhel ]] || [[ "$DISTRO" = ol ]] || [[ "$DISTRO" = centos ]] || [[ "$DISTRO" = mariner ]]; then
echo "Fedora/Red Hat distribution detected"
@@ -760,6 +749,9 @@ else
if [[ -z "$SRCRPM" ]]; then
if [[ "$DISTRO" = fedora ]]; then
wget -P "$TEMPDIR" "http://kojipkgs.fedoraproject.org/packages/kernel/$KVER/$KREL/src/kernel-$KVER-$KREL.src.rpm" 2>&1 | logger || die
+ elif [[ "$DISTRO" = mariner ]]; then
+ source "$RELEASE_FILE"
+ wget -P "$TEMPDIR" "https://packages.microsoft.com/cbl-mariner/$VERSION_ID/prod/base/srpms/kernel-$KVER-$KREL.src.rpm" 2>&1 | logger || die
else
command -v yumdownloader &>/dev/null || die "yumdownloader (yum-utils or dnf-utils) not installed"
yumdownloader --source --destdir "$TEMPDIR" "kernel$ALT-$KVER-$KREL" 2>&1 | logger || die
@@ -773,17 +765,26 @@ else
rpmbuild -D "_topdir $RPMTOPDIR" -bp --nodeps "--target=$(uname -m)" "$RPMTOPDIR"/SPECS/kernel$ALT.spec 2>&1 | logger ||
die "rpmbuild -bp failed. you may need to run 'yum-builddep kernel' first."
- mv "$RPMTOPDIR"/BUILD/kernel-*/linux-* "$KERNEL_SRCDIR" 2>&1 | logger || die
+ if [[ "$DISTRO" = mariner ]]; then
+ mv "$RPMTOPDIR"/BUILD/CBL-Mariner-Linux-Kernel-* "$KERNEL_SRCDIR" 2>&1 | logger || die
+ else
+ mv "$RPMTOPDIR"/BUILD/kernel-*/linux-* "$KERNEL_SRCDIR" 2>&1 | logger || die
+ fi
rm -rf "$RPMTOPDIR"
rm -rf "$KERNEL_SRCDIR/.git"
- if [[ "$ARCHVERSION" == *-* ]]; then
+ if [[ "$DISTRO" != mariner ]] && [[ "$ARCHVERSION" == *-* ]]; then
sed -i "s/^EXTRAVERSION.*/EXTRAVERSION = -${ARCHVERSION##*-}/" "$KERNEL_SRCDIR/Makefile" || die
fi
echo "$ARCHVERSION" > "$VERSIONFILE" || die
- [[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR/configs/kernel$ALT-$KVER-$ARCH.config"
+ if [[ "$DISTRO" = mariner ]]; then
+ [[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR/new_config"
+ sed -i "s/CONFIG_LOCALVERSION=\"\"/CONFIG_LOCALVERSION=\"-$KREL\"/" "$CONFIGFILE"
+ else
+ [[ -z "$CONFIGFILE" ]] && CONFIGFILE="$KERNEL_SRCDIR/configs/kernel$ALT-$KVER-$ARCH.config"
+ fi
(cd "$KERNEL_SRCDIR" && make mrproper 2>&1 | logger) || die
--
2.34.1

Просмотреть файл

@ -0,0 +1,55 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
REPO_ROOT="$(git rev-parse --show-toplevel)"
COMMON_SCRIPTS_FOLDER="$REPO_ROOT/toolkit/scripts"
export PATH="$PATH:$COMMON_SCRIPTS_FOLDER"
# shellcheck source=../../toolkit/scripts/specs/specs_tools.sh
source "$COMMON_SCRIPTS_FOLDER/specs/specs_tools.sh"
function update_manifests {
local kernel_release_number
local package_manifests_dir
kernel_release_number="$1"
echo "Updating package manifests."
package_manifests_dir="$REPO_ROOT/toolkit/resources/manifests/package"
sed -i -E "s/(kernel-headers-.*-)[0-9]+(\.cm.*)/\1$kernel_release_number\2/" "$package_manifests_dir"/{pkggen,toolchain}*.txt
}
function bump_spec_releases {
local kernel_release_number
local specs_dir_path
local specs_signed_dir_path
local specs_to_bump
kernel_release_number="$1"
echo "Bumping kernel specs releases to ($kernel_release_number)."
specs_dir_path="$REPO_ROOT/SPECS"
specs_signed_dir_path="$REPO_ROOT/SPECS-SIGNED"
specs_to_bump="$specs_dir_path/kernel-headers/kernel-headers.spec $specs_signed_dir_path/kernel-signed/kernel-signed.spec"
for spec_to_bump in $specs_to_bump
do
spec_release_number="$(spec_read_release_number "$spec_to_bump")"
if [[ "$kernel_release_number" != "$spec_release_number" ]]
then
update_spec.sh "Bump release number to match kernel release." "$spec_to_bump"
fi
done
}
KERNEL_RELEASE_NUMBER="$(spec_read_release_number "$REPO_ROOT/SPECS/kernel/kernel.spec")"
bump_spec_releases "$KERNEL_RELEASE_NUMBER"
update_manifests "$KERNEL_RELEASE_NUMBER"
"$COMMON_SCRIPTS_FOLDER"/livepatching/update_livepatches.sh

Просмотреть файл

@ -0,0 +1,52 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
REPO_ROOT="$(git rev-parse --show-toplevel)"
COMMON_SCRIPTS_FOLDER="$REPO_ROOT/toolkit/scripts"
SCRIPT_FOLDER="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
export PATH="$PATH:$COMMON_SCRIPTS_FOLDER"
# shellcheck source=../../../toolkit/scripts/specs/specs_tools.sh
source "$COMMON_SCRIPTS_FOLDER/specs/specs_tools.sh"
LIVEPATCH_SPEC_PATH="$1"
if [[ ! -f "$LIVEPATCH_SPEC_PATH" ]]
then
echo "Must provide a livepatch spec file as first argument." >&2
exit 1
fi
echo "Generating signed spec for ($LIVEPATCH_SPEC_PATH)."
KERNEL_VERSION_RELEASE="$(grep -oP "(?<=kernel_version_release ).*" "$LIVEPATCH_SPEC_PATH")"
DESCRIPTION="$(spec_query_srpm "$LIVEPATCH_SPEC_PATH" "%{DESCRIPTION}\n")"
RELEASE_TAG="$(spec_read_release_tag "$LIVEPATCH_SPEC_PATH")"
CHANGELOG="$(dump_changelog "$LIVEPATCH_SPEC_PATH")"
# shellcheck disable=SC2034 # Variable used indirectly inside 'create_new_file_from_template'.
declare -A TEMPLATE_PLACEHOLDERS=(
["@KERNEL_VERSION_RELEASE@"]="$KERNEL_VERSION_RELEASE"
["@DESCRIPTION@"]="$DESCRIPTION"
["@RELEASE_TAG@"]="$RELEASE_TAG"
["@CHANGELOG@"]="$CHANGELOG"
)
LIVEPATCH_SIGNED_SPEC_PATH="$REPO_ROOT/SPECS-SIGNED/livepatch-signed/livepatch-signed-$KERNEL_VERSION_RELEASE.spec"
create_new_file_from_template "$SCRIPT_FOLDER/template_livepatch-signed.spec" "$LIVEPATCH_SIGNED_SPEC_PATH" TEMPLATE_PLACEHOLDERS
# Cgmanifest.json update skipped - already handled by the unsigned version.
echo "Updating licensing info."
license_map.py --no_check --update \
SPECS/LICENSES-AND-NOTICES/data/licenses.json \
SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md \
"$LIVEPATCH_SIGNED_SPEC_PATH"

Просмотреть файл

@ -0,0 +1,111 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
REPO_ROOT="$(git rev-parse --show-toplevel)"
COMMON_SCRIPTS_FOLDER="$REPO_ROOT/toolkit/scripts"
SCRIPT_FOLDER="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
export PATH="$PATH:$COMMON_SCRIPTS_FOLDER"
# shellcheck source=../../../toolkit/scripts/specs/specs_tools.sh
source "$COMMON_SCRIPTS_FOLDER/specs/specs_tools.sh"
function copy_kernel_sources {
echo "Copying kernel config ($LIVEPATCH_CONFIG_FILE) and trusted key ($LIVEPATCH_PUBLIC_KEY_FILE)."
cp "$KERNEL_CONFIG_PATH" "$LIVEPATCH_SPECS_DIR/$LIVEPATCH_CONFIG_FILE"
cp "$KERNEL_PUBLIC_KEY_PATH" "$LIVEPATCH_SPECS_DIR/$LIVEPATCH_PUBLIC_KEY_FILE"
}
function generate_livepatch_signatures {
local config_hash
local kernel_hash
local kernel_tarball_file
local public_key_hash
echo "Generating livepatch signatures."
config_hash="$(jq -r ".Signatures.config" "$KERNEL_SIGNATURES_PATH")"
kernel_tarball_file="kernel-$KERNEL_VERSION.tar.gz"
kernel_hash="$(jq -r ".Signatures.\"$kernel_tarball_file\"" "$KERNEL_SIGNATURES_PATH")"
public_key_hash="$(jq -r ".Signatures.\"$KERNEL_PUBLIC_KEY_FILE\"" "$KERNEL_SIGNATURES_PATH")"
jq -n \
--arg config_hash "$config_hash" \
--arg config_name "$LIVEPATCH_CONFIG_FILE" \
--arg kernel_hash "$kernel_hash" \
--arg kernel_name "$kernel_tarball_file" \
--arg pem_hash "$public_key_hash" \
--arg pem_name "$LIVEPATCH_PUBLIC_KEY_FILE" \
'{"Signatures": {($config_name): $config_hash, ($kernel_name): $kernel_hash, ($pem_name): $pem_hash}}' \
> "$LIVEPATCH_SPECS_DIR/livepatch-$KERNEL_VERSION_RELEASE.signatures.json"
}
function generate_livepatch_spec {
local -A template_placeholders
local latest_existing_release
local patches
local same_version_livepatches
same_version_livepatches="$(find "$LIVEPATCH_SPECS_DIR" -name "livepatch-$KERNEL_VERSION*spec")"
if [[ -n "$same_version_livepatches" ]]
then
echo "Detected older livepatch for the current kernel version - re-using any existing patches."
latest_existing_release=$(find "$LIVEPATCH_SPECS_DIR" -name "livepatch-$KERNEL_VERSION*spec" | grep -oP "(?<=$KERNEL_VERSION-)\d+" | sort -n | tail -1)
patches="$(grep -P "^\s*Patch\d*:.*" "$LIVEPATCH_SPECS_DIR/livepatch-$KERNEL_VERSION-$latest_existing_release".cm*spec)"
fi
# shellcheck disable=SC2034 # Variable used indirectly inside 'create_new_file_from_template'.
template_placeholders=(
["@KERNEL_VERSION_RELEASE@"]="$KERNEL_VERSION_RELEASE"
["@PATCHES@"]="$patches"
)
create_new_file_from_template "$SCRIPT_FOLDER/template_livepatch.spec" "$LIVEPATCH_SPEC_PATH" template_placeholders
update_spec.sh "Original version for CBL-Mariner.\n- License verified." "$LIVEPATCH_SPEC_PATH" 1>/dev/null
}
KERNEL_SPECS_DIR="$REPO_ROOT/SPECS/kernel"
KERNEL_CONFIG_PATH="$KERNEL_SPECS_DIR/config"
KERNEL_PUBLIC_KEYS=("$KERNEL_SPECS_DIR"/cbl-mariner-ca-*.pem)
KERNEL_PUBLIC_KEY_PATH="${KERNEL_PUBLIC_KEYS[0]}"
KERNEL_PUBLIC_KEY_FILE="$(basename "$KERNEL_PUBLIC_KEY_PATH")"
KERNEL_SIGNATURES_PATH="$KERNEL_SPECS_DIR/kernel.signatures.json"
KERNEL_SPEC_PATH="$KERNEL_SPECS_DIR/kernel.spec"
KERNEL_VERSION="$(spec_read_version "$KERNEL_SPEC_PATH")"
KERNEL_VERSION_RELEASE="$(spec_query_srpm "$KERNEL_SPEC_PATH" "%{VERSION}-%{RELEASE}\n")"
LIVEPATCH_CONFIG_FILE="config-$KERNEL_VERSION_RELEASE"
LIVEPATCH_PUBLIC_KEY_FILE="mariner-$KERNEL_VERSION_RELEASE.pem"
LIVEPATCH_SPECS_DIR="$REPO_ROOT/SPECS/livepatch"
LIVEPATCH_SPEC_PATH="$LIVEPATCH_SPECS_DIR/livepatch-$KERNEL_VERSION_RELEASE.spec"
if [[ -f "$LIVEPATCH_SPEC_PATH" ]]
then
echo "Livepatch spec ($LIVEPATCH_SPEC_PATH) alread exists. Exiting."
exit 0
fi
echo "Generating empty livepatch spec for kernel ($KERNEL_VERSION_RELEASE) under ($LIVEPATCH_SPEC_PATH)."
mkdir -p "$LIVEPATCH_SPECS_DIR"
copy_kernel_sources
generate_livepatch_signatures
generate_livepatch_spec
echo "Updating licensing info."
license_map.py --no_check --update \
SPECS/LICENSES-AND-NOTICES/data/licenses.json \
SPECS/LICENSES-AND-NOTICES/LICENSES-MAP.md \
"$LIVEPATCH_SPEC_PATH"
echo "Updating the cgmanifest.json."
update_cgmanifest.py last "$REPO_ROOT/cgmanifest.json" "$LIVEPATCH_SPEC_PATH"

Просмотреть файл

@ -0,0 +1,98 @@
%global debug_package %{nil}
%define kernel_version_release @KERNEL_VERSION_RELEASE@
%define kernel_version %(echo %{kernel_version_release} | grep -oP "^[^-]+")
%define kernel_release %(echo %{kernel_version_release} | grep -oP "(?<=-).+")
# Kpatch module names allow only alphanumeric characters and '_'.
%define livepatch_name %(value="%{name}-%{version}-%{release}"; echo "${value//[^a-zA-Z0-9_]/_}")
%define livepatch_install_dir %{_libdir}/livepatching/%{kernel_version_release}
%define livepatch_module_name %{livepatch_name}.ko
%define livepatch_module_path %{livepatch_install_dir}/%{livepatch_module_name}
%define patch_applicable_for_kernel [[ -f "%{livepatch_module_path}" && "$(uname -r)" == "%{kernel_version_release}" ]]
%define patch_installed kpatch list | grep -qP "%{livepatch_name}.*%{kernel_version_release}"
%define patch_loaded kpatch list | grep -qP "%{livepatch_name}.*enabled"
# Install patch if the RUNNING kernel matches.
# No-op for initial (empty) livepatch.
%define install_if_should \
if %{patch_applicable_for_kernel} && ! %{patch_installed} \
then \
kpatch install %{livepatch_module_path} \
fi
# Load patch, if the RUNNING kernel matches.
# No-op for initial (empty) livepatch.
%define load_if_should \
if %{patch_applicable_for_kernel} && ! %{patch_loaded} \
then \
kpatch load %{livepatch_module_path} \
fi
%define uninstall_if_should \
if %{patch_installed} \
then \
kpatch uninstall %{livepatch_name} \
fi
%define unload_if_should \
if %{patch_loaded} \
then \
kpatch unload %{livepatch_name} \
fi
Summary: Set of livepatches for kernel %{kernel_version_release}
Name: livepatch-%{kernel_version_release}
Version: 1.0.0
Release: @RELEASE_TAG@
License: MIT
Vendor: Microsoft Corporation
Distribution: Mariner
Group: System Environment/Base
URL: https://github.com/microsoft/CBL-Mariner
Source0: https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/%{kernel_version}.tar.gz#/%{livepatch_module_name}
ExclusiveArch: x86_64
Requires: coreutils
Requires: livepatching-filesystem
Requires(post): coreutils
Requires(post): kpatch
Requires(preun): kpatch
Provides: livepatch = %{kernel_version_release}
%description
@DESCRIPTION@
%install
install -dm 755 %{buildroot}%{livepatch_install_dir}
install -m 744 %{SOURCE0} %{buildroot}%{livepatch_module_path}
%post
%load_if_should
%install_if_should
%preun
%uninstall_if_should
%unload_if_should
# Re-enable patch on rollbacks to supported kernel.
%triggerin -- kernel = %{kernel_version_release}
%load_if_should
%install_if_should
# Prevent the patch from being loaded after a reboot to a different kernel.
# Previous kernel is still running, do NOT unload the livepatch.
%triggerin -- kernel > %{kernel_version_release}, kernel < %{kernel_version_release}
%uninstall_if_should
%files
%defattr(-,root,root)
%dir %{livepatch_install_dir}
%{livepatch_module_path}
@CHANGELOG@

Просмотреть файл

@ -0,0 +1,127 @@
%define kernel_version_release @KERNEL_VERSION_RELEASE@
%define kernel_version %(echo %{kernel_version_release} | grep -oP "^[^-]+")
%define kernel_release %(echo %{kernel_version_release} | grep -oP "(?<=-).+")
%define builds_module %([[ -n "$(echo "%{patches}" | grep -oP "CVE-\\d+-\\d+(?=\\.patch)")" ]] && echo 1 || echo 0)
# Kpatch module names allow only alphanumeric characters and '_'.
%define livepatch_name %(value="%{name}-%{version}-%{release}"; echo "${value//[^a-zA-Z0-9_]/_}")
%define livepatch_install_dir %{_libdir}/livepatching/%{kernel_version_release}
%define livepatch_module_name %{livepatch_name}.ko
%define livepatch_module_path %{livepatch_install_dir}/%{livepatch_module_name}
%define patches_description \
%(
echo "Patches list ('*' - fixed, '!' - unfixable through livepatching, kernel update required):"
for patch in %{patches}
do
patch_file=$(basename "$patch")
cve_number="${patch_file%.*}"
patch_suffix="${patch_file#*.}"
if [ "$patch_suffix" = "patch" ]
then
echo "*$cve_number"
else
echo "\!$cve_number: $(cat "$patch")"
fi
done
)
Summary: Set of livepatches for kernel %{kernel_version_release}
Name: livepatch-%{kernel_version_release}
Version: 1.0.0
Release: 0%{?dist}
License: MIT
Vendor: Microsoft Corporation
Distribution: Mariner
Group: System Environment/Base
URL: https://github.com/microsoft/CBL-Mariner
Source0: https://github.com/microsoft/CBL-Mariner-Linux-Kernel/archive/rolling-lts/mariner-2/%{kernel_version}.tar.gz#/kernel-%{kernel_version}.tar.gz
Source1: config-%{kernel_version_release}
Source2: mariner-%{kernel_version_release}.pem
@PATCHES@
ExclusiveArch: x86_64
# Must be kept below the "Patch" tags to correctly evaluate %%builds_module.
%if %{builds_module}
BuildRequires: audit-devel
BuildRequires: bash
BuildRequires: bc
BuildRequires: binutils
BuildRequires: bison
BuildRequires: diffutils
BuildRequires: dwarves
BuildRequires: elfutils-libelf-devel
BuildRequires: flex
BuildRequires: gcc
BuildRequires: glib-devel
BuildRequires: glibc-devel
BuildRequires: kbd
BuildRequires: kernel-debuginfo = %{kernel_version_release}
BuildRequires: kernel-headers = %{kernel_version_release}
BuildRequires: kmod-devel
BuildRequires: kpatch-build
BuildRequires: libdnet-devel
BuildRequires: libmspack-devel
BuildRequires: make
BuildRequires: openssl
BuildRequires: openssl-devel
BuildRequires: pam-devel
BuildRequires: procps-ng-devel
BuildRequires: python3-devel
BuildRequires: rpm-build
%else
%global debug_package %{nil}
# endif builds_module
%endif
Provides: livepatch = %{kernel_version_release}
%description
A set of kernel livepatches addressing CVEs present in Mariner's
kernel version %{kernel_version_release}.
%{patches_description}
%if %{builds_module}
%prep
%setup -q -n CBL-Mariner-Linux-Kernel-rolling-lts-mariner-2-%{kernel_version}
cp %{SOURCE1} .config
cp %{SOURCE2} certs/mariner.pem
sed -i 's#CONFIG_SYSTEM_TRUSTED_KEYS=""#CONFIG_SYSTEM_TRUSTED_KEYS="certs/mariner.pem"#' .config
sed -i 's/CONFIG_LOCALVERSION=""/CONFIG_LOCALVERSION="-%{kernel_release}"/' .config
%build
# Building cumulative patch.
all_patches_file=all.patch
for patch in %{patches}
do
[[ "$patch" == *.patch ]] && cat "$patch" >> $all_patches_file
done
kpatch-build -ddd \
--sourcedir . \
--vmlinux %{_libdir}/debug/lib/modules/%{kernel_version_release}/vmlinux \
--name %{livepatch_name} \
$all_patches_file
%install
install -dm 755 %{buildroot}%{livepatch_install_dir}
install -m 744 %{livepatch_module_name} %{buildroot}%{livepatch_module_path}
# endif builds_module
%endif
%files
%defattr(-,root,root)
%if %{builds_module}
%dir %{livepatch_install_dir}
%{livepatch_module_path}
%endif
%changelog

Просмотреть файл

@ -0,0 +1,17 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
SPECS_DIR="$(git rev-parse --show-toplevel)/SPECS"
SCRIPT_FOLDER="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
echo "Updating livepatch specs."
"$SCRIPT_FOLDER"/generate_livepatch_spec.sh
for livepatch_spec in "$SPECS_DIR/livepatch/"livepatch-*.spec
do
"$SCRIPT_FOLDER"/generate_livepatch-signed_spec.sh "$livepatch_spec"
done

Просмотреть файл

@ -0,0 +1,221 @@
#!/bin/bash
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set -e
# shellcheck source=../../../toolkit/scripts/rpmops.sh
source "$(git rev-parse --show-toplevel)"/toolkit/scripts/rpmops.sh
USER_EMAIL=""
USER_NAME=""
init() {
local init_failed=false
if [[ -z $USER_EMAIL ]]
then
USER_EMAIL="$(git config user.email)"
fi
if [[ -z $USER_NAME ]]
then
USER_NAME="$(git config user.name)"
fi
if [[ -z $USER_EMAIL ]]
then
echo "ERROR: must set git user e-mail. Try running 'git config --local user.email [USER_EMAIL]'." >&2
init_failed=true
fi
if [[ -z $USER_NAME ]]
then
echo "ERROR: must set git user name. Try running 'git config --local user.name [USER_NAME]'." >&2
init_failed=true
fi
if $init_failed
then
exit 1
fi
}
add_changelog_entry() {
local changelog_header
local changelog_indents
local changelog_message
local epoch
local next_release
local spec_path
local version
spec_path="$1"
changelog_message="$2"
next_release=$(spec_read_release_number "$spec_path")
next_release=$((next_release+1))
version=$(spec_read_version "$spec_path")
epoch="$(spec_read_epoch "$spec_path"):"
if [[ "$epoch" == "(none):" ]]
then
epoch=""
fi
changelog_header=$(date "+%a %b %d %Y $USER_NAME <$USER_EMAIL> - $epoch$version-$next_release")
changelog_indents=$(grep -m 1 -P "^\*.*@.*>" "$spec_path" | sed -E "s/^\*(\s+).*/\1/")
if [[ -z "$changelog_indents" ]]
then
changelog_indents=" "
fi
spec_set_release_number "$spec_path" $next_release
sed -i -E "/\s*^%changelog.*/a *$changelog_indents$changelog_header\n-$changelog_indents$changelog_message\n" "$spec_path"
# Remove excessive empty lines, if present.
sed -i -e :a -e '/^\n*$/{$d;N;ba' -e '}' "$spec_path"
}
create_new_file_from_template() {
local -n placeholders
local key
local target_path
local template_path
local value
template_path="$1"
target_path="$2"
placeholders="$3"
echo "Creating a new file under \"$target_path\" from template \"$template_path\"."
mkdir -p "$(dirname "$target_path")"
cp "$template_path" "$target_path"
for key in "${!placeholders[@]}"
do
value="${placeholders[$key]}"
awk -i inplace -v r="$value" "{gsub(/$key/,r)}1" "$target_path"
done
}
dump_changelog() {
local spec_path
spec_path="$1"
sed -n '/%changelog/,$p' "$spec_path"
}
parsed_spec_read_patches() {
parsed_spec_read_tags "$1" "Patch" "$2"
}
parsed_spec_read_tag() {
local spec_path
local tag
spec_path="$1"
tag="$2"
mariner_rpmspec --query --queryformat="%{$tag}\n" --srpm "$spec_path" 2>/dev/null
}
parsed_spec_read_tags() {
local -n results_array
local spec_path
local tag
spec_path="$1"
tag="$2"
results_array="$3"
for result in $(mariner_rpmspec --query --queryformat="[%{$tag}\n]" --srpm "$spec_path" 2>/dev/null | tac)
do
results_array+=("$result")
done
}
spec_query_srpm() {
local query_format
local spec_path
spec_path="$1"
query_format="$2"
mariner_rpmspec -q --queryformat="$query_format" --srpm "$spec_path" 2>/dev/null
}
spec_read_release_number() {
spec_read_tag_skip_macros "$1" "Release"
}
spec_read_release_tag() {
spec_read_tag "$1" "Release"
}
spec_read_tag() {
local spec_path
local tag
spec_path="$1"
tag="$2"
grep "^$tag:" "$spec_path" | sed -E "s/$tag:\s+([^#]+)/\1/"
}
spec_read_tag_skip_macros() {
local spec_path
local tag
spec_path="$1"
tag="$2"
spec_read_tag "$spec_path" "$tag" | grep -oP "^[^%]+"
}
spec_read_epoch() {
parsed_spec_read_tag "$1" "Epoch"
}
spec_read_version() {
parsed_spec_read_tag "$1" "Version"
}
spec_set_tag() {
local spec_path
local tag
local value
spec_path="$1"
tag="$2"
value="$3"
sed -i -E "s/($tag:\s+).*/\1$value/" "$spec_path"
}
spec_set_release_number() {
local spec_path
local value
spec_path="$1"
value="$2"
spec_set_tag "$spec_path" "Release" "$value%{?dist}"
}
spec_set_version() {
local spec_path
local value
spec_path="$1"
value="$2"
spec_set_tag "$spec_path" "Version" "$value"
}
init

Просмотреть файл

@ -8,8 +8,10 @@
# $1 - Changelog message.
# ${@:2} - Paths to spec files to update.
# shellcheck source=../../toolkit/scripts/rpmops.sh
source "$(git rev-parse --show-toplevel)"/toolkit/scripts/rpmops.sh
REPO_ROOT="$(git rev-parse --show-toplevel)"
# shellcheck source=../../toolkit/scripts/specs/specs_tools.sh
source "$REPO_ROOT"/toolkit/scripts/specs/specs_tools.sh
changelog_message="$1"
@ -19,21 +21,6 @@ then
exit 1
fi
user_email="$(git config user.email)"
user_name="$(git config user.name)"
if [[ -z $user_email ]]
then
echo "ERROR: must set git user e-mail. Try running 'git config --local user.email [user_email]'." >&2
exit 1
fi
if [[ -z $user_name ]]
then
echo "ERROR: must set git user name. Try running 'git config --local user.name [user_name]'." >&2
exit 1
fi
for spec_path in "${@:2}"
do
if [[ ! -f "$spec_path" ]]
@ -44,18 +31,5 @@ do
echo "Updating '$spec_path'."
release=$(grep -oP "^Release:\s*\d+" "$spec_path" | grep -oP "\d+$")
release=$((release+1))
version=$(mariner_rpmspec --srpm -q "$spec_path" --qf "%{VERSION}\n" 2>/dev/null)
epoch="$(mariner_rpmspec --srpm -q "$spec_path" --qf "%{EPOCH}\n" 2>/dev/null):"
if [[ "$epoch" == "(none):" ]]
then
epoch=""
fi
sed -i -E "s/^(Release:\s*).*/\1$release%{?dist}/" "$spec_path"
changelog_header=$(date "+%a %b %d %Y $user_name <$user_email> - $epoch$version-$release")
changelog_indents=$(grep -m 1 -P "^\*.*@.*>" "$spec_path" | sed -E "s/^\*(\s+).*/\1/")
sed -i -E "/\s*^%changelog.*/a *$changelog_indents$changelog_header\n-$changelog_indents$changelog_message\n" "$spec_path"
add_changelog_entry "$spec_path" "$changelog_message"
done