blob: cd06a3511aa1673cdced805f7195430202752e3a [file] [log] [blame]
//===- FormatStrings.cpp - Format string function implementation ----------===//
//
// 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 implements intrinsics and runtime wrapper functions for format
// string functions.
//
//===----------------------------------------------------------------------===//
#include "../include/CWE.h"
#include "../include/FormatStringSupport.h"
#include "../include/strnlen.h"
#include "FormatStrings.h"
#include "PoolAllocator.h"
#include "DebugReport.h"
#include <err.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <wchar.h>
#include <iostream>
using std::cerr;
using std::endl;
//
// To do
//
// - Add support for detecting overlapping writes.
// - Add support for puts() which is transformed by compilers from simple
// printf() statements.
//
//
// Error reporting functions
//
void out_of_bounds_error(call_info *c,
pointer_info *p,
size_t obj_len)
{
//
// Attempt to look up information about the memory object for which the
// operation fails.
//
void * ObjStart = 0;
void * ObjEnd = 0;
void * ptr = p->ptr;
PDebugMetaData debugmetadataptr = 0;
dummyPool.DPTree.find (ptr, ObjStart, ObjEnd, debugmetadataptr);
OutOfBoundsViolation v;
v.type = ViolationInfo::FAULT_OUT_OF_BOUNDS;
v.faultPC = __builtin_return_address(0);
v.faultPtr = p->ptr;
v.CWE = CWEBufferOverflow;
v.SourceFile = c->source_info;
v.lineNo = c->line_no;
v.PoolHandle = p->pool;
v.objLen = obj_len;
v.dbgMetaData = debugmetadataptr;
ReportMemoryViolation(&v);
}
void write_out_of_bounds_error(call_info *c,
pointer_info *p,
size_t dst_sz,
size_t src_sz)
{
//
// Attempt to look up information about the memory object for which the
// operation fails.
//
void * ObjStart = 0;
void * ObjEnd = 0;
void * ptr = p->ptr;
PDebugMetaData debugmetadataptr = 0;
dummyPool.DPTree.find (ptr, ObjStart, ObjEnd, debugmetadataptr);
WriteOOBViolation v;
v.type = ViolationInfo::FAULT_WRITE_OUT_OF_BOUNDS;
v.faultPC = __builtin_return_address(0);
v.faultPtr = p->ptr;
v.CWE = CWEBufferOverflow;
v.SourceFile = c->source_info;
v.lineNo = c->line_no;
v.PoolHandle = p->pool;
v.dstSize = dst_sz;
v.srcSize = src_sz;
v.dbgMetaData = debugmetadataptr;
ReportMemoryViolation(&v);
}
void c_library_error(call_info *c, const char *function)
{
CStdLibViolation v;
v.type = ViolationInfo::FAULT_CSTDLIB;
v.faultPC = __builtin_return_address(0);
v.faultPtr = 0;
v.CWE = CWEBufferOverflow;
v.SourceFile = c->source_info;
v.lineNo = c->line_no;
v.function = function;
v.dbgMetaData = NULL;
ReportMemoryViolation(&v);
}
void load_store_error(call_info *c, pointer_info *p)
{
DebugViolationInfo v;
v.type = ViolationInfo::FAULT_LOAD_STORE;
v.faultPC = __builtin_return_address(0);
v.faultPtr = p->ptr;
v.CWE = CWEBufferOverflow;
v.dbgMetaData = NULL;
v.SourceFile = c->source_info;
v.lineNo = c->line_no;
v.PoolHandle = p->pool;
ReportMemoryViolation(&v);
}
//
// Intrinsics
//
//
// __sc_fsparameter
//
// Store the given pointer/pool/completeness information into a pointer_info
// structure that gets passed into the transformed format string function.
//
// Inputs:
// _pool: pointer to the pool to store
// ptr: pointer to store
// _dest: pointer to the pointer_info structure to write the information
// info
// complete: completeness byte
//
// This function returns a pointer to the pointer_info structure (= _dest).
//
void *__sc_fsparameter(void *_pool, void *ptr, void *_dest, uint8_t complete)
{
DebugPoolTy *pool = (DebugPoolTy *) _pool;
pointer_info *dest = (pointer_info *) _dest;
dest->ptr = ptr;
dest->pool = pool;
dest->flags = complete;
return dest;
}
//
// __sc_fscallinfo
//
// Register information about a call to a secured format string function.
// This information is stored into a call_info structure that gets passed into
// the secured format string function.
//
// Inputs:
// _dest: A pointer to the call_info structure to write the information into
// vargc: The number of varargs arguments to the call to the function
//
// The NULL-ended variable argument list consists of the vararg parameters to
// the format string function which are pointer_info structures. The secured
// format string function will only access these values as pointers.
//
// This function returns a pointer to the call_info structure (= _dest).
//
void *__sc_fscallinfo(void *_dest, uint32_t vargc, ...)
{
va_list ap;
call_info *dest = (call_info *) _dest;
dest->vargc = vargc;
void *arg;
unsigned argpos = 0;
va_start(ap, vargc);
do
{
arg = va_arg(ap, void *);
dest->whitelist[argpos++] = arg;
} while (arg != 0);
va_end(ap);
//
// Add empty debugging information.
//
dest->tag = 0;
dest->source_info = "UNKNOWN";
dest->line_no = 0;
return dest;
}
//
// __sc_fscallinfo_debug
//
// Register information about a call to a secured format string function.
// This information is stored into a call_info structure that gets passed into
// the secured format string function and also holds debugging information.
//
// Inputs:
// _dest: A pointer to the call_info structure to write the information into
// vargc: The number of varargs arguments to the call to the function
//
// The NULL-ended variable argument list consists of the vararg parameters to
// the format string function which are pointer_info structures. The secured
// format string function will only access these values as pointers.
//
// After the NULL value is read, there are three more arguments in the
// variable argument list: an integral tag, a char * pointer to a source
// filename, and an integral line number. These are also added to the
// call_info structure.
//
// This function returns a pointer to the call_info structure (= _dest).
//
void *__sc_fscallinfo_debug(void *_dest, uint32_t vargc, ...)
{
va_list ap;
call_info *dest = (call_info *) _dest;
dest->vargc = vargc;
void *arg;
unsigned argpos = 0;
va_start(ap, vargc);
do
{
arg = va_arg(ap, void *);
dest->whitelist[argpos++] = arg;
} while (arg != 0);
//
// Add debugging information.
//
dest->tag = va_arg(ap, uint32_t);
dest->source_info = va_arg(ap, const char *);
dest->line_no = va_arg(ap, uint32_t);
va_end(ap);
return dest;
}
//
// Wrappers for standard library functions
//
//
// Secure runtime wrapper to replace printf()
//
int pool_printf(void *_info, void *_fmt, ...)
{
va_list ap;
int result;
pointer_info *fmt = (pointer_info *) _fmt;
call_info *call = (call_info *) _info;
options_t options = 0x0;
//
// Set up the output parameter structure to point to stdout as the output
// file.
//
output_parameter p;
p.output_kind = output_parameter::OUTPUT_TO_FILE;
p.output.file = stdout;
//
// Lock stdout before calling the function which does the printing.
//
flockfile(stdout);
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
funlockfile(stdout);
return result;
}
//
// Secure runtime wrapper to replace fprintf()
//
int pool_fprintf(void *_info, void *_dest, void *_fmt, ...)
{
va_list ap;
int result;
call_info *call = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
pointer_info *file = (pointer_info *) _dest;
options_t options = 0x0;
//
// Set up the output parameter structure to point to the output file.
//
output_parameter p;
p.output_kind = output_parameter::OUTPUT_TO_FILE;
p.output.file = (FILE *) file->ptr;
//
// Lock the file before calling the function which does the printing.
//
flockfile(p.output.file);
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
funlockfile(p.output.file);
return result;
}
//
// Secure runtime wrapper to replace sprintf()
//
int pool_sprintf(void *_info, void *_dest, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *call = (call_info *) _info;
pointer_info *str = (pointer_info *) _dest;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the output parameter to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_STRING;
p.output.string.string = (char *) str->ptr;
p.output.string.pos = 0;
p.output.string.info = str;
//
// Get the object boundaries of the destination array.
//
find_object(call, str);
if (str->flags & HAVEBOUNDS)
p.output.string.maxsz = (char *) str->bounds[1] - (char *) str->ptr;
else // If boundaries are not found, assume unlimited length.
p.output.string.maxsz = SIZE_MAX;
p.output.string.n = SIZE_MAX; // The caller didn't place a size limitation.
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
//
// Add the terminator byte.
//
p.output.string.string[p.output.string.pos] = '\0';
return result;
}
//
// Secure runtime wrapper to replace snprintf()
//
int pool_snprintf(void *_info, void *_dest, size_t n, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *call = (call_info *) _info;
pointer_info *str = (pointer_info *) _dest;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the output parameter to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_STRING;
p.output.string.string = (char *) str->ptr;
p.output.string.pos = 0;
p.output.string.info = str;
//
// Get the object boundaries of the destination array.
//
find_object(call, str);
if (str->flags & HAVEBOUNDS)
p.output.string.maxsz = (char *) str->bounds[1] - (char *) str->ptr;
else // If boundaries are not found, assume unlimited length.
p.output.string.maxsz = SIZE_MAX;
if (n > 0)
p.output.string.n = n - 1; // Caller-imposed size limitation.
else
p.output.string.n = 0; // Don't write anything.
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
//
// Add the terminator byte, if n is not 0.
// (If n is 0, nothing is written.)
//
if (n > 0)
p.output.string.string[p.output.string.pos] = '\0';
return result;
}
//
// Secure runtime wrapper to replace __printf_chk()
//
// This function is currently identical to pool_printf().
//
int pool___printf_chk(void *_info, int flags, void *_fmt, ...)
{
va_list ap;
int result;
pointer_info *fmt = (pointer_info *) _fmt;
call_info *call = (call_info *) _info;
options_t options = 0x0;
//
// Set up the output parameter structure to point to stdout as the output
// file.
//
output_parameter p;
p.output_kind = output_parameter::OUTPUT_TO_FILE;
p.output.file = stdout;
//
// Lock stdout before calling the function which does the printing.
//
flockfile(stdout);
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
funlockfile(stdout);
return result;
}
//
// Secure runtime wrapper to replace __fprintf_chk()
//
// This function is currently identical to pool_fprintf().
//
int pool___fprintf_chk(void *_info, void *_dest, int flags, void *_fmt, ...)
{
va_list ap;
int result;
call_info *call = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
pointer_info *file = (pointer_info *) _dest;
options_t options = 0x0;
//
// Set up the output parameter structure to point to the output file.
//
output_parameter p;
p.output_kind = output_parameter::OUTPUT_TO_FILE;
p.output.file = (FILE *) file->ptr;
//
// Lock the file before calling the function which does the printing.
//
flockfile(p.output.file);
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
funlockfile(p.output.file);
return result;
}
//
// Secure runtime wrapper to replace __sprintf_chk()
//
// The only difference between this function and pool_sprintf() is that this
// function aborts the program when the parameter 'n' (= the size of the
// output buffer) is 0.
//
int pool___sprintf_chk(void *_i, void *_d, int f, size_t n, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
//
// Abort if n is 0.
//
if (n == 0) {
DebugViolationInfo v;
v.type = DebugViolationInfo::FAULT_LOAD_STORE,
v.faultPC = __builtin_return_address(0),
v.faultPtr = _d;
v.CWE = CWEBufferOverflow;
v.PoolHandle = 0;
v.dbgMetaData = 0;
v.SourceFile = "Unknown";
v.lineNo = 0;
ReportMemoryViolation(&v);
abort();
}
call_info *call = (call_info *) _i;
pointer_info *str = (pointer_info *) _d;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the output parameter to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_STRING;
p.output.string.string = (char *) str->ptr;
p.output.string.pos = 0;
p.output.string.info = str;
//
// Get the object boundaries of the destination array.
//
find_object(call, str);
if (str->flags & HAVEBOUNDS)
p.output.string.maxsz = (char *) str->bounds[1] - (char *) str->ptr;
else // If boundaries are not found, assume unlimited length.
p.output.string.maxsz = SIZE_MAX;
p.output.string.n = SIZE_MAX; // The caller didn't place a size limitation.
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
//
// Add the terminator byte.
//
p.output.string.string[p.output.string.pos] = '\0';
return result;
}
//
// Secure runtime wrapper to replace __snprintf_chk()
//
// This function is the same as pool_snprintf(), except that it aborts the
// program when strlen (= the size of the output buffer) < n.
//
int
pool___snprintf_chk(void *_info,
void *_dest,
size_t n,
int flag,
size_t strlen,
void *_fmt,
...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
//
// Abort if strlen < n.
//
if (strlen < n) {
DebugViolationInfo v;
v.type = DebugViolationInfo::FAULT_LOAD_STORE,
v.faultPC = __builtin_return_address(0),
v.faultPtr = _dest;
v.CWE = CWEBufferOverflow;
v.PoolHandle = 0;
v.dbgMetaData = 0;
v.SourceFile = "Unknown";
v.lineNo = 0;
ReportMemoryViolation(&v);
abort();
}
call_info *call = (call_info *) _info;
pointer_info *str = (pointer_info *) _dest;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the output parameter to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_STRING;
p.output.string.string = (char *) str->ptr;
p.output.string.pos = 0;
p.output.string.info = str;
//
// Get the object boundaries of the destination array.
//
find_object(call, str);
if (str->flags & HAVEBOUNDS)
p.output.string.maxsz = (char *) str->bounds[1] - (char *) str->ptr;
else // If boundaries are not found, assume unlimited length.
p.output.string.maxsz = SIZE_MAX;
if (n > 0)
p.output.string.n = n - 1; // Caller-imposed size limitation.
else
p.output.string.n = 0; // Don't write anything.
va_start(ap, _fmt);
result = gprintf(options, p, *call, *fmt, ap);
va_end(ap);
//
// Add the terminator byte, if n is not 0.
// (If n is 0, nothing is written.)
//
if (n > 0)
p.output.string.string[p.output.string.pos] = '\0';
return result;
}
//
// For functions err(), errx(), warn(), warnx(), and syslog(), which do
// additional work beyond format string processing, we first print the
// string into an allocate buffer, then pass the result into the actual
// function to let it do its additional work.
//
// This is the size of the string to initially allocate for printing into.
//
static const size_t INITIAL_ALLOC_SIZE = 64;
//
// If the wrapper function needs to pass a formatted result to another
// function, but there's been an error during the formatting, it uses this
// message instead.
//
static const char *message_error = "SAFECode: error building message";
//
// Secure runtime wrapper to replace err()
//
void pool_err(void *_info, int eval, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// On a NULL format string, no formatted message is output.
//
if (fmt->ptr == 0)
err(eval, 0); // Doesn't return.
//
// Set up the output parameter to allocate space to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_ALLOCATED_STRING;
p.output.alloced_string.string = (char *) malloc(INITIAL_ALLOC_SIZE);
p.output.alloced_string.bufsz = INITIAL_ALLOC_SIZE;
p.output.alloced_string.pos = 0;
//
// Print into the allocated string.
//
va_start(ap, _fmt);
result = gprintf(options, p, *info, *fmt, ap);
va_end(ap);
//
// Print the resulting string if there was no error making it.
//
if (result < 0)
err(eval, "%s", message_error);
else
// This call exits the program; we can't free the allocated string.
err(eval, "%.*s", result, p.output.alloced_string.string);
}
//
// Secure runtime wrapper to replace errx()
//
void pool_errx(void *_info, int eval, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// On a NULL format string, no formatted message is output.
//
if (fmt->ptr == 0)
errx(eval, 0); // Doesn't return.
//
// Set up the output parameter to allocate space to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_ALLOCATED_STRING;
p.output.alloced_string.string = (char *) malloc(INITIAL_ALLOC_SIZE);
p.output.alloced_string.bufsz = INITIAL_ALLOC_SIZE;
p.output.alloced_string.pos = 0;
//
// Print into the allocated string.
//
va_start(ap, _fmt);
result = gprintf(options, p, *info, *fmt, ap);
va_end(ap);
//
// Print the resulting string if there was no error making it.
//
if (result < 0)
errx(eval, "%s", message_error);
else
// This call exits the program; we can't free the allocated string.
errx(eval, "%.*s", result, p.output.alloced_string.string);
}
//
// Secure runtime wrapper to replace warn()
//
void pool_warn(void *_info, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// On a NULL format string, no formatted message is output.
//
if (fmt->ptr == 0)
{
warn(0);
return;
}
//
// Set up the output parameter to allocate space to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_ALLOCATED_STRING;
p.output.alloced_string.string = (char *) malloc(INITIAL_ALLOC_SIZE);
p.output.alloced_string.bufsz = INITIAL_ALLOC_SIZE;
p.output.alloced_string.pos = 0;
//
// Print into the allocated string.
//
va_start(ap, _fmt);
result = gprintf(options, p, *info, *fmt, ap);
va_end(ap);
//
// Print and free the resulting string if there was no error making it.
//
if (result < 0)
warn("%s", message_error);
else
{
warn("%.*s", result, p.output.alloced_string.string);
free(p.output.alloced_string.string);
}
return;
}
//
// Secure runtime wrapper to replace warnx()
//
void pool_warnx(void *_info, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// On a NULL format string, no formatted message is output.
//
if (fmt->ptr == 0)
{
warnx(0);
return;
}
//
// Set up the output parameter to allocate space to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_ALLOCATED_STRING;
p.output.alloced_string.string = (char *) malloc(INITIAL_ALLOC_SIZE);
p.output.alloced_string.bufsz = INITIAL_ALLOC_SIZE;
p.output.alloced_string.pos = 0;
//
// Print into the allocated string.
//
va_start(ap, _fmt);
result = gprintf(options, p, *info, *fmt, ap);
va_end(ap);
//
// Print and free the resulting string if there was no error making it.
//
if (result < 0)
warnx("%s", message_error);
else
{
warnx("%.*s", result, p.output.alloced_string.string);
free(p.output.alloced_string.string);
}
return;
}
//
// Secure runtime wrapper to replace syslog()
//
void pool_syslog(void *_info, int priority, void *_fmt, ...)
{
va_list ap;
int result;
output_parameter p;
options_t options = 0x0;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the output parameter to allocate space to output into a string.
//
p.output_kind = output_parameter::OUTPUT_TO_ALLOCATED_STRING;
p.output.alloced_string.string = (char *) malloc(INITIAL_ALLOC_SIZE);
p.output.alloced_string.bufsz = INITIAL_ALLOC_SIZE;
p.output.alloced_string.pos = 0;
//
// Print into the allocated string.
//
va_start(ap, _fmt);
result = gprintf(options, p, *info, *fmt, ap);
va_end(ap);
//
// Print and free the resulting string if there was no error making it.
//
if (result < 0)
syslog(priority, "%s", message_error);
else
{
syslog(priority, "%.*s", result, p.output.alloced_string.string);
free(p.output.alloced_string.string);
}
return;
}
//
// Secure runtime wrapper function to replace scanf()
//
int pool_scanf(void *_info, void *_fmt, ...)
{
va_list ap;
int result;
input_parameter p;
call_info *info = (call_info *) _info;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set up the input parameter to read from stdin.
//
p.input_kind = input_parameter::INPUT_FROM_STREAM;
p.input.stream.stream = stdin;
//
// Lock stdin before calling the function to do the scanning.
//
flockfile(stdin);
va_start(ap, _fmt);
result = gscanf(0, p, *info, *fmt, ap);
va_end(ap);
funlockfile(stdin);
return result;
}
//
// Secure runtime wrapper function to replace fscanf()
//
int pool_fscanf(void *_info, void *_src, void *_fmt, ...)
{
va_list ap;
int result;
input_parameter p;
call_info *info = (call_info *) _info;
pointer_info *str = (pointer_info *) _src;
pointer_info *fmt = (pointer_info *) _fmt;
FILE *stream = (FILE *) str->ptr;
//
// Set the input parameter to read from the input stream.
//
p.input_kind = input_parameter::INPUT_FROM_STREAM;
p.input.stream.stream = stream;
//
// Lock the stream before calling the function to do the scanning
//
flockfile(stream);
va_start(ap, _fmt);
result = gscanf(0, p, *info, *fmt, ap);
va_end(ap);
funlockfile(stream);
return result;
}
//
// Secure runtime wrapper to replace sscanf()
//
int pool_sscanf(void *_info, void *_str, void *_fmt, ...)
{
va_list ap;
int result;
input_parameter p;
call_info *info = (call_info *) _info;
pointer_info *str = (pointer_info *) _str;
pointer_info *fmt = (pointer_info *) _fmt;
//
// Set the input parameter to read from a string
//
p.input_kind = input_parameter::INPUT_FROM_STRING;
p.input.string.string = (const char *) str->ptr;
p.input.string.pos = 0;
//
// Check if the input string is terminated within object boundaries,
// if we have the object boundaries.
//
find_object(info, str);
if (str->flags & HAVEBOUNDS)
{
const char *string = (const char *) str->ptr;
size_t maxlen = (char *) str->bounds[1] - string;
size_t len = _strnlen(string, 1 + maxlen);
if (len > maxlen)
{
cerr << "Input string not terminated within object bounds!" << endl;
out_of_bounds_error(info, str, len);
}
}
va_start(ap, _fmt);
result = gscanf(0, p, *info, *fmt, ap);
va_end(ap);
return result;
}
extern int
internal_printf(
const options_t, output_parameter &, call_info &, const char *, va_list
);
extern int
internal_scanf(
const options_t, input_parameter &, call_info &, const char *, va_list
);
//
// gprintf()
//
// Secure general printf() family replacement
//
// Attempts to verify the following:
// - The output string is not written to out of bounds, if there is an output
// string specified.
// - The format string is not read out of bounds.
// - A %s format directive will not result in an out of bounds read of a
// string.
// - A %n format directive will not result in an out of bounds write into
// a parameter.
// - Only the variable arguments that were passed are accessed.
//
// Arguments:
// Options: Parameter describing whether to use various features
// (eg. enable/disable parsing of the %m directive)
// Output: Information about where to print the output
// CInfo: Information about the call parameters (call_info structure)
// FormatString: pointer_info structure holding the format string
// Args: The list of arguments to the format string
//
// Return values:
// If successful, the function returns the number of characters that would have
// been printed if the output had unbounded space.
//
// If a (non memory-safety) error occurred, the function returns a negative
// value.
//
int
gprintf(const options_t Options,
output_parameter &Output,
call_info &CInfo,
pointer_info &FormatString,
va_list Args)
{
int result;
const char *Fmt;
//
// Get the object boundaries for the format string.
//
find_object(&CInfo, &FormatString);
Fmt = (const char *) FormatString.ptr;
//
// Make sure the format string isn't NULL.
//
if (Fmt == 0)
{
cerr << "NULL format string!" << endl;
c_library_error(&CInfo, "printf");
return 0;
}
//
// Check to make sure the format string is within object boundaries, if we
// have them.
//
if (FormatString.flags & HAVEBOUNDS)
{
size_t maxbytes = 1 + (char *) FormatString.bounds[1] - Fmt;
size_t len = _strnlen(Fmt, maxbytes);
if (len == maxbytes)
{
cerr << "Format string not terminated within object bounds!" << endl;
out_of_bounds_error(&CInfo, &FormatString, len);
}
}
result = internal_printf(Options, Output, CInfo, Fmt, Args);
return result;
}
//
// gscanf()
//
// Secure general scanf() family replacement
//
// Attempts to verify the following:
// - The format string is not read out of bounds.
// - Only the variable arguments that were passed are accessed.
// - A format directive which writes into a variable argument is writing into
// a destination object that is large enough to hold the write.
//
// Arguments:
// Input - An input_parameter structure describing the input
// CInfo - A call_info structure describing the call arguments
// FormatString - The pointer_info structure for the format string
// Args - The list of arguments
//
// Returns:
// The function returns the number of format directives that were
// successfully matched with the input, or EOF if there was an input failure
// before any directives could be matched.
//
int
gscanf(const options_t Options,
input_parameter &Input,
call_info &CInfo,
pointer_info &FormatString,
va_list Args)
{
int result;
const char *Fmt;
//
// Get the object boundaries for the formating string.
//
find_object(&CInfo, &FormatString);
Fmt = (const char *) FormatString.ptr;
//
// Make sure the format string isn't NULL.
//
if (Fmt == 0)
{
cerr << "NULL format string!" << endl;
c_library_error(&CInfo, "scanf");
return 0;
}
//
// Check to make sure the format string is a nul-terminated within the
// boundaries of its object, if we have the boundaries.
//
if (FormatString.flags & HAVEBOUNDS)
{
size_t maxbytes = 1 + (char *) FormatString.bounds[1] - Fmt;
size_t len = _strnlen(Fmt, maxbytes);
if (len == maxbytes)
{
cerr << "Format string not terminated within object bounds!" << endl;
out_of_bounds_error(&CInfo, &FormatString, len);
}
}
result = internal_scanf(Options, Input, CInfo, Fmt, Args);
return result;
}