From 188df85f5bef8bc8c1a4fa6b9d494460a4af5391 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Tue, 2 Apr 2019 13:17:51 +0100 Subject: [PATCH 01/13] Add patches to enable loading db and MOK keys Import patches from: http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=keys-uefi that enable a new option that automatically loads keys from db and MOK into the secondary keyring, so that they can be used to verify the signature of kernel modules. Enable the required KCONFIGs. Allows users to self-sign modules (eg: dkms). --- debian/changelog | 3 + debian/config/config | 5 + ...tricted-boot-time-addition-of-keys-t.patch | 98 +++++++ ...002-efi-Add-EFI-signature-data-types.patch | 63 +++++ ...efi-Add-an-EFI-signature-blob-parser.patch | 200 ++++++++++++++ ...t-certificates-from-UEFI-Secure-Boot.patch | 249 ++++++++++++++++++ ...he-db-UEFI-variable-to-be-suppressed.patch | 91 +++++++ ...st-not-complain-about-cert-lists-tha.patch | 112 ++++++++ ...ndary-trust-keyring-for-module-signi.patch | 30 +++ debian/patches/series | 9 + 10 files changed, 860 insertions(+) create mode 100644 debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch diff --git a/debian/changelog b/debian/changelog index e8546daaed95..b17f71d9b0ea 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1103,6 +1103,9 @@ linux (4.19.37-1) UNRELEASED; urgency=medium [ Luca Boccassi ] * libbpf-dev: generate pkg-config file for libbpf by backporting libbpf-generate-pkg-config.patch from bpf-next. + * Import patches to enable loading keys from UEFI db and MOK from + http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git to + allow kernel modules built by users (eg: by dkms) to be verified. [ Bastian Blank ] * Don't longer recommend irqbalance. (closes: #926967) diff --git a/debian/config/config b/debian/config/config index ec09b36407cf..26c7a6189310 100644 --- a/debian/config/config +++ b/debian/config/config @@ -78,6 +78,11 @@ CONFIG_MODULE_SIG_KEY="" #. Whenever the filename changes, this also needs to be updated in #. debian/featureset-*/config CONFIG_SYSTEM_TRUSTED_KEYS="debian/certs/debian-uefi-certs.pem" +#. Add secondary keyring with keys from UEFI db and MOK. +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +CONFIG_EFI_SIGNATURE_LIST_PARSER=y +CONFIG_LOAD_UEFI_KEYS=y ## ## file: crypto/Kconfig diff --git a/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch b/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch new file mode 100644 index 000000000000..69ffb5c04b1e --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch @@ -0,0 +1,98 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From fd416971ea1b441df3e1922c441d1ed66a4ca1d2 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Fri, 5 May 2017 08:21:56 +0100 +Subject: [PATCH 1/7] KEYS: Allow unrestricted boot-time addition of keys to + secondary keyring + +Allow keys to be added to the system secondary certificates keyring during +kernel initialisation in an unrestricted fashion. Such keys are implicitly +trusted and don't have their trust chains checked on link. + +This allows keys in the UEFI database to be added in secure boot mode for +the purposes of module signing. + +Signed-off-by: David Howells +(cherry picked from commit 40db8fc497d010ae6cee6297c3882d3dc3d76d48 + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + certs/internal.h | 18 ++++++++++++++++++ + certs/system_keyring.c | 33 +++++++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + create mode 100644 certs/internal.h + +diff --git a/certs/internal.h b/certs/internal.h +new file mode 100644 +index 000000000000..5dcbefb0c23a +--- /dev/null ++++ b/certs/internal.h +@@ -0,0 +1,18 @@ ++/* Internal definitions ++ * ++ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved. ++ * Written by David Howells (dhowells@redhat.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public Licence ++ * as published by the Free Software Foundation; either version ++ * 2 of the Licence, or (at your option) any later version. ++ */ ++ ++/* ++ * system_keyring.c ++ */ ++#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING ++extern void __init add_trusted_secondary_key(const char *source, ++ const void *data, size_t len); ++#endif +diff --git a/certs/system_keyring.c b/certs/system_keyring.c +index 81728717523d..62cd664ea031 100644 +--- a/certs/system_keyring.c ++++ b/certs/system_keyring.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include "internal.h" + + static struct key *builtin_trusted_keys; + #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING +@@ -266,3 +267,35 @@ int verify_pkcs7_signature(const void *data, size_t len, + EXPORT_SYMBOL_GPL(verify_pkcs7_signature); + + #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ ++ ++#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING ++/** ++ * add_trusted_secondary_key - Add to secondary keyring with no validation ++ * @source: Source of key ++ * @data: The blob holding the key ++ * @len: The length of the data blob ++ * ++ * Add a key to the secondary keyring without checking its trust chain. This ++ * is available only during kernel initialisation. ++ */ ++void __init add_trusted_secondary_key(const char *source, ++ const void *data, size_t len) ++{ ++ key_ref_t key; ++ ++ key = key_create_or_update(make_key_ref(secondary_trusted_keys, 1), ++ "asymmetric", ++ NULL, data, len, ++ (KEY_POS_ALL & ~KEY_POS_SETATTR) | ++ KEY_USR_VIEW, ++ KEY_ALLOC_NOT_IN_QUOTA | ++ KEY_ALLOC_BYPASS_RESTRICTION); ++ ++ if (IS_ERR(key)) ++ pr_err("Problem loading %s X.509 certificate (%ld)\n", ++ source, PTR_ERR(key)); ++ else ++ pr_notice("Loaded %s cert '%s' linked to secondary sys keyring\n", ++ source, key_ref_to_ptr(key)->description); ++} ++#endif /* CONFIG_SECONDARY_TRUSTED_KEYRING */ +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch b/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch new file mode 100644 index 000000000000..9138398fe0bb --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch @@ -0,0 +1,63 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From 31c5efef25006ae5fc1542e4705e863a98b624b6 Mon Sep 17 00:00:00 2001 +From: Dave Howells +Date: Fri, 5 May 2017 08:21:58 +0100 +Subject: [PATCH 2/7] efi: Add EFI signature data types + +Add the data types that are used for containing hashes, keys and +certificates for cryptographic verification along with their corresponding +type GUIDs. + +Signed-off-by: David Howells +(cherry picked from commit 446e0e29d7d53fe7786d33603df5a6682dd00c12 + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + include/linux/efi.h | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/include/linux/efi.h b/include/linux/efi.h +index 401e4b254e30..99cba6fe1234 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -663,6 +663,10 @@ void efi_native_runtime_setup(void); + #define EFI_IMAGE_SECURITY_DATABASE_GUID EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) + #define EFI_SHIM_LOCK_GUID EFI_GUID(0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23) + ++#define EFI_CERT_SHA256_GUID EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) ++#define EFI_CERT_X509_GUID EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) ++#define EFI_CERT_X509_SHA256_GUID EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed) ++ + /* + * This GUID is used to pass to the kernel proper the struct screen_info + * structure that was populated by the stub based on the GOP protocol instance +@@ -933,6 +937,27 @@ typedef struct { + efi_memory_desc_t entry[0]; + } efi_memory_attributes_table_t; + ++typedef struct { ++ efi_guid_t signature_owner; ++ u8 signature_data[]; ++} efi_signature_data_t; ++ ++typedef struct { ++ efi_guid_t signature_type; ++ u32 signature_list_size; ++ u32 signature_header_size; ++ u32 signature_size; ++ u8 signature_header[]; ++ /* efi_signature_data_t signatures[][] */ ++} efi_signature_list_t; ++ ++typedef u8 efi_sha256_hash_t[32]; ++ ++typedef struct { ++ efi_sha256_hash_t to_be_signed_hash; ++ efi_time_t time_of_revocation; ++} efi_cert_x509_sha256_t; ++ + /* + * All runtime access to EFI goes through this structure: + */ +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch b/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch new file mode 100644 index 000000000000..152c907c7b75 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch @@ -0,0 +1,200 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From b0cea6fe6d97f4fa3ac2dbddd54b79d74045c670 Mon Sep 17 00:00:00 2001 +From: Dave Howells +Date: Fri, 5 May 2017 08:21:58 +0100 +Subject: [PATCH 3/7] efi: Add an EFI signature blob parser + +Add a function to parse an EFI signature blob looking for elements of +interest. A list is made up of a series of sublists, where all the +elements in a sublist are of the same type, but sublists can be of +different types. + +For each sublist encountered, the function pointed to by the +get_handler_for_guid argument is called with the type specifier GUID and +returns either a pointer to a function to handle elements of that type or +NULL if the type is not of interest. + +If the sublist is of interest, each element is passed to the handler +function in turn. + +Signed-off-by: David Howells +(cherry picked from commit 41a595bb0dc097c19ad377a0c32c993234aa2525 + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + certs/Kconfig | 8 ++++ + certs/Makefile | 1 + + certs/efi_parser.c | 112 ++++++++++++++++++++++++++++++++++++++++++++ + include/linux/efi.h | 9 ++++ + 4 files changed, 130 insertions(+) + create mode 100644 certs/efi_parser.c + +diff --git a/certs/Kconfig b/certs/Kconfig +index c94e93d8bccf..650ffcb8db79 100644 +--- a/certs/Kconfig ++++ b/certs/Kconfig +@@ -83,4 +83,12 @@ config SYSTEM_BLACKLIST_HASH_LIST + wrapper to incorporate the list into the kernel. Each should + be a string of hex digits. + ++config EFI_SIGNATURE_LIST_PARSER ++ bool "EFI signature list parser" ++ depends on EFI ++ select X509_CERTIFICATE_PARSER ++ help ++ This option provides support for parsing EFI signature lists for ++ X.509 certificates and turning them into keys. ++ + endmenu +diff --git a/certs/Makefile b/certs/Makefile +index 5d0999b9e21b..7e5e179ac685 100644 +--- a/certs/Makefile ++++ b/certs/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o + else + obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o + endif ++obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o + + ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) + +diff --git a/certs/efi_parser.c b/certs/efi_parser.c +new file mode 100644 +index 000000000000..4e396f98f5c7 +--- /dev/null ++++ b/certs/efi_parser.c +@@ -0,0 +1,112 @@ ++/* EFI signature/key/certificate list parser ++ * ++ * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved. ++ * Written by David Howells (dhowells@redhat.com) ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public Licence ++ * as published by the Free Software Foundation; either version ++ * 2 of the Licence, or (at your option) any later version. ++ */ ++ ++#define pr_fmt(fmt) "EFI: "fmt ++#include ++#include ++#include ++#include ++ ++/** ++ * parse_efi_signature_list - Parse an EFI signature list for certificates ++ * @source: The source of the key ++ * @data: The data blob to parse ++ * @size: The size of the data blob ++ * @get_handler_for_guid: Get the handler func for the sig type (or NULL) ++ * ++ * Parse an EFI signature list looking for elements of interest. A list is ++ * made up of a series of sublists, where all the elements in a sublist are of ++ * the same type, but sublists can be of different types. ++ * ++ * For each sublist encountered, the @get_handler_for_guid function is called ++ * with the type specifier GUID and returns either a pointer to a function to ++ * handle elements of that type or NULL if the type is not of interest. ++ * ++ * If the sublist is of interest, each element is passed to the handler ++ * function in turn. ++ * ++ * Error EBADMSG is returned if the list doesn't parse correctly and 0 is ++ * returned if the list was parsed correctly. No error can be returned from ++ * the @get_handler_for_guid function or the element handler function it ++ * returns. ++ */ ++int __init parse_efi_signature_list( ++ const char *source, ++ const void *data, size_t size, ++ efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *)) ++{ ++ efi_element_handler_t handler; ++ unsigned offs = 0; ++ ++ pr_devel("-->%s(,%zu)\n", __func__, size); ++ ++ while (size > 0) { ++ const efi_signature_data_t *elem; ++ efi_signature_list_t list; ++ size_t lsize, esize, hsize, elsize; ++ ++ if (size < sizeof(list)) ++ return -EBADMSG; ++ ++ memcpy(&list, data, sizeof(list)); ++ pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n", ++ offs, ++ list.signature_type.b, list.signature_list_size, ++ list.signature_header_size, list.signature_size); ++ ++ lsize = list.signature_list_size; ++ hsize = list.signature_header_size; ++ esize = list.signature_size; ++ elsize = lsize - sizeof(list) - hsize; ++ ++ if (lsize > size) { ++ pr_devel("<--%s() = -EBADMSG [overrun @%x]\n", ++ __func__, offs); ++ return -EBADMSG; ++ } ++ ++ if (lsize < sizeof(list) || ++ lsize - sizeof(list) < hsize || ++ esize < sizeof(*elem) || ++ elsize < esize || ++ elsize % esize != 0) { ++ pr_devel("- bad size combo @%x\n", offs); ++ return -EBADMSG; ++ } ++ ++ handler = get_handler_for_guid(&list.signature_type); ++ if (!handler) { ++ data += lsize; ++ size -= lsize; ++ offs += lsize; ++ continue; ++ } ++ ++ data += sizeof(list) + hsize; ++ size -= sizeof(list) + hsize; ++ offs += sizeof(list) + hsize; ++ ++ for (; elsize > 0; elsize -= esize) { ++ elem = data; ++ ++ pr_devel("ELEM[%04x]\n", offs); ++ handler(source, ++ &elem->signature_data, ++ esize - sizeof(*elem)); ++ ++ data += esize; ++ size -= esize; ++ offs += esize; ++ } ++ } ++ ++ return 0; ++} +diff --git a/include/linux/efi.h b/include/linux/efi.h +index 99cba6fe1234..2016145e2d6d 100644 +--- a/include/linux/efi.h ++++ b/include/linux/efi.h +@@ -1138,6 +1138,15 @@ extern int efi_memattr_apply_permissions(struct mm_struct *mm, + char * __init efi_md_typeattr_format(char *buf, size_t size, + const efi_memory_desc_t *md); + ++ ++typedef void (*efi_element_handler_t)(const char *source, ++ const void *element_data, ++ size_t element_size); ++extern int __init parse_efi_signature_list( ++ const char *source, ++ const void *data, size_t size, ++ efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *)); ++ + /** + * efi_range_is_wc - check the WC bit on an address range + * @start: starting kvirt address +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch new file mode 100644 index 000000000000..c6628a5924f8 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch @@ -0,0 +1,249 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From 3f74625c50a48b870c7312459d30701b6758b9a3 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 5 May 2017 08:21:59 +0100 +Subject: [PATCH 4/7] MODSIGN: Import certificates from UEFI Secure Boot + +Secure Boot stores a list of allowed certificates in the 'db' variable. +This imports those certificates into the system trusted keyring. This +allows for a third party signing certificate to be used in conjunction +with signed modules. By importing the public certificate into the 'db' +variable, a user can allow a module signed with that certificate to +load. The shim UEFI bootloader has a similar certificate list stored +in the 'MokListRT' variable. We import those as well. + +Secure Boot also maintains a list of disallowed certificates in the 'dbx' +variable. We load those certificates into the newly introduced system +blacklist keyring and forbid any module signed with those from loading and +forbid the use within the kernel of any key with a matching hash. + +This facility is enabled by setting CONFIG_LOAD_UEFI_KEYS. + +Signed-off-by: Josh Boyer +Signed-off-by: David Howells +(cherry picked from commit 7b7aae2efea13b5a7b80305856c28f235ea8b2fa + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + certs/Kconfig | 16 +++++ + certs/Makefile | 4 ++ + certs/load_uefi.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 188 insertions(+) + create mode 100644 certs/load_uefi.c + +diff --git a/certs/Kconfig b/certs/Kconfig +index 650ffcb8db79..548859925c1f 100644 +--- a/certs/Kconfig ++++ b/certs/Kconfig +@@ -91,4 +91,20 @@ config EFI_SIGNATURE_LIST_PARSER + This option provides support for parsing EFI signature lists for + X.509 certificates and turning them into keys. + ++config LOAD_UEFI_KEYS ++ bool "Load certs and blacklist from UEFI db for module checking" ++ depends on SYSTEM_BLACKLIST_KEYRING ++ depends on SECONDARY_TRUSTED_KEYRING ++ depends on EFI ++ depends on EFI_SIGNATURE_LIST_PARSER ++ help ++ If the kernel is booted in secure boot mode, this option will cause ++ the kernel to load the certificates from the UEFI db and MokListRT ++ into the secondary trusted keyring. It will also load any X.509 ++ SHA256 hashes in the dbx list into the blacklist. ++ ++ The effect of this is that, if the kernel is booted in secure boot ++ mode, modules signed with UEFI-stored keys will be permitted to be ++ loaded and keys that match the blacklist will be rejected. ++ + endmenu +diff --git a/certs/Makefile b/certs/Makefile +index 7e5e179ac685..3f127ac80740 100644 +--- a/certs/Makefile ++++ b/certs/Makefile +@@ -12,6 +12,10 @@ obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o + endif + obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o + ++obj-$(CONFIG_LOAD_UEFI_KEYS) += load_uefi.o ++$(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar ++ ++ + ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y) + + $(eval $(call config_filename,SYSTEM_TRUSTED_KEYS)) +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +new file mode 100644 +index 000000000000..b44e464c3ff4 +--- /dev/null ++++ b/certs/load_uefi.c +@@ -0,0 +1,168 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "internal.h" ++ ++static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID; ++static __initdata efi_guid_t efi_cert_x509_sha256_guid = EFI_CERT_X509_SHA256_GUID; ++static __initdata efi_guid_t efi_cert_sha256_guid = EFI_CERT_SHA256_GUID; ++ ++/* ++ * Get a certificate list blob from the named EFI variable. ++ */ ++static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, ++ unsigned long *size) ++{ ++ efi_status_t status; ++ unsigned long lsize = 4; ++ unsigned long tmpdb[4]; ++ void *db; ++ ++ status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); ++ if (status != EFI_BUFFER_TOO_SMALL) { ++ pr_err("Couldn't get size: 0x%lx\n", status); ++ return NULL; ++ } ++ ++ db = kmalloc(lsize, GFP_KERNEL); ++ if (!db) { ++ pr_err("Couldn't allocate memory for uefi cert list\n"); ++ return NULL; ++ } ++ ++ status = efi.get_variable(name, guid, NULL, &lsize, db); ++ if (status != EFI_SUCCESS) { ++ kfree(db); ++ pr_err("Error reading db var: 0x%lx\n", status); ++ return NULL; ++ } ++ ++ *size = lsize; ++ return db; ++} ++ ++/* ++ * Blacklist an X509 TBS hash. ++ */ ++static __init void uefi_blacklist_x509_tbs(const char *source, ++ const void *data, size_t len) ++{ ++ char *hash, *p; ++ ++ hash = kmalloc(4 + len * 2 + 1, GFP_KERNEL); ++ if (!hash) ++ return; ++ p = memcpy(hash, "tbs:", 4); ++ p += 4; ++ bin2hex(p, data, len); ++ p += len * 2; ++ *p = 0; ++ ++ mark_hash_blacklisted(hash); ++ kfree(hash); ++} ++ ++/* ++ * Blacklist the hash of an executable. ++ */ ++static __init void uefi_blacklist_binary(const char *source, ++ const void *data, size_t len) ++{ ++ char *hash, *p; ++ ++ hash = kmalloc(4 + len * 2 + 1, GFP_KERNEL); ++ if (!hash) ++ return; ++ p = memcpy(hash, "bin:", 4); ++ p += 4; ++ bin2hex(p, data, len); ++ p += len * 2; ++ *p = 0; ++ ++ mark_hash_blacklisted(hash); ++ kfree(hash); ++} ++ ++/* ++ * Return the appropriate handler for particular signature list types found in ++ * the UEFI db and MokListRT tables. ++ */ ++static __init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type) ++{ ++ if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) ++ return add_trusted_secondary_key; ++ return 0; ++} ++ ++/* ++ * Return the appropriate handler for particular signature list types found in ++ * the UEFI dbx and MokListXRT tables. ++ */ ++static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type) ++{ ++ if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0) ++ return uefi_blacklist_x509_tbs; ++ if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) ++ return uefi_blacklist_binary; ++ return 0; ++} ++ ++/* ++ * Load the certs contained in the UEFI databases ++ */ ++static int __init load_uefi_certs(void) ++{ ++ efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; ++ efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; ++ void *db = NULL, *dbx = NULL, *mok = NULL; ++ unsigned long dbsize = 0, dbxsize = 0, moksize = 0; ++ int rc = 0; ++ ++ if (!efi.get_variable) ++ return false; ++ ++ /* Get db, MokListRT, and dbx. They might not exist, so it isn't ++ * an error if we can't get them. ++ */ ++ db = get_cert_list(L"db", &secure_var, &dbsize); ++ if (!db) { ++ pr_err("MODSIGN: Couldn't get UEFI db list\n"); ++ } else { ++ rc = parse_efi_signature_list("UEFI:db", ++ db, dbsize, get_handler_for_db); ++ if (rc) ++ pr_err("Couldn't parse db signatures: %d\n", rc); ++ kfree(db); ++ } ++ ++ mok = get_cert_list(L"MokListRT", &mok_var, &moksize); ++ if (!mok) { ++ pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); ++ } else { ++ rc = parse_efi_signature_list("UEFI:MokListRT", ++ mok, moksize, get_handler_for_db); ++ if (rc) ++ pr_err("Couldn't parse MokListRT signatures: %d\n", rc); ++ kfree(mok); ++ } ++ ++ dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); ++ if (!dbx) { ++ pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); ++ } else { ++ rc = parse_efi_signature_list("UEFI:dbx", ++ dbx, dbxsize, ++ get_handler_for_dbx); ++ if (rc) ++ pr_err("Couldn't parse dbx signatures: %d\n", rc); ++ kfree(dbx); ++ } ++ ++ return rc; ++} ++late_initcall(load_uefi_certs); +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch b/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch new file mode 100644 index 000000000000..5224bbc77894 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch @@ -0,0 +1,91 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From 7defba7cee1c8a882fef24cc9037faab9e546e01 Mon Sep 17 00:00:00 2001 +From: Josh Boyer +Date: Fri, 5 May 2017 08:21:59 +0100 +Subject: [PATCH 5/7] MODSIGN: Allow the "db" UEFI variable to be suppressed + +If a user tells shim to not use the certs/hashes in the UEFI db variable +for verification purposes, shim will set a UEFI variable called +MokIgnoreDB. Have the uefi import code look for this and ignore the db +variable if it is found. + +Signed-off-by: Josh Boyer +Signed-off-by: David Howells +(cherry picked from commit b51ca4e4d6c0c8000789de31a1184a41ac611d33 + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + certs/load_uefi.c | 44 ++++++++++++++++++++++++++++++++++---------- + 1 file changed, 34 insertions(+), 10 deletions(-) + +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +index b44e464c3ff4..3d8845986019 100644 +--- a/certs/load_uefi.c ++++ b/certs/load_uefi.c +@@ -12,6 +12,26 @@ static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID; + static __initdata efi_guid_t efi_cert_x509_sha256_guid = EFI_CERT_X509_SHA256_GUID; + static __initdata efi_guid_t efi_cert_sha256_guid = EFI_CERT_SHA256_GUID; + ++/* ++ * Look to see if a UEFI variable called MokIgnoreDB exists and return true if ++ * it does. ++ * ++ * This UEFI variable is set by the shim if a user tells the shim to not use ++ * the certs/hashes in the UEFI db variable for verification purposes. If it ++ * is set, we should ignore the db variable also and the true return indicates ++ * this. ++ */ ++static __init bool uefi_check_ignore_db(void) ++{ ++ efi_status_t status; ++ unsigned int db = 0; ++ unsigned long size = sizeof(db); ++ efi_guid_t guid = EFI_SHIM_LOCK_GUID; ++ ++ status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db); ++ return status == EFI_SUCCESS; ++} ++ + /* + * Get a certificate list blob from the named EFI variable. + */ +@@ -113,7 +133,9 @@ static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_ty + } + + /* +- * Load the certs contained in the UEFI databases ++ * Load the certs contained in the UEFI databases into the secondary trusted ++ * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist ++ * keyring. + */ + static int __init load_uefi_certs(void) + { +@@ -129,15 +151,17 @@ static int __init load_uefi_certs(void) + /* Get db, MokListRT, and dbx. They might not exist, so it isn't + * an error if we can't get them. + */ +- db = get_cert_list(L"db", &secure_var, &dbsize); +- if (!db) { +- pr_err("MODSIGN: Couldn't get UEFI db list\n"); +- } else { +- rc = parse_efi_signature_list("UEFI:db", +- db, dbsize, get_handler_for_db); +- if (rc) +- pr_err("Couldn't parse db signatures: %d\n", rc); +- kfree(db); ++ if (!uefi_check_ignore_db()) { ++ db = get_cert_list(L"db", &secure_var, &dbsize); ++ if (!db) { ++ pr_err("MODSIGN: Couldn't get UEFI db list\n"); ++ } else { ++ rc = parse_efi_signature_list("UEFI:db", ++ db, dbsize, get_handler_for_db); ++ if (rc) ++ pr_err("Couldn't parse db signatures: %d\n", rc); ++ kfree(db); ++ } + } + + mok = get_cert_list(L"MokListRT", &mok_var, &moksize); +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch b/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch new file mode 100644 index 000000000000..420e1304e473 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch @@ -0,0 +1,112 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From 71be2cb73f4def7903c7fe49babe15c908220ac5 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 2 Oct 2017 18:25:29 -0400 +Subject: [PATCH 6/7] Make get_cert_list() not complain about cert lists that + aren't present. + +Signed-off-by: Peter Jones +(cherry picked from commit 0f4d5c7b49b45e7cf038bb769e33451b78a6445d + git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) +--- + certs/load_uefi.c | 37 ++++++++++++++++++++++--------------- + 1 file changed, 22 insertions(+), 15 deletions(-) + +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +index 3d8845986019..9ef34c44fd1b 100644 +--- a/certs/load_uefi.c ++++ b/certs/load_uefi.c +@@ -35,8 +35,8 @@ static __init bool uefi_check_ignore_db(void) + /* + * Get a certificate list blob from the named EFI variable. + */ +-static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, +- unsigned long *size) ++static __init int get_cert_list(efi_char16_t *name, efi_guid_t *guid, ++ unsigned long *size, void **cert_list) + { + efi_status_t status; + unsigned long lsize = 4; +@@ -44,26 +44,33 @@ static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, + void *db; + + status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); ++ if (status == EFI_NOT_FOUND) { ++ *size = 0; ++ *cert_list = NULL; ++ return 0; ++ } ++ + if (status != EFI_BUFFER_TOO_SMALL) { + pr_err("Couldn't get size: 0x%lx\n", status); +- return NULL; ++ return efi_status_to_err(status); + } + + db = kmalloc(lsize, GFP_KERNEL); + if (!db) { + pr_err("Couldn't allocate memory for uefi cert list\n"); +- return NULL; ++ return -ENOMEM; + } + + status = efi.get_variable(name, guid, NULL, &lsize, db); + if (status != EFI_SUCCESS) { + kfree(db); + pr_err("Error reading db var: 0x%lx\n", status); +- return NULL; ++ return efi_status_to_err(status); + } + + *size = lsize; +- return db; ++ *cert_list = db; ++ return 0; + } + + /* +@@ -152,10 +159,10 @@ static int __init load_uefi_certs(void) + * an error if we can't get them. + */ + if (!uefi_check_ignore_db()) { +- db = get_cert_list(L"db", &secure_var, &dbsize); +- if (!db) { ++ rc = get_cert_list(L"db", &secure_var, &dbsize, &db); ++ if (rc < 0) { + pr_err("MODSIGN: Couldn't get UEFI db list\n"); +- } else { ++ } else if (dbsize != 0) { + rc = parse_efi_signature_list("UEFI:db", + db, dbsize, get_handler_for_db); + if (rc) +@@ -164,10 +171,10 @@ static int __init load_uefi_certs(void) + } + } + +- mok = get_cert_list(L"MokListRT", &mok_var, &moksize); +- if (!mok) { ++ rc = get_cert_list(L"MokListRT", &mok_var, &moksize, &mok); ++ if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); +- } else { ++ } else if (moksize != 0) { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + if (rc) +@@ -175,10 +182,10 @@ static int __init load_uefi_certs(void) + kfree(mok); + } + +- dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); +- if (!dbx) { ++ rc = get_cert_list(L"dbx", &secure_var, &dbxsize, &dbx); ++ if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); +- } else { ++ } else if (dbxsize != 0) { + rc = parse_efi_signature_list("UEFI:dbx", + dbx, dbxsize, + get_handler_for_dbx); +-- +2.20.1 + diff --git a/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch b/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch new file mode 100644 index 000000000000..998fbf84addf --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch @@ -0,0 +1,30 @@ +Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git +From 013d7c3f79a2f4df248f69daca9cbf2175788814 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Thu, 3 Aug 2017 16:56:22 +0100 +Subject: [PATCH 7/7] modsign: Use secondary trust keyring for module signing + +Use secondary trust keyring for module signing as that's where the UEFI +keys get stashed. + +Signed-off-by: David Howells +--- + kernel/module_signing.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index f2075ce8e4b3..6b9a926fd86b 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -83,6 +83,7 @@ int mod_verify_sig(const void *mod, struct load_info *info) + } + + return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, +- NULL, VERIFYING_MODULE_SIGNATURE, ++ VERIFY_USE_SECONDARY_KEYRING, ++ VERIFYING_MODULE_SIGNATURE, + NULL, NULL); + } +-- +2.20.1 + diff --git a/debian/patches/series b/debian/patches/series index 603250676486..32ced0d13ec5 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -143,6 +143,15 @@ features/all/lockdown/arm64-add-kernel-config-option-to-lock-down-when.patch # until the "kernel_lockdown.7" manual page exists features/all/lockdown/lockdown-refer-to-debian-wiki-until-manual-page-exists.patch +# load db and MOK keys into secondary keyring for kernel modules verification +features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch +features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch +features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch +features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch +features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch +features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch +features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch + # Security fixes debian/i386-686-pae-pci-set-pci-nobios-by-default.patch bugfix/all/xen-pciback-Don-t-disable-PCI_COMMAND-on-PCI-device-.patch From 643cc8a41c0ce9b38dd577ea62ec8d01cdc9eb60 Mon Sep 17 00:00:00 2001 From: Luca Boccassi Date: Thu, 2 May 2019 23:03:39 +0100 Subject: [PATCH 02/13] Add patches to enable loading dbx and MOKX blacklists Import patches from: https://lore.kernel.org/patchwork/cover/933178/ that allow to also load dbx and MOKX as blacklists for modules. These patches also disable loading MOK/MOKX when secure boot is not enabled, as the variables will not be safe, and to check the variables attributes before accepting them. --- debian/changelog | 3 +- ...t-load-mok-when-secure-boot-disabled.patch | 59 +++++++++ ...002-MODSIGN-load-blacklist-from-MOKx.patch | 55 ++++++++ ...-hash-before-loading-a-kernel-module.patch | 124 ++++++++++++++++++ ...N-check-the-attributes-of-db-and-mok.patch | 103 +++++++++++++++ debian/patches/series | 4 + 6 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch create mode 100644 debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch diff --git a/debian/changelog b/debian/changelog index b17f71d9b0ea..2174aa1ccad0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1105,7 +1105,8 @@ linux (4.19.37-1) UNRELEASED; urgency=medium libbpf-generate-pkg-config.patch from bpf-next. * Import patches to enable loading keys from UEFI db and MOK from http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git to - allow kernel modules built by users (eg: by dkms) to be verified. + allow kernel modules built by users (eg: by dkms) to be verified, and + to load dbx and MOKX for the equivalent blacklisting functionality. [ Bastian Blank ] * Don't longer recommend irqbalance. (closes: #926967) diff --git a/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch b/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch new file mode 100644 index 000000000000..d7d7fa96bf8f --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch @@ -0,0 +1,59 @@ +Origin: https://lore.kernel.org/patchwork/cover/933178/ +From: "Lee, Chun-Yi" +Subject: [PATCH 1/5] MODSIGN: do not load mok when secure boot disabled + +The mok can not be trusted when the secure boot is disabled. Which +means that the kernel embedded certificate is the only trusted key. + +Due to db/dbx are authenticated variables, they needs manufacturer's +KEK for update. So db/dbx are secure when secureboot disabled. + +Signed-off-by: "Lee, Chun-Yi" +--- + certs/load_uefi.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +index 3d88459..d6de4d0 100644 +--- a/certs/load_uefi.c ++++ b/certs/load_uefi.c +@@ -171,17 +171,6 @@ + } + } + +- rc = get_cert_list(L"MokListRT", &mok_var, &moksize, &mok); +- if (rc < 0) { +- pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); +- } else if (moksize != 0) { +- rc = parse_efi_signature_list("UEFI:MokListRT", +- mok, moksize, get_handler_for_db); +- if (rc) +- pr_err("Couldn't parse MokListRT signatures: %d\n", rc); +- kfree(mok); +- } +- + rc = get_cert_list(L"dbx", &secure_var, &dbxsize, &dbx); + if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); +@@ -194,6 +183,21 @@ + kfree(dbx); + } + ++ /* the MOK can not be trusted when secure boot is disabled */ ++ if (!efi_enabled(EFI_SECURE_BOOT)) ++ return 0; ++ ++ rc = get_cert_list(L"MokListRT", &mok_var, &moksize, &mok); ++ if (rc < 0) { ++ pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); ++ } else if (moksize != 0) { ++ rc = parse_efi_signature_list("UEFI:MokListRT", ++ mok, moksize, get_handler_for_db); ++ if (rc) ++ pr_err("Couldn't parse MokListRT signatures: %d\n", rc); ++ kfree(mok); ++ } ++ + return rc; + } + late_initcall(load_uefi_certs); diff --git a/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch b/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch new file mode 100644 index 000000000000..490c44103554 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch @@ -0,0 +1,55 @@ +Origin: https://lore.kernel.org/patchwork/cover/933178/ +From: "Lee, Chun-Yi" +Subject: [PATCH 2/4] MODSIGN: load blacklist from MOKx + +This patch adds the logic to load the blacklisted hash and +certificates from MOKx which is maintained by shim bootloader. + +Signed-off-by: "Lee, Chun-Yi" +--- + certs/load_uefi.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +index f2f372b..dc66a79 100644 +--- a/certs/load_uefi.c ++++ b/certs/load_uefi.c +@@ -148,8 +148,8 @@ + { + efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; + efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; +- void *db = NULL, *dbx = NULL, *mok = NULL; +- unsigned long dbsize = 0, dbxsize = 0, moksize = 0; ++ void *db = NULL, *dbx = NULL, *mok = NULL, *mokx = NULL; ++ unsigned long dbsize = 0, dbxsize = 0, moksize = 0, mokxsize = 0; + int rc = 0; + + if (!efi.get_variable) +@@ -183,7 +183,7 @@ + kfree(dbx); + } + +- /* the MOK can not be trusted when secure boot is disabled */ ++ /* the MOK and MOKx can not be trusted when secure boot is disabled */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + +@@ -198,6 +198,18 @@ + kfree(mok); + } + ++ rc = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &mokx); ++ if (rc < 0) { ++ pr_info("MODSIGN: Couldn't get UEFI MokListXRT\n"); ++ } else if (mokxsize != 0) { ++ rc = parse_efi_signature_list("UEFI:mokx", ++ mokx, mokxsize, ++ get_handler_for_dbx); ++ if (rc) ++ pr_err("Couldn't parse MokListXRT signatures: %d\n", rc); ++ kfree(mokx); ++ } ++ + return rc; + } + late_initcall(load_uefi_certs); diff --git a/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch new file mode 100644 index 000000000000..757e8edd4ab9 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch @@ -0,0 +1,124 @@ +Origin: https://lore.kernel.org/patchwork/cover/933178/ +From: "Lee, Chun-Yi" +Subject: [PATCH 3/4] MODSIGN: checking the blacklisted hash before loading a + kernel module + +This patch adds the logic for checking the kernel module's hash +base on blacklist. The hash must be generated by sha256 and enrolled +to dbx/mokx. + +For example: + sha256sum sample.ko + mokutil --mokx --import-hash $HASH_RESULT + +Whether the signature on ko file is stripped or not, the hash can be +compared by kernel. + +Signed-off-by: "Lee, Chun-Yi" +--- + kernel/module_signing.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- + 1 file changed, 60 insertions(+), 2 deletions(-) + +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index d3d6f95..d30ac74 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -11,9 +11,12 @@ + + #include + #include ++#include + #include + #include + #include ++#include ++#include + #include "module-internal.h" + + enum pkey_id_type { +@@ -42,19 +45,67 @@ + __be32 sig_len; /* Length of signature data */ + }; + ++static int mod_is_hash_blacklisted(const void *mod, size_t verifylen) ++{ ++ struct crypto_shash *tfm; ++ struct shash_desc *desc; ++ size_t digest_size, desc_size; ++ u8 *digest; ++ int ret = 0; ++ ++ tfm = crypto_alloc_shash("sha256", 0, 0); ++ if (IS_ERR(tfm)) ++ goto error_return; ++ ++ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); ++ digest_size = crypto_shash_digestsize(tfm); ++ digest = kzalloc(digest_size + desc_size, GFP_KERNEL); ++ if (!digest) { ++ pr_err("digest memory buffer allocate fail\n"); ++ ret = -ENOMEM; ++ goto error_digest; ++ } ++ desc = (void *)digest + digest_size; ++ desc->tfm = tfm; ++ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ++ ret = crypto_shash_init(desc); ++ if (ret < 0) ++ goto error_shash; ++ ++ ret = crypto_shash_finup(desc, mod, verifylen, digest); ++ if (ret < 0) ++ goto error_shash; ++ ++ pr_debug("%ld digest: %*phN\n", verifylen, (int) digest_size, digest); ++ ++ ret = is_hash_blacklisted(digest, digest_size, "bin"); ++ if (ret == -EKEYREJECTED) ++ pr_err("Module hash %*phN is blacklisted\n", ++ (int) digest_size, digest); ++ ++error_shash: ++ kfree(digest); ++error_digest: ++ crypto_free_shash(tfm); ++error_return: ++ return ret; ++} ++ + /* + * Verify the signature on a module. + */ + int mod_verify_sig(const void *mod, struct load_info *info) + { + struct module_signature ms; +- size_t sig_len, modlen = info->len; ++ size_t sig_len, modlen = info->len, wholelen; ++ int ret;; + + pr_devel("==>%s(,%zu)\n", __func__, modlen); + + if (modlen <= sizeof(ms)) + return -EBADMSG; + ++ wholelen = modlen + sizeof(MODULE_SIG_STRING) - 1; + memcpy(&ms, mod + (modlen - sizeof(ms)), sizeof(ms)); + modlen -= sizeof(ms); + +@@ -82,8 +133,15 @@ + return -EBADMSG; + } + +- return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, ++ ret = verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, + VERIFY_USE_SECONDARY_KEYRING, + VERIFYING_MODULE_SIGNATURE, + NULL, NULL); ++ pr_devel("verify_pkcs7_signature() = %d\n", ret); ++ ++ /* checking hash of module is in blacklist */ ++ if (!ret) ++ ret = mod_is_hash_blacklisted(mod, wholelen); ++ ++ return ret; + } diff --git a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch new file mode 100644 index 000000000000..24857e23659c --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch @@ -0,0 +1,103 @@ +Origin: https://lore.kernel.org/patchwork/cover/933178/ +From: "Lee, Chun-Yi" +Subject: [PATCH 4/4] MODSIGN: check the attributes of db and mok + +That's better for checking the attributes of db and mok variables +before loading certificates to kernel keyring. + +For db and dbx, both of them are authenticated variables. Which +means that they can only be modified by manufacturer's key. So +the kernel should checks EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS +attribute before we trust it. + +For mok-rt and mokx-rt, both of them are created by shim boot loader +to forward the mok/mokx content to runtime. They must be runtime-volatile +variables. So kernel should checks that the attributes map did not set +EFI_VARIABLE_NON_VOLATILE bit before we trust it. + +Signed-off-by: "Lee, Chun-Yi" +--- + certs/load_uefi.c | 35 +++++++++++++++++++++++------------ + 1 file changed, 23 insertions(+), 12 deletions(-) + +diff --git a/certs/load_uefi.c b/certs/load_uefi.c +index dc66a79..52526bd 100644 +--- a/certs/load_uefi.c ++++ b/certs/load_uefi.c +@@ -36,12 +36,14 @@ + * Get a certificate list blob from the named EFI variable. + */ + static __init int get_cert_list(efi_char16_t *name, efi_guid_t *guid, +- unsigned long *size, void **cert_list) ++ unsigned long *size, void **cert_list, ++ u32 pos_attr, u32 neg_attr) + { + efi_status_t status; + unsigned long lsize = 4; + unsigned long tmpdb[4]; + void *db; ++ u32 attr = 0; + + status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); + if (status == EFI_NOT_FOUND) { +@@ -61,12 +63,19 @@ + return -ENOMEM; + } + +- status = efi.get_variable(name, guid, NULL, &lsize, db); ++ status = efi.get_variable(name, guid, &attr, &lsize, db); + if (status != EFI_SUCCESS) { + kfree(db); + pr_err("Error reading db var: 0x%lx\n", status); + return efi_status_to_err(status); + } ++ /* must have positive attributes and no negative attributes */ ++ if ((pos_attr && !(attr & pos_attr)) || ++ (neg_attr && (attr & neg_attr))) { ++ kfree(db); ++ pr_err("Error reading db var attributes: 0x%016x\n", attr); ++ return -1; ++ } + + *size = lsize; + *cert_list = db; +@@ -159,7 +168,8 @@ + * an error if we can't get them. + */ + if (!uefi_check_ignore_db()) { +- rc = get_cert_list(L"db", &secure_var, &dbsize, &db); ++ rc = get_cert_list(L"db", &secure_var, &dbsize, &db, ++ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); + if (rc < 0) { + pr_err("MODSIGN: Couldn't get UEFI db list\n"); + } else if (dbsize != 0) { +@@ -171,7 +181,8 @@ + } + } + +- rc = get_cert_list(L"dbx", &secure_var, &dbxsize, &dbx); ++ rc = get_cert_list(L"dbx", &secure_var, &dbxsize, &dbx, ++ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, 0); + if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); + } else if (dbxsize != 0) { +@@ -187,7 +198,8 @@ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + +- rc = get_cert_list(L"MokListRT", &mok_var, &moksize, &mok); ++ rc = get_cert_list(L"MokListRT", &mok_var, &moksize, &mok, ++ 0, EFI_VARIABLE_NON_VOLATILE); + if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else if (moksize != 0) { +@@ -198,7 +210,8 @@ + kfree(mok); + } + +- rc = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &mokx); ++ rc = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &mokx, ++ 0, EFI_VARIABLE_NON_VOLATILE); + if (rc < 0) { + pr_info("MODSIGN: Couldn't get UEFI MokListXRT\n"); + } else if (mokxsize != 0) { diff --git a/debian/patches/series b/debian/patches/series index 32ced0d13ec5..f713f9f680f2 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -151,6 +151,10 @@ features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Bo features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch +features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch +features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch +features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch +features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch # Security fixes debian/i386-686-pae-pci-set-pci-nobios-by-default.patch From 5be0740b9175ed715177e353a1f5f40ceacc50cc Mon Sep 17 00:00:00 2001 From: Vagrant Cascadian Date: Sat, 4 May 2019 18:39:31 -0700 Subject: [PATCH 03/13] Add changelog entry for "gencontrol_signed.py: Sort list of modules..." --- debian/changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/debian/changelog b/debian/changelog index fb3317006d40..feafefec8652 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1149,6 +1149,10 @@ linux (4.19.37-1) UNRELEASED; urgency=medium IIO_CROS_EC_BARO, RTC_DRV_CROS_EC as modules. - Enable BATTERY_SBS, CHARGER_CROS_USBPD as modules. + [ Vagrant Cascadian ] + * debian/bin/gencontrol_signed.py: Sort list of modules before adding to + .json file, fixing reproducibility issues. + -- Ben Hutchings Mon, 18 Mar 2019 22:50:08 +0000 linux (4.19.28-2) unstable; urgency=medium From 319a580681ffd3e3fd271f9847182d872ca15360 Mon Sep 17 00:00:00 2001 From: Salvatore Bonaccorso Date: Sun, 5 May 2019 10:25:26 +0200 Subject: [PATCH 04/13] Add Debian bug closer for #928457 --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index feafefec8652..456cff542031 100644 --- a/debian/changelog +++ b/debian/changelog @@ -313,7 +313,7 @@ linux (4.19.37-1) UNRELEASED; urgency=medium - [armhf] clocksource/drivers/exynos_mct: Clear timer interrupt when shutdown - [arm64] clocksource/drivers/arch_timer: Workaround for Allwinner A64 - timer instability + timer instability (Closes: #928457) - [s390x] setup: fix early warning messages - [s390x] virtio: handle find on invalid queue gracefully - scsi: virtio_scsi: don't send sc payload with tmfs From d220ad4bb0e87721540bd79766d97d6ceea93a34 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 13:39:46 +0100 Subject: [PATCH 05/13] Fix up headers for db/MOK patches * Set a correct, specific Origin header for each patch, instead of a repo URL and "cherry picked" message * Add back Date header and Cc pseudo-headers for the second series * Note which patches have been modified by Luca --- ...tricted-boot-time-addition-of-keys-t.patch | 5 +---- ...t-load-mok-when-secure-boot-disabled.patch | 7 ++++++- ...002-MODSIGN-load-blacklist-from-MOKx.patch | 7 ++++++- ...002-efi-Add-EFI-signature-data-types.patch | 5 +---- ...-hash-before-loading-a-kernel-module.patch | 9 +++++++-- ...efi-Add-an-EFI-signature-blob-parser.patch | 5 +---- ...t-certificates-from-UEFI-Secure-Boot.patch | 5 +---- ...N-check-the-attributes-of-db-and-mok.patch | 7 ++++++- ...he-db-UEFI-variable-to-be-suppressed.patch | 5 +---- ...st-not-complain-about-cert-lists-tha.patch | 5 +---- ...ndary-trust-keyring-for-module-signi.patch | 20 +++++++++---------- 11 files changed, 40 insertions(+), 40 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch b/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch index 69ffb5c04b1e..43493454d92c 100644 --- a/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch +++ b/debian/patches/features/all/db-mok-keyring/0001-KEYS-Allow-unrestricted-boot-time-addition-of-keys-t.patch @@ -1,9 +1,8 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From fd416971ea1b441df3e1922c441d1ed66a4ca1d2 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 5 May 2017 08:21:56 +0100 Subject: [PATCH 1/7] KEYS: Allow unrestricted boot-time addition of keys to secondary keyring +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=40db8fc497d010ae6cee6297c3882d3dc3d76d48 Allow keys to be added to the system secondary certificates keyring during kernel initialisation in an unrestricted fashion. Such keys are implicitly @@ -13,8 +12,6 @@ This allows keys in the UEFI database to be added in secure boot mode for the purposes of module signing. Signed-off-by: David Howells -(cherry picked from commit 40db8fc497d010ae6cee6297c3882d3dc3d76d48 - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- certs/internal.h | 18 ++++++++++++++++++ certs/system_keyring.c | 33 +++++++++++++++++++++++++++++++++ diff --git a/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch b/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch index d7d7fa96bf8f..bfe9935c328a 100644 --- a/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch +++ b/debian/patches/features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabled.patch @@ -1,6 +1,7 @@ -Origin: https://lore.kernel.org/patchwork/cover/933178/ From: "Lee, Chun-Yi" +Date: Tue, 13 Mar 2018 18:37:59 +0800 Subject: [PATCH 1/5] MODSIGN: do not load mok when secure boot disabled +Origin: https://lore.kernel.org/patchwork/patch/933173/ The mok can not be trusted when the secure boot is disabled. Which means that the kernel embedded certificate is the only trusted key. @@ -8,7 +9,11 @@ means that the kernel embedded certificate is the only trusted key. Due to db/dbx are authenticated variables, they needs manufacturer's KEK for update. So db/dbx are secure when secureboot disabled. +Cc: David Howells +Cc: Josh Boyer +Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" +[Rebased by Luca Boccassi] --- certs/load_uefi.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch b/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch index 490c44103554..9edb217cc855 100644 --- a/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch +++ b/debian/patches/features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch @@ -1,11 +1,16 @@ -Origin: https://lore.kernel.org/patchwork/cover/933178/ From: "Lee, Chun-Yi" +Date: Tue, 13 Mar 2018 18:38:01 +0800 Subject: [PATCH 2/4] MODSIGN: load blacklist from MOKx +Origin: https://lore.kernel.org/patchwork/patch/933177/ This patch adds the logic to load the blacklisted hash and certificates from MOKx which is maintained by shim bootloader. +Cc: David Howells +Cc: Josh Boyer +Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" +[Rebased by Luca Boccassi] --- certs/load_uefi.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch b/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch index 9138398fe0bb..9506ecff0c77 100644 --- a/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch +++ b/debian/patches/features/all/db-mok-keyring/0002-efi-Add-EFI-signature-data-types.patch @@ -1,16 +1,13 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From 31c5efef25006ae5fc1542e4705e863a98b624b6 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Fri, 5 May 2017 08:21:58 +0100 Subject: [PATCH 2/7] efi: Add EFI signature data types +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=446e0e29d7d53fe7786d33603df5a6682dd00c12 Add the data types that are used for containing hashes, keys and certificates for cryptographic verification along with their corresponding type GUIDs. Signed-off-by: David Howells -(cherry picked from commit 446e0e29d7d53fe7786d33603df5a6682dd00c12 - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- include/linux/efi.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch index 757e8edd4ab9..8d2e8b78db99 100644 --- a/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch +++ b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch @@ -1,7 +1,8 @@ -Origin: https://lore.kernel.org/patchwork/cover/933178/ From: "Lee, Chun-Yi" +Date: Tue, 13 Mar 2018 18:38:02 +0800 Subject: [PATCH 3/4] MODSIGN: checking the blacklisted hash before loading a kernel module +Origin: https://lore.kernel.org/patchwork/patch/933175/ This patch adds the logic for checking the kernel module's hash base on blacklist. The hash must be generated by sha256 and enrolled @@ -14,7 +15,11 @@ For example: Whether the signature on ko file is stripped or not, the hash can be compared by kernel. +Cc: David Howells +Cc: Josh Boyer +Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" +[Rebased by Luca Boccassi] --- kernel/module_signing.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) @@ -23,7 +28,7 @@ diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c -@@ -11,9 +11,12 @@ +@@ -11,9 +11,12 @@h #include #include diff --git a/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch b/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch index 152c907c7b75..bc420e0f0604 100644 --- a/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch +++ b/debian/patches/features/all/db-mok-keyring/0003-efi-Add-an-EFI-signature-blob-parser.patch @@ -1,8 +1,7 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From b0cea6fe6d97f4fa3ac2dbddd54b79d74045c670 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Fri, 5 May 2017 08:21:58 +0100 Subject: [PATCH 3/7] efi: Add an EFI signature blob parser +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=41a595bb0dc097c19ad377a0c32c993234aa2525 Add a function to parse an EFI signature blob looking for elements of interest. A list is made up of a series of sublists, where all the @@ -18,8 +17,6 @@ If the sublist is of interest, each element is passed to the handler function in turn. Signed-off-by: David Howells -(cherry picked from commit 41a595bb0dc097c19ad377a0c32c993234aa2525 - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- certs/Kconfig | 8 ++++ certs/Makefile | 1 + diff --git a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch index c6628a5924f8..50577d738bc7 100644 --- a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch +++ b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-Import-certificates-from-UEFI-Secure-Boot.patch @@ -1,8 +1,7 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From 3f74625c50a48b870c7312459d30701b6758b9a3 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 5 May 2017 08:21:59 +0100 Subject: [PATCH 4/7] MODSIGN: Import certificates from UEFI Secure Boot +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=7b7aae2efea13b5a7b80305856c28f235ea8b2fa Secure Boot stores a list of allowed certificates in the 'db' variable. This imports those certificates into the system trusted keyring. This @@ -21,8 +20,6 @@ This facility is enabled by setting CONFIG_LOAD_UEFI_KEYS. Signed-off-by: Josh Boyer Signed-off-by: David Howells -(cherry picked from commit 7b7aae2efea13b5a7b80305856c28f235ea8b2fa - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- certs/Kconfig | 16 +++++ certs/Makefile | 4 ++ diff --git a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch index 24857e23659c..7127cabaaf06 100644 --- a/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch +++ b/debian/patches/features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch @@ -1,6 +1,7 @@ -Origin: https://lore.kernel.org/patchwork/cover/933178/ From: "Lee, Chun-Yi" +Date: Tue, 13 Mar 2018 18:38:03 +0800 Subject: [PATCH 4/4] MODSIGN: check the attributes of db and mok +Origin: https://lore.kernel.org/patchwork/patch/933176/ That's better for checking the attributes of db and mok variables before loading certificates to kernel keyring. @@ -15,7 +16,11 @@ to forward the mok/mokx content to runtime. They must be runtime-volatile variables. So kernel should checks that the attributes map did not set EFI_VARIABLE_NON_VOLATILE bit before we trust it. +Cc: David Howells +Cc: Josh Boyer +Cc: James Bottomley Signed-off-by: "Lee, Chun-Yi" +[Rebased by Luca Boccassi] --- certs/load_uefi.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch b/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch index 5224bbc77894..395cb48bd08c 100644 --- a/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch +++ b/debian/patches/features/all/db-mok-keyring/0005-MODSIGN-Allow-the-db-UEFI-variable-to-be-suppressed.patch @@ -1,8 +1,7 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From 7defba7cee1c8a882fef24cc9037faab9e546e01 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 5 May 2017 08:21:59 +0100 Subject: [PATCH 5/7] MODSIGN: Allow the "db" UEFI variable to be suppressed +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=b51ca4e4d6c0c8000789de31a1184a41ac611d33 If a user tells shim to not use the certs/hashes in the UEFI db variable for verification purposes, shim will set a UEFI variable called @@ -11,8 +10,6 @@ variable if it is found. Signed-off-by: Josh Boyer Signed-off-by: David Howells -(cherry picked from commit b51ca4e4d6c0c8000789de31a1184a41ac611d33 - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- certs/load_uefi.c | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch b/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch index 420e1304e473..a22387656fb7 100644 --- a/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch +++ b/debian/patches/features/all/db-mok-keyring/0006-Make-get_cert_list-not-complain-about-cert-lists-tha.patch @@ -1,13 +1,10 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From 71be2cb73f4def7903c7fe49babe15c908220ac5 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Mon, 2 Oct 2017 18:25:29 -0400 Subject: [PATCH 6/7] Make get_cert_list() not complain about cert lists that aren't present. +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git/commit/?id=0f4d5c7b49b45e7cf038bb769e33451b78a6445d Signed-off-by: Peter Jones -(cherry picked from commit 0f4d5c7b49b45e7cf038bb769e33451b78a6445d - git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git) --- certs/load_uefi.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch b/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch index 998fbf84addf..d0d59fe8c2da 100644 --- a/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch +++ b/debian/patches/features/all/db-mok-keyring/0007-modsign-Use-secondary-trust-keyring-for-module-signi.patch @@ -1,13 +1,14 @@ -Origin: git://git.kernel.org/pub/scm/linux/kernel/git/jforbes/linux.git -From 013d7c3f79a2f4df248f69daca9cbf2175788814 Mon Sep 17 00:00:00 2001 -From: David Howells -Date: Thu, 3 Aug 2017 16:56:22 +0100 -Subject: [PATCH 7/7] modsign: Use secondary trust keyring for module signing +From: Ke Wu +Date: Tue, 6 Nov 2018 15:21:30 -0800 +Subject: modsign: use all trusted keys to verify module signature +Origin: https://git.kernel.org/linus/e84cd7ee630e44a2cc8ae49e85920a271b214cb3 -Use secondary trust keyring for module signing as that's where the UEFI -keys get stashed. +Make mod_verify_sig to use all trusted keys. This allows keys in +secondary_trusted_keys to be used to verify PKCS#7 signature on a +kernel module. -Signed-off-by: David Howells +Signed-off-by: Ke Wu +Signed-off-by: Jessica Yu --- kernel/module_signing.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) @@ -25,6 +26,3 @@ index f2075ce8e4b3..6b9a926fd86b 100644 + VERIFYING_MODULE_SIGNATURE, NULL, NULL); } --- -2.20.1 - From 2c62d20848aa4861c6f367f80f8756be167af980 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 13:47:00 +0100 Subject: [PATCH 06/13] MODSIGN: Make shash allocation failure fatal --- debian/changelog | 1 + ...-make-shash-allocation-failure-fatal.patch | 28 +++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 30 insertions(+) create mode 100644 debian/patches/features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch diff --git a/debian/changelog b/debian/changelog index d43cf7024de1..3b19e7a3d863 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1094,6 +1094,7 @@ linux (4.19.37-1) UNRELEASED; urgency=medium (CVE-2018-12929, CVE-2018-12930, CVE-2018-12931) * [x86] platform: Enable INTEL_ATOMISP2_PM as module * drivers/firmware/google: Adjust configuration for 4.19 + * MODSIGN: Make shash allocation failure fatal [ YunQiang Su ] * [mips*r6] Re-enable CONFIG_JUMP_LABEL, which has been fixed in upstream. diff --git a/debian/patches/features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch b/debian/patches/features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch new file mode 100644 index 000000000000..2ae3ddde4cd7 --- /dev/null +++ b/debian/patches/features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch @@ -0,0 +1,28 @@ +From: Ben Hutchings +Date: Sun, 05 May 2019 13:45:06 +0100 +Subject: MODSIGN: Make shash allocation failure fatal + +mod_is_hash_blacklisted() currently returns 0 (suceess) if +crypto_alloc_shash() fails. This should instead be a fatal error, +so unwrap and pass up the error code. + +Signed-off-by: Ben Hutchings +--- +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -51,11 +51,13 @@ static int mod_is_hash_blacklisted(const + struct shash_desc *desc; + size_t digest_size, desc_size; + u8 *digest; +- int ret = 0; ++ int ret; + + tfm = crypto_alloc_shash("sha256", 0, 0); +- if (IS_ERR(tfm)) ++ if (IS_ERR(tfm)) { ++ ret = PTR_ERR(tfm); + goto error_return; ++ } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + digest_size = crypto_shash_digestsize(tfm); diff --git a/debian/patches/series b/debian/patches/series index f713f9f680f2..dcb83f1d9dcc 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -155,6 +155,7 @@ features/all/db-mok-keyring/0001-MODSIGN-do-not-load-mok-when-secure-boot-disabl features/all/db-mok-keyring/0002-MODSIGN-load-blacklist-from-MOKx.patch features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch features/all/db-mok-keyring/0004-MODSIGN-check-the-attributes-of-db-and-mok.patch +features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch # Security fixes debian/i386-686-pae-pci-set-pci-nobios-by-default.patch From 542ea0941f39058033bcc5362249f60561a36f6f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 13:54:26 +0100 Subject: [PATCH 07/13] Undo typo --- ...ng-the-blacklisted-hash-before-loading-a-kernel-module.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch index 8d2e8b78db99..8ca2009d045c 100644 --- a/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch +++ b/debian/patches/features/all/db-mok-keyring/0003-MODSIGN-checking-the-blacklisted-hash-before-loading-a-kernel-module.patch @@ -28,7 +28,7 @@ diff --git a/kernel/module_signing.c b/kernel/module_signing.c index d3d6f95..d30ac74 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c -@@ -11,9 +11,12 @@h +@@ -11,9 +11,12 @@ #include #include From 55a23e404af058e04acc3a50c18f34ace76cfd54 Mon Sep 17 00:00:00 2001 From: Salvatore Bonaccorso Date: Sun, 5 May 2019 15:54:13 +0200 Subject: [PATCH 08/13] [amd64,arm64] vfio/type1: Limit DMA mappings per container (CVE-2019-3882) --- debian/changelog | 1 + ...pe1-Limit-DMA-mappings-per-container.patch | 94 +++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 96 insertions(+) create mode 100644 debian/patches/bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch diff --git a/debian/changelog b/debian/changelog index 3b19e7a3d863..2f44f8bcefa7 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1117,6 +1117,7 @@ linux (4.19.37-1) UNRELEASED; urgency=medium * xen/pciback: Don't disable PCI_COMMAND on PCI device reset. (CVE-2015-8553) * [x86] Disable R3964 due to lack of security support + * [amd64,arm64] vfio/type1: Limit DMA mappings per container (CVE-2019-3882) [ Aurelien Jarno ] * [mips] Fix indirect syscall tracing & seccomp filtering for big endian diff --git a/debian/patches/bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch b/debian/patches/bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch new file mode 100644 index 000000000000..6046e5e64aaa --- /dev/null +++ b/debian/patches/bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch @@ -0,0 +1,94 @@ +From: Alex Williamson +Date: Wed, 3 Apr 2019 12:36:21 -0600 +Subject: vfio/type1: Limit DMA mappings per container +Origin: https://git.kernel.org/linus/492855939bdb59c6f947b0b5b44af9ad82b7e38c +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-3882 + +Memory backed DMA mappings are accounted against a user's locked +memory limit, including multiple mappings of the same memory. This +accounting bounds the number of such mappings that a user can create. +However, DMA mappings that are not backed by memory, such as DMA +mappings of device MMIO via mmaps, do not make use of page pinning +and therefore do not count against the user's locked memory limit. +These mappings still consume memory, but the memory is not well +associated to the process for the purpose of oom killing a task. + +To add bounding on this use case, we introduce a limit to the total +number of concurrent DMA mappings that a user is allowed to create. +This limit is exposed as a tunable module option where the default +value of 64K is expected to be well in excess of any reasonable use +case (a large virtual machine configuration would typically only make +use of tens of concurrent mappings). + +This fixes CVE-2019-3882. + +Reviewed-by: Eric Auger +Tested-by: Eric Auger +Reviewed-by: Peter Xu +Reviewed-by: Cornelia Huck +Signed-off-by: Alex Williamson +--- + drivers/vfio/vfio_iommu_type1.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/drivers/vfio/vfio_iommu_type1.c b/drivers/vfio/vfio_iommu_type1.c +index 73652e21efec..d0f731c9920a 100644 +--- a/drivers/vfio/vfio_iommu_type1.c ++++ b/drivers/vfio/vfio_iommu_type1.c +@@ -58,12 +58,18 @@ module_param_named(disable_hugepages, + MODULE_PARM_DESC(disable_hugepages, + "Disable VFIO IOMMU support for IOMMU hugepages."); + ++static unsigned int dma_entry_limit __read_mostly = U16_MAX; ++module_param_named(dma_entry_limit, dma_entry_limit, uint, 0644); ++MODULE_PARM_DESC(dma_entry_limit, ++ "Maximum number of user DMA mappings per container (65535)."); ++ + struct vfio_iommu { + struct list_head domain_list; + struct vfio_domain *external_domain; /* domain for external user */ + struct mutex lock; + struct rb_root dma_list; + struct blocking_notifier_head notifier; ++ unsigned int dma_avail; + bool v2; + bool nesting; + }; +@@ -836,6 +842,7 @@ static void vfio_remove_dma(struct vfio_iommu *iommu, struct vfio_dma *dma) + vfio_unlink_dma(iommu, dma); + put_task_struct(dma->task); + kfree(dma); ++ iommu->dma_avail++; + } + + static unsigned long vfio_pgsize_bitmap(struct vfio_iommu *iommu) +@@ -1081,12 +1088,18 @@ static int vfio_dma_do_map(struct vfio_iommu *iommu, + goto out_unlock; + } + ++ if (!iommu->dma_avail) { ++ ret = -ENOSPC; ++ goto out_unlock; ++ } ++ + dma = kzalloc(sizeof(*dma), GFP_KERNEL); + if (!dma) { + ret = -ENOMEM; + goto out_unlock; + } + ++ iommu->dma_avail--; + dma->iova = iova; + dma->vaddr = vaddr; + dma->prot = prot; +@@ -1583,6 +1596,7 @@ static void *vfio_iommu_type1_open(unsigned long arg) + + INIT_LIST_HEAD(&iommu->domain_list); + iommu->dma_list = RB_ROOT; ++ iommu->dma_avail = dma_entry_limit; + mutex_init(&iommu->lock); + BLOCKING_INIT_NOTIFIER_HEAD(&iommu->notifier); + +-- +2.11.0 + diff --git a/debian/patches/series b/debian/patches/series index dcb83f1d9dcc..cb13f13ba87a 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -161,6 +161,7 @@ features/all/db-mok-keyring/modsign-make-shash-allocation-failure-fatal.patch debian/i386-686-pae-pci-set-pci-nobios-by-default.patch bugfix/all/xen-pciback-Don-t-disable-PCI_COMMAND-on-PCI-device-.patch debian/ntfs-mark-it-as-broken.patch +bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch # Fix exported symbol versions bugfix/all/module-disable-matching-missing-version-crc.patch From 4f3fa1e29628512ee4ef8d220beaeb5030f03dc9 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 15:30:46 +0100 Subject: [PATCH 09/13] aio: Apply fixes from 4.19.38 (CVE-2019-10125) --- debian/changelog | 15 + .../all/0001-aio-clear-IOCB_HIPRI.patch | 52 +++ ...-aio-use-assigned-completion-handler.patch | 32 ++ ...-ring-reservation-from-req-allocatio.patch | 101 ++++++ ...-t-zero-entire-aio_kiocb-aio_get_req.patch | 52 +++ ...e-iocb_put-instead-of-open-coding-it.patch | 34 ++ ...lit-out-iocb-copy-from-io_submit_one.patch | 194 +++++++++++ ...-abstract-out-io_event-filler-helper.patch | 47 +++ ...iocb-private-in-case-any-filesystems.patch | 32 ++ ...lify-and-fix-fget-fput-for-io_submit.patch | 312 ++++++++++++++++++ .../all/0010-pin-iocb-through-aio.patch | 112 +++++++ ...ld-lookup_kiocb-into-its-sole-caller.patch | 61 ++++ .../0012-aio-keep-io_event-in-aio_kiocb.patch | 105 ++++++ ...13-aio-store-event-at-final-iocb_put.patch | 101 ++++++ .../bugfix/all/0014-Fix-aio_poll-races.patch | 225 +++++++++++++ debian/patches/series | 14 + 16 files changed, 1489 insertions(+) create mode 100644 debian/patches/bugfix/all/0001-aio-clear-IOCB_HIPRI.patch create mode 100644 debian/patches/bugfix/all/0002-aio-use-assigned-completion-handler.patch create mode 100644 debian/patches/bugfix/all/0003-aio-separate-out-ring-reservation-from-req-allocatio.patch create mode 100644 debian/patches/bugfix/all/0004-aio-don-t-zero-entire-aio_kiocb-aio_get_req.patch create mode 100644 debian/patches/bugfix/all/0005-aio-use-iocb_put-instead-of-open-coding-it.patch create mode 100644 debian/patches/bugfix/all/0006-aio-split-out-iocb-copy-from-io_submit_one.patch create mode 100644 debian/patches/bugfix/all/0007-aio-abstract-out-io_event-filler-helper.patch create mode 100644 debian/patches/bugfix/all/0008-aio-initialize-kiocb-private-in-case-any-filesystems.patch create mode 100644 debian/patches/bugfix/all/0009-aio-simplify-and-fix-fget-fput-for-io_submit.patch create mode 100644 debian/patches/bugfix/all/0010-pin-iocb-through-aio.patch create mode 100644 debian/patches/bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch create mode 100644 debian/patches/bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch create mode 100644 debian/patches/bugfix/all/0013-aio-store-event-at-final-iocb_put.patch create mode 100644 debian/patches/bugfix/all/0014-Fix-aio_poll-races.patch diff --git a/debian/changelog b/debian/changelog index 2f44f8bcefa7..81db5364802a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1095,6 +1095,21 @@ linux (4.19.37-1) UNRELEASED; urgency=medium * [x86] platform: Enable INTEL_ATOMISP2_PM as module * drivers/firmware/google: Adjust configuration for 4.19 * MODSIGN: Make shash allocation failure fatal + * aio: Apply fixes from 4.19.38: + - aio: clear IOCB_HIPRI + - aio: use assigned completion handler + - aio: separate out ring reservation from req allocation + - aio: don't zero entire aio_kiocb aio_get_req() + - aio: use iocb_put() instead of open coding it + - aio: split out iocb copy from io_submit_one() + - aio: abstract out io_event filler helper + - aio: initialize kiocb private in case any filesystems expect it. + - aio: simplify - and fix - fget/fput for io_submit() (CVE-2019-10125) + - pin iocb through aio. + - aio: fold lookup_kiocb() into its sole caller + - aio: keep io_event in aio_kiocb + - aio: store event at final iocb_put() + - Fix aio_poll() races [ YunQiang Su ] * [mips*r6] Re-enable CONFIG_JUMP_LABEL, which has been fixed in upstream. diff --git a/debian/patches/bugfix/all/0001-aio-clear-IOCB_HIPRI.patch b/debian/patches/bugfix/all/0001-aio-clear-IOCB_HIPRI.patch new file mode 100644 index 000000000000..a54e5d09882e --- /dev/null +++ b/debian/patches/bugfix/all/0001-aio-clear-IOCB_HIPRI.patch @@ -0,0 +1,52 @@ +From: Christoph Hellwig +Date: Thu, 22 Nov 2018 16:44:07 +0100 +Subject: [01/14] aio: clear IOCB_HIPRI +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=9101cbe70ef64c7f35fb75552005a3a696cc288e + +commit 154989e45fd8de9bfb52bbd6e5ea763e437e54c5 upstream. + +No one is going to poll for aio (yet), so we must clear the HIPRI +flag, as we would otherwise send it down the poll queues, where no +one will be polling for completions. + +Signed-off-by: Christoph Hellwig + +IOCB_HIPRI, not RWF_HIPRI. + +Reviewed-by: Johannes Thumshirn +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 45d5ef8dd0a8..78aa249070b1 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1438,8 +1438,7 @@ static int aio_prep_rw(struct kiocb *req, struct iocb *iocb) + ret = ioprio_check_cap(iocb->aio_reqprio); + if (ret) { + pr_debug("aio ioprio check cap error: %d\n", ret); +- fput(req->ki_filp); +- return ret; ++ goto out_fput; + } + + req->ki_ioprio = iocb->aio_reqprio; +@@ -1448,7 +1447,13 @@ static int aio_prep_rw(struct kiocb *req, struct iocb *iocb) + + ret = kiocb_set_rw_flags(req, iocb->aio_rw_flags); + if (unlikely(ret)) +- fput(req->ki_filp); ++ goto out_fput; ++ ++ req->ki_flags &= ~IOCB_HIPRI; /* no one is going to poll for this I/O */ ++ return 0; ++ ++out_fput: ++ fput(req->ki_filp); + return ret; + } + diff --git a/debian/patches/bugfix/all/0002-aio-use-assigned-completion-handler.patch b/debian/patches/bugfix/all/0002-aio-use-assigned-completion-handler.patch new file mode 100644 index 000000000000..7aa6b0fd5091 --- /dev/null +++ b/debian/patches/bugfix/all/0002-aio-use-assigned-completion-handler.patch @@ -0,0 +1,32 @@ +From: Jens Axboe +Date: Tue, 6 Nov 2018 14:27:13 -0700 +Subject: [02/14] aio: use assigned completion handler +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=b3373253f0bab538a7521537dfcb73e731b3d732 + +commit bc9bff61624ac33b7c95861abea1af24ee7a94fc upstream. + +We know this is a read/write request, but in preparation for +having different kinds of those, ensure that we call the assigned +handler instead of assuming it's aio_complete_rq(). + +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 78aa249070b1..3df3fb0678e5 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1492,7 +1492,7 @@ static inline void aio_rw_done(struct kiocb *req, ssize_t ret) + ret = -EINTR; + /*FALLTHRU*/ + default: +- aio_complete_rw(req, ret, 0); ++ req->ki_complete(req, ret, 0); + } + } + diff --git a/debian/patches/bugfix/all/0003-aio-separate-out-ring-reservation-from-req-allocatio.patch b/debian/patches/bugfix/all/0003-aio-separate-out-ring-reservation-from-req-allocatio.patch new file mode 100644 index 000000000000..cf5aad9fe326 --- /dev/null +++ b/debian/patches/bugfix/all/0003-aio-separate-out-ring-reservation-from-req-allocatio.patch @@ -0,0 +1,101 @@ +From: Christoph Hellwig +Date: Mon, 19 Nov 2018 15:57:42 -0700 +Subject: [03/14] aio: separate out ring reservation from req allocation +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=730198c889d85db78058cfb57c1b41c65f55c94e + +commit 432c79978c33ecef91b1b04cea6936c20810da29 upstream. + +This is in preparation for certain types of IO not needing a ring +reserveration. + +Signed-off-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 30 +++++++++++++++++------------- + 1 file changed, 17 insertions(+), 13 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 3df3fb0678e5..b9e0df08277b 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -902,7 +902,7 @@ static void put_reqs_available(struct kioctx *ctx, unsigned nr) + local_irq_restore(flags); + } + +-static bool get_reqs_available(struct kioctx *ctx) ++static bool __get_reqs_available(struct kioctx *ctx) + { + struct kioctx_cpu *kcpu; + bool ret = false; +@@ -994,6 +994,14 @@ static void user_refill_reqs_available(struct kioctx *ctx) + spin_unlock_irq(&ctx->completion_lock); + } + ++static bool get_reqs_available(struct kioctx *ctx) ++{ ++ if (__get_reqs_available(ctx)) ++ return true; ++ user_refill_reqs_available(ctx); ++ return __get_reqs_available(ctx); ++} ++ + /* aio_get_req + * Allocate a slot for an aio request. + * Returns NULL if no requests are free. +@@ -1002,24 +1010,15 @@ static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) + { + struct aio_kiocb *req; + +- if (!get_reqs_available(ctx)) { +- user_refill_reqs_available(ctx); +- if (!get_reqs_available(ctx)) +- return NULL; +- } +- + req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL|__GFP_ZERO); + if (unlikely(!req)) +- goto out_put; ++ return NULL; + + percpu_ref_get(&ctx->reqs); + INIT_LIST_HEAD(&req->ki_list); + refcount_set(&req->ki_refcnt, 0); + req->ki_ctx = ctx; + return req; +-out_put: +- put_reqs_available(ctx, 1); +- return NULL; + } + + static struct kioctx *lookup_ioctx(unsigned long ctx_id) +@@ -1813,9 +1812,13 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + return -EINVAL; + } + ++ if (!get_reqs_available(ctx)) ++ return -EAGAIN; ++ ++ ret = -EAGAIN; + req = aio_get_req(ctx); + if (unlikely(!req)) +- return -EAGAIN; ++ goto out_put_reqs_available; + + if (iocb.aio_flags & IOCB_FLAG_RESFD) { + /* +@@ -1878,11 +1881,12 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + goto out_put_req; + return 0; + out_put_req: +- put_reqs_available(ctx, 1); + percpu_ref_put(&ctx->reqs); + if (req->ki_eventfd) + eventfd_ctx_put(req->ki_eventfd); + kmem_cache_free(kiocb_cachep, req); ++out_put_reqs_available: ++ put_reqs_available(ctx, 1); + return ret; + } + diff --git a/debian/patches/bugfix/all/0004-aio-don-t-zero-entire-aio_kiocb-aio_get_req.patch b/debian/patches/bugfix/all/0004-aio-don-t-zero-entire-aio_kiocb-aio_get_req.patch new file mode 100644 index 000000000000..fd67573e3f51 --- /dev/null +++ b/debian/patches/bugfix/all/0004-aio-don-t-zero-entire-aio_kiocb-aio_get_req.patch @@ -0,0 +1,52 @@ +From: Jens Axboe +Date: Tue, 4 Dec 2018 09:44:49 -0700 +Subject: [04/14] aio: don't zero entire aio_kiocb aio_get_req() +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=ef529eead8cfc11c051b90d239e7137f7141ea94 + +commit 2bc4ca9bb600cbe36941da2b2a67189fc4302a04 upstream. + +It's 192 bytes, fairly substantial. Most items don't need to be cleared, +especially not upfront. Clear the ones we do need to clear, and leave +the other ones for setup when the iocb is prepared and submitted. + +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index b9e0df08277b..2547f17b4fef 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1010,14 +1010,15 @@ static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) + { + struct aio_kiocb *req; + +- req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL|__GFP_ZERO); ++ req = kmem_cache_alloc(kiocb_cachep, GFP_KERNEL); + if (unlikely(!req)) + return NULL; + + percpu_ref_get(&ctx->reqs); ++ req->ki_ctx = ctx; + INIT_LIST_HEAD(&req->ki_list); + refcount_set(&req->ki_refcnt, 0); +- req->ki_ctx = ctx; ++ req->ki_eventfd = NULL; + return req; + } + +@@ -1738,6 +1739,10 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, struct iocb *iocb) + if (unlikely(!req->file)) + return -EBADF; + ++ req->head = NULL; ++ req->woken = false; ++ req->cancelled = false; ++ + apt.pt._qproc = aio_poll_queue_proc; + apt.pt._key = req->events; + apt.iocb = aiocb; diff --git a/debian/patches/bugfix/all/0005-aio-use-iocb_put-instead-of-open-coding-it.patch b/debian/patches/bugfix/all/0005-aio-use-iocb_put-instead-of-open-coding-it.patch new file mode 100644 index 000000000000..50564dd0b7c4 --- /dev/null +++ b/debian/patches/bugfix/all/0005-aio-use-iocb_put-instead-of-open-coding-it.patch @@ -0,0 +1,34 @@ +From: Jens Axboe +Date: Sat, 24 Nov 2018 21:33:09 -0700 +Subject: [05/14] aio: use iocb_put() instead of open coding it +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=4d677689742ab60d5be46e20708276368564427a + +commit 71ebc6fef0f53459f37fb39e1466792232fa52ee upstream. + +Replace the percpu_ref_put() + kmem_cache_free() with a call to +iocb_put() instead. + +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 2547f17b4fef..e2b63ab28ecc 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1886,10 +1886,9 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + goto out_put_req; + return 0; + out_put_req: +- percpu_ref_put(&ctx->reqs); + if (req->ki_eventfd) + eventfd_ctx_put(req->ki_eventfd); +- kmem_cache_free(kiocb_cachep, req); ++ iocb_put(req); + out_put_reqs_available: + put_reqs_available(ctx, 1); + return ret; diff --git a/debian/patches/bugfix/all/0006-aio-split-out-iocb-copy-from-io_submit_one.patch b/debian/patches/bugfix/all/0006-aio-split-out-iocb-copy-from-io_submit_one.patch new file mode 100644 index 000000000000..8ea8bdc29b23 --- /dev/null +++ b/debian/patches/bugfix/all/0006-aio-split-out-iocb-copy-from-io_submit_one.patch @@ -0,0 +1,194 @@ +From: Jens Axboe +Date: Sat, 24 Nov 2018 14:46:14 -0700 +Subject: [06/14] aio: split out iocb copy from io_submit_one() +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=d384f8b855a573ea301fd7f5558cc64cb22107e6 + +commit 88a6f18b950e2e4dce57d31daa151105f4f3dcff upstream. + +In preparation of handing in iocbs in a different fashion as well. Also +make it clear that the iocb being passed in isn't modified, by marking +it const throughout. + +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 68 +++++++++++++++++++++++++++++++------------------------- + 1 file changed, 38 insertions(+), 30 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index e2b63ab28ecc..6e1da220f04b 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1416,7 +1416,7 @@ static void aio_complete_rw(struct kiocb *kiocb, long res, long res2) + aio_complete(iocb, res, res2); + } + +-static int aio_prep_rw(struct kiocb *req, struct iocb *iocb) ++static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) + { + int ret; + +@@ -1457,7 +1457,7 @@ static int aio_prep_rw(struct kiocb *req, struct iocb *iocb) + return ret; + } + +-static int aio_setup_rw(int rw, struct iocb *iocb, struct iovec **iovec, ++static int aio_setup_rw(int rw, const struct iocb *iocb, struct iovec **iovec, + bool vectored, bool compat, struct iov_iter *iter) + { + void __user *buf = (void __user *)(uintptr_t)iocb->aio_buf; +@@ -1496,8 +1496,8 @@ static inline void aio_rw_done(struct kiocb *req, ssize_t ret) + } + } + +-static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored, +- bool compat) ++static ssize_t aio_read(struct kiocb *req, const struct iocb *iocb, ++ bool vectored, bool compat) + { + struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; + struct iov_iter iter; +@@ -1529,8 +1529,8 @@ static ssize_t aio_read(struct kiocb *req, struct iocb *iocb, bool vectored, + return ret; + } + +-static ssize_t aio_write(struct kiocb *req, struct iocb *iocb, bool vectored, +- bool compat) ++static ssize_t aio_write(struct kiocb *req, const struct iocb *iocb, ++ bool vectored, bool compat) + { + struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; + struct iov_iter iter; +@@ -1585,7 +1585,8 @@ static void aio_fsync_work(struct work_struct *work) + aio_complete(container_of(req, struct aio_kiocb, fsync), ret, 0); + } + +-static int aio_fsync(struct fsync_iocb *req, struct iocb *iocb, bool datasync) ++static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, ++ bool datasync) + { + if (unlikely(iocb->aio_buf || iocb->aio_offset || iocb->aio_nbytes || + iocb->aio_rw_flags)) +@@ -1719,7 +1720,7 @@ aio_poll_queue_proc(struct file *file, struct wait_queue_head *head, + add_wait_queue(head, &pt->iocb->poll.wait); + } + +-static ssize_t aio_poll(struct aio_kiocb *aiocb, struct iocb *iocb) ++static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + { + struct kioctx *ctx = aiocb->ki_ctx; + struct poll_iocb *req = &aiocb->poll; +@@ -1791,27 +1792,23 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, struct iocb *iocb) + return 0; + } + +-static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, +- bool compat) ++static int __io_submit_one(struct kioctx *ctx, const struct iocb *iocb, ++ struct iocb __user *user_iocb, bool compat) + { + struct aio_kiocb *req; +- struct iocb iocb; + ssize_t ret; + +- if (unlikely(copy_from_user(&iocb, user_iocb, sizeof(iocb)))) +- return -EFAULT; +- + /* enforce forwards compatibility on users */ +- if (unlikely(iocb.aio_reserved2)) { ++ if (unlikely(iocb->aio_reserved2)) { + pr_debug("EINVAL: reserve field set\n"); + return -EINVAL; + } + + /* prevent overflows */ + if (unlikely( +- (iocb.aio_buf != (unsigned long)iocb.aio_buf) || +- (iocb.aio_nbytes != (size_t)iocb.aio_nbytes) || +- ((ssize_t)iocb.aio_nbytes < 0) ++ (iocb->aio_buf != (unsigned long)iocb->aio_buf) || ++ (iocb->aio_nbytes != (size_t)iocb->aio_nbytes) || ++ ((ssize_t)iocb->aio_nbytes < 0) + )) { + pr_debug("EINVAL: overflow check\n"); + return -EINVAL; +@@ -1825,14 +1822,14 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + if (unlikely(!req)) + goto out_put_reqs_available; + +- if (iocb.aio_flags & IOCB_FLAG_RESFD) { ++ if (iocb->aio_flags & IOCB_FLAG_RESFD) { + /* + * If the IOCB_FLAG_RESFD flag of aio_flags is set, get an + * instance of the file* now. The file descriptor must be + * an eventfd() fd, and will be signaled for each completed + * event using the eventfd_signal() function. + */ +- req->ki_eventfd = eventfd_ctx_fdget((int) iocb.aio_resfd); ++ req->ki_eventfd = eventfd_ctx_fdget((int) iocb->aio_resfd); + if (IS_ERR(req->ki_eventfd)) { + ret = PTR_ERR(req->ki_eventfd); + req->ki_eventfd = NULL; +@@ -1847,32 +1844,32 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + } + + req->ki_user_iocb = user_iocb; +- req->ki_user_data = iocb.aio_data; ++ req->ki_user_data = iocb->aio_data; + +- switch (iocb.aio_lio_opcode) { ++ switch (iocb->aio_lio_opcode) { + case IOCB_CMD_PREAD: +- ret = aio_read(&req->rw, &iocb, false, compat); ++ ret = aio_read(&req->rw, iocb, false, compat); + break; + case IOCB_CMD_PWRITE: +- ret = aio_write(&req->rw, &iocb, false, compat); ++ ret = aio_write(&req->rw, iocb, false, compat); + break; + case IOCB_CMD_PREADV: +- ret = aio_read(&req->rw, &iocb, true, compat); ++ ret = aio_read(&req->rw, iocb, true, compat); + break; + case IOCB_CMD_PWRITEV: +- ret = aio_write(&req->rw, &iocb, true, compat); ++ ret = aio_write(&req->rw, iocb, true, compat); + break; + case IOCB_CMD_FSYNC: +- ret = aio_fsync(&req->fsync, &iocb, false); ++ ret = aio_fsync(&req->fsync, iocb, false); + break; + case IOCB_CMD_FDSYNC: +- ret = aio_fsync(&req->fsync, &iocb, true); ++ ret = aio_fsync(&req->fsync, iocb, true); + break; + case IOCB_CMD_POLL: +- ret = aio_poll(req, &iocb); ++ ret = aio_poll(req, iocb); + break; + default: +- pr_debug("invalid aio operation %d\n", iocb.aio_lio_opcode); ++ pr_debug("invalid aio operation %d\n", iocb->aio_lio_opcode); + ret = -EINVAL; + break; + } +@@ -1894,6 +1891,17 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, + return ret; + } + ++static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, ++ bool compat) ++{ ++ struct iocb iocb; ++ ++ if (unlikely(copy_from_user(&iocb, user_iocb, sizeof(iocb)))) ++ return -EFAULT; ++ ++ return __io_submit_one(ctx, &iocb, user_iocb, compat); ++} ++ + /* sys_io_submit: + * Queue the nr iocbs pointed to by iocbpp for processing. Returns + * the number of iocbs queued. May return -EINVAL if the aio_context diff --git a/debian/patches/bugfix/all/0007-aio-abstract-out-io_event-filler-helper.patch b/debian/patches/bugfix/all/0007-aio-abstract-out-io_event-filler-helper.patch new file mode 100644 index 000000000000..13bf1e962f9d --- /dev/null +++ b/debian/patches/bugfix/all/0007-aio-abstract-out-io_event-filler-helper.patch @@ -0,0 +1,47 @@ +From: Jens Axboe +Date: Tue, 20 Nov 2018 20:06:23 -0700 +Subject: [07/14] aio: abstract out io_event filler helper +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=a812f7b68a3940e0369fd0fb24febec794a67623 + +commit 875736bb3f3ded168469f6a14df7a938416a99d5 upstream. + +Reviewed-by: Christoph Hellwig +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 6e1da220f04b..f6ce01ca6903 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1059,6 +1059,15 @@ static inline void iocb_put(struct aio_kiocb *iocb) + } + } + ++static void aio_fill_event(struct io_event *ev, struct aio_kiocb *iocb, ++ long res, long res2) ++{ ++ ev->obj = (u64)(unsigned long)iocb->ki_user_iocb; ++ ev->data = iocb->ki_user_data; ++ ev->res = res; ++ ev->res2 = res2; ++} ++ + /* aio_complete + * Called when the io request on the given iocb is complete. + */ +@@ -1086,10 +1095,7 @@ static void aio_complete(struct aio_kiocb *iocb, long res, long res2) + ev_page = kmap_atomic(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); + event = ev_page + pos % AIO_EVENTS_PER_PAGE; + +- event->obj = (u64)(unsigned long)iocb->ki_user_iocb; +- event->data = iocb->ki_user_data; +- event->res = res; +- event->res2 = res2; ++ aio_fill_event(event, iocb, res, res2); + + kunmap_atomic(ev_page); + flush_dcache_page(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); diff --git a/debian/patches/bugfix/all/0008-aio-initialize-kiocb-private-in-case-any-filesystems.patch b/debian/patches/bugfix/all/0008-aio-initialize-kiocb-private-in-case-any-filesystems.patch new file mode 100644 index 000000000000..8276851eef29 --- /dev/null +++ b/debian/patches/bugfix/all/0008-aio-initialize-kiocb-private-in-case-any-filesystems.patch @@ -0,0 +1,32 @@ +From: Mike Marshall +Date: Tue, 5 Feb 2019 14:13:35 -0500 +Subject: [08/14] aio: initialize kiocb private in case any filesystems expect + it. +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=2afa01cd9186974051b38b7d1f31bb2407e41e3a + +commit ec51f8ee1e63498e9f521ec0e5a6d04622bb2c67 upstream. + +A recent optimization had left private uninitialized. + +Fixes: 2bc4ca9bb600 ("aio: don't zero entire aio_kiocb aio_get_req()") +Reviewed-by: Christoph Hellwig +Signed-off-by: Mike Marshall +Signed-off-by: Jens Axboe +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/fs/aio.c b/fs/aio.c +index f6ce01ca6903..d74fc9e112ac 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1430,6 +1430,7 @@ static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) + if (unlikely(!req->ki_filp)) + return -EBADF; + req->ki_complete = aio_complete_rw; ++ req->private = NULL; + req->ki_pos = iocb->aio_offset; + req->ki_flags = iocb_flags(req->ki_filp); + if (iocb->aio_flags & IOCB_FLAG_RESFD) diff --git a/debian/patches/bugfix/all/0009-aio-simplify-and-fix-fget-fput-for-io_submit.patch b/debian/patches/bugfix/all/0009-aio-simplify-and-fix-fget-fput-for-io_submit.patch new file mode 100644 index 000000000000..e7e2d8d4a41a --- /dev/null +++ b/debian/patches/bugfix/all/0009-aio-simplify-and-fix-fget-fput-for-io_submit.patch @@ -0,0 +1,312 @@ +From: Linus Torvalds +Date: Sun, 3 Mar 2019 14:23:33 -0800 +Subject: [09/14] aio: simplify - and fix - fget/fput for io_submit() +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=d6b2615f7d31d8e58b685d42dbafcc7dc1204bbd +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-10125 + +commit 84c4e1f89fefe70554da0ab33be72c9be7994379 upstream. + +Al Viro root-caused a race where the IOCB_CMD_POLL handling of +fget/fput() could cause us to access the file pointer after it had +already been freed: + + "In more details - normally IOCB_CMD_POLL handling looks so: + + 1) io_submit(2) allocates aio_kiocb instance and passes it to + aio_poll() + + 2) aio_poll() resolves the descriptor to struct file by req->file = + fget(iocb->aio_fildes) + + 3) aio_poll() sets ->woken to false and raises ->ki_refcnt of that + aio_kiocb to 2 (bumps by 1, that is). + + 4) aio_poll() calls vfs_poll(). After sanity checks (basically, + "poll_wait() had been called and only once") it locks the queue. + That's what the extra reference to iocb had been for - we know we + can safely access it. + + 5) With queue locked, we check if ->woken has already been set to + true (by aio_poll_wake()) and, if it had been, we unlock the + queue, drop a reference to aio_kiocb and bugger off - at that + point it's a responsibility to aio_poll_wake() and the stuff + called/scheduled by it. That code will drop the reference to file + in req->file, along with the other reference to our aio_kiocb. + + 6) otherwise, we see whether we need to wait. If we do, we unlock the + queue, drop one reference to aio_kiocb and go away - eventual + wakeup (or cancel) will deal with the reference to file and with + the other reference to aio_kiocb + + 7) otherwise we remove ourselves from waitqueue (still under the + queue lock), so that wakeup won't get us. No async activity will + be happening, so we can safely drop req->file and iocb ourselves. + + If wakeup happens while we are in vfs_poll(), we are fine - aio_kiocb + won't get freed under us, so we can do all the checks and locking + safely. And we don't touch ->file if we detect that case. + + However, vfs_poll() most certainly *does* touch the file it had been + given. So wakeup coming while we are still in ->poll() might end up + doing fput() on that file. That case is not too rare, and usually we + are saved by the still present reference from descriptor table - that + fput() is not the final one. + + But if another thread closes that descriptor right after our fget() + and wakeup does happen before ->poll() returns, we are in trouble - + final fput() done while we are in the middle of a method: + +Al also wrote a patch to take an extra reference to the file descriptor +to fix this, but I instead suggested we just streamline the whole file +pointer handling by submit_io() so that the generic aio submission code +simply keeps the file pointer around until the aio has completed. + +Fixes: bfe4037e722e ("aio: implement IOCB_CMD_POLL") +Acked-by: Al Viro +Reported-by: syzbot+503d4cc169fcec1cb18c@syzkaller.appspotmail.com +Signed-off-by: Linus Torvalds +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 72 +++++++++++++++++++--------------------------- + include/linux/fs.h | 8 +++++- + 2 files changed, 36 insertions(+), 44 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index d74fc9e112ac..46229e663b57 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -161,9 +161,13 @@ struct kioctx { + unsigned id; + }; + ++/* ++ * First field must be the file pointer in all the ++ * iocb unions! See also 'struct kiocb' in ++ */ + struct fsync_iocb { +- struct work_struct work; + struct file *file; ++ struct work_struct work; + bool datasync; + }; + +@@ -177,8 +181,15 @@ struct poll_iocb { + struct work_struct work; + }; + ++/* ++ * NOTE! Each of the iocb union members has the file pointer ++ * as the first entry in their struct definition. So you can ++ * access the file pointer through any of the sub-structs, ++ * or directly as just 'ki_filp' in this struct. ++ */ + struct aio_kiocb { + union { ++ struct file *ki_filp; + struct kiocb rw; + struct fsync_iocb fsync; + struct poll_iocb poll; +@@ -1054,6 +1065,8 @@ static inline void iocb_put(struct aio_kiocb *iocb) + { + if (refcount_read(&iocb->ki_refcnt) == 0 || + refcount_dec_and_test(&iocb->ki_refcnt)) { ++ if (iocb->ki_filp) ++ fput(iocb->ki_filp); + percpu_ref_put(&iocb->ki_ctx->reqs); + kmem_cache_free(kiocb_cachep, iocb); + } +@@ -1418,7 +1431,6 @@ static void aio_complete_rw(struct kiocb *kiocb, long res, long res2) + file_end_write(kiocb->ki_filp); + } + +- fput(kiocb->ki_filp); + aio_complete(iocb, res, res2); + } + +@@ -1426,9 +1438,6 @@ static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) + { + int ret; + +- req->ki_filp = fget(iocb->aio_fildes); +- if (unlikely(!req->ki_filp)) +- return -EBADF; + req->ki_complete = aio_complete_rw; + req->private = NULL; + req->ki_pos = iocb->aio_offset; +@@ -1445,7 +1454,7 @@ static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) + ret = ioprio_check_cap(iocb->aio_reqprio); + if (ret) { + pr_debug("aio ioprio check cap error: %d\n", ret); +- goto out_fput; ++ return ret; + } + + req->ki_ioprio = iocb->aio_reqprio; +@@ -1454,14 +1463,10 @@ static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) + + ret = kiocb_set_rw_flags(req, iocb->aio_rw_flags); + if (unlikely(ret)) +- goto out_fput; ++ return ret; + + req->ki_flags &= ~IOCB_HIPRI; /* no one is going to poll for this I/O */ + return 0; +- +-out_fput: +- fput(req->ki_filp); +- return ret; + } + + static int aio_setup_rw(int rw, const struct iocb *iocb, struct iovec **iovec, +@@ -1515,24 +1520,19 @@ static ssize_t aio_read(struct kiocb *req, const struct iocb *iocb, + if (ret) + return ret; + file = req->ki_filp; +- +- ret = -EBADF; + if (unlikely(!(file->f_mode & FMODE_READ))) +- goto out_fput; ++ return -EBADF; + ret = -EINVAL; + if (unlikely(!file->f_op->read_iter)) +- goto out_fput; ++ return -EINVAL; + + ret = aio_setup_rw(READ, iocb, &iovec, vectored, compat, &iter); + if (ret) +- goto out_fput; ++ return ret; + ret = rw_verify_area(READ, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) + aio_rw_done(req, call_read_iter(file, req, &iter)); + kfree(iovec); +-out_fput: +- if (unlikely(ret)) +- fput(file); + return ret; + } + +@@ -1549,16 +1549,14 @@ static ssize_t aio_write(struct kiocb *req, const struct iocb *iocb, + return ret; + file = req->ki_filp; + +- ret = -EBADF; + if (unlikely(!(file->f_mode & FMODE_WRITE))) +- goto out_fput; +- ret = -EINVAL; ++ return -EBADF; + if (unlikely(!file->f_op->write_iter)) +- goto out_fput; ++ return -EINVAL; + + ret = aio_setup_rw(WRITE, iocb, &iovec, vectored, compat, &iter); + if (ret) +- goto out_fput; ++ return ret; + ret = rw_verify_area(WRITE, file, &req->ki_pos, iov_iter_count(&iter)); + if (!ret) { + /* +@@ -1576,9 +1574,6 @@ static ssize_t aio_write(struct kiocb *req, const struct iocb *iocb, + aio_rw_done(req, call_write_iter(file, req, &iter)); + } + kfree(iovec); +-out_fput: +- if (unlikely(ret)) +- fput(file); + return ret; + } + +@@ -1588,7 +1583,6 @@ static void aio_fsync_work(struct work_struct *work) + int ret; + + ret = vfs_fsync(req->file, req->datasync); +- fput(req->file); + aio_complete(container_of(req, struct aio_kiocb, fsync), ret, 0); + } + +@@ -1599,13 +1593,8 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, + iocb->aio_rw_flags)) + return -EINVAL; + +- req->file = fget(iocb->aio_fildes); +- if (unlikely(!req->file)) +- return -EBADF; +- if (unlikely(!req->file->f_op->fsync)) { +- fput(req->file); ++ if (unlikely(!req->file->f_op->fsync)) + return -EINVAL; +- } + + req->datasync = datasync; + INIT_WORK(&req->work, aio_fsync_work); +@@ -1615,10 +1604,7 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, + + static inline void aio_poll_complete(struct aio_kiocb *iocb, __poll_t mask) + { +- struct file *file = iocb->poll.file; +- + aio_complete(iocb, mangle_poll(mask), 0); +- fput(file); + } + + static void aio_poll_complete_work(struct work_struct *work) +@@ -1743,9 +1729,6 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + + INIT_WORK(&req->work, aio_poll_complete_work); + req->events = demangle_poll(iocb->aio_buf) | EPOLLERR | EPOLLHUP; +- req->file = fget(iocb->aio_fildes); +- if (unlikely(!req->file)) +- return -EBADF; + + req->head = NULL; + req->woken = false; +@@ -1788,10 +1771,8 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + spin_unlock_irq(&ctx->ctx_lock); + + out: +- if (unlikely(apt.error)) { +- fput(req->file); ++ if (unlikely(apt.error)) + return apt.error; +- } + + if (mask) + aio_poll_complete(aiocb, mask); +@@ -1829,6 +1810,11 @@ static int __io_submit_one(struct kioctx *ctx, const struct iocb *iocb, + if (unlikely(!req)) + goto out_put_reqs_available; + ++ req->ki_filp = fget(iocb->aio_fildes); ++ ret = -EBADF; ++ if (unlikely(!req->ki_filp)) ++ goto out_put_req; ++ + if (iocb->aio_flags & IOCB_FLAG_RESFD) { + /* + * If the IOCB_FLAG_RESFD flag of aio_flags is set, get an +diff --git a/include/linux/fs.h b/include/linux/fs.h +index 7b6084854bfe..111c94c4baa1 100644 +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -304,13 +304,19 @@ enum rw_hint { + + struct kiocb { + struct file *ki_filp; ++ ++ /* The 'ki_filp' pointer is shared in a union for aio */ ++ randomized_struct_fields_start ++ + loff_t ki_pos; + void (*ki_complete)(struct kiocb *iocb, long ret, long ret2); + void *private; + int ki_flags; + u16 ki_hint; + u16 ki_ioprio; /* See linux/ioprio.h */ +-} __randomize_layout; ++ ++ randomized_struct_fields_end ++}; + + static inline bool is_sync_kiocb(struct kiocb *kiocb) + { diff --git a/debian/patches/bugfix/all/0010-pin-iocb-through-aio.patch b/debian/patches/bugfix/all/0010-pin-iocb-through-aio.patch new file mode 100644 index 000000000000..210086fcc74f --- /dev/null +++ b/debian/patches/bugfix/all/0010-pin-iocb-through-aio.patch @@ -0,0 +1,112 @@ +From: Linus Torvalds +Date: Wed, 6 Mar 2019 20:22:54 -0500 +Subject: [10/14] pin iocb through aio. +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=c7f2525abfecf8a57a1417837b6a809df79b299e + +commit b53119f13a04879c3bf502828d99d13726639ead upstream. + +aio_poll() is not the only case that needs file pinned; worse, while +aio_read()/aio_write() can live without pinning iocb itself, the +proof is rather brittle and can easily break on later changes. + +Signed-off-by: Linus Torvalds +Signed-off-by: Al Viro +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 37 +++++++++++++++++++++---------------- + 1 file changed, 21 insertions(+), 16 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 46229e663b57..10e5a8f52dce 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1016,6 +1016,9 @@ static bool get_reqs_available(struct kioctx *ctx) + /* aio_get_req + * Allocate a slot for an aio request. + * Returns NULL if no requests are free. ++ * ++ * The refcount is initialized to 2 - one for the async op completion, ++ * one for the synchronous code that does this. + */ + static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) + { +@@ -1028,7 +1031,7 @@ static inline struct aio_kiocb *aio_get_req(struct kioctx *ctx) + percpu_ref_get(&ctx->reqs); + req->ki_ctx = ctx; + INIT_LIST_HEAD(&req->ki_list); +- refcount_set(&req->ki_refcnt, 0); ++ refcount_set(&req->ki_refcnt, 2); + req->ki_eventfd = NULL; + return req; + } +@@ -1061,15 +1064,18 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) + return ret; + } + ++static inline void iocb_destroy(struct aio_kiocb *iocb) ++{ ++ if (iocb->ki_filp) ++ fput(iocb->ki_filp); ++ percpu_ref_put(&iocb->ki_ctx->reqs); ++ kmem_cache_free(kiocb_cachep, iocb); ++} ++ + static inline void iocb_put(struct aio_kiocb *iocb) + { +- if (refcount_read(&iocb->ki_refcnt) == 0 || +- refcount_dec_and_test(&iocb->ki_refcnt)) { +- if (iocb->ki_filp) +- fput(iocb->ki_filp); +- percpu_ref_put(&iocb->ki_ctx->reqs); +- kmem_cache_free(kiocb_cachep, iocb); +- } ++ if (refcount_dec_and_test(&iocb->ki_refcnt)) ++ iocb_destroy(iocb); + } + + static void aio_fill_event(struct io_event *ev, struct aio_kiocb *iocb, +@@ -1743,9 +1749,6 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + INIT_LIST_HEAD(&req->wait.entry); + init_waitqueue_func_entry(&req->wait, aio_poll_wake); + +- /* one for removal from waitqueue, one for this function */ +- refcount_set(&aiocb->ki_refcnt, 2); +- + mask = vfs_poll(req->file, &apt.pt) & req->events; + if (unlikely(!req->head)) { + /* we did not manage to set up a waitqueue, done */ +@@ -1776,7 +1779,6 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + + if (mask) + aio_poll_complete(aiocb, mask); +- iocb_put(aiocb); + return 0; + } + +@@ -1867,18 +1869,21 @@ static int __io_submit_one(struct kioctx *ctx, const struct iocb *iocb, + break; + } + ++ /* Done with the synchronous reference */ ++ iocb_put(req); ++ + /* + * If ret is 0, we'd either done aio_complete() ourselves or have + * arranged for that to be done asynchronously. Anything non-zero + * means that we need to destroy req ourselves. + */ +- if (ret) +- goto out_put_req; +- return 0; ++ if (!ret) ++ return 0; ++ + out_put_req: + if (req->ki_eventfd) + eventfd_ctx_put(req->ki_eventfd); +- iocb_put(req); ++ iocb_destroy(req); + out_put_reqs_available: + put_reqs_available(ctx, 1); + return ret; diff --git a/debian/patches/bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch b/debian/patches/bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch new file mode 100644 index 000000000000..c287368ca684 --- /dev/null +++ b/debian/patches/bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch @@ -0,0 +1,61 @@ +From: Al Viro +Date: Mon, 11 Mar 2019 19:00:36 -0400 +Subject: [11/14] aio: fold lookup_kiocb() into its sole caller +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=592ea630b081a6c97ec56499b0e12f68fd2da2d8 + +commit 833f4154ed560232120bc475935ee1d6a20e159f upstream. + +Signed-off-by: Al Viro +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 29 +++++++---------------------- + 1 file changed, 7 insertions(+), 22 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 10e5a8f52dce..cda193f6de76 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1992,24 +1992,6 @@ COMPAT_SYSCALL_DEFINE3(io_submit, compat_aio_context_t, ctx_id, + } + #endif + +-/* lookup_kiocb +- * Finds a given iocb for cancellation. +- */ +-static struct aio_kiocb * +-lookup_kiocb(struct kioctx *ctx, struct iocb __user *iocb) +-{ +- struct aio_kiocb *kiocb; +- +- assert_spin_locked(&ctx->ctx_lock); +- +- /* TODO: use a hash or array, this sucks. */ +- list_for_each_entry(kiocb, &ctx->active_reqs, ki_list) { +- if (kiocb->ki_user_iocb == iocb) +- return kiocb; +- } +- return NULL; +-} +- + /* sys_io_cancel: + * Attempts to cancel an iocb previously passed to io_submit. If + * the operation is successfully cancelled, the resulting event is +@@ -2038,10 +2020,13 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, + return -EINVAL; + + spin_lock_irq(&ctx->ctx_lock); +- kiocb = lookup_kiocb(ctx, iocb); +- if (kiocb) { +- ret = kiocb->ki_cancel(&kiocb->rw); +- list_del_init(&kiocb->ki_list); ++ /* TODO: use a hash or array, this sucks. */ ++ list_for_each_entry(kiocb, &ctx->active_reqs, ki_list) { ++ if (kiocb->ki_user_iocb == iocb) { ++ ret = kiocb->ki_cancel(&kiocb->rw); ++ list_del_init(&kiocb->ki_list); ++ break; ++ } + } + spin_unlock_irq(&ctx->ctx_lock); + diff --git a/debian/patches/bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch b/debian/patches/bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch new file mode 100644 index 000000000000..ede326d17415 --- /dev/null +++ b/debian/patches/bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch @@ -0,0 +1,105 @@ +From: Al Viro +Date: Thu, 7 Mar 2019 19:43:45 -0500 +Subject: [12/14] aio: keep io_event in aio_kiocb +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=c20202c51d2b6703a4e539235f892f34daabd791 + +commit a9339b7855094ba11a97e8822ae038135e879e79 upstream. + +We want to separate forming the resulting io_event from putting it +into the ring buffer. + +Signed-off-by: Al Viro +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 31 +++++++++++++------------------ + 1 file changed, 13 insertions(+), 18 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index cda193f6de76..ec30f1bdac0c 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -198,8 +198,7 @@ struct aio_kiocb { + struct kioctx *ki_ctx; + kiocb_cancel_fn *ki_cancel; + +- struct iocb __user *ki_user_iocb; /* user's aiocb */ +- __u64 ki_user_data; /* user's data for completion */ ++ struct io_event ki_res; + + struct list_head ki_list; /* the aio core uses this + * for cancellation */ +@@ -1078,15 +1077,6 @@ static inline void iocb_put(struct aio_kiocb *iocb) + iocb_destroy(iocb); + } + +-static void aio_fill_event(struct io_event *ev, struct aio_kiocb *iocb, +- long res, long res2) +-{ +- ev->obj = (u64)(unsigned long)iocb->ki_user_iocb; +- ev->data = iocb->ki_user_data; +- ev->res = res; +- ev->res2 = res2; +-} +- + /* aio_complete + * Called when the io request on the given iocb is complete. + */ +@@ -1098,6 +1088,8 @@ static void aio_complete(struct aio_kiocb *iocb, long res, long res2) + unsigned tail, pos, head; + unsigned long flags; + ++ iocb->ki_res.res = res; ++ iocb->ki_res.res2 = res2; + /* + * Add a completion event to the ring buffer. Must be done holding + * ctx->completion_lock to prevent other code from messing with the tail +@@ -1114,14 +1106,14 @@ static void aio_complete(struct aio_kiocb *iocb, long res, long res2) + ev_page = kmap_atomic(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); + event = ev_page + pos % AIO_EVENTS_PER_PAGE; + +- aio_fill_event(event, iocb, res, res2); ++ *event = iocb->ki_res; + + kunmap_atomic(ev_page); + flush_dcache_page(ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE]); + +- pr_debug("%p[%u]: %p: %p %Lx %lx %lx\n", +- ctx, tail, iocb, iocb->ki_user_iocb, iocb->ki_user_data, +- res, res2); ++ pr_debug("%p[%u]: %p: %p %Lx %Lx %Lx\n", ctx, tail, iocb, ++ (void __user *)(unsigned long)iocb->ki_res.obj, ++ iocb->ki_res.data, iocb->ki_res.res, iocb->ki_res.res2); + + /* after flagging the request as done, we + * must never even look at it again +@@ -1838,8 +1830,10 @@ static int __io_submit_one(struct kioctx *ctx, const struct iocb *iocb, + goto out_put_req; + } + +- req->ki_user_iocb = user_iocb; +- req->ki_user_data = iocb->aio_data; ++ req->ki_res.obj = (u64)(unsigned long)user_iocb; ++ req->ki_res.data = iocb->aio_data; ++ req->ki_res.res = 0; ++ req->ki_res.res2 = 0; + + switch (iocb->aio_lio_opcode) { + case IOCB_CMD_PREAD: +@@ -2009,6 +2003,7 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, + struct aio_kiocb *kiocb; + int ret = -EINVAL; + u32 key; ++ u64 obj = (u64)(unsigned long)iocb; + + if (unlikely(get_user(key, &iocb->aio_key))) + return -EFAULT; +@@ -2022,7 +2017,7 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t, ctx_id, struct iocb __user *, iocb, + spin_lock_irq(&ctx->ctx_lock); + /* TODO: use a hash or array, this sucks. */ + list_for_each_entry(kiocb, &ctx->active_reqs, ki_list) { +- if (kiocb->ki_user_iocb == iocb) { ++ if (kiocb->ki_res.obj == obj) { + ret = kiocb->ki_cancel(&kiocb->rw); + list_del_init(&kiocb->ki_list); + break; diff --git a/debian/patches/bugfix/all/0013-aio-store-event-at-final-iocb_put.patch b/debian/patches/bugfix/all/0013-aio-store-event-at-final-iocb_put.patch new file mode 100644 index 000000000000..b418252a47a2 --- /dev/null +++ b/debian/patches/bugfix/all/0013-aio-store-event-at-final-iocb_put.patch @@ -0,0 +1,101 @@ +From: Al Viro +Date: Thu, 7 Mar 2019 19:49:55 -0500 +Subject: [13/14] aio: store event at final iocb_put() +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=aab66dfb757aa5b211ec6b0c322b42f4ef5ab34f + +commit 2bb874c0d873d13bd9b9b9c6d7b7c4edab18c8b4 upstream. + +Instead of having aio_complete() set ->ki_res.{res,res2}, do that +explicitly in its callers, drop the reference (as aio_complete() +used to do) and delay the rest until the final iocb_put(). + +Signed-off-by: Al Viro +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 33 +++++++++++++++++---------------- + 1 file changed, 17 insertions(+), 16 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index ec30f1bdac0c..556ee620038f 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -1071,16 +1071,10 @@ static inline void iocb_destroy(struct aio_kiocb *iocb) + kmem_cache_free(kiocb_cachep, iocb); + } + +-static inline void iocb_put(struct aio_kiocb *iocb) +-{ +- if (refcount_dec_and_test(&iocb->ki_refcnt)) +- iocb_destroy(iocb); +-} +- + /* aio_complete + * Called when the io request on the given iocb is complete. + */ +-static void aio_complete(struct aio_kiocb *iocb, long res, long res2) ++static void aio_complete(struct aio_kiocb *iocb) + { + struct kioctx *ctx = iocb->ki_ctx; + struct aio_ring *ring; +@@ -1088,8 +1082,6 @@ static void aio_complete(struct aio_kiocb *iocb, long res, long res2) + unsigned tail, pos, head; + unsigned long flags; + +- iocb->ki_res.res = res; +- iocb->ki_res.res2 = res2; + /* + * Add a completion event to the ring buffer. Must be done holding + * ctx->completion_lock to prevent other code from messing with the tail +@@ -1155,7 +1147,14 @@ static void aio_complete(struct aio_kiocb *iocb, long res, long res2) + + if (waitqueue_active(&ctx->wait)) + wake_up(&ctx->wait); +- iocb_put(iocb); ++} ++ ++static inline void iocb_put(struct aio_kiocb *iocb) ++{ ++ if (refcount_dec_and_test(&iocb->ki_refcnt)) { ++ aio_complete(iocb); ++ iocb_destroy(iocb); ++ } + } + + /* aio_read_events_ring +@@ -1429,7 +1428,9 @@ static void aio_complete_rw(struct kiocb *kiocb, long res, long res2) + file_end_write(kiocb->ki_filp); + } + +- aio_complete(iocb, res, res2); ++ iocb->ki_res.res = res; ++ iocb->ki_res.res2 = res2; ++ iocb_put(iocb); + } + + static int aio_prep_rw(struct kiocb *req, const struct iocb *iocb) +@@ -1577,11 +1578,10 @@ static ssize_t aio_write(struct kiocb *req, const struct iocb *iocb, + + static void aio_fsync_work(struct work_struct *work) + { +- struct fsync_iocb *req = container_of(work, struct fsync_iocb, work); +- int ret; ++ struct aio_kiocb *iocb = container_of(work, struct aio_kiocb, fsync.work); + +- ret = vfs_fsync(req->file, req->datasync); +- aio_complete(container_of(req, struct aio_kiocb, fsync), ret, 0); ++ iocb->ki_res.res = vfs_fsync(iocb->fsync.file, iocb->fsync.datasync); ++ iocb_put(iocb); + } + + static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, +@@ -1602,7 +1602,8 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, + + static inline void aio_poll_complete(struct aio_kiocb *iocb, __poll_t mask) + { +- aio_complete(iocb, mangle_poll(mask), 0); ++ iocb->ki_res.res = mangle_poll(mask); ++ iocb_put(iocb); + } + + static void aio_poll_complete_work(struct work_struct *work) diff --git a/debian/patches/bugfix/all/0014-Fix-aio_poll-races.patch b/debian/patches/bugfix/all/0014-Fix-aio_poll-races.patch new file mode 100644 index 000000000000..c507e6812602 --- /dev/null +++ b/debian/patches/bugfix/all/0014-Fix-aio_poll-races.patch @@ -0,0 +1,225 @@ +From: Al Viro +Date: Thu, 7 Mar 2019 21:45:41 -0500 +Subject: [14/14] Fix aio_poll() races +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=e9e47779aaa7212ccb75f8d8d4d16ab188efb313 + +commit af5c72b1fc7a00aa484e90b0c4e0eeb582545634 upstream. + +aio_poll() has to cope with several unpleasant problems: + * requests that might stay around indefinitely need to +be made visible for io_cancel(2); that must not be done to +a request already completed, though. + * in cases when ->poll() has placed us on a waitqueue, +wakeup might have happened (and request completed) before ->poll() +returns. + * worse, in some early wakeup cases request might end +up re-added into the queue later - we can't treat "woken up and +currently not in the queue" as "it's not going to stick around +indefinitely" + * ... moreover, ->poll() might have decided not to +put it on any queues to start with, and that needs to be distinguished +from the previous case + * ->poll() might have tried to put us on more than one queue. +Only the first will succeed for aio poll, so we might end up missing +wakeups. OTOH, we might very well notice that only after the +wakeup hits and request gets completed (all before ->poll() gets +around to the second poll_wait()). In that case it's too late to +decide that we have an error. + +req->woken was an attempt to deal with that. Unfortunately, it was +broken. What we need to keep track of is not that wakeup has happened - +the thing might come back after that. It's that async reference is +already gone and won't come back, so we can't (and needn't) put the +request on the list of cancellables. + +The easiest case is "request hadn't been put on any waitqueues"; we +can tell by seeing NULL apt.head, and in that case there won't be +anything async. We should either complete the request ourselves +(if vfs_poll() reports anything of interest) or return an error. + +In all other cases we get exclusion with wakeups by grabbing the +queue lock. + +If request is currently on queue and we have something interesting +from vfs_poll(), we can steal it and complete the request ourselves. + +If it's on queue and vfs_poll() has not reported anything interesting, +we either put it on the cancellable list, or, if we know that it +hadn't been put on all queues ->poll() wanted it on, we steal it and +return an error. + +If it's _not_ on queue, it's either been already dealt with (in which +case we do nothing), or there's aio_poll_complete_work() about to be +executed. In that case we either put it on the cancellable list, +or, if we know it hadn't been put on all queues ->poll() wanted it on, +simulate what cancel would've done. + +It's a lot more convoluted than I'd like it to be. Single-consumer APIs +suck, and unfortunately aio is not an exception... + +Signed-off-by: Al Viro +Cc: Guenter Roeck +Signed-off-by: Greg Kroah-Hartman +--- + fs/aio.c | 90 +++++++++++++++++++++++++------------------------------- + 1 file changed, 40 insertions(+), 50 deletions(-) + +diff --git a/fs/aio.c b/fs/aio.c +index 556ee620038f..911e23087dfb 100644 +--- a/fs/aio.c ++++ b/fs/aio.c +@@ -175,7 +175,7 @@ struct poll_iocb { + struct file *file; + struct wait_queue_head *head; + __poll_t events; +- bool woken; ++ bool done; + bool cancelled; + struct wait_queue_entry wait; + struct work_struct work; +@@ -1600,12 +1600,6 @@ static int aio_fsync(struct fsync_iocb *req, const struct iocb *iocb, + return 0; + } + +-static inline void aio_poll_complete(struct aio_kiocb *iocb, __poll_t mask) +-{ +- iocb->ki_res.res = mangle_poll(mask); +- iocb_put(iocb); +-} +- + static void aio_poll_complete_work(struct work_struct *work) + { + struct poll_iocb *req = container_of(work, struct poll_iocb, work); +@@ -1631,9 +1625,11 @@ static void aio_poll_complete_work(struct work_struct *work) + return; + } + list_del_init(&iocb->ki_list); ++ iocb->ki_res.res = mangle_poll(mask); ++ req->done = true; + spin_unlock_irq(&ctx->ctx_lock); + +- aio_poll_complete(iocb, mask); ++ iocb_put(iocb); + } + + /* assumes we are called with irqs disabled */ +@@ -1661,31 +1657,27 @@ static int aio_poll_wake(struct wait_queue_entry *wait, unsigned mode, int sync, + __poll_t mask = key_to_poll(key); + unsigned long flags; + +- req->woken = true; +- + /* for instances that support it check for an event match first: */ +- if (mask) { +- if (!(mask & req->events)) +- return 0; ++ if (mask && !(mask & req->events)) ++ return 0; + ++ list_del_init(&req->wait.entry); ++ ++ if (mask && spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { + /* + * Try to complete the iocb inline if we can. Use + * irqsave/irqrestore because not all filesystems (e.g. fuse) + * call this function with IRQs disabled and because IRQs + * have to be disabled before ctx_lock is obtained. + */ +- if (spin_trylock_irqsave(&iocb->ki_ctx->ctx_lock, flags)) { +- list_del(&iocb->ki_list); +- spin_unlock_irqrestore(&iocb->ki_ctx->ctx_lock, flags); +- +- list_del_init(&req->wait.entry); +- aio_poll_complete(iocb, mask); +- return 1; +- } ++ list_del(&iocb->ki_list); ++ iocb->ki_res.res = mangle_poll(mask); ++ req->done = true; ++ spin_unlock_irqrestore(&iocb->ki_ctx->ctx_lock, flags); ++ iocb_put(iocb); ++ } else { ++ schedule_work(&req->work); + } +- +- list_del_init(&req->wait.entry); +- schedule_work(&req->work); + return 1; + } + +@@ -1717,6 +1709,7 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + struct kioctx *ctx = aiocb->ki_ctx; + struct poll_iocb *req = &aiocb->poll; + struct aio_poll_table apt; ++ bool cancel = false; + __poll_t mask; + + /* reject any unknown events outside the normal event mask. */ +@@ -1730,7 +1723,7 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + req->events = demangle_poll(iocb->aio_buf) | EPOLLERR | EPOLLHUP; + + req->head = NULL; +- req->woken = false; ++ req->done = false; + req->cancelled = false; + + apt.pt._qproc = aio_poll_queue_proc; +@@ -1743,36 +1736,33 @@ static ssize_t aio_poll(struct aio_kiocb *aiocb, const struct iocb *iocb) + init_waitqueue_func_entry(&req->wait, aio_poll_wake); + + mask = vfs_poll(req->file, &apt.pt) & req->events; +- if (unlikely(!req->head)) { +- /* we did not manage to set up a waitqueue, done */ +- goto out; +- } +- + spin_lock_irq(&ctx->ctx_lock); +- spin_lock(&req->head->lock); +- if (req->woken) { +- /* wake_up context handles the rest */ +- mask = 0; ++ if (likely(req->head)) { ++ spin_lock(&req->head->lock); ++ if (unlikely(list_empty(&req->wait.entry))) { ++ if (apt.error) ++ cancel = true; ++ apt.error = 0; ++ mask = 0; ++ } ++ if (mask || apt.error) { ++ list_del_init(&req->wait.entry); ++ } else if (cancel) { ++ WRITE_ONCE(req->cancelled, true); ++ } else if (!req->done) { /* actually waiting for an event */ ++ list_add_tail(&aiocb->ki_list, &ctx->active_reqs); ++ aiocb->ki_cancel = aio_poll_cancel; ++ } ++ spin_unlock(&req->head->lock); ++ } ++ if (mask) { /* no async, we'd stolen it */ ++ aiocb->ki_res.res = mangle_poll(mask); + apt.error = 0; +- } else if (mask || apt.error) { +- /* if we get an error or a mask we are done */ +- WARN_ON_ONCE(list_empty(&req->wait.entry)); +- list_del_init(&req->wait.entry); +- } else { +- /* actually waiting for an event */ +- list_add_tail(&aiocb->ki_list, &ctx->active_reqs); +- aiocb->ki_cancel = aio_poll_cancel; + } +- spin_unlock(&req->head->lock); + spin_unlock_irq(&ctx->ctx_lock); +- +-out: +- if (unlikely(apt.error)) +- return apt.error; +- + if (mask) +- aio_poll_complete(aiocb, mask); +- return 0; ++ iocb_put(aiocb); ++ return apt.error; + } + + static int __io_submit_one(struct kioctx *ctx, const struct iocb *iocb, diff --git a/debian/patches/series b/debian/patches/series index cb13f13ba87a..ce60e4117723 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -162,6 +162,20 @@ debian/i386-686-pae-pci-set-pci-nobios-by-default.patch bugfix/all/xen-pciback-Don-t-disable-PCI_COMMAND-on-PCI-device-.patch debian/ntfs-mark-it-as-broken.patch bugfix/all/vfio-type1-Limit-DMA-mappings-per-container.patch +bugfix/all/0001-aio-clear-IOCB_HIPRI.patch +bugfix/all/0002-aio-use-assigned-completion-handler.patch +bugfix/all/0003-aio-separate-out-ring-reservation-from-req-allocatio.patch +bugfix/all/0004-aio-don-t-zero-entire-aio_kiocb-aio_get_req.patch +bugfix/all/0005-aio-use-iocb_put-instead-of-open-coding-it.patch +bugfix/all/0006-aio-split-out-iocb-copy-from-io_submit_one.patch +bugfix/all/0007-aio-abstract-out-io_event-filler-helper.patch +bugfix/all/0008-aio-initialize-kiocb-private-in-case-any-filesystems.patch +bugfix/all/0009-aio-simplify-and-fix-fget-fput-for-io_submit.patch +bugfix/all/0010-pin-iocb-through-aio.patch +bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch +bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch +bugfix/all/0013-aio-store-event-at-final-iocb_put.patch +bugfix/all/0014-Fix-aio_poll-races.patch # Fix exported symbol versions bugfix/all/module-disable-matching-missing-version-crc.patch From 83f5e0f1ef8fb7275c7e3b8043b38b63ad42d742 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 15:42:32 +0100 Subject: [PATCH 10/13] tracing: Fix buffer_ref pipe ops This is preparation for fixing CVE-2019-11487. --- debian/changelog | 1 + .../all/tracing-fix-buffer_ref-pipe-ops.patch | 137 ++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 139 insertions(+) create mode 100644 debian/patches/bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch diff --git a/debian/changelog b/debian/changelog index 81db5364802a..34545147cf17 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1110,6 +1110,7 @@ linux (4.19.37-1) UNRELEASED; urgency=medium - aio: keep io_event in aio_kiocb - aio: store event at final iocb_put() - Fix aio_poll() races + * tracing: Fix buffer_ref pipe ops [ YunQiang Su ] * [mips*r6] Re-enable CONFIG_JUMP_LABEL, which has been fixed in upstream. diff --git a/debian/patches/bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch b/debian/patches/bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch new file mode 100644 index 000000000000..23f6bda0a90e --- /dev/null +++ b/debian/patches/bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch @@ -0,0 +1,137 @@ +From: Jann Horn +Date: Thu, 4 Apr 2019 23:59:25 +0200 +Subject: tracing: Fix buffer_ref pipe ops +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=cffeb9c84d20816a2173e3cfeca210c8bfa8e357 + +commit b987222654f84f7b4ca95b3a55eca784cb30235b upstream. + +This fixes multiple issues in buffer_pipe_buf_ops: + + - The ->steal() handler must not return zero unless the pipe buffer has + the only reference to the page. But generic_pipe_buf_steal() assumes + that every reference to the pipe is tracked by the page's refcount, + which isn't true for these buffers - buffer_pipe_buf_get(), which + duplicates a buffer, doesn't touch the page's refcount. + Fix it by using generic_pipe_buf_nosteal(), which refuses every + attempted theft. It should be easy to actually support ->steal, but the + only current users of pipe_buf_steal() are the virtio console and FUSE, + and they also only use it as an optimization. So it's probably not worth + the effort. + - The ->get() and ->release() handlers can be invoked concurrently on pipe + buffers backed by the same struct buffer_ref. Make them safe against + concurrency by using refcount_t. + - The pointers stored in ->private were only zeroed out when the last + reference to the buffer_ref was dropped. As far as I know, this + shouldn't be necessary anyway, but if we do it, let's always do it. + +Link: http://lkml.kernel.org/r/20190404215925.253531-1-jannh@google.com + +Cc: Ingo Molnar +Cc: Masami Hiramatsu +Cc: Al Viro +Cc: stable@vger.kernel.org +Fixes: 73a757e63114d ("ring-buffer: Return reader page back into existing ring buffer") +Signed-off-by: Jann Horn +Signed-off-by: Steven Rostedt (VMware) +Signed-off-by: Greg Kroah-Hartman +--- + fs/splice.c | 4 ++-- + include/linux/pipe_fs_i.h | 1 + + kernel/trace/trace.c | 28 ++++++++++++++-------------- + 3 files changed, 17 insertions(+), 16 deletions(-) + +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -333,8 +333,8 @@ const struct pipe_buf_operations default + .get = generic_pipe_buf_get, + }; + +-static int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe, +- struct pipe_buffer *buf) ++int generic_pipe_buf_nosteal(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf) + { + return 1; + } +--- a/include/linux/pipe_fs_i.h ++++ b/include/linux/pipe_fs_i.h +@@ -181,6 +181,7 @@ void free_pipe_info(struct pipe_inode_in + void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); + int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); + int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); ++int generic_pipe_buf_nosteal(struct pipe_inode_info *, struct pipe_buffer *); + void generic_pipe_buf_release(struct pipe_inode_info *, struct pipe_buffer *); + void pipe_buf_mark_unmergeable(struct pipe_buffer *buf); + +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -6800,19 +6800,23 @@ struct buffer_ref { + struct ring_buffer *buffer; + void *page; + int cpu; +- int ref; ++ refcount_t refcount; + }; + ++static void buffer_ref_release(struct buffer_ref *ref) ++{ ++ if (!refcount_dec_and_test(&ref->refcount)) ++ return; ++ ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->page); ++ kfree(ref); ++} ++ + static void buffer_pipe_buf_release(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { + struct buffer_ref *ref = (struct buffer_ref *)buf->private; + +- if (--ref->ref) +- return; +- +- ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->page); +- kfree(ref); ++ buffer_ref_release(ref); + buf->private = 0; + } + +@@ -6821,7 +6825,7 @@ static void buffer_pipe_buf_get(struct p + { + struct buffer_ref *ref = (struct buffer_ref *)buf->private; + +- ref->ref++; ++ refcount_inc(&ref->refcount); + } + + /* Pipe buffer operations for a buffer. */ +@@ -6829,7 +6833,7 @@ static const struct pipe_buf_operations + .can_merge = 0, + .confirm = generic_pipe_buf_confirm, + .release = buffer_pipe_buf_release, +- .steal = generic_pipe_buf_steal, ++ .steal = generic_pipe_buf_nosteal, + .get = buffer_pipe_buf_get, + }; + +@@ -6842,11 +6846,7 @@ static void buffer_spd_release(struct sp + struct buffer_ref *ref = + (struct buffer_ref *)spd->partial[i].private; + +- if (--ref->ref) +- return; +- +- ring_buffer_free_read_page(ref->buffer, ref->cpu, ref->page); +- kfree(ref); ++ buffer_ref_release(ref); + spd->partial[i].private = 0; + } + +@@ -6901,7 +6901,7 @@ tracing_buffers_splice_read(struct file + break; + } + +- ref->ref = 1; ++ refcount_set(&ref->refcount, 1); + ref->buffer = iter->trace_buffer->buffer; + ref->page = ring_buffer_alloc_read_page(ref->buffer, iter->cpu_file); + if (IS_ERR(ref->page)) { diff --git a/debian/patches/series b/debian/patches/series index ce60e4117723..bc43f3c267cb 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -176,6 +176,7 @@ bugfix/all/0011-aio-fold-lookup_kiocb-into-its-sole-caller.patch bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch bugfix/all/0013-aio-store-event-at-final-iocb_put.patch bugfix/all/0014-Fix-aio_poll-races.patch +bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch # Fix exported symbol versions bugfix/all/module-disable-matching-missing-version-crc.patch From ece5b4e4cd97b1cd58ae7ead83458d9c80524350 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 15:44:05 +0100 Subject: [PATCH 11/13] mm,fs: Prevent page refcount overflow (CVE-2019-11487) --- debian/changelog | 5 + ...-count-overflow-check-tighter-and-mo.patch | 52 ++++++ ...-mm-add-try_get_page-helper-function.patch | 56 ++++++ ...ser_pages-from-overflowing-page-refc.patch | 155 +++++++++++++++++ ...ge-refcount-overflow-in-pipe_buf_get.patch | 162 ++++++++++++++++++ debian/patches/series | 4 + 6 files changed, 434 insertions(+) create mode 100644 debian/patches/bugfix/all/0001-mm-make-page-ref-count-overflow-check-tighter-and-mo.patch create mode 100644 debian/patches/bugfix/all/0002-mm-add-try_get_page-helper-function.patch create mode 100644 debian/patches/bugfix/all/0003-mm-prevent-get_user_pages-from-overflowing-page-refc.patch create mode 100644 debian/patches/bugfix/all/0004-fs-prevent-page-refcount-overflow-in-pipe_buf_get.patch diff --git a/debian/changelog b/debian/changelog index 34545147cf17..2ba62ab33345 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1111,6 +1111,11 @@ linux (4.19.37-1) UNRELEASED; urgency=medium - aio: store event at final iocb_put() - Fix aio_poll() races * tracing: Fix buffer_ref pipe ops + * mm,fs: Prevent page refcount overflow (CVE-2019-11487): + - mm: make page ref count overflow check tighter and more explicit + - mm: add 'try_get_page()' helper function + - mm: prevent get_user_pages() from overflowing page refcount + - fs: prevent page refcount overflow in pipe_buf_get [ YunQiang Su ] * [mips*r6] Re-enable CONFIG_JUMP_LABEL, which has been fixed in upstream. diff --git a/debian/patches/bugfix/all/0001-mm-make-page-ref-count-overflow-check-tighter-and-mo.patch b/debian/patches/bugfix/all/0001-mm-make-page-ref-count-overflow-check-tighter-and-mo.patch new file mode 100644 index 000000000000..1c4231f1931c --- /dev/null +++ b/debian/patches/bugfix/all/0001-mm-make-page-ref-count-overflow-check-tighter-and-mo.patch @@ -0,0 +1,52 @@ +From: Linus Torvalds +Date: Thu, 11 Apr 2019 10:06:20 -0700 +Subject: mm: make page ref count overflow check tighter and more explicit +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=9f6da5fd05577ef4a05c1744cc7098d0173823af +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-11487 + +commit f958d7b528b1b40c44cfda5eabe2d82760d868c3 upstream. + +We have a VM_BUG_ON() to check that the page reference count doesn't +underflow (or get close to overflow) by checking the sign of the count. + +That's all fine, but we actually want to allow people to use a "get page +ref unless it's already very high" helper function, and we want that one +to use the sign of the page ref (without triggering this VM_BUG_ON). + +Change the VM_BUG_ON to only check for small underflows (or _very_ close +to overflowing), and ignore overflows which have strayed into negative +territory. + +Acked-by: Matthew Wilcox +Cc: Jann Horn +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/mm.h | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/include/linux/mm.h b/include/linux/mm.h +index e899460f1bc5..9965704813dc 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -915,6 +915,10 @@ static inline bool is_device_public_page(const struct page *page) + } + #endif /* CONFIG_DEV_PAGEMAP_OPS */ + ++/* 127: arbitrary random number, small enough to assemble well */ ++#define page_ref_zero_or_close_to_overflow(page) \ ++ ((unsigned int) page_ref_count(page) + 127u <= 127u) ++ + static inline void get_page(struct page *page) + { + page = compound_head(page); +@@ -922,7 +926,7 @@ static inline void get_page(struct page *page) + * Getting a normal page or the head of a compound page + * requires to already have an elevated page->_refcount. + */ +- VM_BUG_ON_PAGE(page_ref_count(page) <= 0, page); ++ VM_BUG_ON_PAGE(page_ref_zero_or_close_to_overflow(page), page); + page_ref_inc(page); + } + diff --git a/debian/patches/bugfix/all/0002-mm-add-try_get_page-helper-function.patch b/debian/patches/bugfix/all/0002-mm-add-try_get_page-helper-function.patch new file mode 100644 index 000000000000..97adc182300c --- /dev/null +++ b/debian/patches/bugfix/all/0002-mm-add-try_get_page-helper-function.patch @@ -0,0 +1,56 @@ +From: Linus Torvalds +Date: Thu, 11 Apr 2019 10:14:59 -0700 +Subject: mm: add 'try_get_page()' helper function +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=0612cae7ec6b79d2ff1b34562bab79d5bf96327a +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-11487 + +commit 88b1a17dfc3ed7728316478fae0f5ad508f50397 upstream. + +This is the same as the traditional 'get_page()' function, but instead +of unconditionally incrementing the reference count of the page, it only +does so if the count was "safe". It returns whether the reference count +was incremented (and is marked __must_check, since the caller obviously +has to be aware of it). + +Also like 'get_page()', you can't use this function unless you already +had a reference to the page. The intent is that you can use this +exactly like get_page(), but in situations where you want to limit the +maximum reference count. + +The code currently does an unconditional WARN_ON_ONCE() if we ever hit +the reference count issues (either zero or negative), as a notification +that the conditional non-increment actually happened. + +NOTE! The count access for the "safety" check is inherently racy, but +that doesn't matter since the buffer we use is basically half the range +of the reference count (ie we look at the sign of the count). + +Acked-by: Matthew Wilcox +Cc: Jann Horn +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/mm.h | 9 +++++++++ + 1 file changed, 9 insertions(+) + +diff --git a/include/linux/mm.h b/include/linux/mm.h +index 9965704813dc..bdec425c8e14 100644 +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -930,6 +930,15 @@ static inline void get_page(struct page *page) + page_ref_inc(page); + } + ++static inline __must_check bool try_get_page(struct page *page) ++{ ++ page = compound_head(page); ++ if (WARN_ON_ONCE(page_ref_count(page) <= 0)) ++ return false; ++ page_ref_inc(page); ++ return true; ++} ++ + static inline void put_page(struct page *page) + { + page = compound_head(page); diff --git a/debian/patches/bugfix/all/0003-mm-prevent-get_user_pages-from-overflowing-page-refc.patch b/debian/patches/bugfix/all/0003-mm-prevent-get_user_pages-from-overflowing-page-refc.patch new file mode 100644 index 000000000000..0b248a14fe96 --- /dev/null +++ b/debian/patches/bugfix/all/0003-mm-prevent-get_user_pages-from-overflowing-page-refc.patch @@ -0,0 +1,155 @@ +From: Linus Torvalds +Date: Thu, 11 Apr 2019 10:49:19 -0700 +Subject: mm: prevent get_user_pages() from overflowing page refcount +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=d972ebbf42ba6712460308ae57c222a0706f2af3 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-11487 + +commit 8fde12ca79aff9b5ba951fce1a2641901b8d8e64 upstream. + +If the page refcount wraps around past zero, it will be freed while +there are still four billion references to it. One of the possible +avenues for an attacker to try to make this happen is by doing direct IO +on a page multiple times. This patch makes get_user_pages() refuse to +take a new page reference if there are already more than two billion +references to the page. + +Reported-by: Jann Horn +Acked-by: Matthew Wilcox +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + mm/gup.c | 45 ++++++++++++++++++++++++++++++++++----------- + mm/hugetlb.c | 13 +++++++++++++ + 2 files changed, 47 insertions(+), 11 deletions(-) + +diff --git a/mm/gup.c b/mm/gup.c +index 0a5374e6e82d..caadd31714a5 100644 +--- a/mm/gup.c ++++ b/mm/gup.c +@@ -153,7 +153,10 @@ static struct page *follow_page_pte(struct vm_area_struct *vma, + } + + if (flags & FOLL_GET) { +- get_page(page); ++ if (unlikely(!try_get_page(page))) { ++ page = ERR_PTR(-ENOMEM); ++ goto out; ++ } + + /* drop the pgmap reference now that we hold the page */ + if (pgmap) { +@@ -296,7 +299,10 @@ static struct page *follow_pmd_mask(struct vm_area_struct *vma, + if (pmd_trans_unstable(pmd)) + ret = -EBUSY; + } else { +- get_page(page); ++ if (unlikely(!try_get_page(page))) { ++ spin_unlock(ptl); ++ return ERR_PTR(-ENOMEM); ++ } + spin_unlock(ptl); + lock_page(page); + ret = split_huge_page(page); +@@ -480,7 +486,10 @@ static int get_gate_page(struct mm_struct *mm, unsigned long address, + if (is_device_public_page(*page)) + goto unmap; + } +- get_page(*page); ++ if (unlikely(!try_get_page(*page))) { ++ ret = -ENOMEM; ++ goto unmap; ++ } + out: + ret = 0; + unmap: +@@ -1368,6 +1377,20 @@ static void undo_dev_pagemap(int *nr, int nr_start, struct page **pages) + } + } + ++/* ++ * Return the compund head page with ref appropriately incremented, ++ * or NULL if that failed. ++ */ ++static inline struct page *try_get_compound_head(struct page *page, int refs) ++{ ++ struct page *head = compound_head(page); ++ if (WARN_ON_ONCE(page_ref_count(head) < 0)) ++ return NULL; ++ if (unlikely(!page_cache_add_speculative(head, refs))) ++ return NULL; ++ return head; ++} ++ + #ifdef CONFIG_ARCH_HAS_PTE_SPECIAL + static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end, + int write, struct page **pages, int *nr) +@@ -1402,9 +1425,9 @@ static int gup_pte_range(pmd_t pmd, unsigned long addr, unsigned long end, + + VM_BUG_ON(!pfn_valid(pte_pfn(pte))); + page = pte_page(pte); +- head = compound_head(page); + +- if (!page_cache_get_speculative(head)) ++ head = try_get_compound_head(page, 1); ++ if (!head) + goto pte_unmap; + + if (unlikely(pte_val(pte) != pte_val(*ptep))) { +@@ -1543,8 +1566,8 @@ static int gup_huge_pmd(pmd_t orig, pmd_t *pmdp, unsigned long addr, + refs++; + } while (addr += PAGE_SIZE, addr != end); + +- head = compound_head(pmd_page(orig)); +- if (!page_cache_add_speculative(head, refs)) { ++ head = try_get_compound_head(pmd_page(orig), refs); ++ if (!head) { + *nr -= refs; + return 0; + } +@@ -1581,8 +1604,8 @@ static int gup_huge_pud(pud_t orig, pud_t *pudp, unsigned long addr, + refs++; + } while (addr += PAGE_SIZE, addr != end); + +- head = compound_head(pud_page(orig)); +- if (!page_cache_add_speculative(head, refs)) { ++ head = try_get_compound_head(pud_page(orig), refs); ++ if (!head) { + *nr -= refs; + return 0; + } +@@ -1618,8 +1641,8 @@ static int gup_huge_pgd(pgd_t orig, pgd_t *pgdp, unsigned long addr, + refs++; + } while (addr += PAGE_SIZE, addr != end); + +- head = compound_head(pgd_page(orig)); +- if (!page_cache_add_speculative(head, refs)) { ++ head = try_get_compound_head(pgd_page(orig), refs); ++ if (!head) { + *nr -= refs; + return 0; + } +diff --git a/mm/hugetlb.c b/mm/hugetlb.c +index 9e5f66cbf711..5fb779cda972 100644 +--- a/mm/hugetlb.c ++++ b/mm/hugetlb.c +@@ -4299,6 +4299,19 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma, + + pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT; + page = pte_page(huge_ptep_get(pte)); ++ ++ /* ++ * Instead of doing 'try_get_page()' below in the same_page ++ * loop, just check the count once here. ++ */ ++ if (unlikely(page_count(page) <= 0)) { ++ if (pages) { ++ spin_unlock(ptl); ++ remainder = 0; ++ err = -ENOMEM; ++ break; ++ } ++ } + same_page: + if (pages) { + pages[i] = mem_map_offset(page, pfn_offset); diff --git a/debian/patches/bugfix/all/0004-fs-prevent-page-refcount-overflow-in-pipe_buf_get.patch b/debian/patches/bugfix/all/0004-fs-prevent-page-refcount-overflow-in-pipe_buf_get.patch new file mode 100644 index 000000000000..4ff0c4f7d335 --- /dev/null +++ b/debian/patches/bugfix/all/0004-fs-prevent-page-refcount-overflow-in-pipe_buf_get.patch @@ -0,0 +1,162 @@ +From: Matthew Wilcox +Date: Fri, 5 Apr 2019 14:02:10 -0700 +Subject: fs: prevent page refcount overflow in pipe_buf_get +Origin: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git/commit?id=0311ff82b70fa12e80d188635bff24029ec06ae1 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2019-11487 + +commit 15fab63e1e57be9fdb5eec1bbc5916e9825e9acb upstream. + +Change pipe_buf_get() to return a bool indicating whether it succeeded +in raising the refcount of the page (if the thing in the pipe is a page). +This removes another mechanism for overflowing the page refcount. All +callers converted to handle a failure. + +Reported-by: Jann Horn +Signed-off-by: Matthew Wilcox +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + fs/fuse/dev.c | 12 ++++++------ + fs/pipe.c | 4 ++-- + fs/splice.c | 12 ++++++++++-- + include/linux/pipe_fs_i.h | 10 ++++++---- + kernel/trace/trace.c | 6 +++++- + 5 files changed, 29 insertions(+), 15 deletions(-) + +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -1989,10 +1989,8 @@ static ssize_t fuse_dev_splice_write(str + rem += pipe->bufs[(pipe->curbuf + idx) & (pipe->buffers - 1)].len; + + ret = -EINVAL; +- if (rem < len) { +- pipe_unlock(pipe); +- goto out; +- } ++ if (rem < len) ++ goto out_free; + + rem = len; + while (rem) { +@@ -2010,7 +2008,9 @@ static ssize_t fuse_dev_splice_write(str + pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); + pipe->nrbufs--; + } else { +- pipe_buf_get(pipe, ibuf); ++ if (!pipe_buf_get(pipe, ibuf)) ++ goto out_free; ++ + *obuf = *ibuf; + obuf->flags &= ~PIPE_BUF_FLAG_GIFT; + obuf->len = rem; +@@ -2033,11 +2033,11 @@ static ssize_t fuse_dev_splice_write(str + ret = fuse_dev_do_write(fud, &cs, len); + + pipe_lock(pipe); ++out_free: + for (idx = 0; idx < nbuf; idx++) + pipe_buf_release(pipe, &bufs[idx]); + pipe_unlock(pipe); + +-out: + kvfree(bufs); + return ret; + } +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -189,9 +189,9 @@ EXPORT_SYMBOL(generic_pipe_buf_steal); + * in the tee() system call, when we duplicate the buffers in one + * pipe into another. + */ +-void generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) ++bool generic_pipe_buf_get(struct pipe_inode_info *pipe, struct pipe_buffer *buf) + { +- get_page(buf->page); ++ return try_get_page(buf->page); + } + EXPORT_SYMBOL(generic_pipe_buf_get); + +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1586,7 +1586,11 @@ retry: + * Get a reference to this pipe buffer, + * so we can copy the contents over. + */ +- pipe_buf_get(ipipe, ibuf); ++ if (!pipe_buf_get(ipipe, ibuf)) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } + *obuf = *ibuf; + + /* +@@ -1660,7 +1664,11 @@ static int link_pipe(struct pipe_inode_i + * Get a reference to this pipe buffer, + * so we can copy the contents over. + */ +- pipe_buf_get(ipipe, ibuf); ++ if (!pipe_buf_get(ipipe, ibuf)) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } + + obuf = opipe->bufs + nbuf; + *obuf = *ibuf; +--- a/include/linux/pipe_fs_i.h ++++ b/include/linux/pipe_fs_i.h +@@ -108,18 +108,20 @@ struct pipe_buf_operations { + /* + * Get a reference to the pipe buffer. + */ +- void (*get)(struct pipe_inode_info *, struct pipe_buffer *); ++ bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); + }; + + /** + * pipe_buf_get - get a reference to a pipe_buffer + * @pipe: the pipe that the buffer belongs to + * @buf: the buffer to get a reference to ++ * ++ * Return: %true if the reference was successfully obtained. + */ +-static inline void pipe_buf_get(struct pipe_inode_info *pipe, ++static inline __must_check bool pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { +- buf->ops->get(pipe, buf); ++ return buf->ops->get(pipe, buf); + } + + /** +@@ -178,7 +180,7 @@ struct pipe_inode_info *alloc_pipe_info( + void free_pipe_info(struct pipe_inode_info *); + + /* Generic pipe buffer ops functions */ +-void generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); ++bool generic_pipe_buf_get(struct pipe_inode_info *, struct pipe_buffer *); + int generic_pipe_buf_confirm(struct pipe_inode_info *, struct pipe_buffer *); + int generic_pipe_buf_steal(struct pipe_inode_info *, struct pipe_buffer *); + int generic_pipe_buf_nosteal(struct pipe_inode_info *, struct pipe_buffer *); +--- a/kernel/trace/trace.c ++++ b/kernel/trace/trace.c +@@ -6820,12 +6820,16 @@ static void buffer_pipe_buf_release(stru + buf->private = 0; + } + +-static void buffer_pipe_buf_get(struct pipe_inode_info *pipe, ++static bool buffer_pipe_buf_get(struct pipe_inode_info *pipe, + struct pipe_buffer *buf) + { + struct buffer_ref *ref = (struct buffer_ref *)buf->private; + ++ if (refcount_read(&ref->refcount) > INT_MAX/2) ++ return false; ++ + refcount_inc(&ref->refcount); ++ return true; + } + + /* Pipe buffer operations for a buffer. */ diff --git a/debian/patches/series b/debian/patches/series index bc43f3c267cb..f20fd6963f9d 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -177,6 +177,10 @@ bugfix/all/0012-aio-keep-io_event-in-aio_kiocb.patch bugfix/all/0013-aio-store-event-at-final-iocb_put.patch bugfix/all/0014-Fix-aio_poll-races.patch bugfix/all/tracing-fix-buffer_ref-pipe-ops.patch +bugfix/all/0001-mm-make-page-ref-count-overflow-check-tighter-and-mo.patch +bugfix/all/0002-mm-add-try_get_page-helper-function.patch +bugfix/all/0003-mm-prevent-get_user_pages-from-overflowing-page-refc.patch +bugfix/all/0004-fs-prevent-page-refcount-overflow-in-pipe_buf_get.patch # Fix exported symbol versions bugfix/all/module-disable-matching-missing-version-crc.patch From 246867281601a72a2ea9453f5e0e4ab0aae8c996 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 19:31:41 +0100 Subject: [PATCH 12/13] lockdown: Replace the other reference to a nonexistent manual page --- ...n-refer-to-debian-wiki-until-manual-page-exists.patch | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/patches/features/all/lockdown/lockdown-refer-to-debian-wiki-until-manual-page-exists.patch b/debian/patches/features/all/lockdown/lockdown-refer-to-debian-wiki-until-manual-page-exists.patch index 1f63552e57fd..1d03d27d8ffa 100644 --- a/debian/patches/features/all/lockdown/lockdown-refer-to-debian-wiki-until-manual-page-exists.patch +++ b/debian/patches/features/all/lockdown/lockdown-refer-to-debian-wiki-until-manual-page-exists.patch @@ -21,3 +21,12 @@ Debian wiki. where); } } +@@ -60,7 +60,7 @@ void __init init_lockdown(void) + bool __kernel_is_locked_down(const char *what, bool first) + { + if (what && first && kernel_locked_down) +- pr_notice("Lockdown: %s is restricted; see man kernel_lockdown.7\n", ++ pr_notice("Lockdown: %s is restricted; see https://wiki.debian.org/SecureBoot\n", + what); + return kernel_locked_down; + } From 5ece558b8dee7df913915c414e1c8e2ab36d7d50 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 5 May 2019 19:32:32 +0100 Subject: [PATCH 13/13] Prepare to release linux (4.19.37-1). --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 2ba62ab33345..11f24ba9681e 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -linux (4.19.37-1) UNRELEASED; urgency=medium +linux (4.19.37-1) unstable; urgency=medium * New upstream stable update: https://www.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.29 @@ -1181,7 +1181,7 @@ linux (4.19.37-1) UNRELEASED; urgency=medium * debian/bin/gencontrol_signed.py: Sort list of modules before adding to .json file, fixing reproducibility issues. - -- Ben Hutchings Mon, 18 Mar 2019 22:50:08 +0000 + -- Ben Hutchings Sun, 05 May 2019 19:32:32 +0100 linux (4.19.28-2) unstable; urgency=medium