Skip to content

Commit 1bd6a1c

Browse files
hbathinimpe
authored andcommitted
powerpc/fadump: handle crash memory ranges array index overflow
Crash memory ranges is an array of memory ranges of the crashing kernel to be exported as a dump via /proc/vmcore file. The size of the array is set based on INIT_MEMBLOCK_REGIONS, which works alright in most cases where memblock memory regions count is less than INIT_MEMBLOCK_REGIONS value. But this count can grow beyond INIT_MEMBLOCK_REGIONS value since commit 142b45a ("memblock: Add array resizing support"). On large memory systems with a few DLPAR operations, the memblock memory regions count could be larger than INIT_MEMBLOCK_REGIONS value. On such systems, registering fadump results in crash or other system failures like below: task: c00007f39a290010 ti: c00000000b738000 task.ti: c00000000b738000 NIP: c000000000047df4 LR: c0000000000f9e58 CTR: c00000000010f180 REGS: c00000000b73b570 TRAP: 0300 Tainted: G L X (4.4.140+) MSR: 8000000000009033 <SF,EE,ME,IR,DR,RI,LE> CR: 22004484 XER: 20000000 CFAR: c000000000008500 DAR: 000007a450000000 DSISR: 40000000 SOFTE: 0 ... NIP [c000000000047df4] smp_send_reschedule+0x24/0x80 LR [c0000000000f9e58] resched_curr+0x138/0x160 Call Trace: resched_curr+0x138/0x160 (unreliable) check_preempt_curr+0xc8/0xf0 ttwu_do_wakeup+0x38/0x150 try_to_wake_up+0x224/0x4d0 __wake_up_common+0x94/0x100 ep_poll_callback+0xac/0x1c0 __wake_up_common+0x94/0x100 __wake_up_sync_key+0x70/0xa0 sock_def_readable+0x58/0xa0 unix_stream_sendmsg+0x2dc/0x4c0 sock_sendmsg+0x68/0xa0 ___sys_sendmsg+0x2cc/0x2e0 __sys_sendmsg+0x5c/0xc0 SyS_socketcall+0x36c/0x3f0 system_call+0x3c/0x100 as array index overflow is not checked for while setting up crash memory ranges causing memory corruption. To resolve this issue, dynamically allocate memory for crash memory ranges and resize it incrementally, in units of pagesize, on hitting array size limit. Fixes: 2df173d ("fadump: Initialize elfcore header and add PT_LOAD program headers.") Cc: [email protected] # v3.4+ Signed-off-by: Hari Bathini <[email protected]> Reviewed-by: Mahesh Salgaonkar <[email protected]> [mpe: Just use PAGE_SIZE directly, fixup variable placement] Signed-off-by: Michael Ellerman <[email protected]>
1 parent 6bd6d86 commit 1bd6a1c

File tree

2 files changed

+77
-17
lines changed

2 files changed

+77
-17
lines changed

arch/powerpc/include/asm/fadump.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,6 @@ struct fadump_crash_info_header {
195195
struct cpumask online_mask;
196196
};
197197

