[PATCH v3] x86_64_exception_frame only performs EFRAME_VERIFY if it is the only flag
by David Mair
x86_64_exception_frame() called with combined flags including
EFRAME_VERIFY does not perform the verify. It's only done when
EFRAME_VERIFY is the only flag set.
Correct the condition to EFRAME_VERIFY if the flag is set. Verify
requests are always performed. Fixes stack overrun "seek errors" seen on
an x86_64 core when backtracing a PID at an IRQ stack where the
interrupt handler doesn't save a pt_regs. Higher layers than the top
frame on the IRQ stack were not displayed. Fixed by this change.
But it breaks bt -e and bt -E for exceptions on userspace stacks. Those
use the constant 0 as the kvaddr argument to x86_64_exception_frame()
and pass the userspace stack position in the local argument.
x86_64_exception_frame() only verifies the kvaddr argument. Zero is not
accessible and EFRAME_VERIFY always fails for those cases.
Modify the EFRAME_VERIFY block in x86_64_exception_frame() to choose
kvaddr or local to verify using the same condition used to assign one of
them to pt_regs_buf later in the same function. Add verify_addr to
locals to hold the choice. Modify the accessible tests to use it instead
of kvaddr. Type of the new variable is the same as the type of kvaddr.
If verifying local argument, translate to a kernel address range using
the stackbuf and stackbase members of the bt argument the same way used
for EFRAME_SEARCH later in x86_64_exception_frame(). local and
bt->stackbuf are char *, the assignment destination and bt->stackbase
are ulong. Cast the char * variables to uintptr_t for the assignment
arithmetic using the local argument to prevent gcc 10.2 errors assigning
char * to ulong...the sum is okay without casts in the uses for function
arguments later.
Signed-off-by: David Mair <dmair(a)suse.com>
---
diff --git a/x86_64.c b/x86_64.c
index fc05e8a..9f4b5c7 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -4412,15 +4412,20 @@ x86_64_exception_frame(ulong flags, ulong kvaddr, char *local,
long r8, r9, r10, r11, r12, r13, r14, r15;
struct machine_specific *ms;
struct syment *sp;
- ulong offset;
+ ulong offset, verify_addr;
char *pt_regs_buf;
long verified;
long err;
char buf[BUFSIZE];
- if (flags == EFRAME_VERIFY) {
- if (!accessible(kvaddr) ||
- !accessible(kvaddr + SIZE(pt_regs) - sizeof(long)))
+ if (flags & EFRAME_VERIFY) {
+ if (kvaddr)
+ verify_addr = kvaddr;
+ else
+ verify_addr = ((uintptr_t)local - (uintptr_t)bt->stackbuf) + bt->stackbase;
+
+ if (!accessible(verify_addr) ||
+ !accessible(verify_addr + SIZE(pt_regs) - sizeof(long)))
return FALSE;
}
4 years, 2 months
[PATCH] Fix "kmem -i" option on Linux 5.9-rc1 and later kernels
by HAGIO KAZUHITO(萩尾 一仁)
On kernels that contain commit 1008fe6dc36d ("block: remove the all_bdevs
list"), without the patch, the "kmem -i" option fails halfway with the
error message 'kmem: cannot resolve: "all_bdevs"'.
Signed-off-by: Kazuhito Hagio <k-hagio-ab(a)nec.com>
---
# sorry, resending this because I sent with inappropriate setting..
defs.h | 2 ++
memory.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
symbols.c | 4 ++++
3 files changed, 64 insertions(+)
diff --git a/defs.h b/defs.h
index 17e98763362b..dfc09bc86618 100644
--- a/defs.h
+++ b/defs.h
@@ -2089,6 +2089,8 @@ struct offset_table { /* stash of commonly-used offsets */
long size_class_size;
long gendisk_private_data;
long zram_table_entry;
+ long super_block_s_inodes;
+ long inode_i_sb_list;
};
struct size_table { /* stash of commonly-used sizes */
diff --git a/memory.c b/memory.c
index 2bea1288251a..c951827162cb 100644
--- a/memory.c
+++ b/memory.c
@@ -252,6 +252,7 @@ static ulonglong get_vm_flags(char *);
static void PG_reserved_flag_init(void);
static void PG_slab_flag_init(void);
static ulong nr_blockdev_pages(void);
+static ulong nr_blockdev_pages_v2(void);
void sparse_mem_init(void);
void dump_mem_sections(int);
void dump_memory_blocks(int);
@@ -501,6 +502,9 @@ vm_init(void)
if (INVALID_MEMBER(address_space_nrpages))
MEMBER_OFFSET_INIT(address_space_nrpages, "address_space", "__nrpages");
+ MEMBER_OFFSET_INIT(super_block_s_inodes, "super_block", "s_inodes");
+ MEMBER_OFFSET_INIT(inode_i_sb_list, "inode", "i_sb_list");
+
MEMBER_OFFSET_INIT(gendisk_major, "gendisk", "major");
MEMBER_OFFSET_INIT(gendisk_fops, "gendisk", "fops");
MEMBER_OFFSET_INIT(gendisk_disk_name, "gendisk", "disk_name");
@@ -8608,6 +8612,9 @@ nr_blockdev_pages(void)
ulong nrpages;
char *block_device_buf, *inode_buf, *address_space_buf;
+ if (!kernel_symbol_exists("all_bdevs"))
+ return nr_blockdev_pages_v2();
+
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
get_symbol_data("all_bdevs", sizeof(void *), &ld->start);
@@ -8652,6 +8659,57 @@ nr_blockdev_pages(void)
}
/*
+ * Emulate 5.9 nr_blockdev_pages() function.
+ */
+static ulong
+nr_blockdev_pages_v2(void)
+{
+ struct list_data list_data, *ld;
+ ulong bd_sb, address_space;
+ ulong nrpages;
+ int i, inode_count;
+ char *inode_buf, *address_space_buf;
+
+ ld = &list_data;
+ BZERO(ld, sizeof(struct list_data));
+
+ get_symbol_data("blockdev_superblock", sizeof(void *), &bd_sb);
+ readmem(bd_sb + OFFSET(super_block_s_inodes), KVADDR, &ld->start,
+ sizeof(ulong), "blockdev_superblock.s_inodes", FAULT_ON_ERROR);
+
+ if (empty_list(ld->start))
+ return 0;
+ ld->flags |= LIST_ALLOCATE;
+ ld->end = bd_sb + OFFSET(super_block_s_inodes);
+ ld->list_head_offset = OFFSET(inode_i_sb_list);
+
+ inode_buf = GETBUF(SIZE(inode));
+ address_space_buf = GETBUF(SIZE(address_space));
+
+ inode_count = do_list(ld);
+
+ /*
+ * go through the s_inodes list, emulating:
+ *
+ * ret += inode->i_mapping->nrpages;
+ */
+ for (i = nrpages = 0; i < inode_count; i++) {
+ readmem(ld->list_ptr[i], KVADDR, inode_buf, SIZE(inode), "inode buffer",
+ FAULT_ON_ERROR);
+ address_space = ULONG(inode_buf + OFFSET(inode_i_mapping));
+ readmem(address_space, KVADDR, address_space_buf, SIZE(address_space),
+ "address_space buffer", FAULT_ON_ERROR);
+ nrpages += ULONG(address_space_buf + OFFSET(address_space_nrpages));
+ }
+
+ FREEBUF(ld->list_ptr);
+ FREEBUF(inode_buf);
+ FREEBUF(address_space_buf);
+
+ return nrpages;
+}
+
+/*
* dump_vmlist() displays information from the vmlist.
*/
diff --git a/symbols.c b/symbols.c
index 2fecaee093a2..123ed00913e1 100644
--- a/symbols.c
+++ b/symbols.c
@@ -9452,6 +9452,8 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(inode_i_fop));
fprintf(fp, " inode_i_mapping: %ld\n",
OFFSET(inode_i_mapping));
+ fprintf(fp, " inode_i_sb_list: %ld\n",
+ OFFSET(inode_i_sb_list));
fprintf(fp, " vfsmount_mnt_next: %ld\n",
OFFSET(vfsmount_mnt_next));
@@ -9488,6 +9490,8 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(super_block_s_type));
fprintf(fp, " super_block_s_files: %ld\n",
OFFSET(super_block_s_files));
+ fprintf(fp, " super_block_s_inodes: %ld\n",
+ OFFSET(super_block_s_inodes));
fprintf(fp, " nlm_file_f_file: %ld\n",
OFFSET(nlm_file_f_file));
--
1.8.3.1
4 years, 2 months
[PATCH] Fix "irq -a" option on Linux 4.3 or later kernels
by HAGIO KAZUHITO(萩尾 一仁)
On kernels that contain commit 9df872faa7e1 ("genirq: Move field
'affinity' from irq_data into irq_common_data"), without the patch,
the "irq -a" option cannot work with the message "irq: -a option not
supported or applicable on this architecture or kernel".
Signed-off-by: Kazuhito Hagio <k-hagio-ab(a)nec.com>
---
defs.h | 3 +++
kernel.c | 18 +++++++++++++-----
symbols.c | 5 +++++
3 files changed, 21 insertions(+), 5 deletions(-)
diff --git a/defs.h b/defs.h
index 17e98763362b..ad91bfe184fc 100644
--- a/defs.h
+++ b/defs.h
@@ -2089,6 +2089,8 @@ struct offset_table { /* stash of commonly-used offsets */
long size_class_size;
long gendisk_private_data;
long zram_table_entry;
+ long irq_common_data_affinity;
+ long irq_desc_irq_common_data;
};
struct size_table { /* stash of commonly-used sizes */
@@ -2247,6 +2249,7 @@ struct size_table { /* stash of commonly-used sizes */
long xarray;
long xa_node;
long zram_table_entry;
+ long irq_common_data;
};
struct array_table {
diff --git a/kernel.c b/kernel.c
index 5ed602108b87..15f9ab4b8449 100644
--- a/kernel.c
+++ b/kernel.c
@@ -550,6 +550,12 @@ kernel_init()
MEMBER_OFFSET_INIT(irq_desc_irq_data, "irq_desc", "irq_data");
}
+ STRUCT_SIZE_INIT(irq_common_data, "irq_common_data");
+ if (VALID_STRUCT(irq_common_data)) {
+ MEMBER_OFFSET_INIT(irq_common_data_affinity, "irq_common_data", "affinity");
+ MEMBER_OFFSET_INIT(irq_desc_irq_common_data, "irq_desc", "irq_common_data");
+ }
+
STRUCT_SIZE_INIT(irq_cpustat_t, "irq_cpustat_t");
MEMBER_OFFSET_INIT(irq_cpustat_t___softirq_active,
"irq_cpustat_t", "__softirq_active");
@@ -6331,10 +6337,9 @@ cmd_irq(void)
if (!machdep->get_irq_affinity)
option_not_supported(c);
- if (VALID_STRUCT(irq_data)) {
- if (INVALID_MEMBER(irq_data_affinity))
- option_not_supported(c);
- } else if (INVALID_MEMBER(irq_desc_t_affinity))
+ if (INVALID_MEMBER(irq_data_affinity) &&
+ INVALID_MEMBER(irq_common_data_affinity) &&
+ INVALID_MEMBER(irq_desc_t_affinity))
option_not_supported(c);
if ((nr_irqs = machdep->nr_irqs) == 0)
@@ -7096,7 +7101,10 @@ generic_get_irq_affinity(int irq)
len = DIV_ROUND_UP(kt->cpus, BITS_PER_LONG) * sizeof(ulong);
affinity = (ulong *)GETBUF(len);
- if (VALID_STRUCT(irq_data))
+ if (VALID_MEMBER(irq_common_data_affinity))
+ tmp_addr = irq_desc_addr + OFFSET(irq_desc_irq_common_data)
+ + OFFSET(irq_common_data_affinity);
+ else if (VALID_MEMBER(irq_data_affinity))
tmp_addr = irq_desc_addr + \
OFFSET(irq_data_affinity);
else
diff --git a/symbols.c b/symbols.c
index 2fecaee093a2..d18c88f2f3b0 100644
--- a/symbols.c
+++ b/symbols.c
@@ -9310,8 +9310,12 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(irq_data_chip));
fprintf(fp, " irq_data_affinity: %ld\n",
OFFSET(irq_data_affinity));
+ fprintf(fp, " irq_common_data_affinity: %ld\n",
+ OFFSET(irq_common_data_affinity));
fprintf(fp, " irq_desc_irq_data: %ld\n",
OFFSET(irq_desc_irq_data));
+ fprintf(fp, " irq_desc_irq_common_data: %ld\n",
+ OFFSET(irq_desc_irq_common_data));
fprintf(fp, " kernel_stat_irqs: %ld\n",
OFFSET(kernel_stat_irqs));
@@ -10663,6 +10667,7 @@ dump_offset_table(char *spec, ulong makestruct)
fprintf(fp, " runqueue: %ld\n", SIZE(runqueue));
fprintf(fp, " irq_desc_t: %ld\n", SIZE(irq_desc_t));
fprintf(fp, " irq_data: %ld\n", SIZE(irq_data));
+ fprintf(fp, " irq_common_data: %ld\n", SIZE(irq_common_data));
fprintf(fp, " task_union: %ld\n", SIZE(task_union));
fprintf(fp, " thread_union: %ld\n", SIZE(thread_union));
fprintf(fp, " prio_array: %ld\n", SIZE(prio_array));
--
1.8.3.1
4 years, 2 months
[PATCH v2] x86_64_exception_frame only performs EFRAME_VERIFY if it is the only flag
by David Mair
x86_64_exception_frame() called with combined flags including
EFRAME_VERIFY does not perform the verify. It's only done when
EFRAME_VERIFY is the only flag set.
Correct the condition to EFRAME_VERIFY if the flag is set. Verify
requests are always performed. Fixes stack overrun "seek errors" seen on
an x86_64 core when backtracing a PID at an IRQ stack where the
interrupt handler doesn't save a pt_regs. Higher layers than the top
frame on the IRQ stack were not displayed. Fixed by this change.
But it breaks bt -e and bt -E for exceptions on userspace stacks. Those
use the constant 0 as the kvaddr argument to x86_64_exception_frame()
and pass the userspace stack position in the local argument.
x86_64_exception_frame() only verifies the kvaddr argument. Zero is not
accessible and EFRAME_VERIFY always fails for those cases.
Modify the EFRAME_VERIFY block in x86_64_exception_frame() to choose
kvaddr or local to verify using the same condition used to assign one of
them to pt_regs_buf later in the same function. Add verify_addr to
locals to hold the choice. Modify the accessible tests to use it instead
of kvaddr. Type of the new variable is the same as the type of kvaddr.
If verifying local argument, translate to a kernel address range using
the stackbuf and stackbase members of the bt argument the same way used
for EFRAME_SEARCH later in x86_64_exception_frame(). local and
bt->stackbuf are char *, the assignment destination and bt->stackbase
are ulong. Cast the char * variables to uintptr_t for the assignment
arithmetic using the local argument to prevent gcc 10.2 errors assigning
char * to ulong...the sum is okay without casts in the uses for function
arguments later.
Signed-off-by: David Mair <dmair(a)suse.com>
---
diff --git a/x86_64.c b/x86_64.c
index fc05e8a..9f4b5c7 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -4412,15 +4412,20 @@ x86_64_exception_frame(ulong flags, ulong
kvaddr, char *local,
long r8, r9, r10, r11, r12, r13, r14, r15;
struct machine_specific *ms;
struct syment *sp;
- ulong offset;
+ ulong offset, verify_addr;
char *pt_regs_buf;
long verified;
long err;
char buf[BUFSIZE];
- if (flags == EFRAME_VERIFY) {
- if (!accessible(kvaddr) ||
- !accessible(kvaddr + SIZE(pt_regs) - sizeof(long)))
+ if (flags & EFRAME_VERIFY) {
+ if (kvaddr)
+ verify_addr = kvaddr;
+ else
+ verify_addr = ((uintptr_t)local - (uintptr_t)bt->stackbuf) +
bt->stackbase;
+
+ if (!accessible(verify_addr) ||
+ !accessible(verify_addr + SIZE(pt_regs) - sizeof(long)))
return FALSE;
}
4 years, 2 months
[PATCH v3] Basic support for PaX's split module layout
by Mathias Krause
PaX and grsecurity kernels split module memory into dedicated r/x and
r/w mappings using '*_rw' and '*_rx' named member variables in 'struct
module'. To add basic support for such kernels detect the split layout
by testing for the corresponding structure members and use these
instead.
So far we limit ourself to only track module code mappings for such
kernels as adding support for separate data mappings violates lots of
invariants in the rest of our code base, thereby would require a major
rework. However, with that patch applied, module code references can be
resolved in backtraces, memory and code dumps, which makes it already
very useful for analyzing such kernels.
Signed-off-by: Mathias Krause <minipli(a)grsecurity.net>
---
v3:
- dump offsets unconditional
- fix indention of 'module_init_size' offset dump
v2:
- add members to end of struct offset_table
- add offsets to dump_offset_table()
defs.h | 13 +++++++++++
kernel.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++----
symbols.c | 38 ++++++++++++++++++++++---------
3 files changed, 103 insertions(+), 16 deletions(-)
diff --git a/defs.h b/defs.h
index 17e98763362b..ae860448ab15 100644
--- a/defs.h
+++ b/defs.h
@@ -654,12 +654,15 @@ struct new_utsname {
#define TIMER_BASES (0x20ULL)
#define IRQ_DESC_TREE_RADIX (0x40ULL)
#define IRQ_DESC_TREE_XARRAY (0x80ULL)
+#define KMOD_PAX (0x100ULL)
#define XEN() (kt->flags & ARCH_XEN)
#define OPENVZ() (kt->flags & ARCH_OPENVZ)
#define PVOPS() (kt->flags & ARCH_PVOPS)
#define PVOPS_XEN() (kt->flags & ARCH_PVOPS_XEN)
+#define PAX_MODULE_SPLIT() (kt->flags2 & KMOD_PAX)
+
#define XEN_MACHINE_TO_MFN(m) ((ulonglong)(m) >> PAGESHIFT())
#define XEN_PFN_TO_PSEUDO(p) ((ulonglong)(p) << PAGESHIFT())
@@ -2089,6 +2092,14 @@ struct offset_table { /* stash of commonly-used offsets */
long size_class_size;
long gendisk_private_data;
long zram_table_entry;
+ long module_core_size_rw;
+ long module_core_size_rx;
+ long module_init_size_rw;
+ long module_init_size_rx;
+ long module_module_core_rw;
+ long module_module_core_rx;
+ long module_module_init_rw;
+ long module_module_init_rx;
};
struct size_table { /* stash of commonly-used sizes */
@@ -2313,6 +2324,8 @@ struct array_table {
* in the offset table, size table or array_table.
*/
#define OFFSET(X) (OFFSET_verify(offset_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X))
+#define MODULE_OFFSET(X,Y) (PAX_MODULE_SPLIT() ? OFFSET(Y) : OFFSET(X))
+#define MODULE_OFFSET2(X,T) MODULE_OFFSET(X, X##_##T)
#define SIZE(X) (SIZE_verify(size_table.X, (char *)__FUNCTION__, __FILE__, __LINE__, #X))
#define INVALID_OFFSET (-1)
#define INVALID_MEMBER(X) (offset_table.X == INVALID_OFFSET)
diff --git a/kernel.c b/kernel.c
index f179375f2d3d..f0268adccd4a 100644
--- a/kernel.c
+++ b/kernel.c
@@ -3540,6 +3540,62 @@ module_init(void)
"module_core");
MEMBER_OFFSET_INIT(module_module_init, "module",
"module_init");
+ } else if (MEMBER_EXISTS("module", "module_core_rx")) {
+ if (CRASHDEBUG(1))
+ error(INFO, "PaX module layout detected.\n");
+ kt->flags2 |= KMOD_PAX;
+
+ MEMBER_OFFSET_INIT(module_core_size_rw, "module",
+ "core_size_rw");
+ MEMBER_OFFSET_INIT(module_core_size_rx, "module",
+ "core_size_rx");
+
+ MEMBER_OFFSET_INIT(module_init_size_rw, "module",
+ "init_size_rw");
+ MEMBER_OFFSET_INIT(module_init_size_rx, "module",
+ "init_size_rx");
+
+ MEMBER_OFFSET_INIT(module_module_core_rw, "module",
+ "module_core_rw");
+ MEMBER_OFFSET_INIT(module_module_core_rx, "module",
+ "module_core_rx");
+
+ MEMBER_OFFSET_INIT(module_module_init_rw, "module",
+ "module_init_rw");
+ MEMBER_OFFSET_INIT(module_module_init_rx, "module",
+ "module_init_rx");
+ } else if (MEMBER_EXISTS("module_layout", "base_rx")) {
+ if (CRASHDEBUG(1))
+ error(INFO, "PaX module layout detected.\n");
+ kt->flags2 |= KMOD_PAX;
+
+ ASSIGN_OFFSET(module_core_size_rw) =
+ MEMBER_OFFSET("module", "core_layout") +
+ MEMBER_OFFSET("module_layout", "size_rw");
+ ASSIGN_OFFSET(module_core_size_rx) =
+ MEMBER_OFFSET("module", "core_layout") +
+ MEMBER_OFFSET("module_layout", "size_rx");
+
+ ASSIGN_OFFSET(module_init_size_rw) =
+ MEMBER_OFFSET("module", "init_layout") +
+ MEMBER_OFFSET("module_layout", "size_rw");
+ ASSIGN_OFFSET(module_init_size_rx) =
+ MEMBER_OFFSET("module", "init_layout") +
+ MEMBER_OFFSET("module_layout", "size_rx");
+
+ ASSIGN_OFFSET(module_module_core_rw) =
+ MEMBER_OFFSET("module", "core_layout") +
+ MEMBER_OFFSET("module_layout", "base_rw");
+ ASSIGN_OFFSET(module_module_core_rx) =
+ MEMBER_OFFSET("module", "core_layout") +
+ MEMBER_OFFSET("module_layout", "base_rx");
+
+ ASSIGN_OFFSET(module_module_init_rw) =
+ MEMBER_OFFSET("module", "init_layout") +
+ MEMBER_OFFSET("module_layout", "base_rw");
+ ASSIGN_OFFSET(module_module_init_rx) =
+ MEMBER_OFFSET("module", "init_layout") +
+ MEMBER_OFFSET("module_layout", "base_rx");
} else {
ASSIGN_OFFSET(module_core_size) =
MEMBER_OFFSET("module", "core_layout") +
@@ -3682,10 +3738,10 @@ module_init(void)
case KALLSYMS_V2:
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
numksyms = UINT(modbuf + OFFSET(module_num_symtab));
- size = UINT(modbuf + OFFSET(module_core_size));
+ size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx));
} else {
numksyms = ULONG(modbuf + OFFSET(module_num_symtab));
- size = ULONG(modbuf + OFFSET(module_core_size));
+ size = ULONG(modbuf + MODULE_OFFSET2(module_core_size, rx));
}
if (!size) {
@@ -3792,7 +3848,7 @@ verify_modules(void)
break;
case KMOD_V2:
mod_base = ULONG(modbuf +
- OFFSET(module_module_core));
+ MODULE_OFFSET2(module_module_core, rx));
break;
}
@@ -3816,10 +3872,10 @@ verify_modules(void)
OFFSET(module_name);
if (THIS_KERNEL_VERSION >= LINUX(2,6,27))
mod_size = UINT(modbuf +
- OFFSET(module_core_size));
+ MODULE_OFFSET2(module_core_size, rx));
else
mod_size = ULONG(modbuf +
- OFFSET(module_core_size));
+ MODULE_OFFSET2(module_core_size, rx));
if (strlen(module_name) < MAX_MOD_NAME)
strcpy(buf, module_name);
else
@@ -5997,6 +6053,8 @@ dump_kernel_table(int verbose)
fprintf(fp, "%sIRQ_DESC_TREE_RADIX", others++ ? "|" : "");
if (kt->flags2 & IRQ_DESC_TREE_XARRAY)
fprintf(fp, "%sIRQ_DESC_TREE_XARRAY", others++ ? "|" : "");
+ if (kt->flags2 & KMOD_PAX)
+ fprintf(fp, "%sKMOD_PAX", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " stext: %lx\n", kt->stext);
diff --git a/symbols.c b/symbols.c
index 2fecaee093a2..311fdb7df29e 100644
--- a/symbols.c
+++ b/symbols.c
@@ -1766,17 +1766,17 @@ store_module_symbols_v2(ulong total, int mods_installed)
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
nksyms = UINT(modbuf + OFFSET(module_num_symtab));
- size = UINT(modbuf + OFFSET(module_core_size));
+ size = UINT(modbuf + MODULE_OFFSET2(module_core_size, rx));
} else {
nksyms = ULONG(modbuf + OFFSET(module_num_symtab));
- size = ULONG(modbuf + OFFSET(module_core_size));
+ size = ULONG(modbuf + MODULE_OFFSET2(module_core_size, rx));
}
mod_name = modbuf + OFFSET(module_name);
lm = &st->load_modules[m++];
BZERO(lm, sizeof(struct load_module));
- lm->mod_base = ULONG(modbuf + OFFSET(module_module_core));
+ lm->mod_base = ULONG(modbuf + MODULE_OFFSET2(module_module_core, rx));
lm->module_struct = mod;
lm->mod_size = size;
if (strlen(mod_name) < MAX_MOD_NAME)
@@ -1795,23 +1795,23 @@ store_module_symbols_v2(ulong total, int mods_installed)
lm->mod_flags = MOD_EXT_SYMS;
lm->mod_ext_symcnt = mcnt;
lm->mod_init_module_ptr = ULONG(modbuf +
- OFFSET(module_module_init));
+ MODULE_OFFSET2(module_module_init, rx));
if (VALID_MEMBER(module_percpu))
lm->mod_percpu = ULONG(modbuf + OFFSET(module_percpu));
if (THIS_KERNEL_VERSION >= LINUX(2,6,27)) {
lm->mod_etext_guess = lm->mod_base +
- UINT(modbuf + OFFSET(module_core_text_size));
+ UINT(modbuf + MODULE_OFFSET(module_core_text_size, module_core_size_rx));
lm->mod_init_size =
- UINT(modbuf + OFFSET(module_init_size));
+ UINT(modbuf + MODULE_OFFSET2(module_init_size, rx));
lm->mod_init_text_size =
- UINT(modbuf + OFFSET(module_init_text_size));
+ UINT(modbuf + MODULE_OFFSET(module_init_text_size, module_init_size_rx));
} else {
lm->mod_etext_guess = lm->mod_base +
- ULONG(modbuf + OFFSET(module_core_text_size));
+ ULONG(modbuf + MODULE_OFFSET(module_core_text_size, module_core_size_rx));
lm->mod_init_size =
- ULONG(modbuf + OFFSET(module_init_size));
+ ULONG(modbuf + MODULE_OFFSET2(module_init_size, rx));
lm->mod_init_text_size =
- ULONG(modbuf + OFFSET(module_init_text_size));
+ ULONG(modbuf + MODULE_OFFSET(module_init_text_size, module_init_size_rx));
}
lm->mod_text_start = lm->mod_base;
@@ -9119,12 +9119,28 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(module_core_size));
fprintf(fp, " module_core_text_size: %ld\n",
OFFSET(module_core_text_size));
- fprintf(fp, " module_init_size: %ld\n",
+ fprintf(fp, " module_init_size: %ld\n",
OFFSET(module_init_size));
fprintf(fp, " module_init_text_size: %ld\n",
OFFSET(module_init_text_size));
fprintf(fp, " module_module_init: %ld\n",
OFFSET(module_module_init));
+ fprintf(fp, " module_module_core_rx: %ld\n",
+ OFFSET(module_module_core_rx));
+ fprintf(fp, " module_module_core_rw: %ld\n",
+ OFFSET(module_module_core_rw));
+ fprintf(fp, " module_core_size_rx: %ld\n",
+ OFFSET(module_core_size_rx));
+ fprintf(fp, " module_core_size_rw: %ld\n",
+ OFFSET(module_core_size_rw));
+ fprintf(fp, " module_module_init_rx: %ld\n",
+ OFFSET(module_module_init_rx));
+ fprintf(fp, " module_module_init_rw: %ld\n",
+ OFFSET(module_module_init_rw));
+ fprintf(fp, " module_init_size_rx: %ld\n",
+ OFFSET(module_init_size_rx));
+ fprintf(fp, " module_init_size_rw: %ld\n",
+ OFFSET(module_init_size_rw));
fprintf(fp, " module_num_symtab: %ld\n",
OFFSET(module_num_symtab));
fprintf(fp, " module_symtab: %ld\n",
--
2.20.1
4 years, 2 months
[PATCH] x86_64_exception_frame only performs EFRAME_VERIFY if it is the only flag
by David Mair
Calls to x86_64_exception_frame() with combined items set in the flags
argument that include EFRAME_VERIFY do not have the EFRAME_VERIFY
operation performed. I have some cores where multiple cases of
attempting to read a not-present pt_regs end a single PID backtrace with
a failure. One instance has the pt_regs read overrunning stacktop
because the pt_regs is not present and the level's stack position is
closer to stacktop than the size of a pt_regs. That results in a
backtrace failing before complete with a "seek error" at the start of
page after stacktop:
crash> bt 7456
PID: 7456 TASK: ffff933fdb960000 CPU: 0 COMMAND: "sh"
#0 [fffffe0000009e58] crash_nmi_callback at ffffffff93260e93
...
#9 [ffffaea5c0003f80] hrtimer_interrupt at ffffffff933313d5
bt: seek error: kernel virtual address: ffffaea5c0004000 type: "pt_regs"
crash>
The correct backtrace would reach level #12 with no seek error.
The condition to perform the EFRAME_VERIFY operation tests if the flags
value equals EFRAME_VERIFY, not if the value includes EFRAME_VERIFY. The
call to x86_64_exception_frame() in x86_64_print_stack_entry() performed
when eframe_check >= 0 supplies a flags value of EFRAME_PRINT |
EFRAME_VERIFY.
In the bt example above backtrace reaches level #9, 128 bytes from the
top of the current stack's pages in an IRQ stack and with a function
name ending in "_interrupt". This leads to x86_64_print_stack_entry()
setting eframe_check to zero and x86_64_exception_frame() being called
with flags EFRAME_PRINT | EFRAME_VERIFY. x86_64_exception_frame()
doesn't perform the verify because flags is not just EFRAME_VERIFY. An
attempt is made to read 168 bytes (SIZE(pt_regs) - 8 bytes) from a
position 128 bytes from the top of the stack. The stack in question is
followed by a not-present page and the read fails attempting to read
from the page following stacktop.
Signed-off-by: David Mair <dmair(a)suse.com>
---
diff --git a/x86_64.c b/x86_64.c
index fc05e8a..cc870e0 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -4418,7 +4418,7 @@ x86_64_exception_frame(ulong flags, ulong kvaddr,
char *local,
long err;
char buf[BUFSIZE];
- if (flags == EFRAME_VERIFY) {
+ if (flags & EFRAME_VERIFY) {
if (!accessible(kvaddr) ||
!accessible(kvaddr + SIZE(pt_regs) - sizeof(long)))
return FALSE;
4 years, 2 months