blob: 78e8b42caca4b606c35269ab11a5fe9e62d83f8d [file] [log] [blame]
//===- FormatStrings.h - Header for the format string function runtime ----===//
//
// 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 contains definitions of structures and functions used by the
// format string functions in the runtime.
//
//===----------------------------------------------------------------------===//
#ifndef _FORMAT_STRINGS_H
#define _FORMAT_STRINGS_H
#include <cstdio>
#include <cstdarg>
#include <cstddef>
#include <iostream>
#include <map>
#include <stdint.h>
#include "PoolAllocator.h"
//
// Enable support for floating point numbers.
//
#define FLOATING_POINT
//
// The pointer_info structure and associated flags
// This holds a pointer argument to a format string function.
// This structure is initialized by a call to sc.fsparameter.
//
#define ISCOMPLETE 0x01 // Whether the pointer is complete according to DSA
#define ISRETRIEVED 0x02 // Whether there has been an attempt made to retrive
// the target object's boundaries
#define HAVEBOUNDS 0x04 // Whether the boundaries were retrieved successfully
#define NULL_PTR 0x08 // Whether the pointer in the structure is NULL
typedef struct
{
void *ptr; // The pointer which is wrapped by this structure
void *pool; // The pool to which the pointer belongs
void *bounds[2]; // Space for retrieving object boundaries
uint8_t flags; // See above
} pointer_info;
//
// The call_info structure, which is initialized by sc.fscallinfo before a call
// to a format string function.
//
typedef struct
{
uint32_t vargc; // The number of varargs to this function call
uint32_t tag; // tag, line_no, source_file hold debug information
uint32_t line_no;
const char *source_info;
void *whitelist[1]; // This is a list of pointer arguments that the
// format string function should treat as varargs
// arguments which are pointers. These arguments are
// all pointer_info structures. The list is terminated
// by a NULL element.
} call_info;
//
// This structure describes where to print the output for the internal printf()
// wrapper.
//
typedef struct
{
// Options for whether the output string goes.
enum
{
// A dynamically allocated string with a maximum length
OUTPUT_TO_BOUNDED_ALLOCATED_STRING,
// A dynamically allocated string
OUTPUT_TO_ALLOCATED_STRING,
// A string
OUTPUT_TO_STRING,
// A file
OUTPUT_TO_FILE
} output_kind;
union
{
FILE *file;
struct
{
pointer_info *info;
char *string;
size_t pos;
size_t maxsz; // Maximum size of the array that can be written into the
// object safely. (SAFECode-imposed)
size_t n; // The maximum number of bytes to write. (user-imposed)
} string;
struct
{
char *string;
size_t bufsz;
size_t pos;
} alloced_string;
} output;
} output_parameter;
//
// Options for the printf() / scanf() runtime function.
//
#define USE_M_DIRECTIVE 0x01 // Enable parsing of the %m directive
#define POINTERS_UNWRAPPED 0x02 // Pointer arguments aren't wrapped
#define NO_STACK_CHECKS 0x04 // Don't check for va_list going out of bounds
#define NO_WLIST_CHECKS 0x08 // Don't check the whitelist
typedef unsigned options_t;
//
// This structure describes where to get input characters for the internal
// scanf() wrapper.
//
typedef struct
{
enum
{
INPUT_FROM_STREAM,
INPUT_FROM_STRING
} input_kind;
union
{
struct
{
FILE *stream;
char lastch;
} stream;
struct
{
const char *string;
size_t pos;
} string;
} input;
} input_parameter;
//
// Error reporting functions
//
extern void
out_of_bounds_error(call_info *, pointer_info *, size_t);
extern void
write_out_of_bounds_error(call_info *, pointer_info *, size_t, size_t);
extern void
c_library_error(call_info *, const char *);
extern void
load_store_error(call_info *c, pointer_info *p);
//
// Printing/scanning functions
//
extern int
gprintf(
const options_t, output_parameter &, call_info &, pointer_info &, va_list
);
extern int
gscanf(
const options_t, input_parameter &, call_info &, pointer_info &, va_list
);
namespace
{
using std::cerr;
using std::endl;
using std::map;
using namespace llvm;
//
// find_object()
//
// Get the object boundaries of the pointer associated with the pointer_info
// structure.
//
// Inputs:
// c - a pointer to the relevant call_info structure
// p - a pointer to a valid pointer_info structure which contains the pointer
// whose object boundaries should be discovered
//
//
static inline void
find_object(call_info *c, pointer_info *p)
{
if (p->flags & ISRETRIEVED)
return;
DebugPoolTy *pool = (DebugPoolTy *) p->pool;
if (p->ptr == 0)
p->flags |= NULL_PTR;
else if ((pool && pool->Objects.find(p->ptr, p->bounds[0], p->bounds[1])) ||
ExternalObjects->find(p->ptr, p->bounds[0], p->bounds[1]))
{
p->flags |= HAVEBOUNDS;
}
else if (p->flags & ISCOMPLETE)
{
cerr << "Object not found in pool!" << endl;
load_store_error(c, p);
}
p->flags |= ISRETRIEVED;
}
//
// is_in_whitelist()
//
// Check if a (non-NULL) pointer_info structure exists in the whitelist of the
// given call_info structure.
//
static inline bool
is_in_whitelist(call_info *c, const options_t options, pointer_info *p)
{
if (options & NO_WLIST_CHECKS)
return true;
void *val = (options & POINTERS_UNWRAPPED) ? p->ptr : (void *) p;
void **whitelist = c->whitelist;
do
{
if (val == *whitelist)
break;
whitelist++;
} while (*whitelist);
return (*whitelist != 0);
}
//
// object_len()
//
// Get the number of bytes in the object that the pointer associated with the
// pointer_info structure points to, from address the pointer points to, until
// the end of the object.
//
// Note: Call find_object() before calling this.
//
static inline size_t
object_len(pointer_info *p)
{
return 1 + (size_t) ((char *) p->bounds[1] - (char *) p->ptr);
}
//
// write_check()
//
// Check if a write into the object associated with the given pointer_info
// structure of n bytes would be safe.
//
// Inputs:
// c - the relevant call_info structure
// p - the pointer_info structure
// n - the size of the write
//
// This function outputs any relevant SAFECode messages. It returns true if
// the write is to be considered safe, and false otherwise.
//
static inline bool
write_check(call_info *c, const options_t options, pointer_info *p, size_t n)
{
size_t max;
//
// First check if the object is a valid pointer_info structure.
//
if (p == 0 || !is_in_whitelist(c, options, p))
{
cerr << "The destination of the write isn't a valid pointer!" << endl;
c_library_error(c, "va_arg");
return false;
}
//
// Look up the object boundaries.
//
find_object(c, p);
//
// Check for NULL pointer writes.
//
if (p->flags & NULL_PTR)
{
cerr << "Writing into a NULL pointer!" << endl;
c_library_error(c, "va_arg");
return false;
}
else if (p->flags & HAVEBOUNDS)
{
max = object_len(p);
if (n > max)
{
cerr << "Writing out of bounds!" << endl;
write_out_of_bounds_error(c, p, max, n);
return false;
}
else
return true;
}
//
// Assume an object without discovered boundaries has enough space.
//
return true;
}
//
// varg_check()
//
// Check if too many arguments are accessed, if so, report an error.
//
// Inputs:
// c - the call_info structure describing the function call
// pos - the number of the variable argument that the function is trying to
// access
//
// This function returns true if an argument is trying to be accessed beyond
// the arguments that exist to the function call, and false otherwise.
//
static inline bool
varg_check(call_info *c, const options_t options, unsigned pos)
{
if (options & NO_STACK_CHECKS)
return true;
else if (pos > c->vargc)
{
if (c->vargc == 1)
{
cerr << "Attempting to access argument " << pos << \
" but there is only 1 argument!" << endl;
}
else
{
cerr << "Attempting to access argument " << pos << \
" but there are only " << c->vargc << " arguments!" << endl;
}
c_library_error(c, "va_arg");
return true;
}
else
return false;
}
//
// unwrap_pointer()
//
// Get the actual pointer argument from the given parameter. If the parameter
// is whitelisted and so a wrapper, this retrieves the pointer from the
// wrapper. Otherwise it just returns the parameter because it isn't
// recognized as a wrapper.
//
// Inputs:
// c - a pointer to the relevant call_info structure
// p - a pointer to the pointer_info structure to query
//
// Returns:
// This function returns p->ptr if p is a valid pointer_info structure found
// in the whitelist, and p otherwise.
//
static inline void *
unwrap_pointer(call_info *c, const options_t options, void *p)
{
if (is_in_whitelist(c, options, (pointer_info *) p))
return ((pointer_info *) p)->ptr;
else
return p;
}
//
// wrap_pointer()
//
// Wraps a pointer in a pointer_info structure, if pointers are unwrapped.
//
// Inputs:
// options - the options passed to the format string function
// ptr - the pointer to wrap
// mp - a map from pointers to their corresponding wrappers
//
// Returns:
// If (options & POINTERS_UNWRAPPED) is false, returns ptr.
// If (options & POINTERS_UNWRAPPED) is true, this function looks up or
// adds an entry to the map mp which is the wrapped version of ptr.
//
static inline void *
wrap_pointer(const options_t options,
void *ptr,
map<void *, pointer_info *> &mp)
{
if (!(options & POINTERS_UNWRAPPED))
return ptr;
// Try to find the pointer wrapper in the map.
map<void *, pointer_info *>::iterator it = mp.find(ptr);
if (it != mp.end()) {
return it->second;
}
// If not found, create a new entry for the pointer...
pointer_info *&p = mp[ptr] = new pointer_info();
p->ptr = ptr;
p->pool = 0;
p->flags = 0; // Don't add any flags.
return p;
}
}
#endif