198-
/* Crash memory ranges */
199-
#define INIT_CRASHMEM_RANGES (INIT_MEMBLOCK_REGIONS + 2)
200-
201198
struct fad_crash_memory_ranges {
202199
unsigned long long base;
203200
unsigned long long size;

arch/powerpc/kernel/fadump.c

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ static struct fadump_mem_struct fdm;
4747
static const struct fadump_mem_struct *fdm_active;
4848

4949
static DEFINE_MUTEX(fadump_mutex);
50-
struct fad_crash_memory_ranges crash_memory_ranges[INIT_CRASHMEM_RANGES];
50+
struct fad_crash_memory_ranges *crash_memory_ranges;
51+
int crash_memory_ranges_size;
5152
int crash_mem_ranges;
53+
int max_crash_mem_ranges;
5254

5355
/* Scan the Firmware Assisted dump configuration details. */
5456
int __init early_init_dt_scan_fw_dump(unsigned long node,
@@ -868,38 +870,88 @@ static int __init process_fadump(const struct fadump_mem_struct *fdm_active)
868870
return 0;
869871
}
870872

871-
static inline void fadump_add_crash_memory(unsigned long long base,
872-
unsigned long long end)
873+
static void free_crash_memory_ranges(void)
874+
{
875+
kfree(crash_memory_ranges);
876+
crash_memory_ranges = NULL;
877+
crash_memory_ranges_size = 0;
878+
max_crash_mem_ranges = 0;
879+
}
880+
881+
/*
882+
* Allocate or reallocate crash memory ranges array in incremental units
883+
* of PAGE_SIZE.
884+
*/
885+
static int allocate_crash_memory_ranges(void)
886+
{
887+
struct fad_crash_memory_ranges *new_array;
888+
u64 new_size;
889+
890+
new_size = crash_memory_ranges_size + PAGE_SIZE;
891+
pr_debug("Allocating %llu bytes of memory for crash memory ranges\n",
892+
new_size);
893+
894+
new_array = krealloc(crash_memory_ranges, new_size, GFP_KERNEL);
895+
if (new_array == NULL) {
896+
pr_err("Insufficient memory for setting up crash memory ranges\n");
897+
free_crash_memory_ranges();
898+
return -ENOMEM;
899+
}
900+
901+
crash_memory_ranges = new_array;
902+
crash_memory_ranges_size = new_size;
903+
max_crash_mem_ranges = (new_size /
904+
sizeof(struct fad_crash_memory_ranges));
905+
return 0;
906+
}
907+
908+
static inline int fadump_add_crash_memory(unsigned long long base,
909+
unsigned long long end)
873910
{
874911
if (base == end)
875-
return;
912+
return 0;
913+
914+
if (crash_mem_ranges == max_crash_mem_ranges) {
915+
int ret;
916+
917+
ret = allocate_crash_memory_ranges();
918+
if (ret)
919+
return ret;
920+
}
876921

877922
pr_debug("crash_memory_range[%d] [%#016llx-%#016llx], %#llx bytes\n",
878923
crash_mem_ranges, base, end - 1, (end - base));
879924
crash_memory_ranges[crash_mem_ranges].base = base;
880925
crash_memory_ranges[crash_mem_ranges].size = end - base;
881926
crash_mem_ranges++;
927+
return 0;
882928
}
883929

884-
static void fadump_exclude_reserved_area(unsigned long long start,
930+
static int fadump_exclude_reserved_area(unsigned long long start,
885931
unsigned long long end)
886932
{
887933
unsigned long long ra_start, ra_end;
934+
int ret = 0;
888935

889936
ra_start = fw_dump.reserve_dump_area_start;
890937
ra_end = ra_start + fw_dump.reserve_dump_area_size;
891938

892939
if ((ra_start < end) && (ra_end > start)) {
893940
if ((start < ra_start) && (end > ra_end)) {
894-
fadump_add_crash_memory(start, ra_start);
895-
fadump_add_crash_memory(ra_end, end);
941+
ret = fadump_add_crash_memory(start, ra_start);
942+
if (ret)
943+
return ret;
944+
945+
ret = fadump_add_crash_memory(ra_end, end);
896946
} else if (start < ra_start) {
897-
fadump_add_crash_memory(start, ra_start);
947+
ret = fadump_add_crash_memory(start, ra_start);
898948
} else if (ra_end < end) {
899-
fadump_add_crash_memory(ra_end, end);
949+
ret = fadump_add_crash_memory(ra_end, end);
900950
}
901951
} else
902-
fadump_add_crash_memory(start, end);
952+
ret = fadump_add_crash_memory(start, end);
953+
954+
return ret;
903955
}
904956

905957
static int fadump_init_elfcore_header(char *bufp)
@@ -939,10 +991,11 @@ static int fadump_init_elfcore_header(char *bufp)
939991
* Traverse through memblock structure and setup crash memory ranges. These
940992
* ranges will be used create PT_LOAD program headers in elfcore header.
941993
*/
942-
static void fadump_setup_crash_memory_ranges(void)
994+
static int fadump_setup_crash_memory_ranges(void)
943995
{
944996
struct memblock_region *reg;
945997
unsigned long long start, end;
998+
int ret;
946999

9471000
pr_debug("Setup crash memory ranges.\n");
9481001
crash_mem_ranges = 0;
@@ -953,7 +1006,9 @@ static void fadump_setup_crash_memory_ranges(void)
9531006
* specified during fadump registration. We need to create a separate
9541007
* program header for this chunk with the correct offset.
9551008
*/
956-
fadump_add_crash_memory(RMA_START, fw_dump.boot_memory_size);
1009+
ret = fadump_add_crash_memory(RMA_START, fw_dump.boot_memory_size);
1010+
if (ret)
1011+
return ret;
9571012

9581013
for_each_memblock(memory, reg) {
9591014
start = (unsigned long long)reg->base;
@@ -973,8 +1028,12 @@ static void fadump_setup_crash_memory_ranges(void)
9731028
}
9741029

9751030
/* add this range excluding the reserved dump area. */
976-
fadump_exclude_reserved_area(start, end);
1031+
ret = fadump_exclude_reserved_area(start, end);
1032+
if (ret)
1033+
return ret;
9771034
}
1035+
1036+
return 0;
9781037
}
9791038

9801039
/*
@@ -1097,6 +1156,7 @@ static int register_fadump(void)
10971156
{
10981157
unsigned long addr;
10991158
void *vaddr;
1159+
int ret;
11001160

11011161
/*
11021162
* If no memory is reserved then we can not register for firmware-
@@ -1105,7 +1165,9 @@ static int register_fadump(void)
11051165
if (!fw_dump.reserve_dump_area_size)
11061166
return -ENODEV;
11071167

1108-
fadump_setup_crash_memory_ranges();
1168+
ret = fadump_setup_crash_memory_ranges();
1169+
if (ret)
1170+
return ret;
11091171

11101172
addr = be64_to_cpu(fdm.rmr_region.destination_address) + be64_to_cpu(fdm.rmr_region.source_len);
11111173
/* Initialize fadump crash info header. */
@@ -1183,6 +1245,7 @@ void fadump_cleanup(void)
11831245
} else if (fw_dump.dump_registered) {
11841246
/* Un-register Firmware-assisted dump if it was registered. */
11851247
fadump_unregister_dump(&fdm);
1248+
free_crash_memory_ranges();
11861249
}
11871250
}
11881251

0 commit comments

Comments
 (0)