blob: 6ea99ae3813d93537439798c034e742d1fe26a4f [file] [log] [blame]
//===------- string.cpp - CStdLib Runtime functions for <string.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
// <string.h>.
//
//===----------------------------------------------------------------------===//
#include "safecode/Config/config.h"
#include "CStdLib.h"
#include <string.h>
#include <algorithm>
//
// Function: poolcheckstr()
//
// Description:
// This function is a generic load check on a string. It is intended to be
// used for C library functions that take a string and read its contents.
//
// Inputs:
// Pool - The pool handle for the pool in which the string should be.
// Pool handles can be NULL.
// str - A pointer to the string to check.
//
// Notes:
// We have versions of poolcheckstr() for incomplete/unknown pointers as
// well as debug versions that pass along debugging information.
//
void
poolcheckstr (DebugPoolTy * Pool, const char * str) {
if (str == NULL) return;
validStringCheck (str, Pool, true, "Generic", DEFAULT_SRC_INFO);
}
void
poolcheckstr_debug (DebugPoolTy * Pool, char * str, TAG, SRC_INFO) {
if (str == NULL) return;
validStringCheck (str, Pool, true, "Generic", SRC_INFO_ARGS);
}
void
poolcheckstrui (DebugPoolTy * Pool, char * str) {
if (str == NULL) return;
validStringCheck (str, Pool, false, "Generic", DEFAULT_SRC_INFO);
}
void
poolcheckstrui_debug (DebugPoolTy * Pool, char * str, TAG, SRC_INFO) {
if (str == NULL) return;
validStringCheck (str, Pool, false, "Generic", SRC_INFO_ARGS);
}
//
// pool_memccpy()
//
// See pool_memccpy_debug().
//
void *
pool_memccpy(DebugPoolTy *dPool,
DebugPoolTy *sPool,
void *d,
void *s,
char c,
size_t n,
const uint8_t complete) {
return pool_memccpy_debug(dPool, sPool, d, s, c, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memccpy()
//
// Inputs:
// dstPool Pool handle for destination memory area
// srcPool Pool handle for source memory area
// dst Destination memory area
// src Source memory area
// c Stop copying when we see this byte
// n Maximum 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:
// A pointer to the first byte after c in dst or, if c was not found in the
// first n bytes of s2, it returns a null pointer.
//
void *
pool_memccpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
int c,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstSize = 0, srcSize = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL, *stop;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound;
// Retrieve both the destination and source buffer's bounds from the handles.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(src, srcPool, SRC_INFO_ARGS);
}
if (srcFound) {
// Calculate the maximum number of bytes to copy.
srcSize = byte_range(src, srcEnd);
// Get the position of the byte which terminates copying.
stop = memchr(src, c, srcSize);
// Get the number of bytes that will be copied over.
size_t bytesToCopy = stop != NULL ? byte_range(src, stop) : n;
if (bytesToCopy > srcSize) {
err << "Cannot copy more bytes than the size of the source!\n";
OOB_VIOLATION(src, srcPool, src, bytesToCopy, SRC_INFO_ARGS);
}
if (dstFound) {
dstSize = byte_range(dst, dstEnd);
if (bytesToCopy > dstSize) {
err << "Cannot copy more bytes than the size of the destination!\n";
WRITE_VIOLATION(dstBegin, dstPool, dstSize, bytesToCopy, SRC_INFO_ARGS);
}
const char *dstLimit = (char *) dst + bytesToCopy - 1;
const char *srcLimit = (char *) src + bytesToCopy - 1;
if (isOverlapped(dst, dstLimit, src, srcLimit)) {
err << "Input memory objects overlap each other!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "memccpy", SRC_INFO_ARGS);
}
}
}
return memccpy(dst, src, c, n);
}
//
// pool_memchr()
//
// See pool_memchr_debug().
//
void *
pool_memchr(DebugPoolTy *stringPool,
void *string,
int c,
size_t n,
const uint8_t complete) {
return pool_memchr_debug(stringPool, string, c, n,complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memchr()
//
// Inputs:
// stringPool Pool handle for the string
// string Memory object
// c A byte value to be found
// n Number of bytes to search in
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
//
// Returns:
// This returns a pointer to the first location of c in the string, or NULL
// if not found.
//
void *
pool_memchr_debug(DebugPoolTy *strPool,
void *str,
int c,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t strSize = 0;
void *strBegin = str, *strEnd = NULL, *stop= NULL;
const bool strComplete = ARG1_COMPLETE(complete);
bool strFound;
// Retrieve the memory buffer's boundaries from the pool.
if (!(strFound = pool_find(strPool, str, strBegin, strEnd)) && strComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(str, strPool, SRC_INFO_ARGS);
}
// If the boundaries are found, determine if memchr() would read beyond them.
if (strFound) {
strSize = std::min(byte_range(str, strEnd), n);
stop = memchr(str, c, strSize);
if (stop != NULL)
return stop;
else if (n > strSize) {
err << "memchr() reads past the end of the memory object!\n";
OOB_VIOLATION(str, strPool, str, n, SRC_INFO_ARGS);
}
}
return memchr(str, c, n);
}
//
// pool_memcmp()
//
// See pool_memcmp_debug().
//
int
pool_memcmp(DebugPoolTy *s1p,
DebugPoolTy *s2p,
void *s1,
void *s2,
size_t num,
const uint8_t complete) {
return pool_memcmp_debug(s1p, s2p, s1, s2, num, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memcmp()
//
// Inputs:
// str1Pool Pool handle for memory object1
// str2Pool Pool handle for memory object1
// num Maximum number of bytes to compare
// str1 Memory object 1 to be compared
// str2 Memory object 2 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 0 if the memory areas are identical or else the
// difference between the first two bytes that are not identical.
//
int
pool_memcmp_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
void *s1,
void *s2,
size_t num,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t s1Size = 0, s2Size = 0;
void *s1Begin = s1, *s1End = NULL, *s2Begin = s2, *s2End = NULL;
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
bool s1Found, s2Found;
if (!(s1Found = pool_find(s1Pool, s1, s1Begin, s1End)) && s1Complete) {
err << "Bytestring 1 not found in pool!\n";
LOAD_STORE_VIOLATION(s1Begin, s1Pool, SRC_INFO_ARGS);
}
if (!(s2Found = pool_find(s2Pool, s2, s2Begin, s2End)) && s2Complete) {
err << "Bytestring 2 not found in pool!\n";
LOAD_STORE_VIOLATION(s2Begin, s2Pool, SRC_INFO_ARGS);
}
// These sizes are how far a read can continue safely.
s1Size = s1Found ? byte_range(s1, s1End) : num;
s2Size = s2Found ? byte_range(s2, s2End) : num;
size_t p;
const unsigned char *cs1 = (unsigned char *) s1;
const unsigned char *cs2 = (unsigned char *) s2;
// If we know the size of the memory objects, we can stop before we read out
// of bounds.
size_t stop = std::min(num, std::min(s1Size, s2Size));
for (p = 0; p < stop; p++)
if (cs1[p] != cs2[p])
return cs1[p] - cs2[p];
if (p == num)
return 0;
if (s1Found && p == s1Size) {
err << "memcmp() reads beyond the end of bytestring 1!\n";
OOB_VIOLATION(s1, s1Pool, s1, s1Size + 1, SRC_INFO_ARGS);
}
if (s2Found && p == s2Size) {
err << "memcmp() reads beyond the end of bytestring 2!\n";
OOB_VIOLATION(s2, s2Pool, s2, s2Size + 1, SRC_INFO_ARGS);
}
return memcmp(s1, s2, num);
}
//
// pool_memcpy()
//
// See pool_memcpy_debug().
//
void *
pool_memcpy(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete) {
return pool_memcpy_debug(dstPool, srcPool, dst, src, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memcpy()
//
// Inputs:
// dstPool Pool handle for destination memory area
// srcPool Pool handle for source memory area
// dst Destination memory area
// src Source memory area
// n Maximum 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 returns the value of dst.
//
void *
pool_memcpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstSize = 0, srcSize = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound = false, srcFound = false;
// Retrieve both the destination and source buffers' bounds from the handles.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Destination object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Source object not found in pool!\n";
LOAD_STORE_VIOLATION(src, srcPool, SRC_INFO_ARGS);
}
// Calculate the maximum number of bytes to copy.
dstSize = byte_range(dst, dstEnd);
srcSize = byte_range(src, srcEnd);
if (srcFound && n > srcSize) {
err << "memcpy() reads beyond the source object's boundaries!\n";
OOB_VIOLATION(src, srcPool, src, n, SRC_INFO_ARGS);
}
if (dstFound && n > dstSize) {
err << "memcpy() writes beyond the destination object's boundaries!\n";
WRITE_VIOLATION(dst, dstPool, dstSize, n, SRC_INFO_ARGS);
}
if (dstFound && srcFound) {
const char *srcLimit = (char *) src + n - 1;
const char *dstLimit = (char *) dst + n - 1;
if (isOverlapped(dst, dstLimit, src, srcLimit)) {
err << "Input memory objects overlap each other!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "memcpy", SRC_INFO_ARGS);
}
}
return memcpy(dst, src, n);
}
//
// pool_memmove()
//
// See pool_memmove_debug().
//
void *
pool_memmove(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete) {
return pool_memmove_debug(dstPool, srcPool, dst, src, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memmove()
//
// Inputs:
// dstPool Pool handle for destination memory area
// srcPool Pool handle for source memory area
// dst Destination memory area
// src Source memory area
// n Maximum 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 returns the value of dst.
//
void *
pool_memmove_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstSize = 0, srcSize = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound;
// Retrieve both the destination and source buffers' bounds from the pools.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Destination object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Source object not found in pool!\n";
LOAD_STORE_VIOLATION(src,srcPool, SRC_INFO_ARGS);
}
// Calculate the maximum number of bytes to copy safely.
dstSize = byte_range(dst, dstEnd);
srcSize = byte_range(src, srcEnd);
if (srcFound && n > srcSize) {
err << "memmove() reads beyond the end of the source bytestring!\n";
OOB_VIOLATION(src, srcPool, src, n, SRC_INFO_ARGS);
}
if (dstFound && n > dstSize) {
err << "memmove() write beyond the end of the destination bytestring!\n";
OOB_VIOLATION(dst, dstPool, dst, n, SRC_INFO_ARGS);
}
// We don't need to check for overlap - memmove() already handles this.
return memmove(dst, src, n);
}
//
// pool_memset()
//
// See pool_memset_debug().
//
void *pool_memset(DebugPoolTy *stringPool,
void *string,
int c,
size_t n,
const uint8_t complete) {
return pool_memset_debug(stringPool, string, c, n,complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace memset()
//
// Inputs:
// sPool Pool handle for the bytestring
// s Bytestring pointer
// c The byte to write
// n Number of bytes to be set
// 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 the value of s.
//
void *
pool_memset_debug(DebugPoolTy *sPool,
void *s,
int c,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t size = 0;
void *sBegin = s, *sEnd = NULL;
const bool sComplete = ARG1_COMPLETE(complete);
bool sFound;
// Retrive the object bounds.
if (!(sFound = pool_find(sPool, s, sBegin, sEnd)) && sComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(s, sPool, SRC_INFO_ARGS);
}
size = byte_range(s, sEnd);
// Check for writing out of bounds error.
if (sFound && n > size) {
err << "memset() writes beyond the end of the destination object!\n";
WRITE_VIOLATION(s, sPool, size, n, SRC_INFO_ARGS);
}
return memset(s, c, n);
}
//
// pool_strcat()
//
// See pool_strcat_debug().
//
char *
pool_strcat(DebugPoolTy *dp,
DebugPoolTy *sp,
char *d,
char *s,
const unsigned char c) {
return pool_strcat_debug(dp, sp, d, s, c, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcat()
//
// Appends the source string to the end of the destination string.
// Attempts to verify the following:
// - source and destination pointers point to valid strings
// - there is no overlap between source and destination strings
// - the destination string's object has enough space to hold the
// concatenation in memory.
//
// Inputs:
// dp Pool handle for destination string
// sp Pool handle for source string
// d Pointer to the destination string
// s Pointer to the source string
// 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 destination string.
//
char *
pool_strcat_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t srcLen = 0, dstLen = 0, maxLen, catLen;
void *dstBegin = NULL, *dstEnd = NULL;
void *srcBegin = NULL, *srcEnd = NULL;
char *dstNulPosition;
bool srcFound, dstFound, srcTerminated = false, dstTerminated = false;
const bool srcComplete = ARG1_COMPLETE(complete);
const bool dstComplete = ARG2_COMPLETE(complete);
// Find the strings' memory objects in the pools.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Destination string not found in pool\n";
LOAD_STORE_VIOLATION(dstBegin, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Source string not found in pool!\n";
LOAD_STORE_VIOLATION(srcBegin, srcPool, SRC_INFO_ARGS);
}
// Check if both src and dst are terminated, if they were found in their pool.
if (dstFound && !(dstTerminated = isTerminated(dst, dstEnd, dstLen))) {
err << "Destination not terminated within bounds\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strcat", SRC_INFO_ARGS);
}
if (srcFound && !(srcTerminated = isTerminated(src, srcEnd, srcLen))) {
err << "Source not terminated within bounds\n";
C_LIBRARY_VIOLATION(src, srcPool, "strcat", SRC_INFO_ARGS);
}
// We assume an object that is not complete and not found is valid.
// So get its length.
if (!srcFound && !srcComplete) {
srcTerminated = true;
srcLen = strlen(src);
}
if (!dstFound && !dstComplete) {
dstTerminated = true;
dstLen = strlen(dst);
}
// The remainder of the checks require the string length to be known.
if (dstTerminated && srcTerminated) {
maxLen = byte_range(dst, dstEnd) - 1;
catLen = srcLen + dstLen;
// Do the check for if concatenation writes out of bounds.
if (catLen > maxLen) {
err << "Concatenation violated destination bounds!\n";
WRITE_VIOLATION(dstBegin, dstPool, maxLen + 1, catLen + 1, SRC_INFO_ARGS);
}
// Overlap occurs exactly when they share the same nul terminator in memory.
if (&dst[dstLen] == &src[srcLen]) {
err << "Concatenating overlapping strings is undefined\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strcat", SRC_INFO_ARGS);
}
// Append at the end of dst so concatenation doesn't have to scan dst again.
dstNulPosition = &dst[dstLen];
strncat(dstNulPosition, src, srcLen);
return dst;
}
else
return strcat(dst, src);
}
//
// pool_strchr()
//
// This is the non-debug version of pool_strchr_debug().
//
//
char *
pool_strchr(DebugPoolTy *sp, char *s, int c, const uint8_t complete) {
return pool_strchr_debug(sp, s, c, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strchr()
//
// Returns pointer to the first instance of the given character in the string,
// or NULL if not found.
//
// Ensures the following:
// - string argument points to a valid string
//
// Inputs:
// sPool Pool handle for string
// s String pointer
// c Character to find
// 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 the given
// character in the string, or NULL if not found.
//
char *
pool_strchr_debug(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete,
TAG,
SRC_INFO) {
validStringCheck(s, sPool, ARG1_COMPLETE(complete), "strchr", SRC_INFO_ARGS);
return strchr(s, c);
}
//
// pool_strcmp()
//
// See pool_strcmp_debug().
//
int
pool_strcmp(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
const uint8_t complete){
return pool_strcmp_debug(s1p, s2p, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcmp()
//
// Inputs:
// s1Pool Pool handle for str1
// s2Pool Pool handle for str2
// s1 C string to be compared
// s2 C 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 a negative, zero, or positive integer depending on
// whether s1 < s2, s1 = s2, or s1 > s2.
//
int
pool_strcmp_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
validStringCheck(s1, s1Pool, s1Complete, "strcmp", SRC_INFO_ARGS);
validStringCheck(s2, s2Pool, s2Complete, "strcmp", SRC_INFO_ARGS);
return strcmp(s1, s2);
}
//
// pool_strcoll()
//
// See pool_strcoll_debug().
//
int
pool_strcoll(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
const uint8_t complete){
return pool_strcoll_debug(s1p, s2p, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcoll()
//
// Inputs:
// s1Pool Pool handle for str1
// s2Pool Pool handle for str2
// s1 C string to be compared
// s2 C 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 a negative, zero, or positive integer depending on
// whether s1 < s2, s1 = s2, or s1 > s2, in the ordering described by the
// value of the LC_COLLATE category of the current locale.
//
int
pool_strcoll_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
validStringCheck(s1, s1Pool, s1Complete, "strcoll", SRC_INFO_ARGS);
validStringCheck(s2, s2Pool, s2Complete, "strcoll", SRC_INFO_ARGS);
return strcoll(s1, s2);
}
//
// pool_strcpy()
//
// See pool_strcpy_debug().
//
char *
pool_strcpy(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
const uint8_t complete) {
return pool_strcpy_debug(dstPool, srcPool, dst, src, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcpy()
//
// Inputs:
// dstPool Pool handle for destination buffer
// srcPool Pool handle for source string
// dst Destination string pointer
// src Source string pointer
// 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 the destination string pointer.
//
char *
pool_strcpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstMax = 0, srcLen = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound, srcTerminated;
// Retrieve both the destination and source buffer's bounds from the pools.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(src,srcPool, SRC_INFO_ARGS);
}
// Check for source termination.
if (srcFound && !(srcTerminated = isTerminated(src, srcEnd, srcLen))) {
err << "Source string is not terminated within object bounds!\n";
C_LIBRARY_VIOLATION(src, srcPool, "strcpy", SRC_INFO_ARGS);
}
if (dstFound) {
// Assume an incomplete and not found object is valid.
if (!srcFound && !srcComplete) {
srcTerminated = true;
srcLen = strlen(src);
}
// The remainder of the checks require us to know the length of src.
if (srcTerminated) {
dstMax = byte_range(dst, dstEnd) - 1;
// Check for writing out of bounds errors.
if (srcLen > dstMax) {
err << "strcpy() writes beyond the end of the destination object!\n";
WRITE_VIOLATION(dst, dstPool, dstMax + 1, srcLen + 1, SRC_INFO_ARGS);
}
// Check for overlap.
void *dstEdge = srcLen + (char *) dst;
void *srcEdge = srcLen + (char *) src;
if (isOverlapped(dst, dstEdge, src, srcEdge)) {
err << "Memory objects in call to strcpy() overlap each other!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strcpy", SRC_INFO_ARGS);
}
}
}
return strcpy(dst, src);
}
//
// pool_strcspn()
//
// See pool_strcspn_debug().
//
size_t pool_strcspn(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
const uint8_t complete) {
return pool_strcspn_debug(s1p, s2p, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcspn()
//
// Inputs:
// str1Pool Pool handle for str1
// str2Pool Pool handle for str2
// str1 C string to be compared
// str2 C 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 the length of the initial portion of str1 that does
// not contain any character from str2.
//
size_t
pool_strcspn_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, "strcspn", SRC_INFO_ARGS);
validStringCheck(str2, str2Pool, str2Complete, "strcspn", SRC_INFO_ARGS);
return strcspn(str1, str2);
}
//
// pool_strlen()
//
// See pool_strlen_debug().
//
size_t
pool_strlen(DebugPoolTy *stringPool,
char *string,
const uint8_t complete) {
return pool_strlen_debug(stringPool, string, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strlen()
//
// Inputs:
// strPool Pool handle for the string
// str String pointer
// 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 the length of the input string.
//
size_t
pool_strlen_debug(DebugPoolTy *strPool,
char *str,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool strComplete = ARG1_COMPLETE(complete);
bool strFound;
size_t len = 0;
void *strBegin = 0, *strEnd = 0;
if (!(strFound = pool_find(strPool, str, strBegin, strEnd)) && strComplete) {
err << "Object for string not found in pool!\n";
LOAD_STORE_VIOLATION(str, strPool, SRC_INFO_ARGS);
}
if (strFound) {
if (!isTerminated(str, strEnd, len)) {
err << "Input string not terminated within object boundaries!\n";
C_LIBRARY_VIOLATION(str, strPool, "strlen", SRC_INFO_ARGS);
}
else
return len;
}
return strlen(str);
}
//
// pool_strncat()
//
// See pool_strncat_debug().
//
char *
pool_strncat(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
size_t n,
const uint8_t complete) {
return pool_strncat_debug(dstPool, srcPool, dst, src, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strncat()
//
// Appends at most n characters of src onto the end of the string dst
// and then adds a nul terminator.
//
// Checks for the following:
// - src and dst are non-null
// - dst is terminated
// - dst has enough space to hold the whole concatention
// - src and dst do not overlap
// - if src is unterminated, the first n characters of src fall within
// the boundaries of src.
//
// Inputs:
// dstPool Pool handle for destination string
// srcPool Pool handle for source string
// dst Destination string pointer
// src Source string pointer
// n Number of characters to copy over
// 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 destination buffer.
//
char *
pool_strncat_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
void *dstBegin = NULL, *dstEnd = NULL;
void *srcBegin = NULL, *srcEnd = NULL;
size_t dstLen = 0, srcLen = 0, maxLen, catLen, srcAmt;
char *dstNulPosition;
bool srcFound, dstFound, srcTerminated = false, dstTerminated;
const bool srcComplete = ARG1_COMPLETE(complete);
const bool dstComplete = ARG2_COMPLETE(complete);
// Retrieve destination and source string memory objects from pool.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Destination string not found in pool!\n";
LOAD_STORE_VIOLATION(dstBegin, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Source string not found in pool!\n";
LOAD_STORE_VIOLATION(srcBegin, srcPool, SRC_INFO_ARGS);
}
// Check if dst is nul terminated.
if (dstFound && !(dstTerminated = isTerminated(dst, dstEnd, dstLen))) {
err << "String not terminated within bounds\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strncat", SRC_INFO_ARGS);
}
// According to POSIX, src doesn't have to be nul-terminated.
// If it isn't, ensure strncat that doesn't read beyond the bounds of src.
if (srcFound) {
srcTerminated = isTerminated(src, srcEnd, srcLen);
} else if (!srcFound && !srcComplete) {
srcLen = _strnlen(src, n);
srcTerminated = srcLen < n;
}
// Check if the number of bytes in source is < the number of bytes we have to
// copy.
// This will result in reading out of bounds.
if (srcFound && !srcTerminated && byte_range(src, srcEnd) < n) {
err << "strncat() reads beyond the boundaries of the source object!\n";
OOB_VIOLATION(src, srcPool, src, srcLen, SRC_INFO_ARGS);
}
// The remaining checks require us to know the lengths of the destination and
// the object boundaries of the source string.
if (srcFound && dstTerminated) {
// Determine the amount of characters to be copied over from src.
// If the string is terminated, this is the smaller of n or the length of
// src.
// Otherwise this is n.
srcAmt = srcTerminated ? std::min(srcLen, n) : n;
// Check for undefined behavior due to overlapping objects.
// Overlap occurs when the characters to be copied from src
// end inside the dst string. &src[srcAmt] represents one past the end of
// what will be copied over.
if (dst < &src[srcAmt] && &src[srcAmt] <= &dst[dstLen]) {
err << "Concatenating overlapping objects is undefined\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strncat", SRC_INFO_ARGS);
}
// maxLen is the maximum length string dst can hold without overflowing.
maxLen = byte_range(dst, dstEnd) - 1;
// catLen is the length of the string resulting from the concatenation.
catLen = srcAmt + dstLen;
// Check if the copy operation would go beyong the bounds of dst.
if (catLen > maxLen) {
err << "Concatenation violated destination bounds!\n";
WRITE_VIOLATION(dst, dstPool, 1+maxLen, 1+catLen, SRC_INFO_ARGS);
}
// Start concatenation the end of dst so strncat() doesn't have to scan dst
// all over again.
dstNulPosition = &dst[dstLen];
strncat(dstNulPosition, src, srcAmt);
// strncat() returns the original destination string.
return dst;
}
else
return strncat(dst, src, n);
}
//
// pool_strncmp()
//
// See pool_strncmp_debug().
//
int
pool_strncmp(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
size_t num,
const uint8_t complete){
return pool_strncmp_debug(s1p,s2p,s1,s2,num,complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strncmp()
//
// Inputs:
// s1Pool Pool handle for string1
// s2Pool Pool handle for string2
// n Maximum number of chars to compare
// s1 string1 to be compared
// s2 string2 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 a negative, zero, or positive integer depending on if
// s1 < s2, s1 = s2, or s1 > s2 in the first n characters.
//
int
pool_strncmp_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t s1Size = 0, s2Size = 0;
void *s1Begin = s1, *s1End = NULL, *s2Begin = s2, *s2End = NULL;
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
bool s1Found, s2Found;
// Find the objects in their pools.
if (!(s1Found = pool_find(s1Pool, s1, s1Begin, s1End)) && s1Complete) {
err << "String 1 not found in pool!\n";
LOAD_STORE_VIOLATION(s1Begin, s1Pool, SRC_INFO_ARGS);
}
if (!(s2Found = pool_find(s2Pool, s2, s2Begin, s2End)) && s2Complete) {
err << "String 2 not found in pool!\n";
LOAD_STORE_VIOLATION(s1Begin, s1Pool, SRC_INFO_ARGS);
}
// These sizes represent the 'safe' range to read.
s1Size = s1Found ? byte_range(s1, s1End) : n;
s2Size = s2Found ? byte_range(s2, s2End) : n;
size_t stop = std::min(n, std::min(s1Size, s2Size));
size_t p;
// Comparison is done using unsigned characters.
const unsigned char *cs1 = (const unsigned char *) s1;
const unsigned char *cs2 = (const unsigned char *) s2;
for (p = 0; p < stop; p++) {
if (cs1[p] != cs2[p])
return cs1[p] - cs2[p];
// We're comparing strings, so a nul terminator indicates we're done.
else if (cs1[p] == 0)
return 0;
}
if (p == n)
return 0;
if (s1Found && p == s1Size) {
err << "strncmp() reads beyond the end of string 1!\n";
OOB_VIOLATION(s1, s1Pool, s1, s1Size + 1, SRC_INFO_ARGS);
}
if (s2Found && p == s2Size) {
err << "strncmp() reads beyond the end of string 2!\n";
OOB_VIOLATION(s2, s2Pool, s2, s2Size + 1, SRC_INFO_ARGS);
}
return strncmp(s1, s2, n);
}
//
// pool_strncpy()
//
// See pool_strncpy_debug().
//
char *
pool_strncpy(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
size_t n,
const uint8_t complete) {
return pool_strncpy_debug(dstPool, srcPool, dst, src, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strncpy()
//
// Copies exactly n bytes to dst, which are read from src until a nul
// terminator is encountered. If len(src) < n then pads the rest of the write
// with zeroes. If len(src) >= n then the copying will be truncated and
// consequently no nul terminator will be appended.
//
// Inputs:
// dstPool Pool handle for destination buffer
// srcPool Pool handle for source string
// dst Destination string pointer
// src Source string pointer
// n Maximum 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 returns the value of the destination string pointer.
//
char *
pool_strncpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstSize = 0, srcSize = 0, srcLen = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound;
// Retrieve both the destination and source object bounds from the pools.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(src, srcPool, SRC_INFO_ARGS);
}
if (srcFound) {
srcSize = byte_range(src, srcEnd);
// Check if src is read out of bounds. This happens when n > the object
// size of src, and src is not terminated before the end of the object.
srcLen = _strnlen(src, srcSize);
if (n > srcSize && srcLen == srcSize) {
err << "strncpy() reads source string out of bounds!\n";
OOB_VIOLATION(src, srcPool, src, srcSize + 1, SRC_INFO_ARGS);
} else {
// Check for overlap. This check doesn't work when the previous
// condition is true, which is why it is in the else clause.
// This is the amount of characters actually read from src.
size_t srcRead = std::min(n, 1 + srcLen);
void *dstEdge = (char *) dst + srcRead - 1;
void *srcEdge = (char *) src + srcRead - 1;
if (isOverlapped(dst, dstEdge, src, srcEdge)) {
err << "The objects passed to strncpy() overlap!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "strncpy()", SRC_INFO_ARGS);
}
}
}
if (dstFound) {
dstSize = byte_range(dst, dstEnd);
if (dstSize < n) {
err << "strncpy() writes beyond end of destination object!\n";
WRITE_VIOLATION(dst, dstPool, dstSize, n, SRC_INFO_ARGS);
}
}
return strncpy(dst, src, n);
}
//
// pool_strpbrk()
//
// See pool_strpbrk_debug().
//
char *
pool_strpbrk(DebugPoolTy *sp,
DebugPoolTy *ap,
char *s,
char *a,
const uint8_t complete) {
return pool_strpbrk_debug(sp, ap, s, a, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strpbrk()
//
// Searches for the first instance in s of any character in a, and returns a
// pointer to that instance, or returns NULL if no instance was found.
//
// Attempts to verify that both s and a are valid strings terminated within
// their memory objects' boundaries.
//
// Inputs:
// sPool Pool handle for source string
// aPool Pool handle for accept string
// s String pointer
// a Pointer to string of characters to find
// 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 in s of some
// character in a, or NULL if none was found.
//
char *
pool_strpbrk_debug(DebugPoolTy *sPool,
DebugPoolTy *aPool,
char *s,
char *a,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool sComplete = ARG1_COMPLETE(complete);
const bool aComplete = ARG2_COMPLETE(complete);
validStringCheck(s, sPool, sComplete, "strpbrk", SRC_INFO_ARGS);
validStringCheck(a, aPool, aComplete, "strpbrk", SRC_INFO_ARGS);
return strpbrk(s, a);
}
//
// pool_strrchr()
//
// See pool_strrchr_debug().
//
char *
pool_strrchr(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete) {
return pool_strrchr_debug(sPool, s, c, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strrchr()
//
// Inputs:
// sPool Pool handle for string
// s String pointer
// c Character to find
// 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 the string s,
// or NULL if none was found.
//
char *
pool_strrchr_debug(DebugPoolTy *sPool,
char *s,
int c,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool sComplete = ARG1_COMPLETE(complete);
validStringCheck(s, sPool, sComplete, "strrchr", SRC_INFO_ARGS);
return strrchr(s, c);
}
//
// pool_strspn()
//
// See pool_strspn_debug().
//
size_t
pool_strspn(DebugPoolTy *s1p,
DebugPoolTy *s2p,
char *s1,
char *s2,
const uint8_t complete) {
return pool_strspn_debug(s1p, s2p, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strspn()
//
// Inputs:
// str1Pool Pool handle for str1
// str2Pool Pool handle for str2
// str1 A string to scan
// str2 A string of allowed characters
// 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 the length of the initial portion of str1 which
// contains characters only from str2.
//
size_t
pool_strspn_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, "strspn", SRC_INFO_ARGS);
validStringCheck(str2, str2Pool, str2Complete, "strspn", SRC_INFO_ARGS);
return strspn(str1, str2);
}
//
// pool_strstr()
//
// See pool_strstr_debug().
//
//
char *
pool_strstr(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete) {
return pool_strstr_debug(s1Pool, s2Pool, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strstr()
//
// Searches for the first occurence of the substring s2 in s1.
// Returns a pointer to the discovered substring, or NULL if not found.
//
// Attempts to verify that s1 and s2 are valid strings terminated within
// their memory objects' boundaries.
//
// Inputs:
// s1Pool Pool handle for s1
// s2Pool Pool handle for s2
// s1 Pointer to string to be searched
// s2 Pointer to substring to search for
// 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 s2 in s1, or NULL
// if none exists.
//
char *
pool_strstr_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
validStringCheck(s1, s1Pool, s1Complete, "strstr", SRC_INFO_ARGS);
validStringCheck(s2, s2Pool, s2Complete, "strstr", SRC_INFO_ARGS);
return strstr(s1, s2);
}
//
// pool_strxfrm()
//
// See pool_strxfrm_debug().
//
size_t
pool_strxfrm(DebugPoolTy *dPool,
DebugPoolTy *sPool,
char *d,
char *s,
size_t n,
const uint8_t complete) {
return pool_strxfrm_debug(dPool, sPool, d, s, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strxfrm()
//
// Uses the current locale information to convert the first n characters of the
// source string into a format suitable for usage with strcmp().
//
// Inputs:
// dPool Pool handle for destionation
// sPool Pool handle for source string
// d Pointer to destination buffer
// s Pointer to source string
// n Number of characters to convert
// 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 the length of the transformed string.
//
size_t
pool_strxfrm_debug(DebugPoolTy *dPool,
DebugPoolTy *sPool,
char *d,
char *s,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool dComplete = ARG1_COMPLETE(complete);
const bool sComplete = ARG2_COMPLETE(complete);
bool dFound;
void *dStart, *dEnd = NULL;
// Retrive the memory object boundaries from the pools.
if (!(dFound = pool_find(dPool, d, dStart, dEnd)) && dComplete) {
err << "Destination object not found in pool!\n";
LOAD_STORE_VIOLATION(d, dPool, SRC_INFO_ARGS);
}
//
// Check only for string termination of s, because we don't know how much of
// s will be read.
//
validStringCheck(s, sPool, sComplete, "strxfrm", SRC_INFO_ARGS);
//
// Check if we write out if bounds.
//
if (dFound && n > 0) {
// Call strxfrm(NULL, s, 0) to discover the length of the transformed
// string.
size_t xfrmLen = std::min(strxfrm(NULL, s, 0), n - 1);
size_t dSize = byte_range(d, dEnd);
if (xfrmLen + 1 > dSize) {
err << "strxfrm() writes past the end of the destination object!\n";
WRITE_VIOLATION(d, dPool, dSize, xfrmLen + 1, SRC_INFO_ARGS);
}
}
return strxfrm(d, s, n);
}
#ifdef HAVE_MEMPCPY
//
// pool_mempcpy()
//
// See pool_mempcpy_debug().
//
void *
pool_mempcpy(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete) {
return pool_mempcpy_debug(dstPool, srcPool, dst, src, n, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace mempcpy()
//
// This function is identical to memcpy(), except it returns a pointer to the
// byte right after the first n bytes of the destination.
//
// Inputs:
// dstPool Pool handle for destination memory area
// srcPool Pool handle for source memory area
// dst Destination memory area
// src Source memory area
// n Maximum 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 returns the value of &((char *) dst)[n].
//
void *
pool_mempcpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
void *dst,
void *src,
size_t n,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t dstSize = 0, srcSize = 0;
void *dstBegin = dst, *dstEnd = NULL, *srcBegin = src, *srcEnd = NULL;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound;
// Retrieve both the destination and source buffer bounds from the handles.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Memory object not found in pool!\n";
LOAD_STORE_VIOLATION(src,srcPool, SRC_INFO_ARGS);
}
// Check 1) if we write or read past the end of the memory objects, and 2) if
// the memory areas overlap.
dstSize = byte_range(dst, dstEnd);
srcSize = byte_range(src, srcEnd);
if (dstFound && n > dstSize) {
err << "mempcpy() writes past the end of the destination object!\n";
WRITE_VIOLATION(dst, dstPool, dstSize, n, SRC_INFO_ARGS);
}
if (srcFound && n > srcSize) {
err << "mempcpy() reads past the end of the source object!\n";
OOB_VIOLATION(src, srcPool, src, n, SRC_INFO_ARGS);
}
#if 0
if (dstFound && srcFound) {
void *dstEdge = (char *) dst + n - 1;
void *srcEdge = (char *) dst + n - 1;
if (isOverlapped(dst, dstEdge, src, srcEdge)) {
err << "Inputs to mempcpy() overlap!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "mempcpy", SRC_INFO_ARGS);
}
}
#endif
return mempcpy(dst, src, n);
}
#endif
#ifdef HAVE_STRCASESTR
//
// pool_strcasestr()
//
// See pool_strcasestr_debug().
//
char *
pool_strcasestr(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete) {
return pool_strcasestr_debug(s1Pool, s2Pool, s1, s2, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strcasestr()
//
// Searches for the first occurence of the substring s2 in s1, case
// insensitively.
// Returns a pointer to the discovered substring, or NULL if not found.
//
// Attempts to verify that s1 and s2 are valid strings terminated within
// their memory objects' boundaries.
//
// Inputs:
// s1Pool Pool handle for s1
// s2Pool Pool handle for s2
// s1 Pointer to string to be searched
// s2 Pointer to substring to search for
// complete Completeness bit vector
// TAG Tag information for debugging purposes
// SRC_INFO Source file and line number information for debugging purposes
// Returns:
// Returns a pointer to the start of the first case-insensitive occurence of
// s2 in s1, or NULL if not found.
//
char *
pool_strcasestr_debug(DebugPoolTy *s1Pool,
DebugPoolTy *s2Pool,
char *s1,
char *s2,
const uint8_t complete,
TAG,
SRC_INFO) {
const bool s1Complete = ARG1_COMPLETE(complete);
const bool s2Complete = ARG2_COMPLETE(complete);
validStringCheck(s1, s1Pool, s1Complete, "strcasestr", SRC_INFO_ARGS);
validStringCheck(s2, s2Pool, s2Complete, "strcasestr", SRC_INFO_ARGS);
return strcasestr(s1, s2);
}
#endif
#ifdef HAVE_STRNLEN
//
// pool_strnlen()
//
// See pool_strnlen_debug().
//
size_t
pool_strnlen(DebugPoolTy *stringPool,
char *string,
size_t maxlen,
const uint8_t complete) {
return pool_strnlen_debug(stringPool, string, maxlen, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace strnlen()
//
// Like strlen(), but searches for the nul terminator only within the first
// maxlen bytes of the string. If the terminator is not found, then it returns
// the value of maxlen.
//
// Inputs:
// strPool Pool handle for the string
// str Pointer to string to analyze
// maxlen Maximum length to search
// 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 strlen(str) if strlen(str) < maxlen, otherwise it
// returns maxlen.
//
size_t
pool_strnlen_debug(DebugPoolTy *strPool,
char *str,
size_t maxlen,
const uint8_t complete,
TAG,
SRC_INFO) {
size_t safelen, len;
void *strBegin = str, *strEnd = NULL;
const bool strComplete = ARG1_COMPLETE(complete);
bool strFound;
if (!(strFound = pool_find(strPool, str, strBegin, strEnd)) && strComplete) {
err << "String not found in pool!\n";
LOAD_STORE_VIOLATION(str, strPool, SRC_INFO_ARGS);
}
if (strFound) {
// This is the maximum number of characters that can be read from str
// without causing a memory safety error.
safelen = byte_range(str, strEnd);
// Thus the maximum length that _strnlen() can return is safelen - 1.
len = _strnlen(str, std::min(maxlen, safelen));
// If _strnlen() returns safelen, then that means that the string is not
// terminated within the first safelen characters, meaning that if maxlen
// > safelen, we will be reading at least safelen + 1 characters to find
// a nul terminator, which causes a memory safety error.
if (len == safelen && maxlen > safelen) {
err << "strnlen() reads beyond the end of the input string's object!\n";
OOB_VIOLATION(str, strPool, str, safelen + 1, SRC_INFO_ARGS);
} else // If no memory safety error occurred, len is guaranteed to be the
// value of strnlen(str, maxlen), so just return it right away.
return len;
}
return strnlen(str, maxlen);
}
#endif
#ifdef HAVE_STPCPY
//
// pool_stpcpy()
//
// See pool_stpcpy_debug().
//
char *
pool_stpcpy(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
const uint8_t complete) {
return pool_stpcpy_debug(dstPool, srcPool, dst, src, complete, DEFAULTS);
}
//
// Secure runtime wrapper function to replace stpcpy()
//
// Copies the string src to dst and returns a pointer to the nul terminator of
// dst.
//
// Attempts to verify the following:
// - src is nul terminated within memory object bounds
// - src and dst do not overlap
// - dst is long enough to hold src.
//
// Inputs:
// dstPool Pool handle for destination string's pool
// srcPool Pool handle for source string's pool
// dst Pointer to destination of copy operation
// src Pointer to string 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 functions returns the value of &dst[strlen(dst)] after the copy
// operation is completed.
//
char *
pool_stpcpy_debug(DebugPoolTy *dstPool,
DebugPoolTy *srcPool,
char *dst,
char *src,
const uint8_t complete,
TAG,
SRC_INFO) {
void *dstBegin = NULL, *dstEnd = NULL, *srcBegin = NULL, *srcEnd = NULL;
size_t srcLen = 0;
const bool dstComplete = ARG1_COMPLETE(complete);
const bool srcComplete = ARG2_COMPLETE(complete);
bool dstFound, srcFound;
// Find the destination and source strings in their pools.
if (!(dstFound = pool_find(dstPool, dst, dstBegin, dstEnd)) && dstComplete) {
err << "Could not find destination object in pool!\n";
LOAD_STORE_VIOLATION(dst, dstPool, SRC_INFO_ARGS);
}
if (!(srcFound = pool_find(srcPool, src, srcBegin, srcEnd)) && srcComplete) {
err << "Could not find source object in pool\n";
LOAD_STORE_VIOLATION(src, srcPool, SRC_INFO_ARGS);
}
// Check if source is terminated.
if (srcFound && !isTerminated(src, srcEnd, srcLen)) {
err << "Source string not terminated within bounds!\n";
C_LIBRARY_VIOLATION(src, srcPool, "stpcpy", SRC_INFO_ARGS);
}
// The remainder of the checks require both objects to be found.
if (dstFound && srcFound) {
// Check for overlap of objects.
if (isOverlapped(dst, &dst[srcLen], src, &src[srcLen])) {
err << "Copying overlapping strings has undefined behavior!\n";
C_LIBRARY_VIOLATION(dst, dstPool, "stpcpy", SRC_INFO_ARGS);
}
// dstLen is the maximum length string that dst can hold.
size_t dstLen = byte_range(dst, dstEnd) - 1;
// Check for overflow of dst.
if (srcLen > dstLen) {
err << "Destination object too short to hold string!\n";
WRITE_VIOLATION(dst, dstPool, dstLen, srcLen, SRC_INFO_ARGS);
}
}
return stpcpy(dst, src);
}
#endif