A Developer’s Guide to Secure Coding with FORTIFY_SOURCE
Tags: security, developer, cpp, developers
Published: 2023-07-04 08:48:34

<p>Secure coding is essential to building robust and resilient software that is less susceptible to exploitation by attackers. One way to ensure secure coding is to use a feature called FORTIFY_SOURCE. In this article, we will explore FORTIFY_SOURCE and how it can be used to enhance the security of your code.</p>

<h2>What is FORTIFY_SOURCE?</h2>

<p>FORTIFY_SOURCE is a feature available in the GNU C Library that provides runtime protection against certain types of security vulnerabilities. Specifically, FORTIFY_SOURCE detects and prevents buffer overflow and formats string vulnerabilities, which are two common types of vulnerabilities that attackers can exploit to take control of a system or steal sensitive data.</p>

<h2>How does FORTIFY_SOURCE work?</h2>

<p>FORTIFY_SOURCE works by providing enhanced versions of certain C library functions that can detect when a buffer overflow or format string vulnerability is about to occur. When a vulnerable function is called, FORTIFY_SOURCE checks the size of the buffer being used and ensures that it is not being overrun. If an overflow or vulnerability is detected, FORTIFY_SOURCE immediately terminates the program to prevent further damage.</p>

<p>For example, consider the following code snippet:</p>

<pre>
<code class="language-cpp">char buffer[8]; 

strcpy(buffer, "hello world");</code></pre>

<p>In this code, the <code>strcpy</code> function is used to copy the "hello world" string into the <code>buffer</code> variable. However, the <code>buffer</code> variable is only allocated eight bytes of memory, which is not enough to hold the entire string. This results in a buffer overflow vulnerability that can be exploited by attackers.</p>

<p>If FORTIFY_SOURCE is enabled, the <code>strcpy</code> function is replaced by a secure version that checks the size of the buffer and prevents an overflow from occurring. In this case, the program would terminate before the vulnerability could be exploited.</p>

<p>Consider another code snippet:</p>

<pre>
<code class="language-cpp">char password[16];

scanf("%s", password);</code></pre>

<p>In this code, the <code>scanf</code> function is used to read input from the user and store it in the <code>password</code> variable. However, the <code>scanf</code> function does not perform any bounds checking, which means that if the user enters more than 16 characters, a buffer overflow vulnerability could occur.</p>

<p>To mitigate this vulnerability using FORTIFY_SOURCE, you can use the secure version of <code>scanf</code>, called <code>scanf_s</code>, which checks the size of the buffer and prevents an overflow from occurring. Here's how the code would look using <code>scanf_s</code>:</p>

<pre>
<code class="language-cpp">char password[16];

scanf_s("%15s", password, sizeof(password));</code></pre>

<p>In this code, <code>scanf_s</code> takes an additional parameter that specifies the maximum number of characters that can be read from the user. In this case, we set the maximum length to 15, which leaves one byte for the null terminator that is added to the end of the string.</p>

<p>By using <code>scanf_s</code> instead of <code>scanf</code>, we can prevent buffer overflow vulnerabilities in our code.</p>

<h2>How to use FORTIFY_SOURCE</h2>

<p>To use FORTIFY_SOURCE in your code, you must first ensure that it is enabled in your development environment. FORTIFY_SOURCE is <span><span><span><span><span><span>typically enabled or disabled by invoking </span></span></span></span></span></span><span><span><span><span><span><span>D_FORTIFY_SOURCE </span></span></span></span></span></span><span><span><span><span><span><span>compiler flags. I</span></span></span></span></span></span><span><span><span><span><span><span>t is enabled by default in rpm macros and used when building all packages, but other uses of GCC need to explicitly enable it.</span></span></span></span></span></span></p>

<p>Follow these steps:</p>

<ol>
	<li>
	<p>Compile your code using a compiler that supports FORTIFY_SOURCE. Most modern C compilers, such as GCC and Clang, support this feature.</p>
	</li>
	<li>
	<p>Enable FORTIFY_SOURCE using the appropriate compiler flag. The flag may vary depending on your compiler and version, but for GCC, you can use the <code>-D_FORTIFY_SOURCE=2</code> flag to enable the feature.</p>
	</li>
	<li>
	<p>Compile your code with the appropriate optimization level. FORTIFY_SOURCE is most effective when the code is compiled with optimization enabled, so be sure to use at least <code>-O1</code> optimization level.</p>
	</li>
