Secure Coding

-Integer(C++)

Baosong Wu

Architect@SR-Intellience

  • Background
  • Integer Fundamentals
  • Integer Vulnerabilities
  • Mitigation Strategies
  • Recap

Agenda

Background

# PRESENTING CODE
  • Secure coding becomes important in the software industry.
  • Integers are the most frequently used data types in coding.
  • Most venerabilities are relevant to integer errors.

Background

# PRESENTING CODE
  • US415588 [Security][Veracode] Achieve Veracode Level 2 for Intelligence Server

Integer Fundamentals

  • Types and Representation
  • Conversions

Integer 

Representation - unsigned integer

# PRESENTING CODE

1 1 0 1 = 13

0 1 1 0 =  6

# PRESENTING CODE

Representation - unsigned integer

Type Minimum Magnitudes x86-32
unsigned char 255 (2^8 - 1) 255 (2^8 - 1)
unsigned short 65536 (2^16 - 1) 65536 (2^16 - 1)
unsigned int 65536 (2^16 - 1) 2^32 - 1
unsigned long 2^32 - 1 2^32 - 1
unsigned long long 2^64 - 1 2^64 - 1
# PRESENTING CODE

Representation - signed integer

0000101011 = 43

 

1000101011 = -43

 

Sign and Magnitude

Ideation

During the ideation phase, expect to discuss the project in depth to clearly understand the goals and requirements.

1.

2.

0000101011 = 43

 

1000101011 = -43

 

3.

Launch

It's time to take the product live - the end if the build phase but the beginning of being in market.

One's complement

0000101011 = 43

 

1111010100 = -43

 

Two's complement

0000101011 = 43

 

1111010101 = −43

Representation - signed integer

# PRESENTING CODE
Value Sign and Magnitude One's complement Two's complement
0 0000000000 0000000000 0000000000
-0 1000000000 1111111111 N/A
1 0000000001 0000000001 0000000001
-1 1000000001 1111111110 1111111111
511 0111111111 0111111111 0111111111
-511 1111111111 1000000000 1000000001
512 N/A N/A N/A
-512 N/A N/A 1000000000

Representation - signed integer

# PRESENTING CODE

Representation - signed integer

# PRESENTING CODE
Type Minimum Magnitudes x86-32
char -127 ~ 127 -128 ~ 127
short          -32767 ~ 32767 -32768 ~ 32767
int -32767 ~ 32767 -2^31 ~ 2^31 - 1
long -(2^31 - 1) ~ 2^31 - 1 -2^31 ~ 2^31 - 1
long long -(2^63 - 1) ~ 2^63 - 1 -2^63 ~ 2^63 - 1

Conversions

# PRESENTING CODE
  •  Type conversions
    • occur explicitly in C and C++ as the result of a cast or
    • implicitly as required by an operation.
  • Conversions can lead to lost or misinterpreted data.
  • C99 rules define how C compilers handle conversions
    • integer promotions
    • integer conversion rank

Conversions - Integer Promotions

# PRESENTING CODE
  • Integer types smaller than int are promoted when an operation is performed on them.
  • Integer promotions are applied as part of the usual arithmetic conversions
  • Example:
char cresult, c1, c2, c3;
c1 = 100; 
c2 = 3; 
c3 = 4; 
cresult = c1 * c2 / c3;
unsigned char uc = UCHAR_MAX; // 0xFF
int i = ~uc;
cout << hex << i << endl;

Conversions - Rank and Rules

# PRESENTING CODE
  • Every integer type has an integer conversion rank that determines how conversions are performed.
    • No two signed integer types have the same rank, even if they have the same representation.
    • The rank of a signed integer type is > the rank of any signed integer type with less precision.
      • long long > long > int > short > char.
    • The rank of any unsigned integer type is equal to the rank of the corresponding signed integer type. 

Conversions - Arithmetic Conversions

# PRESENTING CODE
  • If both operands have the same type no conversion is needed.
  • If both operands are of the same integer type (signed or unsigned), the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
  • If the type of the operand with signed integer type can represent all of the values of the type of the operand with unsigned integer type, the operand with unsigned integer type is converted to the type of the operand with signed integer type.
  • Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type

