diff --git a/pkgs/os-specific/linux/kernel/hardened-patches.json b/pkgs/os-specific/linux/kernel/hardened-patches.json index f916ad13488f..eecb27cdb669 100644 --- a/pkgs/os-specific/linux/kernel/hardened-patches.json +++ b/pkgs/os-specific/linux/kernel/hardened-patches.json @@ -1,27 +1,27 @@ { - "4.14.176": { + "4.14": { + "name": "linux-hardened-4.14.176.a.patch", "sha256": "0pr3m2j63mc746fcbzg1hlwv85im9f87qkl6r4033gwnpa9brcgk", - "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.14.176.a/linux-hardened-4.14.176.a.patch", - "version_suffix": "a" + "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.14.176.a/linux-hardened-4.14.176.a.patch" }, - "4.19.117": { + "4.19": { + "name": "linux-hardened-4.19.117.a.patch", "sha256": "0c8dvh49nzypxwvsls10i896smvpdrk40x8ybljb3qk3r8j7niaw", - "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.19.117.a/linux-hardened-4.19.117.a.patch", - "version_suffix": "a" + "url": "https://github.com/anthraxx/linux-hardened/releases/download/4.19.117.a/linux-hardened-4.19.117.a.patch" }, - "5.4.35": { + "5.4": { + "name": "linux-hardened-5.4.34.a.patch", "sha256": "1xwpqr9nzpjg837b3wnzb8fmrl2g9rz8gz5yb55vnnllbzbz36v6", - "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.4.34.a/linux-hardened-5.4.34.a.patch", - "version_suffix": "a" + "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.4.34.a/linux-hardened-5.4.34.a.patch" }, - "5.5.19": { + "5.5": { + "name": "linux-hardened-5.5.19.a.patch", "sha256": "1ya5nsfhr3nwz6qiz4pdhvm6k9mx1kr0prhdvhx3p40f1vk281sc", - "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.5.19.a/linux-hardened-5.5.19.a.patch", - "version_suffix": "a" + "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.5.19.a/linux-hardened-5.5.19.a.patch" }, - "5.6.7": { + "5.6": { + "name": "linux-hardened-5.6.6.a.patch", "sha256": "0jiqh0frxirjbccgfdk007fca6r6n36n0pkqq4jszkckn59ayl7r", - "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.6.6.a/linux-hardened-5.6.6.a.patch", - "version_suffix": "a" + "url": "https://github.com/anthraxx/linux-hardened/releases/download/5.6.6.a/linux-hardened-5.6.6.a.patch" } } diff --git a/pkgs/os-specific/linux/kernel/patches.nix b/pkgs/os-specific/linux/kernel/patches.nix index 69b0197d4e68..1c4af8c32a6f 100644 --- a/pkgs/os-specific/linux/kernel/patches.nix +++ b/pkgs/os-specific/linux/kernel/patches.nix @@ -39,16 +39,9 @@ }; hardened = let - mkPatch = kernelVersion: patch: let - fullVersion = "${kernelVersion}.${patch.version_suffix}"; - name = "linux-hardened-${fullVersion}"; - in { - inherit name; - patch = fetchurl { - name = "${name}.patch"; - inherit (patch) url sha256; - meta.maintainers = with lib.maintainers; [ emily ]; - }; + mkPatch = kernelVersion: src: { + name = lib.removeSuffix ".patch" src.name; + patch = fetchurl src; }; patches = builtins.fromJSON (builtins.readFile ./hardened-patches.json); in lib.mapAttrs mkPatch patches; diff --git a/pkgs/os-specific/linux/kernel/update-hardened.py b/pkgs/os-specific/linux/kernel/update-hardened.py index 089e991d06bc..7f6949653afc 100755 --- a/pkgs/os-specific/linux/kernel/update-hardened.py +++ b/pkgs/os-specific/linux/kernel/update-hardened.py @@ -17,17 +17,7 @@ HERE = os.path.dirname(os.path.realpath(__file__)) HARDENED_GITHUB_REPO = 'anthraxx/linux-hardened' HARDENED_TRUSTED_KEY = os.path.join(HERE, 'anthraxx.asc') HARDENED_PATCHES_PATH = os.path.join(HERE, 'hardened-patches.json') -MIN_KERNEL = (4, 14) - -HARDENED_VERSION_RE = re.compile(r''' - (?P [\d.]+) \. - (?P [a-z]+) -''', re.VERBOSE) - -def parse_version(version): - match = HARDENED_VERSION_RE.fullmatch(version) - if match: - return match.groups() +MIN_KERNEL_VERSION = [4, 14] def run(*args, **kwargs): try: @@ -78,11 +68,12 @@ def fetch_patch(*, name, release): except StopIteration: raise KeyError(filename) + patch_filename = f'{name}.patch' try: - patch_url = find_asset(f'{name}.patch') - sig_url = find_asset(f'{name}.patch.sig') + patch_url = find_asset(patch_filename) + sig_url = find_asset(patch_filename + '.sig') except KeyError: - print(f'error: {name}.patch{{,sig}} not present', file=sys.stderr) + print(f'error: {patch_filename}{{,.sig}} not present', file=sys.stderr) return None sha256, patch_path = nix_prefetch_url(patch_url) @@ -97,16 +88,32 @@ def fetch_patch(*, name, release): return None return { + 'name': patch_filename, 'url': patch_url, 'sha256': sha256, } -def commit_patches(*, kernel_version, message): +def parse_version(version_str): + version = [] + for component in version_str.split('.'): + try: + version.append(int(component)) + except ValueError: + version.append(component) + return version + +def version_string(version): + return '.'.join(str(component) for component in version) + +def major_kernel_version_key(kernel_version): + return version_string(kernel_version[:-1]) + +def commit_patches(*, kernel_key, message): with open(HARDENED_PATCHES_PATH + '.new', 'w') as new_patches_file: json.dump(patches, new_patches_file, indent=4, sort_keys=True) new_patches_file.write('\n') os.rename(HARDENED_PATCHES_PATH + '.new', HARDENED_PATCHES_PATH) - message = f'linux/hardened-patches/{kernel_version}: {message}' + message = f'linux/hardened-patches/{kernel_key}: {message}' print(message) if os.environ.get('COMMIT'): run( @@ -125,74 +132,96 @@ NIX_VERSION_RE = re.compile(r''' ''', re.VERBOSE) # Get the set of currently packaged kernel versions. -kernel_versions = set() +kernel_versions = {} for filename in os.listdir(HERE): filename_match = re.fullmatch(r'linux-(\d+)\.(\d+)\.nix', filename) if filename_match: - if tuple(int(v) for v in filename_match.groups()) < MIN_KERNEL: - continue with open(os.path.join(HERE, filename)) as nix_file: for nix_line in nix_file: match = NIX_VERSION_RE.fullmatch(nix_line) if match: - kernel_versions.add(match.group('version')) + kernel_version = parse_version(match.group('version')) + if kernel_version < MIN_KERNEL_VERSION: + continue + kernel_key = major_kernel_version_key(kernel_version) + kernel_versions[kernel_key] = kernel_version -# Remove patches for old kernel versions. -for kernel_version in patches.keys() - kernel_versions: - del patches[kernel_version] - commit_patches(kernel_version=kernel_version, message='remove') +# Remove patches for unpackaged kernel versions. +for kernel_key in sorted(patches.keys() - kernel_versions.keys()): + commit_patches(kernel_key=kernel_key, message='remove') g = Github(os.environ.get('GITHUB_TOKEN')) repo = g.get_repo(HARDENED_GITHUB_REPO) -releases = repo.get_releases() -found_kernel_versions = set() failures = False -for release in releases: - remaining_kernel_versions = kernel_versions - found_kernel_versions - - if not remaining_kernel_versions: - break - - version = release.tag_name - name = f'linux-hardened-{version}' - version_info = parse_version(version) - if not version_info: +# Match each kernel version with the best patch version. +releases = {} +for release in repo.get_releases(): + version = parse_version(release.tag_name) + # needs to look like e.g. 5.6.3.a + if len(version) < 4: continue - kernel_version, version_suffix = version_info - if kernel_version in remaining_kernel_versions: - found_kernel_versions.add(kernel_version) - try: - old_version_suffix = patches[kernel_version]['version_suffix'] - old_version = f'{kernel_version}.{old_version_suffix}' - update = old_version_suffix < version_suffix - except KeyError: - update = True - old_version = None + kernel_version = version[:-1] + kernel_key = major_kernel_version_key(kernel_version) + try: + packaged_kernel_version = kernel_versions[kernel_key] + except KeyError: + continue - if update: - patch = fetch_patch(name=name, release=release) - if patch is None: - failures = True + release_info = { + 'version': version, + 'release': release, + } + + if kernel_version == packaged_kernel_version: + releases[kernel_key] = release_info + else: + # Fall back to the latest patch for this major kernel version, + # skipping patches for kernels newer than the packaged one. + if kernel_version > packaged_kernel_version: + continue + elif (kernel_key not in releases or + releases[kernel_key]['version'] < version): + releases[kernel_key] = release_info + +# Update hardened-patches.json for each release. +for kernel_key, release_info in releases.items(): + release = release_info['release'] + version = release_info['version'] + version_str = release.tag_name + name = f'linux-hardened-{version_str}' + + try: + old_filename = patches[kernel_key]['name'] + old_version_str = (old_filename + .replace('linux-hardened-', '') + .replace('.patch', '')) + old_version = parse_version(old_version_str) + update = old_version < version + except KeyError: + update = True + old_version = None + + if update: + patch = fetch_patch(name=name, release=release) + if patch is None: + failures = True + else: + patches[kernel_key] = patch + if old_version: + message = f'{old_version_str} -> {version_str}' else: - patch['version_suffix'] = version_suffix - patches[kernel_version] = patch - if old_version: - message = f'{old_version} -> {version}' - else: - message = f'init at {version}' - commit_patches(kernel_version=kernel_version, message=message) + message = f'init at {version_str}' + commit_patches(kernel_key=kernel_key, message=message) -missing_kernel_versions = kernel_versions - patches.keys() +missing_kernel_versions = kernel_versions.keys() - patches.keys() if missing_kernel_versions: print( f'warning: no patches for kernel versions ' + - ', '.join(missing_kernel_versions) + - '\nwarning: consider manually backporting older patches (bump ' - 'JSON key, set version_suffix to "NixOS-a")', + ', '.join(missing_kernel_versions), file=sys.stderr, ) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index e4c3019ff376..166a4b82527b 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -17047,7 +17047,7 @@ in }; kernelPatches = kernel.kernelPatches ++ [ kernelPatches.tag_hardened - kernelPatches.hardened.${kernel.version} + kernelPatches.hardened.${kernel.meta.branch} ]; modDirVersionArg = kernel.modDirVersion + "-hardened"; });