Home > Articles > Security > Network Security

Preventing Buffer Overflow In Visual C++ Applications

Buffer overflows are currently the most common cause of security flaws in applications. Discover the techniques that professionals use to thwart this problem in this article by John Mueller.
Like this article? We recommend

Like this article? We recommend

One of the most common security issues today is the buffer overflow. This particular security problem is responsible for more virus infections than perhaps all other sources combined. Just about every application and operating system on the market has buffer overflow flaws that a cracker could exploit. The problem is so prevalent with Microsoft Windows that Microsoft is taking a different approach to the problem with releases of products such as Windows XP Service Pack (SP) 2

NOTE

See "Windows XP Service Pack 2: A Developer's View" for a description of the security changes in Windows XP SP 2 from the developer perspective.

The purpose of this article is to help you understand buffer overflows more clearly and to give you simple techniques you can use to reduce (or hopefully eliminate) this problem in your applications.

Understanding Buffer Overflows

Buffer overflows illustrate the point that you can't know what the user will try to type into your application until you actually watch the user interact with it. These attacks rely on a somewhat strange idea. A cracker provides input to a program that exceeds the length of a buffer. The extra information ends up overwriting memory other than the memory controlled by the buffer. In some cases, the memory actually holds executable information (heap memory overrun). Instead of running the original executable code, the application ends up running the cracker's code. In other cases, the cracker overwrites the stack frame for the application (stack memory overrun).

The application sends a return call to another location where the cracker's code resides.

NOTE

The Cyberguard paper titled "Buffer Overrun Attacks" describes buffer overruns in greater detail.

Some crackers actually analyze your code, looking for places to exploit either a heap or stack memory overrun. However, in some cases, an exploit is discovered when a cracker tries typing something into a field to see what will happen. For example, a cracker might try to type a simple script to see if your application will execute it. No matter how the cracker discovers the exploit, the result is the same. Your application loses control to the cracker's code—the cracker now enjoys any privileges once enjoyed by your application.

Many developers think that crackers hatch devious plots to make use of the exploits they create, but many exploits are simple—the simple act of telling the operating system to display a command prompt is enough to gain control in some cases. If the system security is even a little lax, the cracker could gain control of the server. At the very least, a command prompt allows the cracker to probe the system looking for other ways to gain more access. Crackers don't have to gain control of your system on the first try. A little gain here and a little gain there is all they need.

It doesn't take much to understand that you must provide some kind of protection for your application to keep it safe from buffer overruns. The best way to control buffer overrun is to check every input your program receives, even from trusted sources. This article considers four basic checks that every program should perform: checking the data range, verifying the data length, excluding illegal characters, and providing the user with adequate help to ensure good input.

The problem of buffer overruns is so entrenched that you really can't trust any source of information—not even your own code—because some operating system layer could contaminate the data. If you want to write truly secure code, you need to make constant checks. Although this may sound paranoid, a little paranoia is good when working with security.

NOTE

Read Microsoft's "Avoiding Buffer Overruns" for a number of other suggestions.

Validating Data Ranges

Most data ranges provided by programming languages reflect the realities of the underlying hardware, not the requirements of the real world. For example, when you define a value in your code as Int32, it means that the user can enter any value from -2,147,483,648 through 2,147,483,647. This number is based on the requirements of the hardware where the computer stores the number using 31 bits for data and 1 bit as the sign bit (2^31 = 2,147,483,648). However, your application might not find this range acceptable.

When the needs of the hardware don't match the real world needs of your application, you must include special code in your application to check for potential error conditions. You might want to accept numbers from 1 to 40,000 in your code, which is outside the Int16 value range, but well within the Int32 value range. Listing 1 shows an example of such a check for an input control.

Listing 1 Detecting range errors

System::Void btnDataRange_Click(System::Object * sender, 
                System::EventArgs * e)
{
  Int32 TestData;  // Holds the input value.

  try
  {
   // Always attempt to parse the data first.
   TestData = Int32::Parse(txtInput1->Text);
  }
  catch (System::OverflowException *OE)
  {
   // React to the overflow error.
   MessageBox::Show(S"Type a value between 1 and 40,000.",
            S"Input Error",
            MessageBoxButtons::OK,
            MessageBoxIcon::Error);
   return;
  }
  catch (System::FormatException *FE)
  {
   // React to the overflow error.
   MessageBox::Show(S"Type the number without extra charaters.",
            S"Input Error",
            MessageBoxButtons::OK,
            MessageBoxIcon::Error);
   return;
  }

  // Test the specific data range.
  if (TestData < 1 || TestData > 40000)

   // React to the data range error.
   MessageBox::Show(S"Type a value between 1 and 40,000.",
            S"Input Error",
            MessageBoxButtons::OK,
            MessageBoxIcon::Error);
}

