From: Ilya Smith <hidden> Date: 2018-03-22 16:36:36
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
---
v2: Changed the way how gap was chosen. Now we don't get all possible
gaps. Random address generated and used as a tree walking direction.
Tree walked with backtracking till suitable gap will be found.
When the gap was found, address randomly shifted from next vma start.
The vm_unmapped_area_info structure was extended with new field random_shift
what might be used to set arch-depended limit on shift to next vma start.
In case of x86-64 architecture this shift is 256 pages for 32 bit applications
and 0x1000000 pages for 64 bit.
To get the entropy pseudo-random is used. This is because on Intel x86-64
processors instruction RDRAND works very slow if buffer is consumed -
after about 10000 iterations.
This feature could be enabled by setting randomize_va_space with 4.
---
Performance:
After applying this patch single mmap took about 7% longer according to
following test:
before = rdtsc();
addr = mmap(0, SIZE, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
after = rdtsc();
diff = after - before;
munmap(addr, SIZE)
...
unsigned long long total = 0;
for(int i = 0; i < count; ++i) {
total += one_iteration();
}
printf("%lld\n", total);
Time is consumed by div instruction in computation of the address.
make kernel:
echo 2 > /proc/sys/kernel/randomize_va_space
make mrproper && make defconfig && time make
real 11m9.925s
user 10m17.829s
sys 1m4.969s
echo 4 > /proc/sys/kernel/randomize_va_space
make mrproper && make defconfig && time make
real 11m12.806s
user 10m18.305s
sys 1m4.281s
Ilya Smith (2):
Randomization of address chosen by mmap.
Architecture defined limit on memory region random shift.
arch/alpha/kernel/osf_sys.c | 1 +
arch/arc/mm/mmap.c | 1 +
arch/arm/mm/mmap.c | 2 +
arch/frv/mm/elf-fdpic.c | 1 +
arch/ia64/kernel/sys_ia64.c | 1 +
arch/ia64/mm/hugetlbpage.c | 1 +
arch/metag/mm/hugetlbpage.c | 1 +
arch/mips/mm/mmap.c | 1 +
arch/parisc/kernel/sys_parisc.c | 2 +
arch/powerpc/mm/hugetlbpage-radix.c | 1 +
arch/powerpc/mm/mmap.c | 2 +
arch/powerpc/mm/slice.c | 2 +
arch/s390/mm/mmap.c | 2 +
arch/sh/mm/mmap.c | 2 +
arch/sparc/kernel/sys_sparc_32.c | 1 +
arch/sparc/kernel/sys_sparc_64.c | 2 +
arch/sparc/mm/hugetlbpage.c | 2 +
arch/tile/mm/hugetlbpage.c | 2 +
arch/x86/kernel/sys_x86_64.c | 4 +
arch/x86/mm/hugetlbpage.c | 4 +
fs/hugetlbfs/inode.c | 1 +
include/linux/mm.h | 17 ++--
mm/mmap.c | 165 ++++++++++++++++++++++++++++++++++++
23 files changed, 213 insertions(+), 5 deletions(-)
--
2.7.4
@@ -2268,6 +2276,9 @@ extern unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info);staticinlineunsignedlongvm_unmapped_area(structvm_unmapped_area_info*info){+/* How about 32 bit process?? */+if((current->flags&PF_RANDOMIZE)&&randomize_va_space>3)+returnunmapped_area_random(info);if(info->flags&VM_UNMAPPED_AREA_TOPDOWN)returnunmapped_area_topdown(info);else
@@ -1780,6 +1781,169 @@ unsigned long mmap_region(struct file *file, unsigned long addr,returnerror;}+unsignedlongunmapped_area_random(structvm_unmapped_area_info*info)+{+structmm_struct*mm=current->mm;+structvm_area_struct*vma=NULL;+structvm_area_struct*visited_vma=NULL;+unsignedlongentropy[2];+unsignedlonglength,low_limit,high_limit,gap_start,gap_end;+unsignedlongaddr=0;++/* get entropy with prng */+prandom_bytes(&entropy,sizeof(entropy));+/* small hack to prevent EPERM result */+info->low_limit=max(info->low_limit,mmap_min_addr);++/* Adjust search length to account for worst case alignment overhead */+length=info->length+info->align_mask;+if(length<info->length)+return-ENOMEM;++/*+*Adjustsearchlimitsbythedesiredlength.+*Seeimplementationcommentattopofunmapped_area().+*/+gap_end=info->high_limit;+if(gap_end<length)+return-ENOMEM;+high_limit=gap_end-length;++low_limit=info->low_limit+info->align_mask;+if(low_limit>=high_limit)+return-ENOMEM;++/* Choose random addr in limit range */+addr=entropy[0]%((high_limit-low_limit)>>PAGE_SHIFT);+addr=low_limit+(addr<<PAGE_SHIFT);+addr+=(info->align_offset-addr)&info->align_mask;++/* Check if rbtree root looks promising */+if(RB_EMPTY_ROOT(&mm->mm_rb))+return-ENOMEM;++vma=rb_entry(mm->mm_rb.rb_node,structvm_area_struct,vm_rb);+if(vma->rb_subtree_gap<length)+return-ENOMEM;+/* use randomly chosen address to find closest suitable gap */+while(true){+gap_start=vma->vm_prev?vm_end_gap(vma->vm_prev):0;+gap_end=vm_start_gap(vma);+if(gap_end<low_limit)+break;+if(addr<vm_start_gap(vma)){+/* random said check left */+if(vma->vm_rb.rb_left){+structvm_area_struct*left=+rb_entry(vma->vm_rb.rb_left,+structvm_area_struct,vm_rb);+if(addr<=vm_start_gap(left)&&+left->rb_subtree_gap>=length){+vma=left;+continue;+}+}+}elseif(addr>=vm_end_gap(vma)){+/* random said check right */+if(vma->vm_rb.rb_right){+structvm_area_struct*right=+rb_entry(vma->vm_rb.rb_right,+structvm_area_struct,vm_rb);+/* it want go to the right */+if(right->rb_subtree_gap>=length){+vma=right;+continue;+}+}+}+if(gap_start<low_limit){+if(gap_end<=low_limit)+break;+gap_start=low_limit;+}elseif(gap_end>info->high_limit){+if(gap_start>=info->high_limit)+break;+gap_end=info->high_limit;+}+if(gap_end>gap_start&&+gap_end-gap_start>=length)+gotofound;+visited_vma=vma;+break;+}+/* not found */+while(true){+gap_start=vma->vm_prev?vm_end_gap(vma->vm_prev):0;++if(gap_start<=high_limit&&vma->vm_rb.rb_right){+structvm_area_struct*right=+rb_entry(vma->vm_rb.rb_right,+structvm_area_struct,vm_rb);+if(right->rb_subtree_gap>=length&&+right!=visited_vma){+vma=right;+continue;+}+}++check_current:+/* Check if current node has a suitable gap */+gap_end=vm_start_gap(vma);+if(gap_end<=low_limit)+gotogo_back;++if(gap_start<low_limit)+gap_start=low_limit;++if(gap_start<=high_limit&&+gap_end>gap_start&&gap_end-gap_start>=length)+gotofound;++/* Visit left subtree if it looks promising */+if(vma->vm_rb.rb_left){+structvm_area_struct*left=+rb_entry(vma->vm_rb.rb_left,+structvm_area_struct,vm_rb);+if(left->rb_subtree_gap>=length&&+vm_end_gap(left)>low_limit&&+left!=visited_vma){+vma=left;+continue;+}+}+go_back:+/* Go back up the rbtree to find next candidate node */+while(true){+structrb_node*prev=&vma->vm_rb;++if(!rb_parent(prev))+return-ENOMEM;+visited_vma=vma;+vma=rb_entry(rb_parent(prev),+structvm_area_struct,vm_rb);+if(prev==vma->vm_rb.rb_right){+gap_start=vma->vm_prev?+vm_end_gap(vma->vm_prev):low_limit;+gotocheck_current;+}+}+}+found:+/* We found a suitable gap. Clip it with the original high_limit. */+if(gap_end>info->high_limit)+gap_end=info->high_limit;+gap_end-=info->length;+gap_end-=(gap_end-info->align_offset)&info->align_mask;+/* only one suitable page */+if(gap_end==gap_start)+returngap_start;+addr=entropy[1]%(min((gap_end-gap_start)>>PAGE_SHIFT,+0x10000UL));+addr=gap_end-(addr<<PAGE_SHIFT);+addr+=(info->align_offset-addr)&info->align_mask;+returnaddr;+}+unsignedlongunmapped_area(structvm_unmapped_area_info*info){/*
@@ -1301,6 +1301,7 @@ arch_get_unmapped_area_1(unsigned long addr, unsigned long len,info.high_limit=limit;info.align_mask=0;info.align_offset=0;+info.random_shift=0;returnvm_unmapped_area(&info);}
You'll be wanting to update the documentation.
Documentation/sysctl/kernel.txt and
Documentation/admin-guide/kernel-parameters.txt.
quoted hunk
...
@@ -2268,6 +2276,9 @@ extern unsigned long unmapped_area_topdown(struct vm_unmapped_area_info *info); static inline unsigned long vm_unmapped_area(struct vm_unmapped_area_info *info) {+ /* How about 32 bit process?? */+ if ((current->flags & PF_RANDOMIZE) && randomize_va_space > 3)+ return unmapped_area_random(info);
The handling of randomize_va_space is peculiar. Rather than being a
bitfield which independently selects different modes, it is treated as
a scalar: the larger the value, the more stuff we randomize.
I can see the sense in that (and I wonder what randomize_va_space=5
will do). But it is... odd.
Why did you select randomize_va_space=4 for this? Is there a mode 3
already and we forgot to document it? Or did you leave a gap for
something? If the former, please feel free to fix the documentation
(in a separate, preceding patch) while you're in there ;)
quoted hunk
if (info->flags & VM_UNMAPPED_AREA_TOPDOWN)
return unmapped_area_topdown(info);
else
From: Andrew Morton <akpm@linux-foundation.org> Date: 2018-03-22 20:54:48
Please add changelogs. An explanation of what a "limit on memory
region random shift" is would be nice ;) Why does it exist, why are we
doing this, etc. Surely there's something to be said - at present this
is just a lump of random code?
From: Andrew Morton <akpm@linux-foundation.org> Date: 2018-03-22 20:57:29
On Thu, 22 Mar 2018 19:36:36 +0300 Ilya Smith [off-list ref] wrote:
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases.
Perhaps some more effort on the problem description would help. *Are*
people predicting layouts at present? What problems does this cause?
How are they doing this and are there other approaches to solving the
problem?
Mainly: what value does this patchset have to our users? This reader
is unable to determine that from the information which you have
provided. Full details, please.
From: Matthew Wilcox <willy@infradead.org> Date: 2018-03-23 12:48:06
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
From: Ilya Smith <hidden> Date: 2018-03-23 17:25:15
Hello, Andrew
Thanks for reading this patch.
On 22 Mar 2018, at 23:57, Andrew Morton [off-list ref] =
wrote:
=20
On Thu, 22 Mar 2018 19:36:36 +0300 Ilya Smith [off-list ref] =
wrote:
=20
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases.
=20
Perhaps some more effort on the problem description would help. *Are*
people predicting layouts at present? What problems does this cause?=20=
How are they doing this and are there other approaches to solving the
problem?
=20
Sorry, I=E2=80=99ve lost it in first version. In short - memory layout =
could be easily=20
repaired by single leakage. Also any Out of Bounds error may easily be=20=
exploited according to current implementation. All because mmap choose =
address=20
just before previously allocated segment. You can read more about it =
here:=20
http://www.openwall.com/lists/oss-security/2018/02/27/5
Some test are available here https://github.com/blackzert/aslur.=20
To solve the problem Kernel should randomize address on any mmap so
attacker could never easily gain needed addresses.
Mainly: what value does this patchset have to our users? This reader
is unable to determine that from the information which you have
provided. Full details, please.
The value of this patch is to decrease successful rate of exploitation
vulnerable applications.These could be either remote or local vectors.
static inline unsigned long
vm_unmapped_area(struct vm_unmapped_area_info *info)
{
+ /* How about 32 bit process?? */
+ if ((current->flags & PF_RANDOMIZE) && randomize_va_space > 3)
+ return unmapped_area_random(info);
=20
The handling of randomize_va_space is peculiar. Rather than being a
bitfield which independently selects different modes, it is treated as
a scalar: the larger the value, the more stuff we randomize.
=20
I can see the sense in that (and I wonder what randomize_va_space=3D5
will do). But it is... odd.
=20
Why did you select randomize_va_space=3D4 for this? Is there a mode 3
already and we forgot to document it? Or did you leave a gap for
something? If the former, please feel free to fix the documentation
(in a separate, preceding patch) while you're in there ;)
=20
Yes, I was not sure about correct value so leaved some gap for future. =
Also
according to current implementation this value used like a scalar. But =
I=E2=80=99m
agree bitfield looks more flexible for the future. I think right now I =
can leave
3 as value for my patch and it could be fixed any time in the future. =
What
do you think about it?
quoted
if (info->flags & VM_UNMAPPED_AREA_TOPDOWN)
return unmapped_area_topdown(info);
else
@@ -2529,11 +2540,6 @@ int drop_caches_sysctl_handler(struct =
This one what I fix by next patch. I was trying to make patches separate =
to make
it easier to understand them. This constant came from last version =
discussion=20
and honestly doesn=E2=80=99t means much. I replaced it with Architecture =
depended limit
that as I plan would be CONFIG value as well.
This value means maximum number of pages we can move away from the next
vma. The less value means less security but less memory fragmentation. =
Any way
on 64bit systems memory fragmentation is not such a big problem.
From: Ilya Smith <hidden> Date: 2018-03-23 17:48:00
On 22 Mar 2018, at 23:54, Andrew Morton [off-list ref] wrote:
Please add changelogs. An explanation of what a "limit on memory
region random shift" is would be nice ;) Why does it exist, why are we
doing this, etc. Surely there's something to be said - at present this
is just a lump of random code?
Sorry, my bad. The main idea of this limit is to decrease possible memory
fragmentation. This is not so big problem on 64bit process, but really big for
32 bit processes since may cause failure memory allocation. To control memory
fragmentation and protect 32 bit systems (or architectures) this limit was
introduce by this patch. It could be also moved to CONFIG_ as well.
From: Ilya Smith <hidden> Date: 2018-03-23 17:49:03
On 22 Mar 2018, at 23:54, Andrew Morton [off-list ref] =
wrote:
=20
=20
Please add changelogs. An explanation of what a "limit on memory
region random shift" is would be nice ;) Why does it exist, why are we
doing this, etc. Surely there's something to be said - at present =
this
is just a lump of random code?
=20
=20
=20
Sorry, my bad. The main idea of this limit is to decrease possible =
memory=20
fragmentation. This is not so big problem on 64bit process, but really =
big for=20
32 bit processes since may cause failure memory allocation. To control =
memory=20
fragmentation and protect 32 bit systems (or architectures) this limit =
was=20
introduce by this patch. It could be also moved to CONFIG_ as well.=
From: Ilya Smith <hidden> Date: 2018-03-23 17:55:49
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] wrote:
=20
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
Why should this be done in the kernel rather than libc? libc is =
perfectly
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible =
IMO;
2. User mode is not that layer which should be responsible for choosing
random address or handling entropy;
3. Memory fragmentation is unpredictable in this case
Off course user mode could use random =E2=80=98hint=E2=80=99 address, =
but kernel may
discard this address if it is occupied for example and allocate just =
before
closest vma. So this solution doesn=E2=80=99t give that much security =
like=20
randomization address inside kernel.=
On Fri, Mar 23, 2018 at 05:48:06AM -0700, Matthew Wilcox wrote:
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Generally libc does not have a view of the current vm maps, and thus
in passing "random numbers", they would have to be uniform across the
whole vm space and thus non-uniform once the kernel rounds up to avoid
existing mappings. Also this would impose requirements that libc be
aware of the kernel's use of the virtual address space and what's
available to userspace -- for example, on 32-bit archs whether 2GB,
3GB, or full 4GB (for 32-bit-user-on-64-bit-kernel) is available, and
on 64-bit archs where fewer than the full 64 bits are actually valid
in addresses, what the actual usable pointer size is. There is
currently no clean way of conveying this information to userspace.
Rich
From: Matthew Wilcox <willy@infradead.org> Date: 2018-03-23 19:06:18
On Fri, Mar 23, 2018 at 02:00:24PM -0400, Rich Felker wrote:
On Fri, Mar 23, 2018 at 05:48:06AM -0700, Matthew Wilcox wrote:
quoted
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Generally libc does not have a view of the current vm maps, and thus
in passing "random numbers", they would have to be uniform across the
whole vm space and thus non-uniform once the kernel rounds up to avoid
existing mappings.
I'm aware that you're the musl author, but glibc somehow manages to
provide etext, edata and end, demonstrating that it does know where at
least some of the memory map lies. Virtually everything after that is
brought into the address space via mmap, which at least glibc intercepts,
so it's entirely possible for a security-conscious libc to know where
other things are in the memory map. Not to mention that what we're
primarily talking about here are libraries which are dynamically linked
and are loaded by ld.so before calling main(); not dlopen() or even
regular user mmaps.
Also this would impose requirements that libc be
aware of the kernel's use of the virtual address space and what's
available to userspace -- for example, on 32-bit archs whether 2GB,
3GB, or full 4GB (for 32-bit-user-on-64-bit-kernel) is available, and
on 64-bit archs where fewer than the full 64 bits are actually valid
in addresses, what the actual usable pointer size is. There is
currently no clean way of conveying this information to userspace.
Huh, I thought libc was aware of this. Also, I'd expect a libc-based
implementation to restrict itself to, eg, only loading libraries in
the bottom 1GB to avoid applications who want to map huge things from
running out of unfragmented address space.
On Fri, Mar 23, 2018 at 12:06:18PM -0700, Matthew Wilcox wrote:
On Fri, Mar 23, 2018 at 02:00:24PM -0400, Rich Felker wrote:
quoted
On Fri, Mar 23, 2018 at 05:48:06AM -0700, Matthew Wilcox wrote:
quoted
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Generally libc does not have a view of the current vm maps, and thus
in passing "random numbers", they would have to be uniform across the
whole vm space and thus non-uniform once the kernel rounds up to avoid
existing mappings.
I'm aware that you're the musl author, but glibc somehow manages to
provide etext, edata and end, demonstrating that it does know where at
least some of the memory map lies.
Yes, but that's pretty minimal info.
Virtually everything after that is
brought into the address space via mmap, which at least glibc intercepts,
There's also vdso, the program interpreter (ldso), and theoretically
other things the kernel might add. I agree you _could_ track most of
this (and all if you want to open /proc/self/maps), but it seems
hackish and wrong (violating clean boundaries between userspace and
kernel responsibility).
quoted
Also this would impose requirements that libc be
aware of the kernel's use of the virtual address space and what's
available to userspace -- for example, on 32-bit archs whether 2GB,
3GB, or full 4GB (for 32-bit-user-on-64-bit-kernel) is available, and
on 64-bit archs where fewer than the full 64 bits are actually valid
in addresses, what the actual usable pointer size is. There is
currently no clean way of conveying this information to userspace.
Huh, I thought libc was aware of this. Also, I'd expect a libc-based
implementation to restrict itself to, eg, only loading libraries in
the bottom 1GB to avoid applications who want to map huge things from
running out of unfragmented address space.
That seems like a rather arbitrary expectation and I'm not sure why
you'd expect it to result in less fragmentation rather than more. For
example if it started from 1GB and worked down, you'd immediately
reduce the contiguous free space from ~3GB to ~2GB, and if it started
from the bottom and worked up, brk would immediately become
unavailable, increasing mmap pressure elsewhere.
Rich
From: Matthew Wilcox <willy@infradead.org> Date: 2018-03-23 19:29:52
On Fri, Mar 23, 2018 at 03:16:21PM -0400, Rich Felker wrote:
quoted
Huh, I thought libc was aware of this. Also, I'd expect a libc-based
implementation to restrict itself to, eg, only loading libraries in
the bottom 1GB to avoid applications who want to map huge things from
running out of unfragmented address space.
That seems like a rather arbitrary expectation and I'm not sure why
you'd expect it to result in less fragmentation rather than more. For
example if it started from 1GB and worked down, you'd immediately
reduce the contiguous free space from ~3GB to ~2GB, and if it started
from the bottom and worked up, brk would immediately become
unavailable, increasing mmap pressure elsewhere.
By *not* limiting yourself to the bottom 1GB, you'll almost immediately
fragment the address space even worse. Just looking at 'ls' as a
hopefully-good example of a typical app, it maps:
linux-vdso.so.1 (0x00007ffef5eef000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fb3657f5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb36543b000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fb3651c9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb364fc5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb365c3f000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb364da7000)
The VDSO wouldn't move, but look at the distribution of mapping 6 things
into a 3GB address space in random locations. What are the odds you have
a contiguous 1GB chunk of address space? If you restrict yourself to the
bottom 1GB before running out of room and falling back to a sequential
allocation, you'll prevent a lot of fragmentation.
On Fri, Mar 23, 2018 at 12:29:52PM -0700, Matthew Wilcox wrote:
On Fri, Mar 23, 2018 at 03:16:21PM -0400, Rich Felker wrote:
quoted
quoted
Huh, I thought libc was aware of this. Also, I'd expect a libc-based
implementation to restrict itself to, eg, only loading libraries in
the bottom 1GB to avoid applications who want to map huge things from
running out of unfragmented address space.
That seems like a rather arbitrary expectation and I'm not sure why
you'd expect it to result in less fragmentation rather than more. For
example if it started from 1GB and worked down, you'd immediately
reduce the contiguous free space from ~3GB to ~2GB, and if it started
from the bottom and worked up, brk would immediately become
unavailable, increasing mmap pressure elsewhere.
By *not* limiting yourself to the bottom 1GB, you'll almost immediately
fragment the address space even worse. Just looking at 'ls' as a
hopefully-good example of a typical app, it maps:
linux-vdso.so.1 (0x00007ffef5eef000)
libselinux.so.1 => /lib/x86_64-linux-gnu/libselinux.so.1 (0x00007fb3657f5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb36543b000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007fb3651c9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb364fc5000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb365c3f000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fb364da7000)
The VDSO wouldn't move, but look at the distribution of mapping 6 things
into a 3GB address space in random locations. What are the odds you have
a contiguous 1GB chunk of address space? If you restrict yourself to the
bottom 1GB before running out of room and falling back to a sequential
allocation, you'll prevent a lot of fragmentation.
Oh, you're talking about "with random locations" case. Randomizing
each map just hopelessly fragments things no matter what you do on
32-bit. If you reduce the space over which you randomize to the point
where it's not fragmenting/killing your available vm space, there are
so few degrees of freedom left that it's trivial to brute-force. Maybe
"libs randomized in low 1GB, everything else near-sequential in high
addresses" works half decently, but I have a hard time believing you
can get any ASLR that's significantly better than snake oil in a
32-bit address space, and you certainly do pay a high price in total
available vm space.
Rich
From: Michal Hocko <mhocko@kernel.org> Date: 2018-03-26 08:46:50
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] wrote:
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible IMO;
Is this really so helpful?
2. User mode is not that layer which should be responsible for choosing
random address or handling entropy;
Why?
3. Memory fragmentation is unpredictable in this case
Off course user mode could use random ‘hint’ address, but kernel may
discard this address if it is occupied for example and allocate just before
closest vma. So this solution doesn’t give that much security like
randomization address inside kernel.
The userspace can use the new MAP_FIXED_NOREPLACE to probe for the
address range atomically and chose a different range on failure.
--
Michal Hocko
SUSE Labs
From: Ilya Smith <hidden> Date: 2018-03-26 19:45:31
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
=20
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
=20
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] =
wrote:
quoted
quoted
=20
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
Why should this be done in the kernel rather than libc? libc is =
perfectly
quoted
quoted
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible =
IMO;
=20
Is this really so helpful?
Yes, ASLR is one of very important mitigation techniques which are =
really used=20
to protect applications. If there is no ASLR, it is very easy to exploit=20=
vulnerable application and compromise the system. We can=E2=80=99t just =
fix all the=20
vulnerabilities right now, thats why we have mitigations - techniques =
which are=20
makes exploitation more hard or impossible in some cases.
Thats why it is helpful.
=20
quoted
2. User mode is not that layer which should be responsible for =
choosing
quoted
random address or handling entropy;
=20
Why?
Because of the following reasons:
1. To get random address you should have entropy. These entropy =
shouldn=E2=80=99t be=20
exposed to attacker anyhow, the best case is to get it from kernel. So =
this is
a syscall.
2. You should have memory map of your process to prevent remapping or =
big
fragmentation. Kernel already has this map. You will got another one in =
libc.
And any non-libc user of mmap (via syscall, etc) will make hole in your =
map.
This one also decrease performance cause you any way call syscall_mmap=20=
which will try to find some address for you in worst case, but after you =
already
did some computing on it.
3. The more memory you use in userland for these proposal, the easier =
for
attacker to leak it or use in exploitation techniques.
4. It is so easy to fix Kernel function and so hard to support memory
management from userspace.
=20
quoted
3. Memory fragmentation is unpredictable in this case
=20
Off course user mode could use random =E2=80=98hint=E2=80=99 address, =
but kernel may
quoted
discard this address if it is occupied for example and allocate just =
before
quoted
closest vma. So this solution doesn=E2=80=99t give that much security =
like=20
quoted
randomization address inside kernel.
=20
The userspace can use the new MAP_FIXED_NOREPLACE to probe for the
address range atomically and chose a different range on failure.
=20
This algorithm should track current memory. If he doesn=E2=80=99t he may =
cause
infinite loop while trying to choose memory. And each iteration increase =
time
needed on allocation new memory, what is not preferred by any libc =
library
developer.
Thats why I did this patch.
Thanks,
Ilya
From: Michal Hocko <mhocko@kernel.org> Date: 2018-03-27 07:24:32
On Mon 26-03-18 22:45:31, Ilya Smith wrote:
quoted
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] wrote:
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible IMO;
Is this really so helpful?
Yes, ASLR is one of very important mitigation techniques which are really used
to protect applications. If there is no ASLR, it is very easy to exploit
vulnerable application and compromise the system. We can’t just fix all the
vulnerabilities right now, thats why we have mitigations - techniques which are
makes exploitation more hard or impossible in some cases.
Thats why it is helpful.
I am not questioning ASLR in general. I am asking whether we really need
per mmap ASLR in general. I can imagine that some environments want to
pay the additional price and other side effects, but considering this
can be achieved by libc, why to add more code to the kernel?
quoted
quoted
2. User mode is not that layer which should be responsible for choosing
random address or handling entropy;
Why?
Because of the following reasons:
1. To get random address you should have entropy. These entropy shouldn’t be
exposed to attacker anyhow, the best case is to get it from kernel. So this is
a syscall.
/dev/[u]random is not sufficient?
2. You should have memory map of your process to prevent remapping or big
fragmentation. Kernel already has this map.
/proc/self/maps?
You will got another one in libc.
And any non-libc user of mmap (via syscall, etc) will make hole in your map.
This one also decrease performance cause you any way call syscall_mmap
which will try to find some address for you in worst case, but after you already
did some computing on it.
I do not understand. a) you should be prepared to pay an additional
price for an additional security measures and b) how would anybody punch
a hole into your mapping?
3. The more memory you use in userland for these proposal, the easier for
attacker to leak it or use in exploitation techniques.
This is true in general, isn't it? I fail to see how kernel chosen and
user chosen ranges would make any difference.
4. It is so easy to fix Kernel function and so hard to support memory
management from userspace.
Well, on the other hand the new layout mode will add a maintenance
burden on the kernel and will have to be maintained for ever because it
is a user visible ABI.
quoted
quoted
3. Memory fragmentation is unpredictable in this case
Off course user mode could use random ‘hint’ address, but kernel may
discard this address if it is occupied for example and allocate just before
closest vma. So this solution doesn’t give that much security like
randomization address inside kernel.
The userspace can use the new MAP_FIXED_NOREPLACE to probe for the
address range atomically and chose a different range on failure.
This algorithm should track current memory. If he doesn’t he may cause
infinite loop while trying to choose memory. And each iteration increase time
needed on allocation new memory, what is not preferred by any libc library
developer.
Well, I am pretty sure userspace can implement proper free ranges
tracking...
--
Michal Hocko
SUSE Labs
From: Ilya Smith <hidden> Date: 2018-03-27 13:51:08
On 27 Mar 2018, at 10:24, Michal Hocko [off-list ref] wrote:
=20
On Mon 26-03-18 22:45:31, Ilya Smith wrote:
quoted
=20
quoted
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
=20
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
=20
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] =
wrote:
quoted
quoted
quoted
quoted
=20
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by =
mmap.
quoted
quoted
quoted
quoted
quoted
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of =
address
quoted
quoted
quoted
quoted
quoted
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
Why should this be done in the kernel rather than libc? libc is =
perfectly
quoted
quoted
quoted
quoted
capable of specifying random numbers in the first argument of =
mmap.
quoted
quoted
quoted
Well, there is following reasons:
1. It should be done in any libc implementation, what is not =
possible IMO;
quoted
quoted
=20
Is this really so helpful?
=20
Yes, ASLR is one of very important mitigation techniques which are =
really used=20
quoted
to protect applications. If there is no ASLR, it is very easy to =
exploit=20
quoted
vulnerable application and compromise the system. We can=E2=80=99t =
just fix all the=20
quoted
vulnerabilities right now, thats why we have mitigations - techniques =
which are=20
quoted
makes exploitation more hard or impossible in some cases.
=20
Thats why it is helpful.
=20
I am not questioning ASLR in general. I am asking whether we really =
need
per mmap ASLR in general. I can imagine that some environments want to
pay the additional price and other side effects, but considering this
can be achieved by libc, why to add more code to the kernel?
I believe this is the only one right place for it. Adding these 200+ =
lines of=20
code we give this feature for any user - on desktop, on server, on IoT =
device,=20
on SCADA, etc. But if only glibc will implement =E2=80=98user-mode-aslr=E2=
=80=99 IoT and SCADA=20
devices will never get it.
quoted
quoted
=20
quoted
2. User mode is not that layer which should be responsible for =
choosing
quoted
quoted
quoted
random address or handling entropy;
=20
Why?
=20
Because of the following reasons:
1. To get random address you should have entropy. These entropy =
shouldn=E2=80=99t be=20
quoted
exposed to attacker anyhow, the best case is to get it from kernel. =
So this is
quoted
a syscall.
=20
/dev/[u]random is not sufficient?
Using /dev/[u]random makes 3 syscalls - open, read, close. This is a =
performance
issue.
=20
quoted
2. You should have memory map of your process to prevent remapping or =
big
quoted
fragmentation. Kernel already has this map.
=20
/proc/self/maps?
Not any system has /proc and parsing /proc/self/maps is robust so it is =
the=20
performance issue. libc will have to do it on any mmap. And there is a =
possible=20
race here - application may mmap/unmap memory with native syscall during =
other=20
thread reading maps.
quoted
You will got another one in libc.
And any non-libc user of mmap (via syscall, etc) will make hole in =
your map.
quoted
This one also decrease performance cause you any way call =
syscall_mmap=20
quoted
which will try to find some address for you in worst case, but after =
you already
quoted
did some computing on it.
=20
I do not understand. a) you should be prepared to pay an additional
price for an additional security measures and b) how would anybody =
punch
a hole into your mapping?=20
=20
I was talking about any code that call mmap directly without libc =
wrapper.
quoted
3. The more memory you use in userland for these proposal, the easier =
for
quoted
attacker to leak it or use in exploitation techniques.
=20
This is true in general, isn't it? I fail to see how kernel chosen and
user chosen ranges would make any difference.
My point here was that libc will have to keep memory representation as a =
tree=20
and this tree increase attack surface. It could be hidden in kernel as =
it is right now.
=20
quoted
4. It is so easy to fix Kernel function and so hard to support memory
management from userspace.
=20
Well, on the other hand the new layout mode will add a maintenance
burden on the kernel and will have to be maintained for ever because =
it
is a user visible ABI.
Thats why I made this patch as RFC and would like to discuss this ABI =
here. I=20
made randomize_va_space parameter to allow disable randomisation per =
whole=20
system. PF_RANDOMIZE flag may disable randomization for concrete process =
(or=20
process groups?). For architecture I=E2=80=99ve made info.random_shift =3D=
0 , so if your=20
arch has small address space you may disable shifting. I also would like =
to add=20
some sysctl to allow process/groups to change this value and allow some=20=
processes to have shifts bigger then another. Lets discuss it, please.
=20
quoted
quoted
quoted
3. Memory fragmentation is unpredictable in this case
=20
Off course user mode could use random =E2=80=98hint=E2=80=99 =
address, but kernel may
quoted
quoted
quoted
discard this address if it is occupied for example and allocate =
just before
quoted
quoted
quoted
closest vma. So this solution doesn=E2=80=99t give that much =
security like=20
quoted
quoted
quoted
randomization address inside kernel.
=20
The userspace can use the new MAP_FIXED_NOREPLACE to probe for the
address range atomically and chose a different range on failure.
=20
=20
This algorithm should track current memory. If he doesn=E2=80=99t he =
may cause
quoted
infinite loop while trying to choose memory. And each iteration =
increase time
quoted
needed on allocation new memory, what is not preferred by any libc =
library
quoted
developer.
=20
Well, I am pretty sure userspace can implement proper free ranges
tracking=E2=80=A6
I think we need to know what libc developers will say on implementing =
ASLR in=20
user-mode. I am pretty sure they will say =E2=80=98nether=E2=80=99 or =
=E2=80=98some-day=E2=80=99. And problem=20
of ASLR will stay forever.
Thanks,
Ilya
From: Michal Hocko <mhocko@kernel.org> Date: 2018-03-27 14:38:20
On Tue 27-03-18 16:51:08, Ilya Smith wrote:
quoted
On 27 Mar 2018, at 10:24, Michal Hocko [off-list ref] wrote:
On Mon 26-03-18 22:45:31, Ilya Smith wrote:
quoted
quoted
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] wrote:
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible IMO;
Is this really so helpful?
Yes, ASLR is one of very important mitigation techniques which are really used
to protect applications. If there is no ASLR, it is very easy to exploit
vulnerable application and compromise the system. We can’t just fix all the
vulnerabilities right now, thats why we have mitigations - techniques which are
makes exploitation more hard or impossible in some cases.
Thats why it is helpful.
I am not questioning ASLR in general. I am asking whether we really need
per mmap ASLR in general. I can imagine that some environments want to
pay the additional price and other side effects, but considering this
can be achieved by libc, why to add more code to the kernel?
I believe this is the only one right place for it. Adding these 200+ lines of
code we give this feature for any user - on desktop, on server, on IoT device,
on SCADA, etc. But if only glibc will implement ‘user-mode-aslr’ IoT and SCADA
devices will never get it.
I guess it would really help if you could be more specific about the
class of security issues this would help to mitigate. My first
understanding was that we we need some randomization between program
executable segments to reduce the attack space when a single address
leaks and you know the segments layout (ordering). But why do we need
_all_ mmaps to be randomized. Because that complicates the
implementation consirably for different reasons you have mentioned
earlier.
Do you have any specific CVE that would be mitigated by this
randomization approach?
I am sorry, I am not a security expert to see all the cosequences but a
vague - the more randomization the better - sounds rather weak to me.
--
Michal Hocko
SUSE Labs
From: "Theodore Y. Ts'o" <tytso@mit.edu> Date: 2018-03-27 22:16:35
On Tue, Mar 27, 2018 at 04:51:08PM +0300, Ilya Smith wrote:
quoted
/dev/[u]random is not sufficient?
Using /dev/[u]random makes 3 syscalls - open, read, close. This is a performance
issue.
You may want to take a look at the getrandom(2) system call, which is
the recommended way getting secure random numbers from the kernel.
quoted
Well, I am pretty sure userspace can implement proper free ranges
tracking…
I think we need to know what libc developers will say on implementing ASLR in
user-mode. I am pretty sure they will say ‘nether’ or ‘some-day’. And problem
of ASLR will stay forever.
Why can't you send patches to the libc developers?
Regards,
- Ted
On Tue, Mar 27, 2018 at 6:51 AM, Ilya Smith [off-list ref] wrote:
quoted
On 27 Mar 2018, at 10:24, Michal Hocko [off-list ref] wrote:
On Mon 26-03-18 22:45:31, Ilya Smith wrote:
quoted
quoted
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] wrote=
:
quoted
quoted
quoted
quoted
quoted
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is per=
fectly
quoted
quoted
quoted
quoted
quoted
capable of specifying random numbers in the first argument of mmap.
Well, there is following reasons:
1. It should be done in any libc implementation, what is not possible=
IMO;
quoted
quoted
quoted
Is this really so helpful?
Yes, ASLR is one of very important mitigation techniques which are real=
ly used
quoted
quoted
to protect applications. If there is no ASLR, it is very easy to exploi=
t
quoted
quoted
vulnerable application and compromise the system. We can=E2=80=99t just=
fix all the
quoted
quoted
vulnerabilities right now, thats why we have mitigations - techniques w=
hich are
quoted
quoted
makes exploitation more hard or impossible in some cases.
Thats why it is helpful.
I am not questioning ASLR in general. I am asking whether we really need
per mmap ASLR in general. I can imagine that some environments want to
pay the additional price and other side effects, but considering this
can be achieved by libc, why to add more code to the kernel?
I believe this is the only one right place for it. Adding these 200+ line=
s of
code we give this feature for any user - on desktop, on server, on IoT de=
vice,
on SCADA, etc. But if only glibc will implement =E2=80=98user-mode-aslr=
=E2=80=99 IoT and SCADA
devices will never get it.
I agree: pushing this off to libc leaves a lot of things unprotected.
I think this should live in the kernel. The question I have is about
making it maintainable/readable/etc.
The state-of-the-art for ASLR is moving to finer granularity (over
just base-address offset), so I'd really like to see this supported in
the kernel. We'll be getting there for other things in the future, and
I'd like to have a working production example for researchers to
study, etc.
-Kees
--=20
Kees Cook
Pixel Security
From: Matthew Wilcox <willy@infradead.org> Date: 2018-03-27 23:49:04
On Tue, Mar 27, 2018 at 03:53:53PM -0700, Kees Cook wrote:
I agree: pushing this off to libc leaves a lot of things unprotected.
I think this should live in the kernel. The question I have is about
making it maintainable/readable/etc.
The state-of-the-art for ASLR is moving to finer granularity (over
just base-address offset), so I'd really like to see this supported in
the kernel. We'll be getting there for other things in the future, and
I'd like to have a working production example for researchers to
study, etc.
One thing we need is to limit the fragmentation of this approach.
Even on 64-bit systems, we can easily get into a situation where there isn't
space to map a contiguous terabyte.
On Tue, Mar 27, 2018 at 4:49 PM, Matthew Wilcox [off-list ref] wrote:
On Tue, Mar 27, 2018 at 03:53:53PM -0700, Kees Cook wrote:
quoted
I agree: pushing this off to libc leaves a lot of things unprotected.
I think this should live in the kernel. The question I have is about
making it maintainable/readable/etc.
The state-of-the-art for ASLR is moving to finer granularity (over
just base-address offset), so I'd really like to see this supported in
the kernel. We'll be getting there for other things in the future, and
I'd like to have a working production example for researchers to
study, etc.
One thing we need is to limit the fragmentation of this approach.
Even on 64-bit systems, we can easily get into a situation where there isn't
space to map a contiguous terabyte.
FWIW, I wouldn't expect normal systems to use this. I am curious about
fragmentation vs entropy though. Are workloads with a mis of lots of
tiny allocations and TB-allocations? AIUI, glibc uses larger mmap()
regions for handling tiny mallocs().
-Kees
--
Kees Cook
Pixel Security
On Tue, Mar 27, 2018 at 06:16:35PM -0400, Theodore Y. Ts'o wrote:
On Tue, Mar 27, 2018 at 04:51:08PM +0300, Ilya Smith wrote:
quoted
quoted
/dev/[u]random is not sufficient?
Using /dev/[u]random makes 3 syscalls - open, read, close. This is a performance
issue.
You may want to take a look at the getrandom(2) system call, which is
the recommended way getting secure random numbers from the kernel.
Yes, while opening /dev/urandom is not acceptable due to needing an
fd, getrandom and existing fallbacks for it have this covered if
needed.
quoted
quoted
Well, I am pretty sure userspace can implement proper free ranges
tracking…
I think we need to know what libc developers will say on implementing ASLR in
user-mode. I am pretty sure they will say ‘nether’ or ‘some-day’. And problem
of ASLR will stay forever.
Why can't you send patches to the libc developers?
I can tell you right now that any patch submitted for musl that
depended on trying to duplicate knowledge of the entire virtual
address space layout in userspace as part of mmap would be rejected,
and I would recommend glibc do the same.
Not only does it vastly increase complexity; it also has all sorts of
failure modes (fd exhastion, etc.) which would either introduce new
and unwanted ways for mmap to fail, or would force fallback to the
normal (no extra randomization) strategy under conditions an attacker
could potentially control, defeating the whole purpose. It would also
potentially make it easier for an attacker to examine the vm layout
for attacks, since it would be recorded in userspace.
There's also the issue of preserving AS-safety of mmap. POSIX does not
actually require mmap to be AS-safe, and on musl munmap is not fully
AS-safe anyway because of some obscure issues it compensates for, but
we may be able to make it AS-safe (this is a low-priority open issue).
If mmap were manipulating data structures representing the vm space in
userspace, though, the only way to make it anywhere near AS-safe would
be to block all signals and take a lock every time mmap or munmap is
called. This would significantly increase the cost of each call,
especially now that meltdown/spectre mitigations have greatly
increased the overhead of each syscall.
Overall, asking userspace to take a lead role in management of process
vm space is a radical change in the split of what user and kernel are
responsible for, and it really does not make sense as part of a
dubious hardening measure. Something this big would need to be really
well-motivated.
Rich
On Tue, Mar 27, 2018 at 04:49:04PM -0700, Matthew Wilcox wrote:
On Tue, Mar 27, 2018 at 03:53:53PM -0700, Kees Cook wrote:
quoted
I agree: pushing this off to libc leaves a lot of things unprotected.
I think this should live in the kernel. The question I have is about
making it maintainable/readable/etc.
The state-of-the-art for ASLR is moving to finer granularity (over
just base-address offset), so I'd really like to see this supported in
the kernel. We'll be getting there for other things in the future, and
I'd like to have a working production example for researchers to
study, etc.
One thing we need is to limit the fragmentation of this approach.
Even on 64-bit systems, we can easily get into a situation where there isn't
space to map a contiguous terabyte.
The default limit of only 65536 VMAs will also quickly come into play
if consecutive anon mmaps don't get merged. Of course this can be
raised, but it has significant resource and performance (fork) costs.
Rich
From: Rob Landley <hidden> Date: 2018-03-28 04:50:02
On 03/23/2018 02:06 PM, Matthew Wilcox wrote:
On Fri, Mar 23, 2018 at 02:00:24PM -0400, Rich Felker wrote:
quoted
On Fri, Mar 23, 2018 at 05:48:06AM -0700, Matthew Wilcox wrote:
quoted
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
Why should this be done in the kernel rather than libc? libc is perfectly
capable of specifying random numbers in the first argument of mmap.
Generally libc does not have a view of the current vm maps, and thus
in passing "random numbers", they would have to be uniform across the
whole vm space and thus non-uniform once the kernel rounds up to avoid
existing mappings.
I'm aware that you're the musl author, but glibc somehow manages to
provide etext, edata and end, demonstrating that it does know where at
least some of the memory map lies.
You can parse /proc/self/maps, but it's really expensive and disgusting.
Rob
From: Ilya Smith <hidden> Date: 2018-03-28 18:47:15
On 27 Mar 2018, at 17:38, Michal Hocko [off-list ref] wrote:
=20
On Tue 27-03-18 16:51:08, Ilya Smith wrote:
quoted
=20
quoted
On 27 Mar 2018, at 10:24, Michal Hocko [off-list ref] wrote:
=20
On Mon 26-03-18 22:45:31, Ilya Smith wrote:
quoted
=20
quoted
On 26 Mar 2018, at 11:46, Michal Hocko [off-list ref] wrote:
=20
On Fri 23-03-18 20:55:49, Ilya Smith wrote:
quoted
=20
quoted
On 23 Mar 2018, at 15:48, Matthew Wilcox [off-list ref] =
wrote:
quoted
quoted
quoted
quoted
quoted
quoted
=20
On Thu, Mar 22, 2018 at 07:36:36PM +0300, Ilya Smith wrote:
quoted
Current implementation doesn't randomize address returned by =
mmap.
quoted
quoted
quoted
quoted
quoted
quoted
quoted
All the entropy ends with choosing mmap_base_addr at the =
process
quoted
quoted
quoted
quoted
quoted
quoted
quoted
creation. After that mmap build very predictable layout of =
address
quoted
quoted
quoted
quoted
quoted
quoted
quoted
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
Why should this be done in the kernel rather than libc? libc is =
perfectly
quoted
quoted
quoted
quoted
quoted
quoted
capable of specifying random numbers in the first argument of =
mmap.
quoted
quoted
quoted
quoted
quoted
Well, there is following reasons:
1. It should be done in any libc implementation, what is not =
possible IMO;
quoted
quoted
quoted
quoted
=20
Is this really so helpful?
=20
Yes, ASLR is one of very important mitigation techniques which are =
really used=20
quoted
quoted
quoted
to protect applications. If there is no ASLR, it is very easy to =
exploit=20
quoted
quoted
quoted
vulnerable application and compromise the system. We can=E2=80=99t =
just fix all the=20
quoted
quoted
quoted
vulnerabilities right now, thats why we have mitigations - =
techniques which are=20
quoted
quoted
quoted
makes exploitation more hard or impossible in some cases.
=20
Thats why it is helpful.
=20
I am not questioning ASLR in general. I am asking whether we really =
need
quoted
quoted
per mmap ASLR in general. I can imagine that some environments want =
to
quoted
quoted
pay the additional price and other side effects, but considering =
this
quoted
quoted
can be achieved by libc, why to add more code to the kernel?
=20
I believe this is the only one right place for it. Adding these 200+ =
lines of=20
quoted
code we give this feature for any user - on desktop, on server, on =
IoT device,=20
quoted
on SCADA, etc. But if only glibc will implement =E2=80=98user-mode-aslr=
=E2=80=99 IoT and SCADA=20
quoted
devices will never get it.
=20
I guess it would really help if you could be more specific about the
class of security issues this would help to mitigate. My first
understanding was that we we need some randomization between program
executable segments to reduce the attack space when a single address
leaks and you know the segments layout (ordering). But why do we need
_all_ mmaps to be randomized. Because that complicates the
implementation consirably for different reasons you have mentioned
earlier.
=20
There are following reasons:
1) To protect layout if one region was leaked (as you said).=20
2) To protect against exploitation of Out-of-bounds vulnerabilities in =
some=20
cases (CWE-125 , CWE-787)
3) To protect against exploitation of Buffer Overflows in some cases =
(CWE-120)
4) To protect application in cases when attacker need to guess the =
address=20
(paper ASLR-NG by Hector Marco-Gisbert and Ismael Ripoll-Ripoll)
And may be more cases.
Do you have any specific CVE that would be mitigated by this
randomization approach?
I am sorry, I am not a security expert to see all the cosequences but =
a
vague - the more randomization the better - sounds rather weak to me.
It is hard to name concrete CVE number, sorry. Mitigations are made to =
prevent=20
exploitation but not to fix vulnerabilities. It means good mitigation =
will make=20
vulnerable application crash but not been compromised in most cases. =
This means=20
the better randomization, the less successful exploitation rate.
Thanks,
Ilya
The default limit of only 65536 VMAs will also quickly come into play
if consecutive anon mmaps don't get merged. Of course this can be
raised, but it has significant resource and performance (fork) costs.
Could the random mmap address chooser look for how many existing
VMAs have space before/after and the right attributes to merge with the
new one you want to create? If this is above some threshold (100?) then
pick one of them randomly and allocate the new address so that it will
merge from below/above with an existing one.
That should still give you a very high degree of randomness, but prevent
out of control numbers of VMAs from being created.
-Tony
From: Ilya Smith <hidden> Date: 2018-03-28 21:07:35
On 28 Mar 2018, at 02:49, Matthew Wilcox [off-list ref] wrote:
=20
On Tue, Mar 27, 2018 at 03:53:53PM -0700, Kees Cook wrote:
quoted
I agree: pushing this off to libc leaves a lot of things unprotected.
I think this should live in the kernel. The question I have is about
making it maintainable/readable/etc.
=20
The state-of-the-art for ASLR is moving to finer granularity (over
just base-address offset), so I'd really like to see this supported =
in
quoted
the kernel. We'll be getting there for other things in the future, =
and
quoted
I'd like to have a working production example for researchers to
study, etc.
=20
One thing we need is to limit the fragmentation of this approach.
Even on 64-bit systems, we can easily get into a situation where there =
isn't
space to map a contiguous terabyte.
As I wrote before, shift_random is introduced to be fragmentation limit. =
Even=20
without it, the main question here is =E2=80=98if we can=E2=80=99t =
allocate memory with N size=20
bytes, how many bytes we already allocated?=E2=80=99. =46rom these point =
of view I=20
already showed in previous version of patch that if application uses not =
so big=20
memory allocations, it will have enough memory to use. If it uses XX =
Gigabytes=20
or Terabytes memory, this application has all chances to be exploited =
with=20
fully randomization or without. Since it is much easier to find(or =
guess) any=20
usable pointer, etc. For the instance you have only 128 terabytes of =
memory for=20
user space, so probability to exploit this application is 1/128 what is =
not=20
secure at all. This is very rough estimate but I try to make things =
easier to=20
understand.
Best regards,
Ilya
From: Pavel Machek <hidden> Date: 2018-03-30 07:55:08
Hi!
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
From: Ilya Smith <hidden> Date: 2018-03-30 09:07:58
Hi
On 30 Mar 2018, at 10:55, Pavel Machek [off-list ref] wrote:
=20
Hi!
=20
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
How will this interact with people debugging their application, and
getting different behaviours based on memory layout?
=20
strace, strace again, get different results?
=20
Honestly I=E2=80=99m confused about your question. If the only one way =
for debugging=20
application is to use predictable mmap behaviour, then something went =
wrong in=20
this live and we should stop using computers at all.
Thanks,
Ilya
From: Pavel Machek <hidden> Date: 2018-03-30 09:57:35
On Fri 2018-03-30 12:07:58, Ilya Smith wrote:
Hi
quoted
On 30 Mar 2018, at 10:55, Pavel Machek [off-list ref] wrote:
Hi!
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
How will this interact with people debugging their application, and
getting different behaviours based on memory layout?
strace, strace again, get different results?
Honestly I’m confused about your question. If the only one way for debugging
application is to use predictable mmap behaviour, then something went wrong in
this live and we should stop using computers at all.
From: Ilya Smith <hidden> Date: 2018-03-30 11:10:21
On 30 Mar 2018, at 12:57, Pavel Machek [off-list ref] wrote:
=20
On Fri 2018-03-30 12:07:58, Ilya Smith wrote:
quoted
Hi
=20
quoted
On 30 Mar 2018, at 10:55, Pavel Machek [off-list ref] wrote:
=20
Hi!
=20
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
=20
How will this interact with people debugging their application, and
getting different behaviours based on memory layout?
=20
strace, strace again, get different results?
=20
=20
Honestly I=E2=80=99m confused about your question. If the only one =
way for debugging=20
quoted
application is to use predictable mmap behaviour, then something went =
wrong in=20
quoted
this live and we should stop using computers at all.
=20
I'm not saying "only way". I'm saying one way, and you are breaking
that. There's advanced stuff like debuggers going "back in time".
=20
Correct me if I wrong, when you run gdb for instance and try to debug =
some=20
application, gdb will disable randomization. This behaviour works with =
gdb=20
command: set disable-randomization on. As I know, gdb remove flag =
PF_RANDOMIZE=20
from current personality thats how it disables ASLR for debugging =
process.=20
According to my patch, flag PF_RANDOMIZE is checked before calling=20
unmapped_area_random. So I don=E2=80=99t breaking debugging. If you =
talking about the=20
case, when your application crashes under customer environment and you =
want to
debug it; in this case layout of memory is what you don=E2=80=99t =
control at all and=20
you have to understand what is where. So for debugging memory process =
layout is
not what you should care of.
Thanks,
Ilya
On Fri, Mar 30, 2018 at 09:55:08AM +0200, Pavel Machek wrote:
Hi!
quoted
Current implementation doesn't randomize address returned by mmap.
All the entropy ends with choosing mmap_base_addr at the process
creation. After that mmap build very predictable layout of address
space. It allows to bypass ASLR in many cases. This patch make
randomization of address on any mmap call.
How will this interact with people debugging their application, and
getting different behaviours based on memory layout?
strace, strace again, get different results?
Normally gdb disables ASLR for the process when invoking a program to
debug. I don't see why that would be terribly useful with strace but
you can do the same if you want.
Rich
From: Ilya Smith <hidden> Date: 2018-04-03 00:11:50
On 29 Mar 2018, at 00:07, Luck, Tony [off-list ref] wrote:
=20
quoted
The default limit of only 65536 VMAs will also quickly come into play
if consecutive anon mmaps don't get merged. Of course this can be
raised, but it has significant resource and performance (fork) costs.
=20
Could the random mmap address chooser look for how many existing
VMAs have space before/after and the right attributes to merge with =
the
new one you want to create? If this is above some threshold (100?) =
then
pick one of them randomly and allocate the new address so that it will
merge from below/above with an existing one.
=20
That should still give you a very high degree of randomness, but =
prevent
out of control numbers of VMAs from being created.
I think this wouldn=E2=80=99t work. For example these 100 allocation may =
happened on=20
process initialization. But when attacker come to the server all his=20
allocations would be made on the predictable offsets from each other. So =
in=20
result we did nothing just decrease performance of first 100 =
allocations. I=20
think I can make ioctl to turn off this randomization per process and it =
could=20
be used if needed. For example if application going to allocate big =
chunk or=20
make big memory pressure, etc.
Best regards,
Ilya