blob: 1580e1d72e228d34847ead4d93aa578787f54afd [file] [log] [blame]
//===------- strings.cpp - CStdLib runtime for functions in <strings.h> ---===//
//
// The SAFECode Compiler
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides CStdLib runtime wrapper versions of functions found in
// <strings.h>.
//
//===----------------------------------------------------------------------===//
#include "safecode/Config/config.h"
#include "CStdLib.h"
#include <algorithm>
#include <cctype>
#include <strings.h>
//
// pool_bcmp()
//
// This is the non-debug version of pool_bcmp_debug().
//
int
pool_bcmp(DebugPoolTy *aPool,
DebugPoolTy *bPool,
void *a,
void *b,
size_t n,
const uint8_t complete) {
return pool_bcmp_debug(aPool, bPool, a, b, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace bcmp()
//
// Returns 0 if the first n bytes of the memory areas pointed to by a and b are
// identical in value, and nonzero otherwise.
//
// Inputs:
// aPool Pool handle for a
// bPool Pool handle for b
// a Pointer to first memory area
// b Pointer to second memory area
// n Number of bytes to compare
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// Returns 0 if the first n bytes of the areas pointed to by a and b are
// identical in value, and nonzero otherwise.
//
int
pool_bcmp_debug(DebugPoolTy *aPool,
DebugPoolTy *bPool,
void *a,
void *b,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
void *aStart = NULL, *aEnd = NULL;
void *bStart = NULL, *bEnd = NULL;
const bool aComplete = ARG1_COMPLETE(complete);
const bool bComplete = ARG2_COMPLETE(complete);
bool aFound, bFound;
// Retrieve both objects from their pointers' pools.
if (!(aFound = pool_find(aPool, (void *) a, aStart, aEnd)) && aComplete) {
err << "Object for 1st argument to bcmp() not found in pool!\n";
LOAD_STORE_VIOLATION(a, aPool, SRC_INFO_ARGS);
}
if (!(bFound = pool_find(bPool, (void *) b, bStart, bEnd)) && bComplete) {
err << "Object for 2nd argument to bcmp() not found in pool!\n";
LOAD_STORE_VIOLATION(b, bPool, SRC_INFO_ARGS);
}
// Determine if both pointers can be read safely.
size_t aSize = aFound ? byte_range(a, aEnd) : n;
size_t bSize = bFound ? byte_range(b, bEnd) : n;
size_t safelen = std::min(n, std::min(aSize, bSize));
int result = bcmp(a, b, safelen);
if (safelen == n)
return result;
else if (result == 0) {
err << "bcmp() reads beyond object boundaries!\n";
if (aSize <= bSize)
OOB_VIOLATION(a, aPool, a, safelen + 1, SRC_INFO_ARGS);
if (bSize <= aSize)
OOB_VIOLATION(b, bPool, b, safelen + 1, SRC_INFO_ARGS);
return bcmp(a, b, n);
}
else
return 1;
}
//
// pool_bcopy()
//
// See pool_bcopy_debug().
//
void
pool_bcopy(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
void *s1,
void *s2,
size_t n,
const uint8_t complete) {
pool_bcopy_debug(s1Pool, s2Pool, s1, s2, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace bcopy()
//
// Copies n bytes from s1 into s2.
//
// Attempts to verify that:
// - the first n bytes of s1 are completely contained in s1's memory object
// - the area pointed to by s2 has enough space to hold the result of the copy
//
// Inputs:
// s1Pool Pool handle for s1
// s2Pool Pool handle for s2
// s1 Pointer to source memory area
// s2 Pointer to destination memory area
// n Number of bytes to copy
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// This function does not return a value.
//
void
pool_bcopy_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
void *s1,
void *s2,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
void *s1Start = NULL, *s1End = NULL;
void *s2Start = NULL, *s2End = NULL;
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
bool s1Found, s2Found;
// Retrieve both memory objects' boundaries from their pointers' pools.
if (!(s1Found = pool_find(s1Pool, s1, s1Start, s1End)) && s1Complete) {
err << "Source object not found in pool!\n";
LOAD_STORE_VIOLATION(s1, s1Pool, SRC_INFO_ARGS);
}
if (!(s2Found = pool_find(s2Pool, s2, s2Start, s2End)) && s2Complete) {
err << "Destination object not found in pool!\n";
LOAD_STORE_VIOLATION(s2, s2Pool, SRC_INFO_ARGS);
}
// Determine if the copy operation does not read or write data out of bounds
// of the pointer's memory areas.
size_t s1Bytes = byte_range(s1, s1End);
if (s1Found && n > s1Bytes) {
err << "bcopy() reads beyond the end of the source object!\n";
OOB_VIOLATION(s1, s1Pool, s1, n, SRC_INFO_ARGS);
}
size_t s2Bytes = byte_range(s2, s2End);
if (s2Found && n > s2Bytes) {
err << "bcopy() writes beyond the end of the destination object!\n";
WRITE_VIOLATION(s2, s2Pool, s2Bytes, n, SRC_INFO_ARGS);
}
// No need to handle overlap - bcopy() takes care of this.
bcopy(s1, s2, n);
}
//
// pool_bzero()
//
// See pool_bzero_debug().
//
void
pool_bzero(DebugPoolTy *sPool,
void *s,
size_t n,
const uint8_t complete) {
pool_bzero_debug(sPool, s, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace bzero()
//
// Overwrites the first n bytes of the memory area pointed to by s with bytes
// of value 0.
//
// Attempts to verify that the first n bytes of s are completely contained
// in s's memory object.
//
// Inputs:
// sPool Pool handle for s
// s Pointer to memory area to be zeroed
// n Number of bytes to zero
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// This function does not return a value.
//
void
pool_bzero_debug(DebugPoolTy *sPool,
void *s,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
void *sStart = NULL, *sEnd = NULL;
const bool sComplete = ARG1_COMPLETE(complete);
bool sFound;
// Get the memory object that s points to from the pool.
if (!(sFound = pool_find(sPool, s, sStart, sEnd)) && sComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(s, sPool, SRC_INFO_ARGS);
}
// Determine if the write operation would write beyond the end of the
// memory object.
size_t sBytes = byte_range(s, sEnd);
if (sFound && n > sBytes) {
err << "bzero() writes beyond the end of the destination memory object!\n";
WRITE_VIOLATION(s, sPool, sBytes, n, SRC_INFO_ARGS);
}
bzero(s, n);
}
//
// pool_index()
//
// See pool_index_debug().
//
char *
pool_index(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete) {
return pool_index_debug(sPool, s, c, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace index()
//
// Returns a pointer to the first instance of the character c in the string s,
// or NULL if c is not found.
//
// Attempts to verify that s is a string terminated within object boundaries.
//
// Inputs:
// sPool Pool handle for s
// s String to search
// c Character to find in s
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// This function returns a pointer to the first instance of c in s, or NULL if
// c is not found in s.
//
char *
pool_index_debug(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool sComplete = ARG1_COMPLETE(complete);
validStringCheck(s, sPool, sComplete, "index", SRC_INFO_ARGS);
return index(s, c);
}
//
// pool_rindex()
//
// See pool_rindex_debug().
//
char *
pool_rindex(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete) {
return pool_rindex_debug(sPool, s, c, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace rindex()
//
// Returns a pointer to the last instance of the character c in the string s,
// or NULL if c is not found.
//
// Attempts to verify that s is a string terminated within object boundaries.
//
// Inputs:
// sPool Pool handle for s
// s Pointer to string to search
// c Character to find in s
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// This function returns a pointer to the last instance of c in s, or NULL if
// c is not found in s.
//
char *
pool_rindex_debug(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool sComplete = ARG1_COMPLETE(complete);
validStringCheck(s, sPool, sComplete, "rindex", SRC_INFO_ARGS);
return rindex(s, c);
}
//
// pool_strcasecmp()
//
// See pool_strcasecmp_debug().
//
int
pool_strcasecmp(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
const uint8_t complete) {
return pool_strcasecmp_debug(s1p, s2p, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcasecmp()
//
// Compares str1 and str2 in a case-insensitive manner.
//
// Attempts to verify that str1 and str2 point to valid strings terminated
// within their memory objects' boundaries.
//
// Inputs:
// str1Pool Pool handle for str1
// str2Pool Pool handle for str2
// str1 First string to be compared
// str2 Second string to be compared
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// This function returns zero if str1 = str2, a positive integer if str1 >
// str2, and a negative integer if str1 < str2, with string comparison done
// case insensitively.
//
int
pool_strcasecmp_debug(DebugPoolTy *str1Pool,
DebugPoolTy *str2Pool,
char *str1,
char *str2,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool str1Complete = ARG1_COMPLETE(complete);
const bool str2Complete = ARG2_COMPLETE(complete);
validStringCheck(str1, str1Pool, str1Complete, "strcasecmp", SRC_INFO_ARGS);
validStringCheck(str2, str2Pool, str2Complete, "strcasecmp", SRC_INFO_ARGS);
return strcasecmp(str1, str2);
}
//
// pool_strncasecmp()
//
// See pool_strncasecmp_debug().
//
int
pool_strncasecmp(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
size_t num,
const uint8_t complete){
return pool_strncasecmp_debug(s1p, s2p, s1, s2, num, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strncasecmp()
//
// Compares at most the first n characters from s1 and s2 in a case insensitive
// manner.
//
// Inputs:
// s1Pool Pool handle for s1
// s2Pool Pool handle for s2
// s1 First string to be compared
// s2 Second string to be compared
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// Let str1 = s1[0..n-1] and str2 = s2[0..n-1]. The return value is negative
// when str1 < str2, zero when str1 = str2, and positive when str1 > str2.
// The comparison is done case insensitively.
//
int
pool_strncasecmp_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO)
{
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
bool s1Found, s2Found;
void *s1Start, *s1End = NULL, *s2Start, *s2End = NULL;
if (!(s1Found = pool_find(s1Pool, s1, s1Start, s1End)) && s1Complete) {
err << "Memory object containing string 1 not found in pool!\n";
LOAD_STORE_VIOLATION(s1, s1Pool, SRC_INFO_ARGS);
}
if (!(s2Found = pool_find(s2Pool, s2, s2Start, s2End)) && s2Complete) {
err << "Memory object containing string 2 not found in pool!\n";
LOAD_STORE_VIOLATION(s2, s2Pool, SRC_INFO_ARGS);
}
const unsigned char *str1 = (const unsigned char *) s1;
const unsigned char *str2 = (const unsigned char *) s2;
size_t s1Safe = s1Found ? byte_range(s1, s1End) : n;
size_t s2Safe = s2Found ? byte_range(s2, s2End) : n;
// Get the maximum number of characters we can look into without reading out
// of bounds.
size_t safe = std::min(n, std::min(s1Safe, s2Safe));
// Compare the strings safely.
for (size_t i = 0; i < safe; ++i) {
if (std::tolower(str1[i]) != std::tolower(str2[i]))
return str1[i] - str2[i];
else if (str1[i] == 0) // End of strings reached.
return 0;
}
if (safe == n) // Strings are equal up to the first n characters.
return 0;
else {
err << "strncasecmp() reads beyond the end of string's object!\n";
if (s1Safe <= s2Safe)
OOB_VIOLATION(s1, s1Pool, s1, s1Safe + 1, SRC_INFO_ARGS);
if (s2Safe <= s1Safe)
OOB_VIOLATION(s2, s2Pool, s2, s2Safe + 1, SRC_INFO_ARGS);
return strncasecmp(s1, s2, n);
}
}