In Defense of C/C++ (Part 2)

With great power comes great responsibility

​​​​​Spiderman

Welcome back to this second part of the series. In the first part, we looked into why there are recommendations for moving away from C/C++. In this part, we will look into how we can mitigate and remove some of the very real risks that C and C++ bring with them.

There are differences between C and C++, so I have divided up the recommendations into sections. First C, then C++.

Secure C Programming

Secure programming in C requires careful attention due to inherent vulnerabilities related to memory management, input validation, and system-level interactions. Below is comprehensive guidance for secure C programming:

1. Input Validation

Always validate inputs. Never trust user-supplied data.
Verify inputs against length and format constraints before processing.
Utilize whitelisting (allow-lists) rather than blacklisting (deny-lists).

2. Buffer Overflow Protection

Use safe functions instead of unsafe ones:
o Prefer fgets() over gets().
o Prefer snprintf() over sprintf().
o Prefer strncpy() (with proper null-termination) or safer alternatives such as strlcpy() if available.
Check boundaries explicitly when dealing with arrays and pointers.

Especially the point about using safe functions is important when writing new code, the later C standards come with functionality that can avoid the most egregious errors when manipulating buffers.

3. Memory Management

o Always pair memory allocations (malloc(), calloc()) with free() to avoid memory leaks.
o After free(), explicitly set pointers to NULL to prevent dangling pointers.
o Validate that memory allocation succeeds before use.

Example:

4. Use of Compiler and Security Tools

o Compile with warnings (-Wall, -Wextra) and treat warnings as errors (-Werror).
o Utilize compiler security flags:
o -fstack-protector-strong
o -D_FORTIFY_SOURCE=2
o -pie -fPIE (for Position Independent Executable)
o Enable ASLR (Address Space Layout Randomization)
o Run static analysis and security-focused tools (e.g., Clang Static Analyzer, Coverity, Valgrind).

Example using GCC:

gcc -Wall –WextraWerrorfstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -pie –fPIEo program program.c

Quick Security Checklist:

☑ Input validation

☑ Avoid buffer overflows

☑ Safe memory management

☑ Prevent integer overflows

☑ Avoid format-string vulnerabilities

☑ Secure temp files

☑ Check error conditions explicitly

☑ Compile securely

☑ Test with static/dynamic analysis tools

☑ Minimize privileges

Following this guidance significantly reduces vulnerabilities and ensures secure, robust, and reliable C programs, when writing new code in C. Unfortunately, there are millions of lines of code out there already written in older versions of C, without the benefits of later C standards.

Secure C++ Programming

Fortunately, we have more options for secure programming in C++, since this language is situated at a little higher level than C. Secure programming in C++ involves both language-specific best practices and general security measures. C++ provides features that can enhance security compared to C, but misuse can also introduce vulnerabilities. Here’s detailed guidance:

1. Input Validation

o Always verify and sanitize inputs.
o Use strong type checking and validation before processing data.
o Use regular expressions (std::regex) to validate input formats.

Example:

2. Avoiding Buffer Overflows

o Use std::string, std::vector, std::array, and iterators instead of raw arrays.
o Avoid C-style strings and unsafe functions (strcpy, sprintf).
o If low-level arrays must be used, prefer std::array for automatic bounds checking.

Unsafe:

Safe:

3. Safe Memory Management

o Prefer RAII (Resource Acquisition Is Initialization) by using smart pointers (std::unique_ptr, std::shared_ptr) instead of manual new and delete.
o Avoid memory leaks, dangling pointers, and double-free errors.

Smart pointers have been in C++ since C++ 11. I can hardly overstate the benefits for security regarding smart pointers, so I have included a few more examples of the benefits here.

Smart pointers in C++ provide automated and safer memory management, adhering to the RAII (Resource Acquisition Is Initialization) principle. Their benefits include:

1. Automatic Memory Management

o Automatically deallocate memory when the pointer goes out of scope.
o Eliminates the need for explicit delete, reducing the risk of memory leaks.

2. Resource Safety

o Prevent memory leaks, dangling pointers, and double-free issues.
o Improve reliability by ensuring resources are properly released, even when exceptions occur.

3. Exception Safety

o Guarantee resource cleanup even if exceptions are thrown.
o Enable strong exception safety guarantees in your code.

4. Clear Ownership Semantics

o Clearly convey the ownership model:
o std::unique_ptr: Exclusive ownership, non-copyable.
o std::shared_ptr: Shared ownership via reference counting.
o std::weak_ptr: Non-owning, observing pointer to avoid circular references.

5. Simplified Code

o Shorter, cleaner, and more readable code due to reduced boilerplate.
o Less manual memory management logic.

6. Reduced Cognitive Load

o Programmers spend less effort tracking manual memory allocation and deallocation.
o Allows focus on the logic and functionality rather than memory intricacies.

7. Interoperability with Standard Containers

o Work seamlessly with STL containers (std::vector, std::map, etc.), enhancing overall consistency.

Example without using smart pointers:

Example with smart pointers:

Quick Checklist for Secure C++ Programming:

☑ Always validate input

☑ Use RAII and smart pointers

☑ Favor safe STL container methods

☑ Use exceptions carefully and consistently

☑ Prevent integer overflows

☑ Prefer streams over C-style I/O

☑ Use secure random number generators

☑ Compile with security flags and warnings

☑ Employ static analysis regularly

☑ Practice least privilege

☑ Avoid undefined behavior

Outro

I hope that this part of the series has given you the knowledge that there are features within the later standards for C and C++, that can help us write more secure code. The latest standards for both C and C++ are C23 and C++23, both of which come with new features related to the security of using them.

Within the C and C++ communities, there is a recognition that C and C++ must become easier to use in a secure manner, if they are to remain relevant in the coming years. Recently there was an article in the registrar where Bjarne Stoustrup, the creator of C++  calls on the ISO committee responsible for the C++ standard to prioritize more defences within the language to defend against the many serious attacks we have seen in recent years. You can find that article here: C++ creator calls for action to address ‘serious attacks’ • The Register. In the third and last part of this series I will be looking into some of the tooling that can help us write secure code in C and C++.

The post In Defense of C/C++ (Part 2) first appeared on Cybersecurity Magazine.


Source link