Solaris Non-Executable Stack Overview
By gbrunett-Oracle on Jul 25, 2007
The ability to configure a Solaris system to run with non-executable stacks is not overly new. That functionality was originally introduced into the Solaris 2.6 operating system with the noexec_user_stack kernel parameter. Looking at the source code, this is how this parameter was documented (in usr/src/uts/common/vm/seg_vn.c):
207 /\* 208 \* Patching this variable to non-zero allows the system to run with 209 \* stacks marked as "not executable". It's a bit of a kludge, but is 210 \* provided as a tweakable for platforms that export those ABIs 211 \* (e.g. sparc V8) that have executable stacks enabled by default. 212 \* There are also some restrictions for platforms that don't actually 213 \* implement 'noexec' protections. 214 \* 215 \* Once enabled, the system is (therefore) unable to provide a fully 216 \* ABI-compliant execution environment, though practically speaking, 217 \* most everything works. The exceptions are generally some interpreters 218 \* and debuggers that create executable code on the stack and jump 219 \* into it (without explicitly mprotecting the address range to include 220 \* PROT_EXEC). 221 \* 222 \* One important class of applications that are disabled are those 223 \* that have been transformed into malicious agents using one of the 224 \* numerous "buffer overflow" attacks. See 4007890. 225 \*/
While non-executable stacks provide are a very useful technique for thwarting certain kinds of buffer overflow attacks, it should be noted that there exist other attack methods that do not rely on executable stacks. One such method was discussed back in 1999 on Bugtraq, but even in this case the author noted that there was inherent value in non-executable stacks (if only as an additional defense in depth layer):
Hopefully, these exploits demonstrate that it is important to make sure that programs that run at an elevated privilege are free of buffer overflow bugs. The stack protection will certainly help protect you from the majority of intruders, but moderately competent intruders will probably be able to bypass it.
Just as with minimization, hardening, and the deployment of services with reduced privilege, non-executable stacks are just another layer or tool to be used as part of a more comprehensive security architecture. But anyway, back to our story...
As with other kernel parameters, the non-executable stack state can be be adjusted (enabled or disabled) using the /etc/system file. For example, the following statement added to /etc/system would enable this feature:
As noted in the inline documentation above, experience has shown that "most everything works". In fact, the recommendation to enable this feature has been in Sun BluePrints since 1999 and similarly in the Solaris Security Toolkit since its inception. Looking even further, you find this common recommendation across the industry.
As a companion to this parameter, the noexec_user_stack_log parameter could be used to enable logging when this feature (if enabled) detected an attempt to run code from the stack. By default, this parameter is enabled if the noexec_user_stack parameter is enabled so no further action is required unless of course you want to prevent such logging. That has not stopped authors of tools and articles from recommending to enable it anyway using the command:
When this parameter is enabled and there is an attempt to execute code on the stack, a message such as the following will be generated and delivered via syslog to kern.notice:
Jul 25 14:48:02 quasar genunix: [ID 533030 kern.notice] NOTICE: myprog attempt to execute code on stack by uid 101
In this way, a system administrator can detect such attempts and take appropriate action.
Back in the days of Solaris 2.6, this parameter really only applied to the SPARC platform. Years passed and this feature continued to be available in Solaris 7, Solaris 8 and so on. As good fortune would have it, Intel and AMD got on board with the idea and the NX Bit was born. Technically speaking, Intel refers to its implementation as the XD Bit (for Execute Disable) while AMD has used the term NX (for No Execute), but for the purposes of Sun's implementation and this article, we will consistently use the term "NX" to refer to this functionality.
To find out if your system supports the NX bit, you can check in with the dmesg(1M) command:
$ dmesg | grep features Jun 28 11:00:05 sec1 unix: [ID 126719 kern.info] features: 1176fdf<cpuid,cmp,sse3,nx,asysc,sse2,sse,pat,cx8,pae,mca,mmx,cmov,pge,mtrr,msr,tsc,lgpg>
Similarly, if you have syslog configured to log kernel.info messages, you can also get the information from your system log files:
$ grep "features:" /var/adm/debug Jul 19 16:43:06 quasar unix: [ID 126719 kern.info] features: 1076fff<cpuid,sse3,nx,asysc,sse2,sse,pat,cx8,pae,mca,mmx,cmov,de,pge,mtrr,msr,tsc,lgpg>
The first example was taken from a SunFire X2100 system whereas the second example was taken from an Ultra 20. The same commands should be able to be used on other x86/x64 systems in order to determine if this CPU feature is available.
On the SPARC platform, the non-executable stack functionality is available but disabled by default (for SPARC V8) in order to support a fully ABI-compliant execution environment. For 64-bit SPARC platforms, however, the SPARC V9 ABI specifies a non-executable stack by default. Note that 32-bit applications running on a 64-bit kernel do not automatically get this protection by default and would rely on the noexec_user_stack parameter being set to 1 for example.
On NX-capable x86/x64 platforms, Solaris OS uses the NX bit by default whenever PROT_EXEC is not specified. Stack segments, however, use PROT_EXEC by default, so the NX functionality must be explicitly enabled on these platforms to provide stack protection. As noted above, this can be globally configured using the noexec_user_stack parameter just as with SPARC-based platforms.
From the product documentation, it should be noted that a system administrator can disable all use of the NX bit (non-SPARC platforms) by using the eeprom(1M) command to set enforce-prot-exec to off. This variable is provided as a transition workaround for any system with legacy applications that are missing PROT_EXEC.
In this article, we have taken a brief look at the history of non-executable stacks in Solaris dating back to the original integration of this functionality in Solaris 2.6 all the way to the present. In the next article, we will talk a little bit about how this functionality can be enabled on a per-file basis in the Solaris 10 OS.