Notice that the code converts the input to an Int32 first by using the Parse() method. This simple conversion locates many input problems. In this case, the code checks for values that are either too large or too small using the System::OverflowException exception and values that aren't in the right format using the System::FormatException exception. After the code ensures that the input value is actually a legitimate Int32 value, it then checks for the actual input range.

Value data types are the easiest to check because they have specific ranges. Unlike an object, values have no hidden elements and few surprises for the developer.

NOTE

Microsoft provides a complete list of value types supported by the .NET Framework.

In general, all you need to do to validate a value data range is define the upper and lower boundaries, then check for them in your code.

The problems with data range validation begin with objects. For example, you might require that the user provide one of several strings as input. (Contrary to what some developers believe, strings are objects.) Using list boxes to reduce user choices to the options that you have in mind does help. A user can't enter invalid information, such as a script, when faced with a list box that allows a fixed number of choices.

Sometimes you have to devise unique solutions for problems. For example, what if you need to ensure that a particular method receives a fixed number of inputs in a discontinuous range? An enumeration can save the day in this case. Listing 2 shows how you can use an enumeration as an automatic range change in your code.

Listing 2 Using enumerations to check data ranges

System::Void btnTestEnum_Click(System::Object * sender, 
                System::EventArgs * e)
{
  // Call the DisplayString function.
  DisplayString(SomeStrings::One);
}

// Create the enumerated values.
__value enum SomeStrings
{
  One,
  Two,
  Three,
  Four
};

System::Void DisplayString(SomeStrings Input)
{
  // Convert the input value to a string.
  String* DataStr = Enum::GetName(__typeof(SomeStrings), __box(Input));

  // Display the input value.
  MessageBox::Show(DataStr);
}

Notice that the declaration of DisplayString() requires the input of a SomeStrings enumeration type. The caller can't use any other type as input, which means the DisplayString() method is automatically protected from many forms of bad input. For example, you couldn't supply a script as input because it isn't the correct type.

After the DisplayString() method receives the correct input type, it converts it to a string using the Enum::GetName() method. Notice that you must __box() Input because Input is a value type, not an object type. The code simply displays the resulting DataStr object.

Verifying Data Length

Some data types don't lend themselves to quick checks. For example, a string can contain any number of characters, at least up to the limit set by the .NET Framework and the machine. Of course, very few people really need a string that long. Normally, a developer needs a string with a minimum and maximum length range. Consequently, you not only need to verify that you've received a string, but that the string is the right length. Otherwise, someone could send a string of any length, and that could lead to a buffer overrun. Listing 3 shows how you can prevent this problem by validating the data length of each argument.

Listing 3 Verifying the data length

System::Boolean ProcessData(String *Input, 
              Int32 UpperLimit, 
              Int32 LowerLimit)
{
  StringBuilder *ErrorMsg; // Error message.

  // Check for an input error.
  if (UpperLimit < LowerLimit)
  {
   // Create the error message.
   ErrorMsg = new StringBuilder();
   ErrorMsg->Append(S"The UpperLimit input must be greater than ");
   ErrorMsg->Append(S"the LowerLimit number.");

   // Define a new error.
   System::ArgumentException  *AE;
   AE = new ArgumentException(ErrorMsg->ToString(),
                 S"UpperLimit");

   // Throw the error.
   throw(AE);
  }

  // Check for a data length error condition.
  if (Input->Length < LowerLimit || Input->Length > UpperLimit)
  {
   // Create the error message.
   ErrorMsg = new StringBuilder();
   ErrorMsg->Append(S"String is the wrong length. Use a string ");
   ErrorMsg->Append(S"between 4 and 8 characters long.");

   // Define a new error.
   System::Security::SecurityException *SE;
   SE = new SecurityException(ErrorMsg->ToString());

   // Throw the error.
   throw(SE);
  }

  // If the data is correct, return true.
  return true;
}

System::Void btnDataLength_Click(System::Object * sender,
                 System::EventArgs * e)
{
  try
  {
   // Process the input text.
   if (ProcessData(txtInput2->Text, 8, 4))

     // Display a result message for correct input.
     MessageBox::Show(txtInput2->Text, 
             "Input String", 
             MessageBoxButtons::OK, 
             MessageBoxIcon::Information);
  }
  catch (System::Security::SecurityException *SE)
  {
   // Display an error message for incorrect input.
   MessageBox::Show(SE->Message, 
            "Input Error", 
            MessageBoxButtons::OK, 
            MessageBoxIcon::Error);
  }
  catch (System::ArgumentException *AE)
  {
   // Display an error message for incorrect input.
   MessageBox::Show(AE->Message, 
            "Argument Error", 
            MessageBoxButtons::OK, 
            MessageBoxIcon::Error);
  }
}