Conversions - Unsigned Integer

# PRESENTING CODE
  • Conversions of smaller unsigned integer types to larger unsigned integer types is
    • always safe
    • typically accomplished by zero-extending the value
  • When a larger unsigned integer is converted to a smaller unsigned integer type the
    • larger value is truncated
    • low-order bits are preserved
  • When unsigned integer types are converted to the corresponding signed integer type
    • the bit pattern is preserved so no data is lost
    • the high-order bit becomes the sign bit
    • If the sign bit is set, both the sign and magnitude of the value changes

Conversions - Signed Integer

# PRESENTING CODE
  • When a signed integer is converted to a signed integer of greater size
    • the signed integer is sign-extended
  • A signed integer is converted to a shorter signed
    • the high-order bits are truncated.
  • When signed integers are converted to unsigned integers
    • bit pattern is preserved - no lost data
    • high-order bit loses its function as a sign bit
    • If the value of the signed integer is not negative, the value is unchanged.
    • If the value is negative, the resulting unsigned value is evaluated as a large, unsigned integer

Integer Vulnerabilities

  • Integer overflow
  • Truncation
  • Sign error

Vulnerabilities

Vulnerabilities - Overflow / Wraparound

# PRESENTING CODE
  • An integer overflow occurs when an integer is​
    • increased beyond its maximum value or
    • decreased beyond its minimum value.
  • Overflows can be signed or unsigned.
    • An unsigned overflow occurs when the underlying
      representation can no longer represent a value
    • A signed overflow occurs when a value is carried over to the sign bit

Vulnerabilities - Overflow Example 1

# PRESENTING CODE
    // overflow
    int i;
    unsigned int j;
    
    i = numeric_limits<int>::max(); // 2,147,483,647
    i++;
    cout << "i = " << i << endl;
    
    j = numeric_limits<unsigned int>::max(); // 4,294,967,295;
    j++;
    cout << "j = " << j << endl;

    i = numeric_limits<int>::min(); // -2,147,483,648;
    i--;
    cout << "i = " << i << endl;
    
    j = 0;
    j--;
    cout << "j = " << j << endl;
# PRESENTING CODE

Vulnerabilities - Overflow Example 2

void getComment(size_t len, char *src) 
{
	size_t size;
	size = len - 2;
	char *comment = (char *)malloc(size + 1);
	memcpy(comment, src, size);
	return;
}

Vulnerabilities - Truncation Errors

# PRESENTING CODE
  • Truncation errors occur when
    • an integer is converted to a smaller integer type and
    • the value of the original integer is outside the range of the smaller type
  • Low-order bits of the original value are preserved and the high-order bits are lost.

Vulnerabilities - Truncation Example

# PRESENTING CODE
    // truncation
    char c3, c1, c2;
    c1 = 100;
    c2 = 90;
    c3 = c1 + c2;
int main(int argc, char *argv[]) 
{
	unsigned short int total;
	total = strlen(argv[1]) + strlen(argv[2]) + 1;
	char *buff = (char *)malloc(total);
	strcpy(buff, argv[1]);
	strcat(buff, argv[2]);
	/* ... */
}

Vulnerabilities - Sign Errors

# PRESENTING CODE
  • Converting an unsigned integer to a signed integer of
    • Equal size - preserve bit pattern; high-order bit becomes sign bit
    • Greater size - the value is zero-extended then converted
    • Lesser size - preserve low-order bits
  • If the high-order bit of the unsigned integer is
    • Not set - the value is unchanged
    • Set - results in a negative value

Vulnerabilities - Sign Errors

# PRESENTING CODE
  • Converting a signed integer to an unsigned
    integer of
    • Equal size - bit pattern of the original integer is
      preserved
    • Greater size - the value is sign-extended then converted
    • Lesser size - preserve low-order bits
  • If the value of the signed integer is
    • Not negative - the value is unchanged
    • Negative - a (typically large) positive value

Vulnerabilities - Sign Error Example

# PRESENTING CODE
    // sign error
    int i = -1;
    unsigned int u;
    u = i;
    cout << "u = " << u << endl;
