Skip to content

Commit 342f43a

Browse files
maurizio-lombardikonradwilk
authored andcommitted
iscsi_ibft: fix crash due to KASLR physical memory remapping
Starting with commit a799c2b ("x86/setup: Consolidate early memory reservations") memory reservations have been moved earlier during the boot process, before the execution of the Kernel Address Space Layout Randomization code. setup_arch() calls the iscsi_ibft's find_ibft_region() function to find and reserve the memory dedicated to the iBFT and this function also saves a virtual pointer to the iBFT table for later use. The problem is that if KALSR is active, the physical memory gets remapped somewhere else in the virtual address space and the pointer is no longer valid, this will cause a kernel panic when the iscsi driver tries to dereference it. iBFT detected. BUG: unable to handle page fault for address: ffff888000099fd8 #PF: supervisor read access in kernel mode #PF: error_code(0x0000) - not-present page PGD 0 P4D 0 Oops: 0000 [Rust-for-Linux#1] SMP PTI ..snip.. Call Trace: ? ibft_create_kobject+0x1d2/0x1d2 [iscsi_ibft] do_one_initcall+0x44/0x1d0 ? kmem_cache_alloc_trace+0x119/0x220 do_init_module+0x5c/0x270 __do_sys_init_module+0x12e/0x1b0 do_syscall_64+0x40/0x80 entry_SYSCALL_64_after_hwframe+0x44/0xae Fix this bug by saving the address of the physical location of the ibft; later the driver will use isa_bus_to_virt() to get the correct virtual address. N.B. On each reboot KASLR randomizes the virtual addresses so assuming phys_to_virt before KASLR does its deed is incorrect. Simplify the code by renaming find_ibft_region() to reserve_ibft_region() and remove all the wrappers. Signed-off-by: Maurizio Lombardi <[email protected]> Reviewed-by: Mike Rapoport <[email protected]> Signed-off-by: Konrad Rzeszutek Wilk <[email protected]>
1 parent 62fb987 commit 342f43a

File tree

4 files changed

+32
-54
lines changed

4 files changed

+32
-54
lines changed

arch/x86/kernel/setup.c

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -571,16 +571,6 @@ void __init reserve_standard_io_resources(void)
571571

572572
}
573573

