C Coding Guide

Source: Internet
Author: User
Tags posix sprintf stdin strcmp syslog
1. String Operation Security

1.1 ensure that all strings are null-terminated
The C language takes ' Terminator ' as a string, or null terminator.
The failure to use the null terminator correctly can result in a buffer overflow and other undefined behavior.
To avoid buffer overflows, some dangerous functions are often replaced with a relatively secure string manipulation function that restricts the number of characters
-strncpy () instead of strcpy ()
-Strncat () instead of strcat ()
-snprintf () instead of sprintf ()
-Fgets () instead of gets ()

These functions truncate strings that exceed the specified limit, but note that they do not guarantee that the destination string always ends in null.

Error free, a null terminator is not followed by
Char a[16];
strncpy (A, "0123456789abcdef", sizeof (a));

After the code above calls strncpy (), there is no null terminator in the string of a.

Correct writing: Truncate the string to ensure that the string is null-terminated
char a[16];
strncpy (A, "0123456789abcdef", sizeof (a)-1);
A[sizeof (a)-1] = ';

1.2 do not write ambiguous strings to a fixed-length array
A string with ambiguous bounds (for example, a string from gets (), getenv (), scanf ()) may be longer than the length of the target array, and a direct copy into a fixed-length array can easily result in a buffer overflow.

Error Example:
char buff[256];
Char *editor = getenv ("editor");
if (editor!= NULL)
{
    strcpy (buff,editor);
}

Editor actual length may be greater than 256 causing overflow.

Correct writing: Calculates the actual length of the string, using malloc to allocate memory char *buff of the specified length
;
Char *editor = getenv ("editor");
if (editor!= NULL)
{
    buff = malloc (strlen (editor) +1);
    if (Buff!= NULL)
    {
        strcpy (buff,editor); 
    }
}
2. Integer security

The C99 standard defines integral operations for integral type elevation, integer conversion level, and normal arithmetic conversion. However, these operations actually pose a security risk.
2.1 Avoid integer overflow
An integer overflow occurs when a certificate is added to the maximum value, and an underflow occurs when the minimum value is reduced. Both signed and unsigned numbers can overflow.

The overflow and underflow int I of signed and unsigned integers
;
unsigned int J;
i = Int_max;  2147483647
i++;
printf ("i =%d\n", i); i = -214748348

j = Uint_max;//4294967295
j + +;
printf ("j =%u\n", j); 0

i = int_min;//-2147483648
i--;
printf ("i =%d\n", i);  i = 2147483647

j = 0;
j--;
printf ("j =%u\n", j); 4294967295

When the length is added and reduced, the length check is added

Length may be less than fsm_hdrlen
unsigned int length;
Length-= Fsm_hdrlen;  When length < Fsm_hdrlen, an underflow is found, returning a very large number

//correct writing: The length of the Fsm_hdr_len is determined by
{return
    vos_error ;
}
Length-= Fsm_hdrlen;

2.2 Avoid symbol errors
Sometimes converting from a signed integer to an unsigned integer will cause a symbolic error that does not lose data, but loses its original meaning.

A signed bit integer is converted to an unsigned integral type, and the highest bit (High-order bit) loses its function as a symbol bit. If the value of the signed integer is non-negative, the converted value is unchanged, and if the value of the signed integer is negative, the result of the conversion is a very large positive number.

