Skip to content

Commit

Permalink
Basic support for PaX's split module layout
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
minipli-oss authored and sdimitro committed Sep 9, 2020
1 parent 5753350 commit e781015
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 16 deletions.
13 changes: 13 additions & 0 deletions defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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())

Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down
68 changes: 63 additions & 5 deletions kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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") +
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}

Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down
38 changes: 27 additions & 11 deletions symbols.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;

Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit e781015

Please sign in to comment.