Programming safe and secure code - generic
One should always write safe and secure code. Students should not get in the habit of writing insecure or buggy code. The following are minimum standards to follow.
General Guidelines
- User input: user input should be validated to make sure it is within the range expected by the program. When user input is not valid, the program should either ask again or notify the user of the bad input and exit gracefully.
- Edge cases: the program should properly handle edge cases. For example, if computing an average of grades, the program should be correct if there are 0 grades (see above - that may be considered bad input), 1 grade, or more.
- Files: when opening a file for reading, the program should make sure it was opened successfully (i.e., that the file exists). When opening a file for writing, the program should make sure it was opened successfully (i.e., that it was a valid file name that the program has permission to write to). On failure, the program should notify the user and either ask again for a valid file name or exit gracefully.
- Function return values: for any (built in) functions that are used, the program should check their return value to make sure they completed successfully. If a function fails, the program should handle this appropriately (what to do depends on the individual case). For example, if using a function to open and read a website url, the program should check to make sure the link was read successfully before using the result.
C Programs
The following are specific to writing C programs.
- Inputting C strings: never use a method that reads arbitrarily long strings because this can result in a buffer overflow. For example: don't ever use
gets
, and if usingscanf
then do not do something likescanf("%d", &s);
but instead do something likechar s[100]; scanf("%99s", s);
. Note that this also applies to reading from files or other devices. - C string size: always allocate enough space for the largest string you will need + 1 for the terminating NULL character. For example, the following is not ok:
char s[5]; strcpy(s, "hello");
becauses
should have been declared with size at least 6. - Array size: any time your code accesses an array, make sure the index is valid (not negative and not past the end of the array). If the array index is ultimately coming from the user, then there needs to be a check somewhere to make sure it is in range.
- Large arrays: any array that takes up more than 10k bytes or so should be created using dynamic memory (i.e.,
malloc
. Do not do something like this:char buffer[10000000];
because variables that are declared like this are in the "memory stack" which does not have enough space to hold large arrays (this can result in a "stack overflow"). - Malloc'ed memory: any memory that is from the "memory heap" (e.g., coming from using
malloc
,realloc
) needs to be free'ed before the program ends. Not free'ing malloc'ed memory is a memory leak which can result in poor performance or your program crashing. - Dereferencing pointers: always make sure that a pointer you are dereferencing is valid (.e.g, not NULL). Note that pointers are typically dereferenced with
*
,->
, or[ ]
, or by passing the pointer to a built in function.