Writing Secure Code in C, You should know

Writing secure code is very important. If you are c developer, then you should aware because in C there is no direct method to handle the exception (no inbuilt try and catch like another high-level language like C#). It is a responsibility of the developer to handle the all the exception manually. In this article, I will describe a few points that make your code cleaner and secure.

Understand the requirement first:

Before writing the code, it is very important to understand all the requirements. If you have clear visibility of the final product then it helps you to create test cases for the testing. It is very important to create proper test cases for the final product, it makes your product wonderful.

Create proper flow before writing a module:

Before writing the code it is a good idea to draw your thought that means create a flow diagram for each scenario. Believe me, it saves your many hours.

Previously I was working on a module where I needed to handle a lot of condition for the different scenario within a single call back function. What I had done a mistake that without creating the flow diagram I did the coding. Really it was a mistake and destroy my many hours in resolving the bugs. Finally, my all problem getting solved when I created the flow diagram.

So It is my advise to create the flow diagram before starting the coding and helps to make your code secure.

Initialize variables and pointer before use:

This is a good habit to initialize the variable and pointer at the time of declaration. It avoids the strange behavior during accessing these objects. It is also necessary to initialize these objects with defined state.

This rule does not only apply on predefined data type it is also applied to the user-defined data type (like structure). So you also have to make sure that your complex type functions, such as typedef structs, are initialized first.

Let’s take an example, suppose you have a complex type of structure of function pointers that are used in TCP/IP communication. So in that scenario at the time of object creation, you should initialize these function pointer and also need to take a flag that allows only single initialization.

Now at the time of object creation initialize the structure of function pointers.

Later on during construction of the object, you can check the flag for the initialization of function pointers, shown below

 

If you want to learn more about the c language, here 10 Free days C video course for you.

C tutorial

Don’t ignore compiler warnings:

Nowadays compilers are very smart if they find any strange constructs then they throw the warning. So don’t avoid these warning because it may be preventing you from the future bugs.

Check return values:

There is a lot of developers that they avoid the return value of the function.  It could be dangerous and might be the cause of the application crash. You should check the return value of each function, it helps you to detect the bugs easily prevent application crashing.

This rule does not only apply to the user created function, it also applies to the library function and standard library function. You should always check the return value and handle the scenario to displaying a proper error message.

Let’s see an example code,

In below code, everything is fine until the malloc function doesn’t return the null pointer. If malloc returns the NULL, the code will crash.

 

We can resolve the above problem to verify the return value of the malloc function. If malloc returns the null pointer, the code will display an error message and terminate the execution.

 

Use enums as error types:

You should categorize the errors using the enum for each module. An enum is far better than the macro or numeric value. This categorization of error for each module helps you to find the error at the time of debugging. This technique also helps the other developer who is assigned later on this module.

In below example, I have created the list of some errors related to the file operation using the enum. The benefits to adding the last enum are that it provides the total number of enum entries.

 

Check input values:

If your module expects input value from another module then don’t believe on incoming data. It is your responsibility to verify the incoming data, either might be you dereference the invalid pointer or access the array beyond its boundary that can be a cause of crashing or undefined behavior. This type of issue can be waste your many hours.

Let see an example,

Suppose you have a lookup table that contains the message for different scenarios and you need to create the module that used to displays the messages. To avoid any crash or undefined behavior you should check the incoming index for a lookup table. In this scenario enum is a good choice, you can map the enum element with lookup table message.

Use string safe function:

Buffer overflow is a critical problem, it is also a point of entry for the hackers and attacker. If you are working on the POS application, then you should learn to how to play with string. There is a lot of string function in C but in which some functions are not secured, so you should be careful before working on string functions.

Let see an example,

A strcpy() is a well-known string function which is used to copy the data from the source to the destination buffer. This function has a lot of bugs, now c committee introduces the new safe version of string function strcpy_s in C11. So it is my advise that use only string safe functions.

Syntax of strcpy_s(),

errno_t strcpy_s(char * restrict dst,rsize_t max, const char * restrict src);

The max parameter is used by strcpy_s() to check that the src is not bigger than the dst buffer. If there is any problem occur then it returns the error code.

For more detail see, Microsoft Developer Network website

Code readability:

You should always think that you are not writing the code for you. If anybody read your code then they should have the clear visibility. It is a very good habit to write the readable code, your code should be like a book that can be understood by any person easily.

There are following points that make your code more readable

Braces:

You should always use a brace with conditional and branching statements like, if, else, switch, while, do while and for keywords. It will increase the readability of your code and reduce the risk of bugs.

For example,

Don’t do this,

if(flag)
amount = 50.0;

you should do,

if(flag)
{
amount = 50.0;
}

just like above also use braces for the loop even they have only single or empty statement.

you should do,

while (!flag)
{
// empty statement
}

Variable and function naming:

Don’t use i,j,k ..etc for the variable name. Use the proper name for the variable that explains the functionality. This rule is also applicable for function naming, you should write function name in the way that explains the functionality of the function.

Let see an example,

Suppose you require to create two integer variable to store the value of month and day.

Don’t do,

int i;
int j;

You should do,

int day;
int month;

Suppose you have require to create function to calculate sallary,

Don’t do,

int test()
{
//Calculate salary
return 0;
}

You should do,

int calculateSallary()
{
//Calculate salary
return 0;
}

 

comment:

Good comments increase the readability of the code. Evey module should have the good commenting, it helps the developer who comes on the project after you and it also help to maintain the code base.

One thing you should remember if you are commenting the code which has multiple lines then you should use preprocessors conditional compilation feature (for example, #if 0 … #endif), it increases the code clarity.

See the below example,

 

Don’t write complex code:

During the development, The code of a product will be changed and extended many times. You should not be thinking about the initial stage of development but you should think about all stage of development. At the time of coding you should remember one thing, there are a lot of people who will come on this project after you. So don’t write the code only for you, think about the other.

 

Use qualifiers properly:

You should know to how to use qualifiers (const. volatile, …etc) properly either you will face a lot of problems. In C, one of the most popular qualifiers are const and volatile, we can also use this qualifier together. See this article for more detail, Application of const and volatile together.

Below find some important places where you should use const:

  • In call by reference function argument, if you don’t want to change the actual value which has passed in function.
    Eg.
    int PrintData ( const char *pcMessage);
  • In some places, const is better then macro because const handle by the compiler and have a type checking.
    Eg.
    const int ciData = 100;
  • In the case of I/O and memory mapped register const is used with the volatile qualifier for efficient access.
    Eg.
    const volatile uint32_t *DEVICE_STATUS = (uint32_t *) 0x80102040;
  • When you don’t want to change the value of an initialized variable.

Below find some important places where you should use volatile:

  • Accessing the memory-mapped peripherals register or hardware status register.

 

  • Sharing the global variables or buffers between the multiple threads.
  • Accessing the global variables in an interrupt routine or signal handler.

 

Signed and Unsigned Integers:

Don’t mix signed and unsigned integer together. If we mixed signed and unsigned int in the program then it can create issues because as per the c standard if we perform the arithmetic operation on signed and unsigned number then the resultant value can be implementation dependent or undefined in some scenarios.

In C99, integer promotion is clearly defined that If an int can represent all values of the original type, the value is converted to an int, otherwise, it is converted to an unsigned int. All other types are unchanged by the integer promotions.

Note: My advice is that never mixed the signed and unsigned and always enable the warning option in your IDE.

See the below program and think the output of,

If you are familiar with integer promotion then, of course, you know the answer either you need to read the integer promotion. So it is my recommendation when you performed an arithmetic operation where the operands are signed and unsigned then carefully perform the operation either you will get the undefined result.

You can check this link,

Bit-wise Operators:

No doubt Bit-wise operators are a good choice but sometimes avoid to use of bit-wise operators. If you are working on negative integer numbers then it will be a good decision to avoid bitwise operators.

Let’s see the example code,

If you will compile the above code, then you will get an undefined output.

Fixed-width Data Types:

You should use fixed length data type (uint8_t,uint16_t …etc) in place of implementation defined (int,long, …etc). In C99, C committee introduce <stdint.h> that define fixed length data types.

Writing Secure Code in C

Expose only what is needed:

In C, like other objective languages, there is no option to hide the information. If you are working on C, then you already know that every global variable and function that have no static keyword have global scope.

The global function and variable have the global scope that might be possible they access by another module and this module can change their functionality and value accidentally.

So we should use the static keyword with function and variable that do not require outside the module in which they are declared.

Use code analyzer tool:

Every company has some coding guideline but still, you should analyze your code with the code analyzer. Nowadays there is a lot of code analyzer available, you can check below link to see the list of some code analyzer for C/C++.

Link for code analyzer

 

Get your complete list of C interview questions in eBook format with live instructor support and more. 

                                      Download Now | $9



Leave a Reply