Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 525494 - dev-python/cffi: PaX denied RWX mmap of <anonymous mapping> in allocator for the ffi trampoline
Summary: dev-python/cffi: PaX denied RWX mmap of <anonymous mapping> in allocator for ...
Status: RESOLVED FIXED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: Hardened (show other bugs)
Hardware: AMD64 Linux
: Normal normal with 1 vote (vote)
Assignee: The Gentoo Linux Hardened Team
URL: https://bitbucket.org/cffi/cffi/issue...
Whiteboard:
Keywords:
: 541706 548098 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-10-15 12:00 UTC by Tomasz Wasiak
Modified: 2015-05-02 17:35 UTC (History)
11 users (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
Kernel configuration (.config,90.15 KB, text/plain)
2014-10-15 16:32 UTC, Tomasz Wasiak
Details
temp fix PROC_EXEC -> ~PROC_EXEC (cffi_proc_exec.patch,512 bytes, patch)
2014-12-31 17:56 UTC, Magnus Granberg
Details | Diff
Add check for Emutramp (cffi_proc_exec.patch,1.93 KB, patch)
2015-01-02 12:37 UTC, Magnus Granberg
Details | Diff
Check Emutramp (cffi_proc_exec.patch,1.92 KB, patch)
2015-01-03 12:53 UTC, Magnus Granberg
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tomasz Wasiak 2014-10-15 12:00:01 UTC
app-portage/layman segfaults with grsec: denied RWX mmap of <anonymous mapping> by /usr/lib64/python-exec/python2.7/layman[layman:15194] uid/euid:0/0 gid/egid:0/0, parent /bin/bash[bash:22713] uid/euid:0/0 gid/egid:0/0

This is fresh install using ~amd64. At the beginning I tried to use only CONFIG_PAX_XATTR_PAX_FLAGS but still portage used only ELF header marking so I manually copied all flags into extended attributes - nothing changed. Recompiling kernel with both CONFIG_PAX_PT_PAX_FLAGS and CONFIG_PAX_XATTR_PAX_FLAGS does not help (nor using only CONFIG_PAX_PT_PAX_FLAGS).

Reproducible: Always

Steps to Reproduce:
1.Try to run layman, for example layman -S
2.
3.
Actual Results:  
Segmentation fault

Expected Results:  
Synchronized overlays

#paxctl-ng -v /usr/bin/python2*
/usr/bin/python2.7:
        PT_PAX    : -E---
        XATTR_PAX : -E---
/usr/bin/python2:
        PT_PAX    : -E---
        XATTR_PAX : -E---

#grep -e "^CONFIG_PAX" /usr/src/linux/.config
CONFIG_PAX=y
CONFIG_PAX_ASLR=y
CONFIG_PAX_CONSTIFY_PLUGIN=y
CONFIG_PAX_EMUTRAMP=y
CONFIG_PAX_HAVE_ACL_FLAGS=y
CONFIG_PAX_KERNEXEC=y
CONFIG_PAX_KERNEXEC_PLUGIN=y
CONFIG_PAX_KERNEXEC_PLUGIN_METHOD="bts"
CONFIG_PAX_KERNEXEC_PLUGIN_METHOD_BTS=y
CONFIG_PAX_MEMORY_UDEREF=y
CONFIG_PAX_MPROTECT=y
CONFIG_PAX_NOEXEC=y
CONFIG_PAX_PAGEEXEC=y
CONFIG_PAX_PER_CPU_PGD=y
CONFIG_PAX_PT_PAX_FLAGS=y
CONFIG_PAX_RANDMMAP=y
CONFIG_PAX_RANDUSTACK=y
CONFIG_PAX_REFCOUNT=y
CONFIG_PAX_USERCOPY=y
CONFIG_PAX_USERCOPY_SLABS=y
CONFIG_PAX_XATTR_PAX_FLAGS=y
Comment 1 Anthony Basile gentoo-dev 2014-10-15 12:19:40 UTC
(In reply to Tomasz Wasiak from comment #0)
> app-portage/layman segfaults with grsec: denied RWX mmap of <anonymous
> mapping> by /usr/lib64/python-exec/python2.7/layman[layman:15194]
> uid/euid:0/0 gid/egid:0/0, parent /bin/bash[bash:22713] uid/euid:0/0
> gid/egid:0/0

Does this only happen with layman, or all python packages?  Try `python -c "import ctypes"` to see if its a python issue.

I don' see what you're doing wrong.  I don't think its a matter of PT vs XATTR PaX markings since it happens both ways.  As a temporary work around, and for diagnostics, see if paxctl-ng -m on your python exe fixes it.

Also let's get your kernel version and full config just in case.
Comment 2 Tomasz Wasiak 2014-10-15 16:31:55 UTC
#python -c "import ctypes"

exits cleanly without any output with exit status 0.

Now I am using 3.16.5-hardened kernel with a few patches which I do believe does not interfere with PAX features. In the past I was trying hardened-sources 3.14.20, 3.16.4 and 3.16.5 without any additional patches without any difference.

Please find attached current kernel configuration.
Comment 3 Tomasz Wasiak 2014-10-15 16:32:26 UTC
Created attachment 386734 [details]
Kernel configuration
Comment 4 Tomasz Wasiak 2014-10-15 16:37:56 UTC
I forgot to mention that I have such entry in logs after trying to run layman: 

layman[15194]: segfault at c ip 0000027a635e141b sp 00000398fefc4280 error 6 in libffi.so.6.0.2[27a635db000+8000]

I have not found any other issues with python applications but it might be just because I am not using python, it is installed mainly for portage/layman and other "system/internal" uses.
Comment 5 Magnus Granberg gentoo-dev 2014-10-15 17:42:56 UTC
What use flags do you have on libffi?
Comment 6 Tomasz Wasiak 2014-10-15 19:16:15 UTC
I have only pax_kernel use flag active on dev-libs/libffi (3.1-r3).

@Anthony Basile
Disabling MPROTECT on python2.7 resolves my issues but I want to know why it is not documented anywhere? EMUTRAMP trick is only mentioned on Gentoo forums. Those informations should be available in Handbook IMHO.
Comment 7 Tomasz Wasiak 2014-10-15 19:17:52 UTC
I think layman (or even python) ebuild should be corrected to mark binary so it will work on hardened systems...
Comment 8 Anthony Basile gentoo-dev 2014-10-15 20:33:41 UTC
(In reply to Tomasz Wasiak from comment #7)
> I think layman (or even python) ebuild should be corrected to mark binary so
> it will work on hardened systems...

It shouldn't be layman's ebuild to do the markings.  It should be python and from what you said, it seems like something else is going on but I can't figure it out from this information.

Layman did undergo quite a rewrite recently, but I don't think that affected anything.  Is layman the only python program causing this?  Does it only happen with the most recent version?


(In reply to Tomasz Wasiak from comment #6)
> 
> @Anthony Basile
> Disabling MPROTECT on python2.7 resolves my issues but I want to know why it
> is not documented anywhere? EMUTRAMP trick is only mentioned on Gentoo
> forums. Those informations should be available in Handbook IMHO.

These are discussed in https://wiki.gentoo.org/wiki/Hardened/PaX_Quickstart.  paxctl-ng -m is not the optimal solution.  emutramp should have worked so 

I don't know what else to suggest at this point.  You ahve a workaround and we can leave this bug open in case others hit it and we can get a better handle on how to reproduce it.
Comment 9 Tomasz Wasiak 2014-10-15 22:10:16 UTC
(In reply to Anthony Basile from comment #8)
> (In reply to Tomasz Wasiak from comment #7)
> > I think layman (or even python) ebuild should be corrected to mark binary so
> > it will work on hardened systems...
> 
> It shouldn't be layman's ebuild to do the markings.  It should be python and
> from what you said, it seems like something else is going on but I can't
> figure it out from this information.
> 
> Layman did undergo quite a rewrite recently, but I don't think that affected
> anything.  Is layman the only python program causing this?  Does it only
> happen with the most recent version?

As I told before I am not using python too much so it will be hard for me to find any other program having issues. I have not seen any other issues on my system but that means only that sys-apps/portage works (and maybe some other small utilities but nothing comes to my mind now).

Please tell me which older layman version I should check and I will do it.

> 
> (In reply to Tomasz Wasiak from comment #6)
> > 
> > @Anthony Basile
> > Disabling MPROTECT on python2.7 resolves my issues but I want to know why it
> > is not documented anywhere? EMUTRAMP trick is only mentioned on Gentoo
> > forums. Those informations should be available in Handbook IMHO.
> 
> These are discussed in https://wiki.gentoo.org/wiki/Hardened/PaX_Quickstart.
> paxctl-ng -m is not the optimal solution.  emutramp should have worked so 
> 
> I don't know what else to suggest at this point.  You ahve a workaround and
> we can leave this bug open in case others hit it and we can get a better
> handle on how to reproduce it.

I agree disabling MPROTECT is not optimal.

If anyone needs something more to find a proper solution do not hesitate to ask.
Comment 10 Devan Franchini (RETIRED) gentoo-dev 2014-10-17 21:40:55 UTC
Hello Tom,

Do you run into this issue with an old version of layman? Can you test it out for me on 2.1.0-r3?

If it doesn't work then can you test out 2.0.0-r3?

I wanna trace down where it's breaking.

Thank you for the cooperation. :)
Comment 11 Karl-Johan Karlsson 2014-10-18 13:36:17 UTC
I had this same problem on some, but not all, of my hardened machines after upgrading to Layman 2.2.0*. Looking at the similarities and differences between the machines, the only thing sticking out was that the ones where it worked had CONFIG_PAX_MPROTECT_COMPAT=y and the ones where Layman segfaulted had CONFIG_PAX_MPROTECT_COMPAT unset. Sure enough, setting CONFIG_PAX_MPROTECT_COMPAT=y fixed the problem.

The Python binary has the same PaX flags everywhere:

/usr/bin/python2.7:
        PT_PAX    : -E---
        XATTR_PAX : not found
Comment 12 Anthony Basile gentoo-dev 2014-10-18 15:28:21 UTC
(In reply to Karl-Johan Karlsson from comment #11)
> I had this same problem on some, but not all, of my hardened machines after
> upgrading to Layman 2.2.0*. Looking at the similarities and differences
> between the machines, the only thing sticking out was that the ones where it
> worked had CONFIG_PAX_MPROTECT_COMPAT=y and the ones where Layman segfaulted
> had CONFIG_PAX_MPROTECT_COMPAT unset. Sure enough, setting
> CONFIG_PAX_MPROTECT_COMPAT=y fixed the problem.
> 
> The Python binary has the same PaX flags everywhere:
> 
> /usr/bin/python2.7:
>         PT_PAX    : -E---
>         XATTR_PAX : not found

Aha! Mystery solve (probably).  It looks like python has got some *other* rwx mmapping that isn't caught my our emutramp patch.  PAX_MPROTECT_COMPAT allows a one time transition between writeable mmap-ing to an executable one to alow JIT.  Its a weaker form of mprotect.  I suspect something in the new version of layman is triggering some new python rwx mmap-ings.

I'll try to reproduce this in a vm, but it would help if someone gave me an strace -f of a failure.
Comment 13 PaX Team 2014-10-18 17:07:17 UTC
great, this is a new breakage, this time in dev-python/cffi. what happens is that python eventually ends up in this call tree:

(gdb) bt
#0  ffi_prep_closure_loc (closure=closure@entry=0x0, cif=0x32f14371350, fun=fun@entry=0x32f1546abb0 <invoke_callback>,user_data=user_data@entry=0x32f153e3a00, codeloc=codeloc@entry=0x0) at /var/tmp/portage/dev-libs/libffi-3.1-r3/work/libffi-3.1/src/x86/ffi64.c:555
#1  0x0000032f15bbfb68 in ffi_prep_closure (closure=closure@entry=0x0, cif=<optimized out>, fun=fun@entry=0x32f1546abb0 <invoke_callback>,user_data=user_data@entry=0x32f153e3a00) at /var/tmp/portage/dev-libs/libffi-3.1-r3/work/libffi-3.1/src/prep_cif.c:242
#2  0x0000032f1546afaf in b_callback (self=<optimized out>, args=<optimized out>) at c/_cffi_backend.c:4581
#3  0x0000032f1a5410fa in call_function (oparg=<optimized out>, pp_stack=0x3a5bdb97ed0) at /var/tmp/portage/dev-lang/python-2.7.8/work/Python-2.7.8/Python/ceval.c:4033

c/_cffi_backend.c:b_callback() in cffi does this:

4564     closure = cffi_closure_alloc();

which in turn ends up in c/malloc_closure.h:cffi_closure_alloc():

116     if (!free_list) 
117         more_core();

where more_core() does the dreaded mmap(PROT_WRITE|PROT_EXEC) which of course fails under MPROTECT. in other words, cffi provides its own allocator for the ffi trampoline therefore the current fix in src/closures.c:dlmmap() in libffi has to be ported to cffi's more_core as well now (or perhaps cffi can be told to not manage libffi's allocations).
Comment 14 Anthony Basile gentoo-dev 2014-10-19 00:11:57 UTC
(In reply to PaX Team from comment #13)
> great, this is a new breakage, this time in dev-python/cffi. what happens is
> that python eventually ends up in this call tree:
> 

pipacs, I having troubles seeing how layman-2.2.0-r3 is pulling in cffi.

Anyhow, the issue with c/malloc_closure.h is obvious.  But we might be able to avoid backporting the fix because the MS_WIN32 code immediately above the rwx mmap-ing does call libffi's VirtualAlloc() rather than doing the allocation itself.  Maybe we can scavange that code.
Comment 15 PaX Team 2014-10-19 00:34:54 UTC
(In reply to Anthony Basile from comment #14)
> (In reply to PaX Team from comment #13)
> > great, this is a new breakage, this time in dev-python/cffi. what happens is
> > that python eventually ends up in this call tree:
> > 
> 
> pipacs, I having troubles seeing how layman-2.2.0-r3 is pulling in cffi.

i don't know how all these python dependencies work but the gdb backtrace is clear on how python ends up in cffi... it may not be a layman specific problem at all, but that of python in general?

> Anyhow, the issue with c/malloc_closure.h is obvious.  But we might be able
> to avoid backporting the fix because the MS_WIN32 code immediately above the
> rwx mmap-ing does call libffi's VirtualAlloc() rather than doing the
> allocation itself.  Maybe we can scavange that code.

VirtualAlloc is a win32 api, not that of libffi.
Comment 16 Anthony Basile gentoo-dev 2014-10-21 14:15:27 UTC
(In reply to PaX Team from comment #15)
> 
> VirtualAlloc is a win32 api, not that of libffi.

Yes a closer grep of the libffi code shows that its consuming VirtualAlloc (if MS_WIN32), not providing it.
Comment 17 Devan Franchini (RETIRED) gentoo-dev 2014-10-21 17:20:06 UTC
Tomasz, does this issue happen whenever you try to run layman at all? Regardless of command? Can you try this for me?: "layman -n -S" please let me know how that works out for you.
Comment 18 Magnus Granberg gentoo-dev 2014-12-31 15:21:16 UTC
It affect ganeti 2.11 to.
dev-python/pyopenss-0.14 -> dev-python/cryptography -> dev-python/cffi
Comment 19 Magnus Granberg gentoo-dev 2014-12-31 17:56:09 UTC
Created attachment 392796 [details, diff]
temp fix PROC_EXEC -> ~PROC_EXEC

This change PROC_EXEC to ~PROC_EXEC
It make the testsuit pass.
The real fix is to add check with is_emutramp_enabled() from libffi
to use PROC_EXEC or ~PROC_EXEC.
Comment 20 Anthony Basile gentoo-dev 2015-01-01 13:12:39 UTC
(In reply to Magnus Granberg from comment #19)
> Created attachment 392796 [details, diff] [details, diff]
> temp fix PROC_EXEC -> ~PROC_EXEC
> 
> This change PROC_EXEC to ~PROC_EXEC
> It make the testsuit pass.
> The real fix is to add check with is_emutramp_enabled() from libffi
> to use PROC_EXEC or ~PROC_EXEC.

That will make it build but not work.  You need rwx mmaps for foreign functions.  While I don't know the implementation details, I can only imagine it being similar to jit.  Something like this:

#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

int main() {
    const char *code =
        "\x6a\x3b\x58\x99\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x52\x53"
        "\x54\x5f\x52\x57\x54\x5e\x0f\x05"; 
    size_t *m = mmap( NULL, 1024, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 );
    if( m == MAP_FAILED )
        exit(EXIT_FAILURE);
    strcpy((char *)m, code);
    ((void(*)())m)() ;
    exit(EXIT_SUCCESS);
}


where the code is some random shell code I got somewhere online.  You should get a shell when it runs.  If you change PROT_EXEC to ~PROT_EXEC it will fail.  Of course mark sure to `paxctl-ng -vm` otherwise you'll get the failure from the hardened kernel and not from the PROT_EXEC -> ~PROT_EXEC change.
Comment 21 Magnus Granberg gentoo-dev 2015-01-02 12:37:15 UTC
Created attachment 392950 [details, diff]
Add check for Emutramp

We use same check as in libffi
Comment 22 Anthony Basile gentoo-dev 2015-01-02 13:35:59 UTC
(In reply to Magnus Granberg from comment #21)
> Created attachment 392950 [details, diff] [details, diff]
> Add check for Emutramp
> 
> We use same check as in libffi

Ignore my previous comment, since I forgot that this is taken care of by emutramp.
Comment 23 PaX Team 2015-01-02 13:44:29 UTC
drop the ~PROT_EXEC, it does the wrong thing since it sets all the other bits which is most definitely not what you want ;).
Comment 24 Magnus Granberg gentoo-dev 2015-01-03 12:53:59 UTC
Created attachment 393062 [details, diff]
Check Emutramp

Updated patch with PROC_EXEC removed.
Comment 25 Magnus Granberg gentoo-dev 2015-02-26 19:44:06 UTC
Bug is fixed upstream
Comment 26 Magnus Granberg gentoo-dev 2015-02-28 21:02:49 UTC
*** Bug 541706 has been marked as a duplicate of this bug. ***
Comment 27 Magnus Granberg gentoo-dev 2015-02-28 23:53:14 UTC
Fixed in cffi-0.8.6-r1 in cvs
Comment 28 Magnus Granberg gentoo-dev 2015-05-02 17:35:28 UTC
*** Bug 548098 has been marked as a duplicate of this bug. ***