//Error Example: symbol error bypassing length check #define BUF_SIZE int main (int argc, char* argv[]) {int length;
    Char Buf[buf_size];
    if (argc!= 3) {return-1; Length = Atoi (argv[1]); If Atoi returns a negative number if (length < buf_size)//length is a negative number, the length check is invalid {//the signed length is converted to the size_t type unsigned integer and the negative value is interpreted as a polar
        A large positive integer.  
        A BUF buffer overflow memcpy (buf, argv[2], length) is raised when memcpy () is invoked; 
    printf ("Data copied\n");
    else {printf ("Too many data\n"); return 0;} 
Correct writing 1: Declare length as unsigned integer
#define buf_size
int main (int argc, char* argv[])
{
    unsigned int length;
    Char buf[buf_size];
    if (argc!= 3)
    {
        return-1;
    }
    Length = Atoi (argv[1]); If the length returned by Atoi is not likely to be a negative number if
    (length < buf_size)   //length is not a negative number, the length check is valid
    {
        memcpy (BUF, argv[2], Len );  
        printf ("Data copied\n"); 
    }
    else
    {
        printf ("Too many data\n");
    }
    return 0;
}
Correct writing 2: A more effective range check for length
#define buf_size
int main (int argc, char* argv[])
{
    unsigned int length;
    Char buf[buf_size];
    if (argc!= 3)
    {
        return-1;
    }
    Length = Atoi (argv[1]); If the length returned by Atoi is a negative number if
    (length > 0 && length < buf_size)   //length is negative, the length check is valid
    {
        memcpy ( BUF, argv[2], length);  
        printf ("Data copied\n"); 
    }
    else
    {
        printf ("Too many data\n");
    }
    return 0;
}

2.3 Avoid truncation errors
Converts a larger integer to a smaller integer, and the original value of the number exceeds the representation range of the smaller type, a truncation error occurs, the lower of the original value is retained, and the high position is discarded. Truncation errors can cause data loss, and using truncated variables for memory operations is likely to cause problems.

Error Example: truncation error
int main (int argc, char* argv[])
{
    //total is unsigned short, strlen () size_t is unsigned long.< c4/>//If you enter a parameter length of 65500 and
    //Then total = (65500 + + 1)% 65536 = 1
    //strcpy () and strcat () function buffer overflow
    unsigned Short total = strlen (argv[1]) + strlen (argv[2]) + 1;
    char * buffer = (char*) malloc (tatal);
    strcpy (buffer, argv[1]);
    strcat (buffer, argv[2]);
    Free (buffer);
    return 0;
}
Correct writing: Declare a variable that involves a calculation as a unified type, and check the result
int main (int argc, char* argv[])
{size_t total  = strlen (argv[1 ] + strlen (argv[2]) + 1;
    if (Total < strlen (AR)) | | ())
    char * buffer = (char*) malloc (tatal);
    strcpy (buffer, argv[1]);
    strcat (buffer, argv[2]);
    Free (buffer);
    return 0;
}
3. Formatted output security

3.1 ensure format characters and parameters match
3.2 avoids the use of user input as part or all of a formatted string
When calling formatted I/O, do not directly or indirectly use user input as part or all of the formatted string.
An attacker has partial or complete control over a formatted string, with the following risks: Process crashes, viewing stack content, overwriting memory, or even executing malicious code.

Error code example:
char input[1000];
if (fgets (input, sizeof (input)-1, stdin) = = NULL)
{
    return-1
}
input[sizeof (Input)-1] = ';
printf (input);

The above code input comes directly from user input and is passed directly to printf () as a formatted character, and can trigger invalid pointers or unmapped address reads when the user enters "%s%s%s%s%s%s%s%s%s". Format character%s Displays the memory of the address specified by the corresponding parameter on the stack. Here input is treated as a formatted character without providing parameters, so printf () reads any memory location in the stack until the formatted character is exhausted or an invalid pointer or unmapped address is encountered.

Correct writing: Pass two parameters, the format of the string to set down
char input[1000];
if (fgets (input, sizeof (input)-1, stdin) = = NULL)
{
    return-1
}
input[sizeof (Input)-1] = '; c15/>printf ("%s\n", input);
Error example:
void Check_password (char *user, char *password)
{
    if (strcmp (password (user), password)!= 0)
    {
        char *msg = malloc (strlen (user) +);
        if (!msg)
        {
            return-1;
        }
        sprintf (MSG, "%s login incorrect", user);
        fprintf (stderr,msg);
        Syslog (Log_info, msg);
        Free (msg);
    //...
}

The preceding code checks to see if a given user name and password match, displays an error message when it does not match, and writes the error message to the log.
If user is "%s%s%s%s%s%s%s%s%s%s", after the assembly of the formatted function sprintf (), the string that msg points to is "%s%s%s%s%s%s%s%s%s%s login incorrect", Then the next fprintf () and syslog will have the problem of formatting characters.

Proper writing: formatting strings are determined by code, and user input without checking for filtering can only be used as a parameter.
void Check_password (char *user, char *password)
{
    if (strcmp password (user), password)!= 0)
    {
        char *msg = malloc (strlen (user) +);
        if (!msg)
        {
            return-1;
        }
        sprintf (MSG, "%s login incorrect", user);
        fprintf (STDERR, "%s", msg);
        Syslog (Log_info, "%s", msg);
        Free (msg);
    //...
}
4. File I/O security

4.1 Avoid using the strlen () function to compute the length of binary data
The strlen () function evaluates the length of a string, which returns the number of characters before the first null terminator in the string.
Therefore, it is important to use strlen () to handle file I/O functions when reading content, because the content may be binary or text.

Error Example:
char buf[buf_size];
if (buf,sizeof (BUF), fp) = = NULL)
{
    //handle error
}
Buf[strlen (BUF)-1] = ' fgets ';

The code above attempts to delete line breaks (\ n) from an input row, and if Null,strlen (BUF) returns 0 when the first character of the BUF is returned, the buf[0-1 operation crosses the bounds.

Correct writing: Do not use a string manipulation function that relies on a null terminator when you cannot determine which type to read from a file.
Char buf[buf_size];
char *p;
if (Fgets (buf,sizeof (KBUF), FP)
{
    p = strchr (buf, ' \ n ');
    if (p)
    {
        *p = ' i ';
    }
}
else 
{
    //handle error
}

4.2 uses the INT type variable to accept the return value of the character I/O function
The character I/O functions fgetc (), getc () and GetChar () read a character from one stream and return it as an int value.
If the stream reaches the end of the file or a read error occurs, the function returns EOF.
FPUTC (), PUTC (), Putchar () and ungetc () also return a character or EOF.

If the return value of these I/O functions needs to be compared with EOF, do not convert the return value to the char type.
Because Char is a signed 8-bit value, int is a 32-bit value. If the ASCII value of the character returned by GetChar () is 0xFF, the conversion to the char type will be interpreted as EOF. Because this value is performed by a signed extension of 0xFFFFFFFF (EOF) for comparison.

Error Example:
char buf[buf_size];
char ch;
int i = 0;
while (ch = getchar ())!= ' \ n ' && ch!= EOF)
{
    if (I < buf_size-1)
    {
        buf[i++] = ch;
    }
}
Correct: Use a variable of type int to accept the return value of GetChar ()
char buf[buf_size];
int ch;
int i = 0;
while ((ch = getchar ())!= ' \ n ') && ch!= EOF)
{
    if (I < buf_size-1)
    {
        buf[i++] = ch;
    }
}
Cuf[i] = ' the ';

If some machines sizeof (int) = = sizeof (char), the return value received with int may not be distinguishable from EOF, and the end of file and file errors are checked with feof () and ferror (). 5. Prevent command injection

The C99 function system () executes a specified program/command by calling a system-defined command parser (such as the CMD.exe of Unix shell,windows). Similarly, the POSIX function Popen ().
If the parameters of System () are made up of user input, a malicious user can alter the behavior of System () calls by constructing malicious input.

System (sprintf ("Any_exe%s", input);

If the user input: happy; Useradd attacker
The final shell will string "Any_exe happy;" Useradd Attcker "interpreted as two separate commands.

Proper notation: Use the POSIX function Execve () instead of system ()
void Secuexec (char *input)
{
    pid_t pid;
    Char *const args[] = {"", input,null};
    Char *const envs[] = {NULL};
    PID = fork ();
    if (PID = = 1)
    {
        puts ("fork error");
    }
    else if (PID = = 0)
    {
        if (Execve ("/usr/bin/any_exe", args, Envs) = = 1)
        {
            puts ("Error executing any_ EXE ");
        }
    return;
}

The Windows environment may not be very well supported for EXECVE (), and it is recommended that you use the CreateProcess () of the Win32 API instead of system ().

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.