void initialize_array(int size) 
{
	if (size < MAX_ARRAY_SIZE) 
	{
		array = malloc(size);
		/* initialize array */
	} 
	else 
	{
 		/* handle error */
	}
}

Mitigation Strategies

  • Integer Type Selection
  • Range Check
  • Tools

Mitigation

Type Range Check

# PRESENTING CODE
  • Integer vulnerabilities result from integer type range errors.
    • Type range checking, if properly applied, can eliminate all integer vulnerabilities
  • Languages such as Pascal and Ada allow range restrictions on any scalar type to form subtypes.
    • Ada allows range restrictions to be declared on derived types using the range keyword:
      type day is new INTEGER range 1..31;
    • Range restrictions are enforced by the language
      runtime.
  • C and C++ are not nearly as good at enforcing type
    safety, unfortunately...

The first step - Integer Type Selection

# PRESENTING CODE
  • Unsigned integer values should be used to represent integer values that can never be negative, and signed integer values should be used for values that can become negative.
  • In general, use the smallest signed or unsigned type that can fully represent the range of possible values for any given variable to conserve memory.
  • When memory consumption is not an issue, declare variables as signed int or unsigned int to minimize potential conversion errors.
short total = strlen(argv[1])+ 1;
# PRESENTING CODE

Integer Type Selection - Example 1

=>

size_t total = strlen(argv[1])+ 1;
vector<char> a;
...
size_t cnt = a.size();

for (unsigned int i = cnt-2; i >= 0; i--) {
	a[i] += a[i+1];
}
# PRESENTING CODE

Integer Type Selection - Example 2

=>

for (int i = cnt-2; i >= 0; i--) {
	a[i] += a[i+1];
}
# PRESENTING CODE

Integer Type Selection - Strong Typing

  • Strong typing should be used so that the compiler can be more effective in identifying range problems.
unsigned char waterTemperature;
using watertemp_t = unsigned char;
class watertemp_t {

};
# PRESENTING CODE

Range Checking - What?

  • External inputs should be evaluated to determine whether there are identifiable upper and lower bounds.
    • these limits should be enforced by the interface
    • easier to find and correct input problems than it is to trace internal errors back to faulty inputs
  • Limit input of excessively large or small integers
# PRESENTING CODE

Overflow Checking - Precondition Test

unsigned int ui1, ui2, usum;

/* Initialize ui1 and ui2 */

if (UINT_MAX - ui1 < ui2) {
	/* handle error condition */
}
else {
	usum = ui1 + ui2;
}
# PRESENTING CODE

Overflow Checking - Postcondition Test

unsigned int ui1, ui2, usum;

/* Initialize ui1 and ui2 */

usum = ui1 + ui2;

if (usum < ui1) {
	/* handle error condition */
}
# PRESENTING CODE

Range Checking - SafeInt

int main(int argc, char *const *argv) {
  try{
    SafeInt<size_t> s1(strlen(argv[1]));
    SafeInt<size_t> s2(strlen(argv[2]));
    char *buff = (char *) malloc(s1 + s2 + 1);
    strcpy(buff, argv[1]);
    strcat(buff, argv[2]);
  }
  catch(SafeIntException err) {
    abort();
  }
}

SafeInt Summary

# PRESENTING CODE
  • SafeInt is a C++ template class
    • https://github.com/dcleblanc/SafeInt
    • https://github.microstrategy.com/ThirdParty/SafeInt
  • Implements a precondition approach that tests the values of operands before performing an operation to determine if an error will occur.
  • Note its performance impacts.
*  Due to the highly nested nature of this class, you can expect relatively poor
*  performance in unoptimized code. In tests of optimized code vs. correct inline checks
*  in native code, this class has been found to take approximately 8% more CPU time (this varies),
*  most of which is due to exception handling.

Tools

# PRESENTING CODE
  • Static code analysis
    • SonarQube
    • Microsoft Visual Studio
    • GCC
  • Static Binary scan
    • Veracode
  • Runtime check
    • GUS (Google UndefinedBehaviorSanitizer)

Recap

Thank you!

Embed Editable Code from CodePen

# PRESENTING CODE

Secure Coding - Integer (C++)

By bawu

Secure Coding - Integer (C++)

  • 23