unsafe functions in C

Source: Internet
Author: User
Tags first string sprintf syslog

Most buffer overflow problems in C can be traced directly to the standard C library. The worst culprits are problematic string operations (strcpy, Strcat, sprintf, and get) that do not perform self-variable checks. In general, strict rules such as "Avoid using strcpy ()" and "Never use gets" are close to this requirement.

Today, the programs that are written still take advantage of these calls because no one has ever taught developers to avoid using them. Some people get a hint from everywhere, but even good developers get screwed up. They may use their own summary-written checks on the arguments of the dangerous function, or falsely infer that the potentially dangerous function is "safe" in some special cases.

The first public Enemy is the gets (). Never use gets (). The function reads a line of text entered by the user from the standard input, and it does not stop reading the text until it encounters an EOF character or a newline character. That is: gets () does not perform bounds checking at all. Therefore, using get () always has the potential to overflow any buffer. As an alternative, you can use Method Fgets (). It can do the same thing as gets (), but it accepts a size parameter that restricts the number of characters that are read, thus providing a way to prevent buffer overflow. For example, do not use the following code:

void Main ()  {  char buf[1024];  Gets (BUF);  }

Instead, use the following code:

#define BUFSIZE 1024void Main ()  {  char buf[bufsize];  Fgets (buf, BUFSIZE, stdin);  }

The main pitfalls in C programming

Some standard functions in C are likely to get you into trouble. But not all functions are bad to use. In general, using one of these functions requires any input to be passed to the function. This list includes:

    • strcpy ()
    • Strcat ()
    • sprintf ()
    • scanf ()
    • SSCANF ()
    • FSCANF ()
    • VFSCANF ()
    • vsprintf
    • VSCANF ()
    • VSSCANF ()
    • Streadd ()
    • strecpy ()
    • Strtrns ()

The bad news is that we recommend that if there is any possibility, avoid using these functions. The good news is that in most cases there is a reasonable alternative. We will examine each of them carefully, so we can see what constitutes their misuse and how to avoid it.

The strcpy () function copies the source string to a buffer. The exact number of characters to copy is not specified. The number of characters copied directly depends on the number in the source string. If the source string happens to come from user input and does not specifically limit its size, it is possible to get into big trouble!

If you know the size of the destination buffer, you can add an explicit check:

