CyberDefenseMagazine

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 –Wextra –Werror –fstack-protector-strong -D_FORTIFY_SOURCE=2 -O2 -pie –fPIE –o 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