</ol>

<p>Here's an example command to compile your code with FORTIFY_SOURCE enabled using GCC:</p>

<pre>
<code class="language-bash">gcc -D_FORTIFY_SOURCE=2 -O1 -o myprogram myprogram.c</code></pre>

<p>Once your code is compiled with FORTIFY_SOURCE enabled, the enhanced secure library functions provided by the feature will automatically replace the standard C library functions, such as <code>strcpy</code>, <code>scanf</code>, and <code>printf</code>. This means that any calls to these functions in your code will be automatically replaced with the secure versions provided by FORTIFY_SOURCE.</p>

<p>Once FORTIFY_SOURCE is enabled, you can use the enhanced secure library functions provided by the feature instead of the standard C library functions. For example, you can use <code>strncpy_s</code> instead of <code>strcpy</code> to safely copy a string into a buffer.</p>

<p>It is important to note that FORTIFY_SOURCE does not provide complete protection against all types of security vulnerabilities. It only protects against buffer overflow and format string vulnerabilities. Therefore, it is important to use other secure coding practices in conjunction with FORTIFY_SOURCE to ensure that your code is as secure as possible.</p>

<h2>Advanced usage of FORTIFY_SOURCE</h2>

<p>When using FORTIFY_SOURCE, you can specify a level of protection between 0 and 3. The higher the level, the more security features are enabled. The default level is 1.</p>

<p>FORTIFY_SOURCE=3 provides the highest level of protection and includes all the security features of levels 1 and 2, plus additional checks for potentially dangerous constructs in the code. These additional checks are designed to detect a wider range of security issues, including:</p>

<ul>
	<li>
	<p>Dangerous use of memcpy and memmove functions.</p>
	</li>
	<li>
	<p>Dangerous use of snprintf, vsnprintf, and similar functions.</p>
	</li>
	<li>
	<p>Dangerous use of string manipulation functions like strtok, strncat, and strpbrk.</p>
	</li>
</ul>

<p>However, it's important to note that enabling FORTIFY_SOURCE=3 may have some performance implications, as it adds additional code to perform the security checks. <span><span><span><span><span><span>Therefore, it might be desirable to use a lower level of protection in performance critical code but the programmer must be mindful of additional security risks.</span></span></span></span></span></span></p>

<p>To enable FORTIFY_SOURCE=3, you can use the <code>-O2</code> optimization level in addition to the <code>-D_FORTIFY_SOURCE=3</code> flag when compiling your code with GCC. Here's an example command to enable FORTIFY_SOURCE=3:</p>

<pre>
<code class="language-bash">gcc -D_FORTIFY_SOURCE=3 -O2 -o myprogram myprogram.c</code></pre>

<p>FORTIFY_SOURCE=3 provides the highest level of protection against security issues in C and C++ programs, but it may come at a performance cost. Therefore, it's important to carefully consider the level of protection you need and balance it with the performance requirements of your application.</p>

<h3>How to check FORTIFY_SOURCE</h3>

<p>In this example, we're copying a string that is longer than the size of the <code>buf</code> array, which can lead to a buffer overflow if FORTIFY_SOURCE is not enabled.</p>

<pre>
<code class="language-cpp">#include &lt;stdio.h&gt;

#include &lt;string.h&gt;



int main() {

    char buf[10];

    strcpy(buf, "1234567890");

    printf("%s\n", buf);

    return 0;

}</code></pre>

<p>To enable FORTIFY_SOURCE, we can compile the code with the <code>-O2</code> optimization flag and the <code>-D_FORTIFY_SOURCE=2</code> preprocessor flag:</p>

<pre>
<code class="language-bash">gcc -O2 -D_FORTIFY_SOURCE=2 -o test test.c</code></pre>

<p>Now when we run the program, we should see a runtime error indicating that a buffer overflow has occurred.</p>

<pre>
<code class="language-bash">*** buffer overflow detected ***: ./test terminated

Aborted (core dumped)</code></pre>

<p>Also, we can use <code>objdump</code> to examine the compiled binary and look for references to FORTIFY_SOURCE. We can use the following command:</p>

<pre>
<code class="language-bash">objdump -R test</code></pre>

<p>This will show us the dynamic relocations table for our binary, which includes information about any shared libraries or symbols used by the program.</p>

