diff --git a/SOURCES/0001-Fix-for-kmem-s-S-option-on-Linux-5.7-and-later-kerne.patch b/SOURCES/0001-Fix-for-kmem-s-S-option-on-Linux-5.7-and-later-kerne.patch new file mode 100644 index 0000000..fd8b759 --- /dev/null +++ b/SOURCES/0001-Fix-for-kmem-s-S-option-on-Linux-5.7-and-later-kerne.patch @@ -0,0 +1,58 @@ +From 647a5c33e1c94054d7b63168cd6c12901591cb77 Mon Sep 17 00:00:00 2001 +From: Lianbo Jiang +Date: Thu, 27 May 2021 18:02:11 +0800 +Subject: [PATCH] Fix for "kmem -s|-S" option on Linux 5.7 and later kernels + +Linux 5.7 and later kernels that contain kernel commit 1ad53d9fa3f6 +("slub: improve bit diffusion for freelist ptr obfuscation") changed +the calculation formula in the freelist_ptr(), which added a swab() +call to mix bits a little more. When kernel is configured with the +"CONFIG_SLAB_FREELIST_HARDENED=y", without the patch, the "kmem -s|-S" +options display wrong statistics and state whether slab objects are +in use or free and can print the following errors: + + crash> kmem -s + CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME + 87201e00 528 0 0 0 8k xfs_dqtrx + 87201f00 496 0 0 0 8k xfs_dquot + kmem: xfs_buf: slab: 37202e6e900 invalid freepointer: b844bab900001d70 + kmem: xfs_buf: slab: 3720250fd80 invalid freepointer: b8603f9400001370 + ... + +Signed-off-by: Lianbo Jiang +--- + memory.c | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/memory.c b/memory.c +index 8c6bbe409922..a3cf8a86728d 100644 +--- a/memory.c ++++ b/memory.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + struct meminfo { /* general purpose memory information structure */ + ulong cache; /* used by the various memory searching/dumping */ +@@ -19336,10 +19337,14 @@ count_free_objects(struct meminfo *si, ulong freelist) + static ulong + freelist_ptr(struct meminfo *si, ulong ptr, ulong ptr_addr) + { +- if (VALID_MEMBER(kmem_cache_random)) ++ if (VALID_MEMBER(kmem_cache_random)) { + /* CONFIG_SLAB_FREELIST_HARDENED */ ++ ++ if (THIS_KERNEL_VERSION >= LINUX(5,7,0)) ++ ptr_addr = (sizeof(long) == 8) ? bswap_64(ptr_addr) ++ : bswap_32(ptr_addr); + return (ptr ^ si->random ^ ptr_addr); +- else ++ } else + return ptr; + } + +-- +2.30.2 + diff --git a/SOURCES/0002-Fix-waitq-command-for-Linux-4.13-and-later-kernels.patch b/SOURCES/0002-Fix-waitq-command-for-Linux-4.13-and-later-kernels.patch new file mode 100644 index 0000000..70a9a22 --- /dev/null +++ b/SOURCES/0002-Fix-waitq-command-for-Linux-4.13-and-later-kernels.patch @@ -0,0 +1,132 @@ +From eaf14f852ae79f7745934e213661f1c6abac711e Mon Sep 17 00:00:00 2001 +From: Greg Edwards +Date: Wed, 23 Jun 2021 13:50:47 -0600 +Subject: [PATCH] Fix 'waitq' command for Linux 4.13 and later kernels + +The wait queue structs and members were renamed in 4.13 in commits: + + ac6424b981bc ("sched/wait: Rename wait_queue_t => wait_queue_entry_t") + 9d9d676f595b ("sched/wait: Standardize internal naming of wait-queue heads") + 2055da97389a ("sched/wait: Disambiguate wq_entry->task_list and wq_head->task_list naming") + +Add support to the 'waitq' command for these more recent kernels. + +[ kh: suppressed compilation warnings ] + +Signed-off-by: Greg Edwards +Signed-off-by: Kazuhito Hagio +Signed-off-by: Lianbo Jiang +--- + defs.h | 4 ++++ + kernel.c | 27 +++++++++++++++++++++++---- + symbols.c | 10 +++++++++- + 3 files changed, 36 insertions(+), 5 deletions(-) + +diff --git a/defs.h b/defs.h +index 42c8074e6ac6..6bb00e29d811 100644 +--- a/defs.h ++++ b/defs.h +@@ -2138,6 +2138,9 @@ struct offset_table { /* stash of commonly-used offsets */ + long atomic_long_t_counter; + long block_device_bd_device; + long block_device_bd_stats; ++ long wait_queue_entry_private; ++ long wait_queue_head_head; ++ long wait_queue_entry_entry; + }; + + struct size_table { /* stash of commonly-used sizes */ +@@ -2300,6 +2303,7 @@ struct size_table { /* stash of commonly-used sizes */ + long printk_info; + long printk_ringbuffer; + long prb_desc; ++ long wait_queue_entry; + }; + + struct array_table { +diff --git a/kernel.c b/kernel.c +index 528f6ee524f6..e123f760e036 100644 +--- a/kernel.c ++++ b/kernel.c +@@ -615,7 +615,15 @@ kernel_init() + kt->flags |= TVEC_BASES_V1; + + STRUCT_SIZE_INIT(__wait_queue, "__wait_queue"); +- if (VALID_STRUCT(__wait_queue)) { ++ STRUCT_SIZE_INIT(wait_queue_entry, "wait_queue_entry"); ++ if (VALID_STRUCT(wait_queue_entry)) { ++ MEMBER_OFFSET_INIT(wait_queue_entry_private, ++ "wait_queue_entry", "private"); ++ MEMBER_OFFSET_INIT(wait_queue_head_head, ++ "wait_queue_head", "head"); ++ MEMBER_OFFSET_INIT(wait_queue_entry_entry, ++ "wait_queue_entry", "entry"); ++ } else if (VALID_STRUCT(__wait_queue)) { + if (MEMBER_EXISTS("__wait_queue", "task")) + MEMBER_OFFSET_INIT(__wait_queue_task, + "__wait_queue", "task"); +@@ -9367,9 +9375,9 @@ dump_waitq(ulong wq, char *wq_name) + struct list_data list_data, *ld; + ulong *wq_list; /* addr of wait queue element */ + ulong next_offset; /* next pointer of wq element */ +- ulong task_offset; /* offset of task in wq element */ ++ ulong task_offset = 0; /* offset of task in wq element */ + int cnt; /* # elems on Queue */ +- int start_index; /* where to start in wq array */ ++ int start_index = -1; /* where to start in wq array */ + int i; + + ld = &list_data; +@@ -9397,9 +9405,20 @@ dump_waitq(ulong wq, char *wq_name) + ld->list_head_offset = OFFSET(__wait_queue_task_list); + ld->member_offset = next_offset; + ++ start_index = 1; ++ } else if (VALID_STRUCT(wait_queue_entry)) { ++ ulong head_offset; ++ ++ next_offset = OFFSET(list_head_next); ++ task_offset = OFFSET(wait_queue_entry_private); ++ head_offset = OFFSET(wait_queue_head_head); ++ ld->end = ld->start = wq + head_offset + next_offset; ++ ld->list_head_offset = OFFSET(wait_queue_entry_entry); ++ ld->member_offset = next_offset; ++ + start_index = 1; + } else { +- return; ++ error(FATAL, "cannot determine wait queue structures\n"); + } + + hq_open(); +diff --git a/symbols.c b/symbols.c +index 370d4c3e8ac0..67c135f12984 100644 +--- a/symbols.c ++++ b/symbols.c +@@ -9817,7 +9817,13 @@ dump_offset_table(char *spec, ulong makestruct) + OFFSET(__wait_queue_head_task_list)); + fprintf(fp, " __wait_queue_task_list: %ld\n", + OFFSET(__wait_queue_task_list)); +- ++ fprintf(fp, " wait_queue_entry_private: %ld\n", ++ OFFSET(wait_queue_entry_private)); ++ fprintf(fp, " wait_queue_head_head: %ld\n", ++ OFFSET(wait_queue_head_head)); ++ fprintf(fp, " wait_queue_entry_entry: %ld\n", ++ OFFSET(wait_queue_entry_entry)); ++ + fprintf(fp, " pglist_data_node_zones: %ld\n", + OFFSET(pglist_data_node_zones)); + fprintf(fp, " pglist_data_node_mem_map: %ld\n", +@@ -10717,6 +10723,8 @@ dump_offset_table(char *spec, ulong makestruct) + fprintf(fp, " wait_queue: %ld\n", SIZE(wait_queue)); + fprintf(fp, " __wait_queue: %ld\n", + SIZE(__wait_queue)); ++ fprintf(fp, " wait_queue_entry: %ld\n", ++ SIZE(wait_queue_entry)); + fprintf(fp, " device: %ld\n", SIZE(device)); + fprintf(fp, " net_device: %ld\n", SIZE(net_device)); + +-- +2.30.2 + diff --git a/SOURCES/0003-Handle-task_struct-state-member-changes-for-kernels-.patch b/SOURCES/0003-Handle-task_struct-state-member-changes-for-kernels-.patch new file mode 100644 index 0000000..4ccbfe3 --- /dev/null +++ b/SOURCES/0003-Handle-task_struct-state-member-changes-for-kernels-.patch @@ -0,0 +1,76 @@ +From d6b4f36d6b22b70fb14e692f36d20910ef5563c1 Mon Sep 17 00:00:00 2001 +From: Alexander Egorenkov +Date: Tue, 29 Jun 2021 08:39:00 +0200 +Subject: [PATCH] Handle task_struct state member changes for kernels >= + 5.14-rc1 + +Kernel commit 2f064a59a11ff9bc22e52e9678bc601404c7cb34 ("sched: Change +task_struct::state") renamed the member state of task_struct to __state +and its type changed from long to unsigned int. Without the patch, +crash fails to start up with the following error: + + crash: invalid structure member offset: task_struct_state + FILE: task.c LINE: 5929 FUNCTION: task_state() + +Signed-off-by: Alexander Egorenkov +--- + defs.h | 1 + + symbols.c | 1 + + task.c | 10 +++++++++- + 3 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/defs.h b/defs.h +index 6bb00e29d811..5d32954905c2 100644 +--- a/defs.h ++++ b/defs.h +@@ -2304,6 +2304,7 @@ struct size_table { /* stash of commonly-used sizes */ + long printk_ringbuffer; + long prb_desc; + long wait_queue_entry; ++ long task_struct_state; + }; + + struct array_table { +diff --git a/symbols.c b/symbols.c +index 67c135f12984..bf6d94db84af 100644 +--- a/symbols.c ++++ b/symbols.c +@@ -10678,6 +10678,7 @@ dump_offset_table(char *spec, ulong makestruct) + SIZE(page_cache_bucket)); + fprintf(fp, " pt_regs: %ld\n", SIZE(pt_regs)); + fprintf(fp, " task_struct: %ld\n", SIZE(task_struct)); ++ fprintf(fp, " task_struct_state: %ld\n", SIZE(task_struct_state)); + fprintf(fp, " task_struct_flags: %ld\n", SIZE(task_struct_flags)); + fprintf(fp, " task_struct_policy: %ld\n", SIZE(task_struct_policy)); + fprintf(fp, " thread_info: %ld\n", SIZE(thread_info)); +diff --git a/task.c b/task.c +index 36cf259e5d7b..672b41697e75 100644 +--- a/task.c ++++ b/task.c +@@ -297,6 +297,11 @@ task_init(void) + } + + MEMBER_OFFSET_INIT(task_struct_state, "task_struct", "state"); ++ MEMBER_SIZE_INIT(task_struct_state, "task_struct", "state"); ++ if (INVALID_MEMBER(task_struct_state)) { ++ MEMBER_OFFSET_INIT(task_struct_state, "task_struct", "__state"); ++ MEMBER_SIZE_INIT(task_struct_state, "task_struct", "__state"); ++ } + MEMBER_OFFSET_INIT(task_struct_exit_state, "task_struct", "exit_state"); + MEMBER_OFFSET_INIT(task_struct_pid, "task_struct", "pid"); + MEMBER_OFFSET_INIT(task_struct_comm, "task_struct", "comm"); +@@ -5926,7 +5931,10 @@ task_state(ulong task) + if (!tt->last_task_read) + return 0; + +- state = ULONG(tt->task_struct + OFFSET(task_struct_state)); ++ if (SIZE(task_struct_state) == sizeof(ulong)) ++ state = ULONG(tt->task_struct + OFFSET(task_struct_state)); ++ else ++ state = UINT(tt->task_struct + OFFSET(task_struct_state)); + exit_state = VALID_MEMBER(task_struct_exit_state) ? + ULONG(tt->task_struct + OFFSET(task_struct_exit_state)) : 0; + +-- +2.30.2 + diff --git a/SOURCES/0004-Handle-task_struct-cpu-member-changes-for-kernels-5..patch b/SOURCES/0004-Handle-task_struct-cpu-member-changes-for-kernels-5..patch new file mode 100644 index 0000000..2c41306 --- /dev/null +++ b/SOURCES/0004-Handle-task_struct-cpu-member-changes-for-kernels-5..patch @@ -0,0 +1,56 @@ +From 488a59f85a57961fb0527aac40ae30fc7a17a24c Mon Sep 17 00:00:00 2001 +From: Alexander Egorenkov +Date: Wed, 13 Oct 2021 10:56:39 +0200 +Subject: [PATCH] Handle task_struct cpu member changes for kernels >= 5.16-rc1 + +Kernel commit bcf9033e5449bdcaa9bed46467a7141a8049dadb +("sched: move CPU field back into thread_info if THREAD_INFO_IN_TASK=y") +moved the member cpu of task_struct back into thread_info. +Without the patch, crash fails with the following error message +during session initialization: + + crash: invalid structure member offset: task_struct_cpu + FILE: task.c LINE: 2904 FUNCTION: add_context() + +Signed-off-by: Alexander Egorenkov +Signed-off-by: Kazuhito Hagio +--- + task.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/task.c b/task.c +index 672b41697e75..bb6a5da8ad33 100644 +--- a/task.c ++++ b/task.c +@@ -278,8 +278,10 @@ task_init(void) + } else if (VALID_MEMBER(task_struct_stack)) + MEMBER_OFFSET_INIT(task_struct_thread_info, "task_struct", "stack"); + ++ MEMBER_OFFSET_INIT(task_struct_cpu, "task_struct", "cpu"); ++ + if (VALID_MEMBER(task_struct_thread_info)) { +- if (tt->flags & THREAD_INFO_IN_TASK) { ++ if (tt->flags & THREAD_INFO_IN_TASK && VALID_MEMBER(task_struct_cpu)) { + MEMBER_OFFSET_INIT(thread_info_flags, "thread_info", "flags"); + /* (unnecessary) reminders */ + ASSIGN_OFFSET(thread_info_task) = INVALID_OFFSET; +@@ -315,7 +317,6 @@ task_init(void) + MEMBER_OFFSET_INIT(task_struct_has_cpu, "task_struct", "has_cpu"); + MEMBER_OFFSET_INIT(task_struct_cpus_runnable, + "task_struct", "cpus_runnable"); +- MEMBER_OFFSET_INIT(task_struct_cpu, "task_struct", "cpu"); + MEMBER_OFFSET_INIT(task_struct_active_mm, "task_struct", "active_mm"); + MEMBER_OFFSET_INIT(task_struct_next_run, "task_struct", "next_run"); + MEMBER_OFFSET_INIT(task_struct_flags, "task_struct", "flags"); +@@ -2900,7 +2901,7 @@ add_context(ulong task, char *tp) + else + tc->thread_info = ULONG(tp + OFFSET(task_struct_thread_info)); + fill_thread_info(tc->thread_info); +- if (tt->flags & THREAD_INFO_IN_TASK) ++ if (tt->flags & THREAD_INFO_IN_TASK && VALID_MEMBER(task_struct_cpu)) + processor_addr = (int *) (tp + OFFSET(task_struct_cpu)); + else + processor_addr = (int *) (tt->thread_info + +-- +2.30.2 + diff --git a/SOURCES/0005-Fix-live-debugging-with-lockdown-integrity.patch b/SOURCES/0005-Fix-live-debugging-with-lockdown-integrity.patch new file mode 100644 index 0000000..f9bfe8a --- /dev/null +++ b/SOURCES/0005-Fix-live-debugging-with-lockdown-integrity.patch @@ -0,0 +1,57 @@ +From e90c0011d6e9476104c4c43aa76d2d0830a15946 Mon Sep 17 00:00:00 2001 +From: Philipp Rudo +Date: Tue, 9 Nov 2021 14:52:22 +0100 +Subject: [PATCH] Fix live debugging with lockdown=integrity + +With kernel lockdown the access to kernel interfaces that allow to +extract confidential information (lockdown=confidentiality) or modify a +running kernel (lockdown=integrity) can be restricted. Two of the +interfaces that can be restricted are /dev/mem (integrity & +confidentiality) and /proc/kcore (confidentiality). With +lockdown=integrity this leads to a situation where /dev/mem exists but +is not readable while /proc/kcore exists and is readable. This breaks +crash's live debugging when it is invoked without argument, i.e. + +$ crash +[...] +crash: /dev/mem: Operation not permitted + +while passing /proc/kcore as image succeeds. The reason for this is that +crash always picks /dev/mem as source when it exits but doesn't check if +it is readable. Fix this by only selecting /dev/mem when it is readable. + +Signed-off-by: Philipp Rudo +--- + filesys.c | 2 +- + main.c | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/filesys.c b/filesys.c +index 3361b6c12ed3..43cbe826fc79 100644 +--- a/filesys.c ++++ b/filesys.c +@@ -3666,7 +3666,7 @@ get_live_memory_source(void) + if (pc->live_memsrc) + goto live_report; + +- if (file_exists("/dev/mem", NULL)) ++ if (file_readable("/dev/mem")) + pc->live_memsrc = "/dev/mem"; + else if (file_exists("/proc/kcore", NULL)) { + pc->flags &= ~DEVMEM; +diff --git a/main.c b/main.c +index 388ac46c3834..dfd343cd9dd4 100644 +--- a/main.c ++++ b/main.c +@@ -1119,7 +1119,7 @@ setup_environment(int argc, char **argv) + pc->flags2 |= REDZONE; + pc->confd = -2; + pc->machine_type = MACHINE_TYPE; +- if (file_exists("/dev/mem", NULL)) { /* defaults until argv[] is parsed */ ++ if (file_readable("/dev/mem")) { /* defaults until argv[] is parsed */ + pc->readmem = read_dev_mem; + pc->writemem = write_dev_mem; + } else if (file_exists("/proc/kcore", NULL)) { +-- +2.30.2 + diff --git a/SOURCES/rhel8_freepointer.patch b/SOURCES/rhel8_freepointer.patch new file mode 100644 index 0000000..e7aded5 --- /dev/null +++ b/SOURCES/rhel8_freepointer.patch @@ -0,0 +1,29 @@ +From e09e3c038c853f9a332cf05a17e5fdee1c7837e0 Mon Sep 17 00:00:00 2001 +From: Lianbo Jiang +Date: Thu, 18 Nov 2021 09:55:45 +0800 +Subject: [PATCH] fix freepointer issue + +Signed-off-by: Lianbo Jiang +--- + memory.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/memory.c b/memory.c +index a3cf8a86728d..81db9c7bee9f 100644 +--- a/memory.c ++++ b/memory.c +@@ -19340,9 +19340,8 @@ freelist_ptr(struct meminfo *si, ulong ptr, ulong ptr_addr) + if (VALID_MEMBER(kmem_cache_random)) { + /* CONFIG_SLAB_FREELIST_HARDENED */ + +- if (THIS_KERNEL_VERSION >= LINUX(5,7,0)) +- ptr_addr = (sizeof(long) == 8) ? bswap_64(ptr_addr) +- : bswap_32(ptr_addr); ++ ptr_addr = (sizeof(long) == 8) ? bswap_64(ptr_addr) ++ : bswap_32(ptr_addr); + return (ptr ^ si->random ^ ptr_addr); + } else + return ptr; +-- +2.30.2 + diff --git a/SPECS/crash.spec b/SPECS/crash.spec index 2c3f833..2ebe779 100644 --- a/SPECS/crash.spec +++ b/SPECS/crash.spec @@ -4,7 +4,7 @@ Summary: Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles Name: crash Version: 7.3.0 -Release: 2%{?dist} +Release: 3%{?dist} License: GPLv3 Group: Development/Debuggers Source0: https://github.com/crash-utility/crash/archive/crash-%{version}.tar.gz @@ -19,6 +19,12 @@ Provides: bundled(gdb) = 7.6 Patch0: lzo_snappy.patch Patch1: rhel8_build.patch Patch2: rhel8_arm64_52_bit_fix.patch +Patch3: 0001-Fix-for-kmem-s-S-option-on-Linux-5.7-and-later-kerne.patch +Patch4: 0002-Fix-waitq-command-for-Linux-4.13-and-later-kernels.patch +Patch5: 0003-Handle-task_struct-state-member-changes-for-kernels-.patch +Patch6: 0004-Handle-task_struct-cpu-member-changes-for-kernels-5..patch +Patch7: 0005-Fix-live-debugging-with-lockdown-integrity.patch +Patch8: rhel8_freepointer.patch %description The core analysis suite is a self-contained tool that can be used to @@ -42,6 +48,12 @@ offered by Mission Critical Linux, or the LKCD kernel patch. %patch0 -p1 -b lzo_snappy.patch %patch1 -p1 -b rhel8_build.patch %patch2 -p1 -b rhel8_arm64_52_bit_fix.patch +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 %build cp %{SOURCE1} . @@ -72,6 +84,13 @@ rm -rf %{buildroot} %{_includedir}/* %changelog +* Thu Nov 18 2021 Lianbo Jiang - 7.3.0-3 +- Fix for "sched: Change task_struct::state" +- Fix for "sched: move CPU field back into thread_info if THREAD_INFO_IN_TASK=y" +- Fix live debugging with lockdown=integrity +- Fix 'waitq' command for Linux 4.13 and later kernels +- Fix for "kmem -s|-S" option on Linux 5.7 and later kernels + * Fri May 14 2021 Lianbo Jiang - 7.3.0-2 - Update the sha512 hash in the sources file to solve the compilation issues