Writing Secure Programs
If a program is receiving data on which it must operate, there should be some type of validation that the data is correct, does not exceed the maximum size and is free of non-valid types. For instance, if the data is limited to uppercase letters from A to Z, the function should reject anything else. It also should check to make sure the length of the data is valid. Many years ago, everyone thought of data as 80 characters, the size of a punch card. Today, data literally can be any size; it can be text or binary or encrypted. It still has some type of limit though. This should be checked, and if it fails, reject it.
Not only should you check for the maximum size of a record or piece of data but, in some cases, check for a minimum size. Strings should be checked for legal values or legal patterns. If the data being checked contains binary data that needs to be kept that way, it may be better to use the common escape character to signal that the data is binary. If the data is numeric, range checking should be done. If it is any positive integer, check if it is less than zero. If there is a maximum value, check for that. The file limits.h defines the maximum and minimum values for most values, so it is easy to check for system limits.
The dilemma most developers get into is the code already exists, and there is little time and manpower to spend checking for potential security issues. After all, the code is not broken, so why fix it? This attitude prevails in a lot of organizations. Once the code has been found susceptible, however, fixing it becomes a high priority, as does assigning blame.
What can be done to find potential problems short of code inspection? I have learned of three tools that are capable of finding potential problems and flagging them in a report. These tools could be used on an embedded system, but most development is done in a cross-hosted environment. Do the heavy work on the host workstation, and leave the fine-tuning to the target. The information on where to get the tools is listed in the Resources section.
Flawfinder, RATS and ITS4 are three packages that scan the source tree and display a report about potential problems. The display is a list of what is wrong, in which source module and at what line. All of this information also is weighted as to its degree of vulnerability. Listing 1 shows a snippet from a Flawfinder execution on the sample code. The severity level is from 0 to 5, with 0 being very little risk and 5 being high risk.
Listing 1. Flawfinder Example
Flawfinder version 1.21, (C) 2001-2002 David A. Wheeler. Number of dangerous functions in C/C++ ruleset: 127 Examining ../../example_code/msgqueue/mksem.c ../../example_code/msg_queue/msgtool.c:73  (buffer) strcpy: Does not check for buffer overflows when copying to destination. Consider using strncpy or strlcpy (warning, strncpy is easily misused). ../../example_code/msgqueue/mksem.c:34  (shell) system: This causes a new program to execute and is difficult to use safely. Try using a library call that implements the same functionality if available. ../../example_code/pipes/fifo/fifo_out.c:28  (race) access: This usually indicates a security flaw. If an attacker can change anything along the path between the call to access() and the file's actual use (e.g., by moving files), the attacker can exploit the race condition. Set up the correct permissions (e.g., using setuid()) and try to open the file directly. ../../example_code/process_control/proc_mem_info/proc_mem_info.c:139  (buffer) sscanf: The scanf() family's %s operation, without a limit specification, permits buffer overflows. Specify a limit to %s, or use a different input function. ../../example_code/msg_queue/sender/snd_thread.c:70  (random) srand: This function is not sufficiently random for security-related functions such as key and nonce creation. Use a more secure technique for acquiring random values. ../../example_code/dlopen/dltest.c:30  (misc) fopen: Check when opening files - can an attacker redirect it (via symlinks), force the opening of special file type (e.g., device files), move things around to create a race condition, control its ancestors, or change its contents? ../../example_code/msg_queue/receiver/rcvr.c:51  (buffer) char: Statically-sized arrays can be overflowed. Perform bounds checking, use functions that limit length, or ensure that the size is larger than the maximum possible length. ../../example_code/dlopen/another_dlopen_test/obj.c:15  (buffer) strlen: Does not handle strings that are not \0-terminated (it could cause a crash if unprotected). ... Number of hits = 139 Number of Lines Analyzed = 5491 in 2.67 seconds (2527 lines/second) Not every hit is necessarily a security vulnerability. There may be other security vulnerabilities; review your code!
Even though several messages are returned, the implementers can choose to fix or ignore the potential problems. Some developers might argue that these tools should change the code, but it is much better to change code selectively rather than to make wholesale edits un-aided. The Flawfinder program uses an internal database called a ruleset. This ruleset is a list of the common security flaws. These flaws are general issues that can have an impact on C/C++ and a number of specific problematic runtime functions.