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
(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.
#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.
Created attachment 386734 [details] Kernel configuration
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.
What use flags do you have on libffi?
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.
I think layman (or even python) ebuild should be corrected to mark binary so it will work on hardened systems...
(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.
(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.
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. :)
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
(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.
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).
(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.
(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.
(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.
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.
It affect ganeti 2.11 to. dev-python/pyopenss-0.14 -> dev-python/cryptography -> dev-python/cffi
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.
(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.
Created attachment 392950 [details, diff] Add check for Emutramp We use same check as in libffi
(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.
drop the ~PROT_EXEC, it does the wrong thing since it sets all the other bits which is most definitely not what you want ;).
Created attachment 393062 [details, diff] Check Emutramp Updated patch with PROC_EXEC removed.
Bug is fixed upstream
*** Bug 541706 has been marked as a duplicate of this bug. ***
Fixed in cffi-0.8.6-r1 in cvs
*** Bug 548098 has been marked as a duplicate of this bug. ***