Hacker News new | past | comments | ask | show | jobs | submit login
Warn HN: stacks made executable on GNU by mere presence of assembly code
149 points by kazinator on April 30, 2016 | hide | past | favorite | 25 comments
If you're developing some native executables (C, C++, whatever) on a GNU/Linux platform and possibly GNU/WhateverElse, and some of your object files are custom assembly language produced by hand (or not by a compiler whose developers are aware of this), and not inline assembly but in their own .S or .s file assembled to a separate .o, watch out!

If you do not have this blurb:

  .section .note.GNU-stack,"",@progbits
in your assembly code, and do not use a linker option to prevent it, your entire executable or shared library will be silently marked as requiring an executable stack (in all threads).

When such an executable is loaded, the stack is made executable. So are any new thread stacks after that.

If such a shared library is loaded into an application (even late, with dlopen, I think), a hook is called out of the linker into glibc, which will call mprotect() on all of the existing thread stacks to allow execution.

Oops!

(This is, of course, a monumentally stupid programmer trap. Write correct, good assembly code without a secret GNU handshake: unknowingly inflict added security risk on your users.)




The title is slightly misleading. Inline assembly code in asm statements has no effect on the stack.

If you have your own .S files, and those do not contain the magic handshake, then the stack will stay executable, and this is indeed a programmer trap.

The reason for this is that historically the stack was always executable, so it could not be make non-executable by default, it had to be opt in. In fact, gcc will generate code that requires the stack to be executable if you use a certain gcc extension (related to nested functions).

The other way to opt-in into non-executable stack is passing --noexecstack to as(1) (or when via gcc, use -Wa,--noexecstack)

This ought to be better documented, but it's not. The rationaly is probably that you should not be writing assembly code if you don't know this kind of detail? I can only guess about this.


I just checked and it's mentioned in the elf and ld manpages. It should probably be called out more, but there is at least some documentation for it.

from man elf:

.note.GNU-stack This section is used in Linux object files for declaring stack attributes. This section is of type SHT_PROGBITS. The only attribute used is SHF_EXECINSTR. This indicates to the GNU linker that the object file requires an executable stack.


The text you quoted doesn't inform anyone that if you don't have that section at all, an executable stack is also required. I can't find any such explanation in the elf man page.

Nobody is going to read "man elf" or "man ld", if they already know how to write assembly code and link it to their C, and whatever they know appears to be working fine.


People who know how to write assembly code and link it into their C programs are probably one of the most likely folks to read man elf and man ld, but I definitely agree the documentation should be better. Submit a patch?


I edited the title quite a few times to reduce verbiage, and did mention in the body that inline code isn't affected. Earlier revisions of the title had wording to the effect of assembly language modules, or source files, etc. I left it a little click-baity, but I figured this is likely of use to people doing inline assembly (what is inline today could bust out into its own module tomorrow), and the navigation says within HN anyway.


What is the "magic handshake"?

Also are the gcc extensions related to nested functions that will generate an executable stack documented? What would I search for in the documentation?


The magic handshake is:

.section .note.GNU-stack,"",@progbits


I've seen toolchains emit a warning for programs that require an executable stack; however, I can't seem to track down what toolchains and warning options produce that. In any case, I've filed https://sourceware.org/bugzilla/show_bug.cgi?id=20025 to get such a warning enabled by default.

While I don't think it's possible at this point to default to marking the stack non-executable without explicit information, it seems perfectly reasonable to emit a warning about that by default. That would cause far more developers to notice.


People ignore warnings.

The build should just fail if the result would have an executable stack, unless the final linking step includes an option to explicitly demand an executable stack.

The error message should mention how to get rid of the code that causes the stack to be executable. No mention should be made of how to continue on with an executable stack; people can read the man page if they really really want to have that.


Also, dlopen should fail on an object that requires an executable stack, unless some RTLD_WHATEVER flag is passed to allow it. There should also be some finer granularity than opening execute access in every single thread's stack.


Well, that's their mistake. Many of us do use -Wall -Werror though.


As discussed earlier today, but without the obvious conclusion called out: https://news.ycombinator.com/item?id=11601725

I'm surprised the stack is still left executable. They should have taken the 64-bit transition as an opportunity to change the defaults.


> earlier today

That's not earlier. It is a semantic, if not literal, dupe inspired by my submission (look at the item numbers).

My own submission was prompted by actually running into this, rather than reading about it on HN.


The discussion is older, so I stand by the wording. The post is dated earlier as well... a mystery. Perhaps this is some artefact of the ever-fickle HN voting process.


> marked as requiring an executable stack (in all threads)

Not a C/C++ developer here. What's the significance of this? Would it make buffer overflows easier to exploit?


Yes - a common way to exploit a stack overflow is to basically "write code" in the stack and then execute it. If the stack isn't executable, you can't execute the "code you wrote" in the stack.


> and do not use a linker option to prevent it

What's the linker option?


-z noexecstack


I think SafeSEH on Windows has a similar pitfall, though in this case it is a /SAFESEH switch in ML.


Only on x86 though.


Many distros build with CFLAGS that include -fstack-protector-strong. Is that enough?


are there any potential cons to switching the linker to default to adding stack protection? I'm trying to think of any legitimate use cases for an executable stack, and can't think of any. It should definitely be better documented, but I'm wondering if we could just change the default behavior and have done with it.


It does seem as if these mechanisms were put in place in a transitional time when non-executable stacks weren't completely "taken to heart", and it didn't seem like a big deal to default to what had until that time been the normal behavior of executable stacks, since the dawn of computing.


Executable stacks can be used for JIT compilation-and-execution.


gcc uses it for their nested functions extension to C.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: