These tips are not, I repeat, not intended to be a complete list of tips on writing secure code in C or C++.
These are things that, I believe, should be practiced by anyone who writes program in C or C++ no matter how low the security risk. I also believe that these same principles can be used in any programming languages.
Use Comments Wisely
I had a professor who once said "it's better to over-comment and make sure that everyone understands why your code is the way it is than to not put comment and annoy everyone that has to read your code."
Use comments to help "document" what your code is actually doing. Open-source software are notorious for not having any comments in their code. Many would argue that good code will explain what is happening, I agree to an extent.
The "why's" should be put as comment to explain why certain sections of code are the way they are.Obviously, over-commenting is just as bad since it clutters up the source code making things harder to read. Balance is needed to make comments useful.
Heed the warning messages
When a compiler gives a warning message it's for a good reason. Compiler warnings typically deal with your code's structure that could lead to potential problem with how your code is either executed or how data is presented to your code for processing.
If you are using a compiler that has an option to have all warnings on, you should do so. Make modifications to your code so that the warning message goes away; if at all possible don't simply do an explicit typecast (more on this in the next sections).
If for some reason, you have a compelling reason to leave your code in a way that the compiler gives a warning, make sure to put a comment in your source as to why the warning can be ignored.
Use the right data type for the data being handled
Don't use an int data type to store a char data; or use a float when you are only dealing with whole numbers; and use the right data type for the range of values that your variable will be holding.
Here's the deal: when you use a certain data type, that's memory being allocated for your application, and chances are this memory allocation is sandwiched between 2 code segments.
At the minimum this is extra memory that you are using needlessly and could affect either your application or the system's performance as a whole (think pagination). At the worst case, that could be an area that an attacker could write execution code in the stack that they can come back to at a later time.
Granted that most privilege escalations take advantage of being able to modify the code being executed because the application will write to memory locations past what it allocated; however, if you have a variable that uses up more space than the amount of data that you really care for, that's extra space for an attacker to use.
I think it's worth pointing out that the early computer viruses didn't target buffer overruns, they appended themselves to executables. The virus code executed along with the original executable; not like most of today's attack code where they left the executing application to crash when they're done.
Be wary of typecasts--especially implicit ones
Whenever you have to typecast from one data type to another you should check carefully what your typecasting and why. If you're using a method that either takes in or returns a certain data type, it would be best to find out why why they it returns that data type. We are going for ease of readability here.
Yes, I know that standard C functions like fgetc(), getc(), and getchar() returns int when they really only deal with character data. Well, there was a time in Windows when getc() can be used to read strings that are 4-characters long. It really irks me that C and C++ textbooks still provides examples where the return of these functions are stored in an int variable.
My point is, not just because the standards allows it--or even requires it--doesn't make it any less correct. I've seen this lead to all sort of confusion with people who first learn to program in the newer languages like Java or Python.
If for some reason that you are left with no recourse but to typecast, make sure to make it explicit. Not just because an int data can conveniently convert to a char data doesn't mean you should just use implicit typecasting.
Use the right data structure methods
Understand when to use a union, enum, or struct to define your data type, and know the implications of each. Figure out what the data structure will be used for. Enums work great for defining "one of these things" items and they can be used in switch-case statements and assigned unique values relative to members in the enum definition.
Unions work great for "can be a.." items; they amount of memory used is for the biggest "member". However, you have to be careful not to inadvertently use the wrong member and inadvertently typecast the data (compiler warnings will not help here at all).
Structures are probably the least confusing of them all. However, this bears repeating: make sure that all it's members actually belongs there. Don't simply create a struct type to save you from having to type a 20 parameters to your functions; to be honest there's probably something really wrong with your design.
Cross-posted from Home+Power