574-
static __init void reserve_ibft_region(void)
575-
{
576-
unsigned long addr, size = 0;
577-
578-
addr = find_ibft_region(&size);
579-
580-
if (size)
581-
memblock_reserve(addr, size);
582-
}
583-
584574
static bool __init snb_gfx_workaround_needed(void)
585575
{
586576
#ifdef CONFIG_PCI

drivers/firmware/iscsi_ibft.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,10 @@ MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information");
8484
MODULE_LICENSE("GPL");
8585
MODULE_VERSION(IBFT_ISCSI_VERSION);
8686

87+
static struct acpi_table_ibft *ibft_addr;
88+
8789
#ifndef CONFIG_ISCSI_IBFT_FIND
88-
struct acpi_table_ibft *ibft_addr;
90+
phys_addr_t ibft_phys_addr;
8991
#endif
9092

9193
struct ibft_hdr {
@@ -858,11 +860,13 @@ static int __init ibft_init(void)
858860
int rc = 0;
859861

860862
/*
861-
As on UEFI systems the setup_arch()/find_ibft_region()
863+
As on UEFI systems the setup_arch()/reserve_ibft_region()
862864
is called before ACPI tables are parsed and it only does
863865
legacy finding.
864866
*/
865-
if (!ibft_addr)
867+
if (ibft_phys_addr)
868+
ibft_addr = isa_bus_to_virt(ibft_phys_addr);
869+
else
866870
acpi_find_ibft_region();
867871

868872
if (ibft_addr) {

drivers/firmware/iscsi_ibft_find.c

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
/*
3232
* Physical location of iSCSI Boot Format Table.
3333
*/
34-
struct acpi_table_ibft *ibft_addr;
35-
EXPORT_SYMBOL_GPL(ibft_addr);
34+
phys_addr_t ibft_phys_addr;
35+
EXPORT_SYMBOL_GPL(ibft_phys_addr);
3636

3737
static const struct {
3838
char *sign;
@@ -47,13 +47,24 @@ static const struct {
4747
#define VGA_MEM 0xA0000 /* VGA buffer */
4848
#define VGA_SIZE 0x20000 /* 128kB */
4949

50-
static int __init find_ibft_in_mem(void)
50+
/*
51+
* Routine used to find and reserve the iSCSI Boot Format Table
52+
*/
53+
void __init reserve_ibft_region(void)
5154
{
5255
unsigned long pos;
5356
unsigned int len = 0;
5457
void *virt;
5558
int i;
5659

60+
ibft_phys_addr = 0;
61+
62+
/* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
63+
* only use ACPI for this
64+
*/
65+
if (efi_enabled(EFI_BOOT))
66+
return;
67+
5768
for (pos = IBFT_START; pos < IBFT_END; pos += 16) {
5869
/* The table can't be inside the VGA BIOS reserved space,
5970
* so skip that area */
@@ -70,35 +81,12 @@ static int __init find_ibft_in_mem(void)
7081
/* if the length of the table extends past 1M,
7182
* the table cannot be valid. */
7283
if (pos + len <= (IBFT_END-1)) {
73-
ibft_addr = (struct acpi_table_ibft *)virt;
74-
pr_info("iBFT found at 0x%lx.\n", pos);
75-
goto done;
84+
ibft_phys_addr = pos;
85+
memblock_reserve(ibft_phys_addr, PAGE_ALIGN(len));
86+
pr_info("iBFT found at 0x%lx.\n", ibft_phys_addr);
87+
return;
7688
}
7789
}
7890
}
7991
}
80-
done:
81-
return len;
82-
}
83-
/*
84-
* Routine used to find the iSCSI Boot Format Table. The logical
85-
* kernel address is set in the ibft_addr global variable.
86-
*/
87-
unsigned long __init find_ibft_region(unsigned long *sizep)
88-
{
89-
ibft_addr = NULL;
90-
91-
/* iBFT 1.03 section 1.4.3.1 mandates that UEFI machines will
92-
* only use ACPI for this */
93-
94-
if (!efi_enabled(EFI_BOOT))
95-
find_ibft_in_mem();
96-
97-
if (ibft_addr) {
98-
*sizep = PAGE_ALIGN(ibft_addr->header.length);
99-
return (u64)virt_to_phys(ibft_addr);
100-
}
101-
102-
*sizep = 0;
103-
return 0;
10492
}

include/linux/iscsi_ibft.h

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,22 @@
1313
#ifndef ISCSI_IBFT_H
1414
#define ISCSI_IBFT_H
1515

16-
#include <linux/acpi.h>
16+
#include <linux/types.h>
1717

1818
/*
19-
* Logical location of iSCSI Boot Format Table.
20-
* If the value is NULL there is no iBFT on the machine.
19+
* Physical location of iSCSI Boot Format Table.
20+
* If the value is 0 there is no iBFT on the machine.
2121
*/
22-
extern struct acpi_table_ibft *ibft_addr;
22+
extern phys_addr_t ibft_phys_addr;
2323

2424
/*
2525
* Routine used to find and reserve the iSCSI Boot Format Table. The
26-
* mapped address is set in the ibft_addr variable.
26+
* physical address is set in the ibft_phys_addr variable.
2727
*/
2828
#ifdef CONFIG_ISCSI_IBFT_FIND
29-
unsigned long find_ibft_region(unsigned long *sizep);
29+
void reserve_ibft_region(void);
3030
#else
31-
static inline unsigned long find_ibft_region(unsigned long *sizep)
32-
{
33-
*sizep = 0;
34-
return 0;
35-
}
31+
static inline void reserve_ibft_region(void) {}
3632
#endif
3733

3834
#endif /* ISCSI_IBFT_H */

0 commit comments

Comments
 (0)