<p>If FORTIFY_SOURCE is enabled, we should see a reference to the symbol <code>__strcpy_chk</code> in the relocations table, which is a fortified version of the <code>strcpy</code> function that performs runtime checks for buffer overflows.</p>

<p>Here's an example of what the output might look like if FORTIFY_SOURCE is enabled:</p>

<pre>
<code class="language-bash">test:     file format elf64-x86-64



DYNAMIC RELOCATION RECORDS

...

0000000000601018 R_X86_64_JUMP_SLOT  __strcpy_chk@GLIBC_2.3.4

...</code></pre>

<p>This indicates that our program is using the fortified <code>__strcpy_chk</code> function, which is provided by the GNU C library and performs runtime checks to prevent buffer overflows.</p>

<p>Examining the dynamic relocation table of our compiled binary with <code>objdump</code>, we can check if FORTIFY_SOURCE is enabled and ensure that our code is properly secured against common security issues.</p>

<p>We can also use&nbsp;<code>checksec</code> tool which&nbsp;primarily used for assessing the security features and hardening options of an executable or shared object file. While it is not specifically designed to check for the presence of FORTIFY_SOURCE in a binary, it can provide valuable information about the overall security posture of the program.</p>

<p>When using <code>checksec</code> to assess a binary that has been compiled with FORTIFY_SOURCE, it can indicate the presence of certain security features that are commonly enabled by FORTIFY_SOURCE, such as stack canaries or enhanced buffer overflow protections. <code>checksec</code> can also detect other security mitigations that may have been enabled during compilation, such as Address Space Layout Randomization (ASLR) or Data Execution Prevention (DEP).</p>

<p>Lets see the output of running&nbsp;checksec against our test program:</p>

<pre>
<code class="language-yaml">$ checksec --file=./test

RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   test
</code></pre>

<p>In the output, we can observe the following several security features being reported.</p>

<ul>
	<li><strong>Full RELRO</strong> indicates that all relocations have been resolved at load time, providing protection against certain types of attacks like GOT (Global Offset Table) overwrite attacks.</li>
	<li><strong>Canary found</strong>: The presence of a stack canary indicates the usage of a security mechanism designed to detect stack-based buffer overflows. FORTIFY_SOURCE often enables stack canaries to protect against these types of vulnerabilities.</li>
	<li><strong>NX enabled</strong>: NX (Non-Executable) marking prevents the execution of code in memory regions that are intended for data. This feature enhances security by preventing the execution of injected or malicious code.</li>
	<li><strong>PIE enabled</strong>: Position Independent Executable (PIE) makes the binary's base address random, providing Address Space Layout Randomization (ASLR) to thwart memory-based attacks. Fortify Source can be used in combination with PIE to strengthen the overall security of the executable.</li>
</ul>

<p>While this output doesn't explicitly state the usage of FORTIFY_SOURCE, the presence of stack canaries and other security features suggests that the binary may have been compiled with FORTIFY_SOURCE or similar security-enhancing techniques. To confirm the usage of FORTIFY_SOURCE, it's best to refer to the build configuration or examine the compiler flags and options used during the compilation process.</p>

<h2>FORTIFY_SOURCE improves code security</h2>

<p>FORTIFY_SOURCE is a valuable feature that can enhance the security of your code by providing runtime protection against buffer overflow and format string vulnerabilities. By enabling FORTIFY_SOURCE in your development environment and using secure library functions, you can reduce the risk of security vulnerabilities in your code. Remember that FORTIFY_SOURCE is just one tool in your toolbox and should be used in conjunction with other secure coding practices to ensure the security of your code.</p>

<p>For more information, refer to these articles:</p>

<ul>
	<li><a href="https://www.redhat.com/en/blog/security-technologies-fortifysource" target="_blank">Security Technologies: FORTIFY_SOURCE</a></li>
	<li><a href="https://www.redhat.com/en/blog/enhance-application-security-fortifysource" target="_blank">Enhance application security with FORTIFY_SOURCE</a></li>
	<li><a href="https://developers.redhat.com/blog/2021/04/16/broadening-compiler-checks-for-buffer-overflows-in-_fortify_source" target="_blank">Broadening compiler checks for buffer overflows in FORTIFYSOURCE</a></li>
	<li><a href="https://www.redhat.com/en/blog/hardening-elf-binaries-using-relocation-read-only-relro"><span><span><span><span><span><span><span>Hardening ELF binaries using Relocation Read-Only (RELRO)</span></span></span></span></span></span></span></a></li>
</ul>