if (strlen (src) >= dst_size) {/  * do something appropriate, such as throw an error. */  }       else {  strcpy (d St, SRC);

The easier way to accomplish the same goal is to use the strncpy () library routines:

strncpy (DST, SRC, dst_size-1);  Dst[dst_size-1] = ' + '; /* Always does this to be safe! */

If SRC is larger than DST, the function does not throw an error; When the maximum size is reached, it simply stops copying characters. Notice that the above call-1 in strncpy (). If SRC is longer than DST, then that leaves us room to put a null character at the end of the DST array.

Of course, the possibility of using strcpy () does not pose any potential security problems, as seen in the following example:

strcpy (buf, "hello!");

Even though this operation caused an overflow of buf, it was just a few characters. Since we statically know what those characters are, and it is obvious that there is no harm, there is no need to worry here-unless, of course, the static memory where the string "Hello" is located can be overridden in other ways.

Another way to ensure that strcpy () does not overflow is to allocate space when it is needed, ensuring that enough space is allocated by calling strlen () on the source string. For example:

DST = (char *) malloc (strlen (SRC));  strcpy (DST, SRC);

The strcat () function is very similar to strcpy (), except that it can merge a string into the end of the buffer. It also has a similar, safer alternative to strncat (). If possible, use Strncat () instead of strcat ().

The Functions sprintf () and vsprintf () are common functions used to format text and buffer it. They can mimic the strcpy () behavior in a direct manner. In other words, using sprintf () and vsprintf () is as easy as using strcpy () to cause a buffer overflow to the program. For example, consider the following code:

void Main (int argc, char **argv)  {  char usage[1024];  sprintf (Usage, "Usage:%s-f flag [arg1]\n", argv[0]);  }

We often see code similar to the above. It does not seem to harm anything. It creates a string that knows how to invoke the program. That way, you can change the name of the binary, and the output of the program will automatically reflect that change. Even so, the code has serious problems. The file system tends to limit the name of any file to a specific number of characters. Well, you should think that if your buffer is large enough to handle the longest possible name, your program will be safe, right? Just change the 1024 to any number that suits our operating system, okay? But that's not the case. By writing our own small program to overturn what is said above, it is possible to overturn this restriction easily:

void Main ()  {  execl ("/path/to/above/program",   <<insert really long string here>>,   NULL );  }

The function execl () launches the program named in the first parameter. The second parameter is passed as argv[0] to the called program. How long can we make that string?

So how to solve the problem of {v}sprintf ()? Unfortunately, there are no fully portable methods. Some architectures provide the snprintf () method, which allows programmers to specify how many characters are copied from each source into the buffer. For example, if we have snprintf on our system, we can fix an example to be:

void Main (int argc, char **argv)  {  char usage[1024];  Char format_string = "USAGE:%s-f flag [arg1]\n";  snprintf (usage, format_string, argv[0],   1024-strlen (format_string) + 1);   }

Note that before the fourth variable, snprintf () is the same as sprintf (). The fourth variable specifies the maximum number of characters from the third variable that should be copied to the buffer. Note that 1024 is the number of errors! We must ensure that the total length of the string to be copied to the buffer does not exceed the size of the buffer. Therefore, you must consider a null character, plus these characters in all format strings, minus the format specifier%s. The number result is 1000, but the above code is more maintainable because it does not go wrong if the format string is accidentally changed.

Many, but not all, versions of {v}sprintf () have a more secure approach that uses both functions. You can specify the precision of each argument of the format string itself. For example, another way to fix sprintf () with the problem above is to:

void Main (int argc, char **argv)  {  char usage[1024];  sprintf (Usage, "Usage:%.1000s-f flag [arg1]\n", argv[0]);   }

Note that the percent semicolon is followed by the s before the. 1000. This syntax indicates that no more than 1000 characters are copied from the relevant variable (in this case, argv[0]).

If either solution does not work on a system that your program must run, the best solution is to put the working version of snprintf () in a package with your code. You can find free-to-use versions in the SH archive format; see Resources.

Continue, the functions of the scanf series are poorly designed. In this case, the destination buffer will overflow. Consider the following code:

void Main (int argc, char **argv)  {  char buf[256];  SSCANF (Argv[0], "%s", &buf);  }

If the input word is greater than the size of the BUF, there is an overflow condition. Fortunately, there is an easy way to solve this problem. Consider the following code, which has no security weaknesses:

void Main (int argc, char **argv)  {  char buf[256];  SSCANF (Argv[0], "%255s", &buf);  }

A 255 between percent and s specifies that no more than 255 characters are actually stored in the variable buf from argv[0]. The remaining matched characters will not be copied.

Next, we discuss Streadd () and strecpy (). Since not every machine starts with these calls, programmers with these functions should be careful when using them. These functions can convert strings that contain unreadable characters into printable representations. For example, consider the following procedure:

#include <libgen.h>void main (int argc, char **argv)  {  char buf[20];  Streadd (buf, "\t\n", "" ");  printf (%s\n ", buf);  }

The program prints:

\t\n

Instead of printing all the blanks. The Streadd () and strecpy () functions may be problematic if the programmer does not anticipate how much output buffers are required to handle the input buffers (no buffer overflows occur). If the input buffer contains a single character-assuming ASCII 001 (CONTROL-A)-then it will be printed as four characters "\001". This is the worst case of string growth. If you do not allocate enough space so that the output buffer size is always four times times the size of the input buffer, a buffer overflow may occur.

Another less-used function is strtrns (), because there is no such function on many machines. The function Strtrns () takes three strings and the resulting string should be placed in one of its buffers, as its arguments. The first string must be copied to the buffer. A character is copied from the first string to the buffer, unless that character appears in the second string. If present, the characters in the same index in the third string are replaced. That sounds a little confusing. Let's take a look at an example of converting all lowercase characters to uppercase characters:

#include <libgen.h>voidMainintargcChar**argv) {  CharLower[] ="abcdefghijklmnopqrstuvwxyz"; CharUpper[] ="abcdefghijklmnopqrstuvwxyz"; Char*buf; if(ARGC <2) {printf ("USAGE:%s arg\n", argv[0]); Exit (0); } buf= (Char*)malloc(Strlen (argv[1])); Strtrns (argv[1], lower, upper, buf); printf ("%s\n", BUF); }

The above code does not actually contain a buffer overflow. However, if we use a fixed-size static buffer instead of allocating enough space for malloc () to replicate argv[1], it can cause a buffer overflow condition.

Avoid internal buffer overflow

The Realpath () function accepts a string that may contain a relative path and converts it to a string that exponentially the same file, but through an absolute path. When doing this, it expands all the symbolic links.

The function takes two arguments, the first as a string to normalize, and the second as the buffer that will store the result. Of course, you need to make sure that the resulting buffer is large enough to handle any size path. The allocated Maxpathlen buffer should be large enough. However, using Realpath () has another problem. If the path size passed to it to normalize is greater than Maxpathlen, then the Realpath () implementation of the internal static buffer overflows! Although there is actually no access to the overflow buffer, it will hurt you anyway. As a result, you should explicitly not use Realpath () unless you are sure to check that the path you are trying to normalize does not exceed the length of Maxpathlen.

Other widely available calls have similar problems. The frequently used syslog () call also has a similar problem, and it was not until recently that the problem was noticed and fixed. This problem has been corrected on most machines, but you should not rely on the correct behavior. It is best to always assume that the code is running in the most hostile environment possible, just in case it really does. The various implementations of the GETOPT () series invocation, as well as the Getpass () function, can produce internal static buffer overflow problems. If you have to use these functions, the best solution is to set thresholds for the input lengths passed to these functions.

It is very easy to simulate the security of Get () and all the problems. For example, the following code:

Char buf[1024x768];   int 0 ;   Char ch;    while ' \ n ' )  {  if(ch = =-1break;  Buf[i+ +] = ch;

Oh! This problem exists with any function that can be used to read characters, including GetChar (), fgetc (), getc (), and read ().

The guidelines for buffer overflow problems are: always make sure to do a boundary check.

C and C + + are not able to do boundary checks automatically, which is not good, but there are really a lot of reasons why not to do so. The cost of boundary checks is efficiency. Generally speaking, C focuses on efficiency in most cases. However, the cost of efficiency is that C programmers must be very vigilant and have a strong sense of security in order to prevent problems with their programs, and even if they do not make the code a problem is not easy.

At this time, variable checking does not seriously affect the efficiency of the program. Most applications will not notice this difference. Therefore, boundary checks should always be performed. Check the data length before copying the data to your own buffer. Again, check to make sure you don't pass too much data to another library, because you can't trust someone else's code! (Recall the internal buffer overflow discussed earlier.) )

What are the other dangers?

Unfortunately, even the "safe" version of the system call-for example, strncpy () relative to strcpy ()-is not entirely secure. It's also possible to mess things up. Even a "safe" call can sometimes leave an unterminated string, or a subtle difference in the one-bit error occurs. Of course, if you accidentally use a result buffer that is smaller than the source buffer, you may find yourself in a very difficult situation.

These errors are often difficult to make compared to what we are discussing, but you should still be aware of them. When you use such calls, consider them carefully. Many functions behave erratically if the buffer size is not carefully observed, including bcopy (), fgets (), memcpy (), snprintf (), strccpy (), Strcadd (), strncpy (), and vsnprintf ().

Another system call to avoid is getenv (). The biggest problem with using getenv () is that you can never assume that a particular environment variable is of any particular length. We will discuss the various problems of environment variables in the following column.

So far, we've given a whole bunch of common C functions that are prone to buffer overflow problems. Of course, there are many functions that have the same problem. In particular, note the third-party COTS software. Don't assume anything about the behavior of other people's software. Also realize that we haven't examined every common library on each platform (we don't want to do that), and there may be other problematic calls.

Even if we examine the various locations of each common library, you should be very skeptical if we try to claim that we have listed all the issues that will be encountered at any time. We just want to give you a head. The rest depends on you.



Java and stack protection can provide help

As mentioned in the previous column (see Resources), Stack Smash is one of the worst buffer overflow attacks, especially when the stack is smashed in privileged mode. An excellent solution to this problem is the non-executable stack. Typically, the code is written on the program stack and executed there. (We'll explain how this is done in the next column.) It is possible to get a non-executable stack patch for many operating systems, including Linux and Solaris. (Some operating systems do not even require such patches; they are in themselves.) )

A non-executable stack involves some performance issues. (There's no free lunch.) In addition, they are prone to problems in programs that have both stack overflow and heap overflow. A stack overflow can be used to jump the program to exploit code that is placed on the heap. There is no code in the actual execution stack, only code in the heap. These basic questions are very important and we will publish them in the next column.

Another option, of course, is to use a type-safe language, such as Java. A more modest measure is to get a compiler that checks the bounds of an array in a C program. There is such a tool for GCC. This technique prevents all buffer overflows, heaps, and stacks. The downside is that this technique can affect performance for programs that are critical to the use of pointers and speeds. But in most cases, the technology works very well.

The Stackguard tool implements techniques that are more effective than general boundary checks. It places some data at the end of the allocated data stack, and later checks to see if the data is still there before a buffer overflow can occur. This model is called "Canary". (The Welsh miners put the canary in the mine to show the dangerous situation.) When the air begins to become poisonous, the canary will faint, giving the miner enough time to notice and flee. )

The Stackguard method is not as secure as a generic boundary check, but is still quite useful. The main disadvantage of Stackguard is that it does not prevent heap overflow attacks compared to generic bounds checking. In general, it is best to use such a tool to protect the entire operating system, otherwise the unprotected libraries called by the program (for example, the standard library) can still open the door for stack-based exploit code attacks.

A tool similar to Stackguard is a memory integrity check package, such as the Purify of Rational. Such tools can even protect programs from heap overflow, but these tools are not typically used in product code due to performance overhead.

Conclusion

Function Severity Solution Solutions
Gets Most dangerous Use Fgets (buf, size, stdin). This is almost always a big problem!
strcpy It's dangerous. Use strncpy instead.
Strcat It's dangerous. Use Strncat instead.
sprintf It's dangerous. Use snprintf instead, or use the precision specifier.
scanf It's dangerous. Use the precision specifier, or resolve it yourself.
sscanf It's dangerous. Use the precision specifier, or resolve it yourself.
fscanf It's dangerous. Use the precision specifier, or resolve it yourself.
vfscanf It's dangerous. Use the precision specifier, or resolve it yourself.
vsprintf It's dangerous. Use vsnprintf instead, or use the precision specifier.
vscanf It's dangerous. Use the precision specifier, or resolve it yourself.
vsscanf It's dangerous. Use the precision specifier, or resolve it yourself.
Streadd It's dangerous. Ensure that the assigned destination parameter size is four times times the size of the source parameter.
strecpy It's dangerous. Ensure that the assigned destination parameter size is four times times the size of the source parameter.
Strtrns Dangerous Check manually to see if the destination size is at least equal to the source string.
Realpath Very dangerous (or slightly smaller, depending on the implementation) The allocation buffer size is maxpathlen. Again, check the parameters manually to ensure that the input parameters do not exceed Maxpathlen.
Syslog Very dangerous (or slightly smaller, depending on the implementation) Truncate all string input to a reasonable size before passing the string input to the function.
Getopt Very dangerous (or slightly smaller, depending on the implementation) Truncate all string input to a reasonable size before passing the string input to the function.
Getopt_long Very dangerous (or slightly smaller, depending on the implementation) Truncate all string input to a reasonable size before passing the string input to the function.
Getpass Very dangerous (or slightly smaller, depending on the implementation) Truncate all string input to a reasonable size before passing the string input to the function.
GetChar Medium hazard If you use this function in a loop, be sure to check the buffer bounds.
Fgetc Medium hazard If you use this function in a loop, be sure to check the buffer bounds.
Getc Medium hazard If you use this function in a loop, be sure to check the buffer bounds.
Read Medium hazard If you use this function in a loop, be sure to check the buffer bounds.
Bcopy Low risk Make sure the buffer size is as large as it says.
Fgets Low risk Make sure the buffer size is as large as it says.
memcpy Low risk Make sure the buffer size is as large as it says.
snprintf Low risk Make sure the buffer size is as large as it says.
strccpy Low risk Make sure the buffer size is as large as it says.
Strcadd Low risk Make sure the buffer size is as large as it says.
strncpy Low risk Make sure the buffer size is as large as it says.
vsnprintf Low risk Make sure the buffer size is as large as it says.

Reprinted from: http://blog.csdn.net/uniquecapo/article/details/38235149

unsafe functions in C

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.