PIE and noexecstack for Linux builds

Hi, long time Renoise fan and recent customer (felt I had to give something back after using the demo for so many years) here, with a couple of technical queries about the Linux builds.

Firstly:

$ pspax -p $(pgrep renoise)
USER PID PAX MAPS ETYPE NAME CAPS ATTR 
root 4277 PemRs w|x ET_EXEC renoise = cap_ipc_lock,cap_sys_nice+ep staff_u:staff_r:renoise_t:s0

(no I’m not running Renoise as root! that’s just pspax being buggy)

Note the ET_EXEC – it really ought to be ET_DYN (i.e. built with fPIE) instead. Is there any reason why the 64-bit builds aren’t PIE? I can understand why 32-bit builds wouldn’t be, but the performance hit is so tiny on 64-bit that there’s no reason not to do it.

Secondly, is there a reason why Renoise needs W|X (i.e. allowing execmem in SELinux) since 3.0? Did you switch from Lua to Luajit? If not, what’s the cause? Lots of programs these days use these kinds of permissions for absolutely no benefit (e.g. JITs that are spun up even when though they’re not going to be used, hacky optimizations, badly written bindings for dynamic languages), and whenever I find something that needs them, I always wonder whether it’s actually necessary (the Python ctypes module is a famous one – it uses execmem every time you import it for some bug workaround, but the bug in question doesn’t even affect Linux, so distros have to patch those lines out in order to get it to play nicely with SELinux).

I guess most audio people would avoid SELinux anyway for performance reasons, but for anyone that does use Renoise with SELinux, the only options are to allow execmem globally (very very bad), use PaX to enforce it instead (good, but means using a non-vanilla kernel and it’s only user-friendly if you use Gentoo or Arch), or write a policy to allow it just for Renoise (which is what I do, but most people wouldn’t bother).

It would be a good idea to mention the requirement in the documentation at least, so that people aren’t tempted to just disable SELinux or allow execmem everywhere without understanding the consequences first.

Bumpity bump.

If I set /sys/fs/selinux/checkreqprot to 0 (which is now the default apparently – I’ve used the same kernel config for years with oldconfig so I didn’t notice the change), Renoise doesn’t launch at all even with a dedicated policy that allows execmem – it’s hitting a denial before it even transitions to renoise_t (meaning it’s failing to even load the executable let alone run it).

$ renoise
Segmentation fault
localhost kernel: [1282.027692] audit: type=1400 audit(1458929470.503:78): avc: denied { execmem } for pid=3425 comm="renoise" scontext=staff_u:staff_r:staff_t:s0 tcontext=staff_u:staff_r:staff_t:s0 tclass=process permissive=0
# setsebool allow_execmem 1
$ renoise
renoise: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory
localhost kernel: [1406.168162] audit: type=1400 audit(1458929594.634:80): avc: denied { execute } for pid=3465 comm="renoise" path="/etc/ld.so.cache" dev="dm-0" ino=278925 scontext=staff_u:staff_r:renoise_t:s0 tcontext=root:object_r:ld_so_cache_t:s0 tclass=file permissive=0
# selocal -a 'allow renoise_t ld_so_cache_t:file execute;' ; selocal --build ; selocal --load

…and then it works (albeit with a ton of SELinux denials like the ld_so_cache_t one, except for things like font files, meaning it’s running with READ_IMPLIES_EXEC).

I haven’t read the SELinux source to see what checkreqprot=0 actually does, but a quick search shows that it extends some permissions checks to cover mappings created while loading programs and those caused by READ_IMPLIES_EXEC, which breaks programs that have an executable stack, such as Renoise:

$ readelf -l $(which renoise) | grep -A1 GNU_STACK
 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
        0x0000000000000000 0x0000000000000000 RWE 8

(note the RWE, or read+write+execute)

You should consider adding ‘-Wa,–noexecstack’ to your ASFLAGS and ‘-Wl,-z,noexecstack’ to your LDFLAGS to fix it. This will remove the execmem requirement (if there’s no other reason for it), remove the implicit READ_IMPLIES_EXEC (fixing all of the execute denials on random files with checkreqprot=0), and generally make Renoise much more secure (as it stands now, it can’t take advantage of NX at all, which is pretty dangerous).

EDIT: I’ve tried modifying the GNU_STACK header manually (to match what the LDFLAGS fix would do), and now Renoise doesn’t need execmem at all. Everything seems to work so far, so I guess the ASFLAGS/LDFLAGS change above should be safe.

Very few things actually need an executable stack (e.g. some crusty old VMs/interpreters, and things that use GCC-specific crap like nested functions/trampolines), so most cases of executable stacks are false positives (hand-rolled assembly usually – assemblers are dumb and don’t create a GNU_STACK note unless you tell them to). I assume you rewrote some stuff in assembly (or refactored some inline assembly out into .S files) for 3.0?

Further reading if you’re interested:

https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart

https://wiki.ubuntu.com/SecurityTeam/Roadmap/ExecutableStacks

Sorry for the overly technical post and for not following the bug report guidelines (I originally just posted this as a suggestion).

One more bump for good measure.

This is a potentially serious security issue, as any stack buffer overflows (which are probably all over the place in Renoise, as it uses XML for everything and pretty much all common XML parsers are notorious for them) are turned into trivially exploitable code executions. All it takes is for a user to import a crafted song/instrument/device chain/theme/etc (or even just use paste in almost any area of Renoise that isn’t a text field, with a clipboard full of malicious XML), and their system is owned. I’d make a proof of concept, but I don’t have the time or skills.

All you have to do to fix this is add one linker flag (or add a note to all of your .S files). smile.png