[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <e9c4d88b-86db-47e9-4299-3fac45a7e3fd@virtuozzo.com>
Date: Mon, 3 Aug 2020 14:39:32 +0300
From: Evgenii Shatokhin <eshatokhin@...tuozzo.com>
To: Kristen Carlson Accardi <kristen@...ux.intel.com>
Cc: Miroslav Benes <mbenes@...e.cz>, keescook@...omium.org,
tglx@...utronix.de, mingo@...hat.com, bp@...en8.de,
arjan@...ux.intel.com, x86@...nel.org,
linux-kernel@...r.kernel.org, kernel-hardening@...ts.openwall.com,
rick.p.edgecombe@...el.com, live-patching@...r.kernel.org,
Joe Lawrence <joe.lawrence@...hat.com>,
Josh Poimboeuf <jpoimboe@...hat.com>
Subject: Re: [PATCH v4 00/10] Function Granular KASLR
Hi,
> On Fri, 17 Jul 2020, Kristen Carlson Accardi wrote:
>
>> Function Granular Kernel Address Space Layout Randomization (fgkaslr)
>> ---------------------------------------------------------------------
>>
>> This patch set is an implementation of finer grained kernel address space
>> randomization. It rearranges your kernel code at load time
>> on a per-function level granularity, with only around a second added to
>> boot time.
>
> [...]
>> Modules
>> -------
>> Modules are randomized similarly to the rest of the kernel by shuffling
>> the sections at load time prior to moving them into memory. The module must
>> also have been build with the -ffunction-sections compiler option.
It seems, a couple more adjustments are needed in the module loader code.
With function granular KASLR, modules will have lots of ELF sections due
to -ffunction-sections.
On my x86_64 system with kernel 5.8-rc7 with FG KASLR patches, for
example, xfs.ko has 4849 ELF sections total, 2428 of these are loaded
and shown in /sys/module/xfs/sections/.
There are at least 2 places where high-order memory allocations might
happen during module loading. Such allocations may fail if memory is
fragmented, while physically contiguous memory areas are not really
needed there. I suggest to switch to kvmalloc/kvfree there.
1. kernel/module.c, randomize_text():
Elf_Shdr **text_list;
...
int max_sections = info->hdr->e_shnum;
...
text_list = kmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
The size of the allocated memory area is (8 * total_number_of_sections),
if I understand it right, which is 38792 for xfs.ko, a 4th order allocation.
2. kernel/module.c, mod_sysfs_setup() => add_sect_attrs().
This allocation can be larger than the first one.
We found this issue with livepatch modules some time ago (these modules
are already built with -ffunction-sections) [1], but, with FG KASLR, it
affects all kernel modules. Large ones like xfs.ko, btrfs.ko, etc.,
could suffer the most from it.
When a module is loaded sysfs attributes are created for its ELF
sections (visible as /sys/module/<module_name>/sections/*). and contain
the start addresses of these ELF sections. A single memory chunk is
allocated
for all these:
size[0] = ALIGN(sizeof(*sect_attrs)
+ nloaded * sizeof(sect_attrs->attrs[0]),
sizeof(sect_attrs->grp.attrs[0]));
size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.attrs[0]);
sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
'nloaded' is the number of loaded ELF section in the module.
For the kernel 5.8-rc7 on my system, the total size is 56 + 72 *
nloaded, which is 174872 for xfs.ko, 43 pages, 6th order allocation.
I enabled 'mm_page_alloc' tracepoint with filter 'order > 3' to confirm
the issue and, indeed, got these two allocations when modprobe'ing xfs:
----------------------------
/sys/kernel/debug/tracing/trace:
modprobe-1509 <...>: mm_page_alloc: <...> order=4
migratetype=0 gfp_flags=GFP_KERNEL|__GFP_COMP
modprobe-1509 <stack trace>
=> __alloc_pages_nodemask
=> alloc_pages_current
=> kmalloc_order
=> kmalloc_order_trace
=> __kmalloc
=> load_module
modprobe-1509 <...>: mm_page_alloc: <...> order=6
migratetype=0 gfp_flags=GFP_KERNEL|__GFP_COMP|__GFP_ZERO
modprobe-1509 <stack trace>
=> __alloc_pages_nodemask
=> alloc_pages_current
=> kmalloc_order
=> kmalloc_order_trace
=> __kmalloc
=> mod_sysfs_setup
=> load_module
----------------------------
I suppose, something like this can be used as workaround:
* for randomize_text():
-----------
diff --git a/kernel/module.c b/kernel/module.c
index 0f4f4e567a42..a2473db1d0a3 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2433,7 +2433,7 @@ static void randomize_text(struct module *mod,
struct load_info *info)
if (sec == 0)
return;
- text_list = kmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
+ text_list = kvmalloc_array(max_sections, sizeof(*text_list), GFP_KERNEL);
if (!text_list)
return;
@@ -2466,7 +2466,7 @@ static void randomize_text(struct module *mod,
struct load_info *info)
shdr->sh_entsize = get_offset(mod, &size, shdr, 0);
}
- kfree(text_list);
+ kvfree(text_list);
}
/* Lay out the SHF_ALLOC sections in a way not dissimilar to how ld
-----------
* for add_sect_attrs():
-----------
diff --git a/kernel/module.c b/kernel/module.c
index 0f4f4e567a42..a2473db1d0a3 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1541,7 +1541,7 @@ static void free_sect_attrs(struct
module_sect_attrs *sect_attrs)
for (section = 0; section < sect_attrs->nsections; section++)
kfree(sect_attrs->attrs[section].battr.attr.name);
- kfree(sect_attrs);
+ kvfree(sect_attrs);
}
static void add_sect_attrs(struct module *mod, const struct load_info
*info)
@@ -1558,7 +1558,7 @@ static void add_sect_attrs(struct module *mod,
const struct load_info *info)
size[0] = ALIGN(struct_size(sect_attrs, attrs, nloaded),
sizeof(sect_attrs->grp.bin_attrs[0]));
size[1] = (nloaded + 1) * sizeof(sect_attrs->grp.bin_attrs[0]);
- sect_attrs = kzalloc(size[0] + size[1], GFP_KERNEL);
+ sect_attrs = kvzalloc(size[0] + size[1], GFP_KERNEL);
if (sect_attrs == NULL)
return;
-----------
[1] https://github.com/dynup/kpatch/pull/1131
Regards,
Evgenii
Powered by blists - more mailing lists