The validation occurs in the ProcessData() method, which accepts the input string, the minimum string length, and the maximum string length as inputs. Notice that the code first verifies that the input arguments are correct. The UpperLimit argument must be larger than the LowerLimit argument. This portion of the code demonstrates a good practice for all developers—never trust the input you receive. Notice that the code raises a System::ArgumentException exception, not a generic exception. Too many developers use a generic exception when a specific exception will work better. When the .NET Framework fails to provide a specific exception for your coding need, it's time to create a custom exception that does.

The code validates the string next. If the string doesn't have enough characters or if it has too many, the code raises a System::Security::SecurityException exception. The security exception is correct in this case because of the events that lead to the exception. A user might decide to input a long string in order to create a buffer overflow condition. Even when the user has made a mistake, the fact that you raise this event as a security exception means that you can at least verify the reason for the exception, rather than simply pass it off as a user having a bad day.

The test code for this example appears in the btnDataLength_Click() method. The code executes within a try...catch block to ensure that the exceptions are trapped. The actual check is a simple if statement. The code includes one catch statement for each exception. Trapping the exceptions is important if you want to ensure that the application notes any security exceptions and handles them appropriately.

Excluding Illegal Characters

Crackers often include extra illegal characters in their input to see what happens. For example, a cracker can often create a script by adding special characters. In many cases, the system will execute the script without any warning, giving the cracker access to the system. Web applications are more susceptible than desktop applications to this exploit, but you need to protect both.

NOTE

See an interesting description of one of the more interesting Web application exploits at this Web site.

Fortunately, the .NET Framework provides great regular expression support. A regular expression defines the acceptable input for a string, so you can detect illegal characters easily. Listing 4 shows one method for using regular expressions.

Listing 4 Using regular expressions

System::Boolean CheckChars(System::String *Input)
{
  StringBuilder *ErrorMsg; // Error message.
  Regex     *R;     // Regular expression.

  // Create a regular expression for match purposes.
  R = new Regex("[A-Za-z]");

  // Check for a data length error condition.
  if (R->Matches(Input)->Count < Input->Length)
  {
   // Create the error message.
   ErrorMsg = new StringBuilder();
   ErrorMsg->Append(S"String contains incorrect characters. ");
   ErrorMsg->Append(S"Use only A through Z and a through z.");

   // Define the exception.
   System::Security::SecurityException *SE;
   SE = new SecurityException(ErrorMsg->ToString());

   // Throw the exception.
   throw(SE);
  }

  // If the data is correct, return true.
  return true;
}

The code begins by building a Regex object. In this case, the only acceptable inputs are letters (you can't even include spaces). Regular expressions can encompass a vast array of inputs. In fact, there are many default templates provided as part of the validator support for ASP.NET applications. The point is that you can build a string that defines acceptable input, including input patterns such as telephone numbers.

A Regex object can perform a number of comparisons. It uses the Matches() method in this case to check the length of the string against the number of comparisons. When the two numbers match, the input is correct. Otherwise, the input contains illegal characters, and the CheckChars() method raises an exception.

Providing Superior User Help

Many developers would never associate help with good security, but good help does improve security by making user mistakes a little less likely. For example, a good help file can prevent many kinds of user input errors by showing the user precisely what your application expects to receive. Reducing input errors makes it possible to perform thorough analyses of the errors that remain, which reduces security risks from incorrect input in the end.

Help comes in all forms, including useful error messages. Some data types present special challenges that your application must handle to ensure data integrity, as well as address security concerns. For example, a date is a common data entry item that can present problems. First, you need to consider the format of the date. A user could type 1 June 2003, 06/01/2003, June 1, 2003, 2003/06/01, or any other acceptable variant. Restricting your application to allow only one date format makes it easier to check the data for invalid information. The error messages and help files must tell the user what form to use, however, so the user doesn't become frustrated when entering a valid date using an incorrect format.

No matter what you do, some users will attempt to abuse the system. They'll enter the date using the wrong format or even type something that has nothing to do with a date. However, by providing good help, you now have a basis to question the user. You can invoke security measures that ensure that users know that the behavior is unacceptable. Reducing buffer overflows is a proactive process. You must defend against the invalid input, provide good help to users who aren't informed, and be willing to take punitive measures against those users who decide to ignore the rules.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020