From: Sean Christopherson sean.j.christopherson@intel.com
mainline inclusion from mainline-v5.11-rc1 commit 95bb7c42ac8a94ce3d0eb059ad64430390351ccb category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I4SIGI CVE: NA
--------------------------------
Background ==========
1. SGX enclave pages are populated with data by copying from normal memory via ioctl() (SGX_IOC_ENCLAVE_ADD_PAGES), which will be added later in this series. 2. It is desirable to be able to restrict those normal memory data sources. For instance, to ensure that the source data is executable before copying data to an executable enclave page. 3. Enclave page permissions are dynamic (just like normal permissions) and can be adjusted at runtime with mprotect().
This creates a problem because the original data source may have long since vanished at the time when enclave page permissions are established (mmap() or mprotect()).
The solution (elsewhere in this series) is to force enclave creators to declare their paging permission *intent* up front to the ioctl(). This intent can be immediately compared to the source data’s mapping and rejected if necessary.
The “intent” is also stashed off for later comparison with enclave PTEs. This ensures that any future mmap()/mprotect() operations performed by the enclave creator or done on behalf of the enclave can be compared with the earlier declared permissions.
Problem =======
There is an existing mmap() hook which allows SGX to perform this permission comparison at mmap() time. However, there is no corresponding ->mprotect() hook.
Solution ========
Add a vm_ops->mprotect() hook so that mprotect() operations which are inconsistent with any page's stashed intent can be rejected by the driver.
Intel-SIG: commit 95bb7c42ac8a mm: Add 'mprotect' hook to struct vm_operations_struct Backport for SGX Foundations support
Signed-off-by: Sean Christopherson sean.j.christopherson@intel.com Co-developed-by: Jarkko Sakkinen jarkko@kernel.org Signed-off-by: Jarkko Sakkinen jarkko@kernel.org Signed-off-by: Borislav Petkov bp@suse.de Acked-by: Jethro Beekman jethro@fortanix.com Acked-by: Dave Hansen dave.hansen@intel.com Acked-by: Mel Gorman mgorman@techsingularity.net Acked-by: Hillf Danton hdanton@sina.com Cc: linux-mm@kvack.org Link: https://lkml.kernel.org/r/20201112220135.165028-11-jarkko@kernel.org Signed-off-by: Fan Du fan.du@intel.com #openEuler_contributor Signed-off-by: Laibin Qiu qiulaibin@huawei.com --- include/linux/mm.h | 7 +++++++ mm/mprotect.c | 7 +++++++ 2 files changed, 14 insertions(+)
diff --git a/include/linux/mm.h b/include/linux/mm.h index 4831348f31e9..2db253209f81 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -595,6 +595,13 @@ struct vm_operations_struct { void (*close)(struct vm_area_struct * area); int (*split)(struct vm_area_struct * area, unsigned long addr); int (*mremap)(struct vm_area_struct * area); + /* + * Called by mprotect() to make driver-specific permission + * checks before mprotect() is finalised. The VMA must not + * be modified. Returns 0 if eprotect() can proceed. + */ + int (*mprotect)(struct vm_area_struct *vma, unsigned long start, + unsigned long end, unsigned long newflags); vm_fault_t (*fault)(struct vm_fault *vmf); vm_fault_t (*huge_fault)(struct vm_fault *vmf, enum page_entry_size pe_size); diff --git a/mm/mprotect.c b/mm/mprotect.c index 56c02beb6041..ab709023e9aa 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -616,9 +616,16 @@ static int do_mprotect_pkey(unsigned long start, size_t len, tmp = vma->vm_end; if (tmp > end) tmp = end; + + if (vma->vm_ops && vma->vm_ops->mprotect) + error = vma->vm_ops->mprotect(vma, nstart, tmp, newflags); + if (error) + goto out; + error = mprotect_fixup(vma, &prev, nstart, tmp, newflags); if (error) goto out; + nstart = tmp;
if (nstart < prev->vm_end)