This was reported by grsecurity to full-disclosure http://lists.netsys.com/pipermail/full-disclosure/2005-January/030660.html the 'culprit' patch is how the default RLIM_MEMLOCK and the privilege to call mlockall have changed in 2.6.9. namely, the former has been reduced to 32 pages while the latter has been relaxed to allow it for otherwise unprivileged users if their RLIM_MEMLOCK is bigger than the currently allocated vm. which is normally good enough, except as you now know there's a path that can increase the allocated vm without checking for RLIM_MEMLOCK.
Created attachment 109501 [details] Proposed fix for this issue
Can we get a reproducer? If we mlock a stack page, and then increase the stack size, separate vma's get created, with the previously locked stack page getting its own vma. So we don't know how expand_stack() is being called such that you can exploit it? The link in comment #1 does not respond.
Jason Baron forwarded this as the reproducer: > 6) 2.4/2.6 RLIMIT_MEMLOCK bypass and (2.6) unprivileged user DoS > > Taken from the mail from the PaX team to Linus and Andrew Morton: > > the 'culprit' patch is how the default RLIM_MEMLOCK and the privilege > to call mlockall have changed in 2.6.9. namely, the former has been > reduced to 32 pages while the latter has been relaxed to allow it for > otherwise unprivileged users if their RLIM_MEMLOCK is bigger than the > currently allocated vm. which is normally good enough, except as you > now know there's a path that can increase the allocated vm without > checking for RLIM_MEMLOCK. > > i'm attaching a small i386-specific demonstration, use the makefile to > create the small self-contained executable, e.g., 'make alloc=0x100000' > to have it allocate 1MB of stack and lock all of it. for demonstrating > the full effect of locking down arbitrary amounts of memory, you'll have > to set your stack rlimit to infinity (ulimit -s unlimited) and allocate > as much memory as your memory overcommit policy allows (this may mean > that you'll have to run multiple instances, if you have lots of memory). > > surprisingly, in my tests the kernel survived pretty well, it just crawled > to a snail's speed as every mapped page access required disk i/o ;-). i > didn't play with overcommit policies nor any special workloads, so there > may very well be worse effects with that much locked memory. in any case, > this may warrant 2.6.10.1 because as soon as the fix goes into -bk, anyone > reading the logs can easily figure it out and reproduce the 'exploit'. > > the attached patch is the excerpt from PaX that survives the exploit, so > i think it's good to go. Here's the little mlock-dos.S assembly program: ;nasm -f elf mlock-dos.S ;ld -static mlock-dos.o bits 32 %define MCL_CURRENT 1 %define MCL_FUTURE 2 %define __NR_mlockall 152 section .text global _start _start: mov ebx,MCL_CURRENT | MCL_FUTURE mov eax,__NR_mlockall int 0x80 sub esp,ALLOCATE mov edi,esp mov ecx,ALLOCATE cld rep stosb .x: jmp .x Following the directions: $ ulimit -s unlimited $ make alloc=0x100000 nasm -f elf -DALLOCATE=0x100000 mlock-dos.S ld -static mlock-dos.o $ a.out Then looking at the a.out VM from crash: crash> vm 11106 PID: 11106 TASK: cbda4000 CPU: 1 COMMAND: "a.out" MM PGD RSS TOTAL_VM d5c00500 dff59600 1036k 1040k VMA START END FLAGS FILE d22eaf9c 8048000 8049000 1875 /tmp/a.out cd7f38b4 bfefd000 c0000000 100177 crash> You can see that the mlockall attempt failed, because the stack's VMA has flags of 100177, i.e., not including VM_LOCKED (0x2000). The text section remains unlocked as well (1875). I'm sure I don't understand why the assembly program's mlockall() was expected to work. It certainly fails from a C program. RHEL3's sys_mlockall() has this: lock_limit = current->rlim[RLIMIT_MEMLOCK].rlim_cur; lock_limit >>= PAGE_SHIFT; ret = -ENOMEM; if (current->mm->total_vm > lock_limit && !capable(CAP_IPC_LOCK)) goto out; Since our user RLIMIT_MEMLOCK defaults to 4096 bytes, the MCL_CURRENT|MCL_FUTURE request to mlockall() fails immediately. Perhaps the upstream RLIMIT_MEMLOCK is significantly larger?
It is -- in our rlimits initializer, the RLIMIT_MEMLOCK is PAGE_SIZE: #define INIT_RLIMITS \ { \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { _STK_LIM, RLIM_INFINITY }, \ { 0, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { 0, 0 }, \ { INR_OPEN, INR_OPEN }, \ { PAGE_SIZE, PAGE_SIZE }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ } Upstream, it's RLIM_INFINITY: #define INIT_RLIMITS \ { \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { _STK_LIM, RLIM_INFINITY }, \ { 0, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { 0, 0 }, \ { INR_OPEN, INR_OPEN }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ { RLIM_INFINITY, RLIM_INFINITY }, \ } That being the case, I believe this is NOTABUG.
Closing per last comment.
Reopening due to further discussion with Jason ... seems that RHEL3 kernels would still have a problem if the sysadmin were to have raised the default mlock rlimit (in /etc/security/limits.conf). But I do agree with Dave in that default installations are not exploitable.
A fix for this problem has just been committed to the RHEL3 U6 patch pool this evening (in kernel version 2.4.21-32.7.EL).
An advisory has been issued which should help the problem described in this bug report. This report is therefore being closed with a resolution of ERRATA. For more information on the solution and/or where to find the updated files, please follow the link below. You may reopen this bug report if the solution does not work for you. http://rhn.redhat.com/errata/RHSA-2005-663.html