//===- FormatStrings.cpp - Secure calls to printf/scanf style functions ---===//
// 
//                            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 a pass to insert calls to runtime wrapper functions for
// printf(), scanf(), and related format string functions.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "formatstrings"

#include "llvm/ADT/Statistic.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Support/CallSite.h"

#include "safecode/FormatStrings.h"
#include "safecode/Utility.h"
#include "safecode/VectorListHelper.h"

#include <set>
#include <map>
#include <vector>
#include <algorithm>

using std::map;
using std::max;
using std::set;
using std::vector;

namespace llvm
{

static RegisterPass<FormatStringTransform>
R("formatstrings", "Secure calls to format string functions");

#define ADD_STATISTIC_FOR(func) \
  STATISTIC(stat_ ## func, "Number of calls to " #func "() that were secured")

ADD_STATISTIC_FOR(printf);
ADD_STATISTIC_FOR(fprintf);
ADD_STATISTIC_FOR(sprintf);
ADD_STATISTIC_FOR(snprintf);
ADD_STATISTIC_FOR(err);
ADD_STATISTIC_FOR(errx);
ADD_STATISTIC_FOR(warn);
ADD_STATISTIC_FOR(warnx);
ADD_STATISTIC_FOR(syslog);
ADD_STATISTIC_FOR(scanf);
ADD_STATISTIC_FOR(fscanf);
ADD_STATISTIC_FOR(sscanf);
ADD_STATISTIC_FOR(__printf_chk);
ADD_STATISTIC_FOR(__fprintf_chk);
ADD_STATISTIC_FOR(__sprintf_chk);
ADD_STATISTIC_FOR(__snprintf_chk);
ADD_STATISTIC_FOR(__isoc99_scanf);
ADD_STATISTIC_FOR(__isoc99_fscanf);
ADD_STATISTIC_FOR(__isoc99_sscanf);

char FormatStringTransform::ID = 0;


//
// Constructs a FunctionType which is consistent with the type of a tranformed
// format string function.
//
// Inputs:
//   argc - the expected number of fixed arguments the function type takes
//   F    - the original function type
//
FunctionType *
FormatStringTransform::xfrmFType(FunctionType *F, LLVMContext &ctx) const
{
  Type *int8ptr = Type::getInt8PtrTy(ctx);
  vector<Type *> NewParamTypes;
  //
  // The initial argument is a pointer to the call_info structure.
  //
  NewParamTypes.push_back(int8ptr);

  //
  // Append all other arguments.
  //
  FunctionType::param_iterator i = F->param_begin(), end = F->param_end();
  for (; i != end; ++i)
    NewParamTypes.push_back(isa<PointerType>(*i) ? int8ptr : *i);

  return FunctionType::get(F->getReturnType(), NewParamTypes, true);
}

bool
FormatStringTransform::runOnModule(Module &M)
{
  //
  // Get the type of the pointer_info structure.
  //
  PointerInfoType = makePointerInfoType(M.getContext());

  FSCallInfo = FSParameter = 0;

  bool changed = false;

  struct FormatStringFuncEntry
  {
    const char *name;
    unsigned fargc;
    Statistic *stat;
    const char *replacement;
  } Entries[] =
  {
    { "printf",          1, &stat_printf,          "pool_printf"              },
    { "fprintf",         2, &stat_fprintf,         "pool_fprintf"             },
    { "sprintf",         2, &stat_sprintf,         "pool_sprintf"             },
    { "snprintf",        3, &stat_snprintf,        "pool_snprintf"            },
    { "err",             2, &stat_err,             "pool_err"                 },
    { "errx",            2, &stat_errx,            "pool_errx"                },
    { "warn",            1, &stat_warn,            "pool_warn"                },
    { "warnx",           1, &stat_warnx,           "pool_warnx"               },
    { "syslog",          2, &stat_syslog,          "pool_syslog"              },
    { "scanf",           1, &stat_scanf,           "pool_scanf"               },
    { "fscanf",          2, &stat_fscanf,          "pool_fscanf"              },
    { "sscanf",          2, &stat_sscanf,          "pool_sscanf"              },
    //
    // The __printf_chk() family is like printf(), but it attempts to make sure
    // the stack isn't accessed improperly. The SAFECode runtime also does this
    // (and more) so we can transform calls to this function.
    //
    { "__printf_chk",    2, &stat___printf_chk,    "pool___printf_chk"        },
    { "__fprintf_chk",   3, &stat___fprintf_chk,   "pool___fprintf_chk"       },
    { "__sprintf_chk",   4, &stat___sprintf_chk,   "pool___sprintf_chk"       },
    { "__snprintf_chk",  5, &stat___snprintf_chk,  "pool___snprintf_chk"      },
    //
    // The __isoc99_scanf() family is found in glibc and is like scanf() without
    // GNU extensions, which is the same functionality as the SAFECode version.
    //
    { "__isoc99_scanf",  1, &stat___isoc99_scanf,  "pool_scanf"               },
    { "__isoc99_fscanf", 2, &stat___isoc99_fscanf, "pool_fscanf"              },
    { "__isoc99_sscanf", 2, &stat___isoc99_sscanf, "pool_sscanf"              }
  };

  for (size_t i = 0; i < sizeof(Entries) / sizeof(FormatStringFuncEntry); i++)
  {
    FormatStringFuncEntry &e = Entries[i];
    changed |= transform(M, e.name, e.fargc, e.replacement, *e.stat);
  }

  //
  // The transformations use placehold arrays of size 0. This call changes
  // those arrays to be allocated to the proper size.
  //
  if (changed)
    fillArraySizes(M);

  return changed;
}

//
// Adds declarations of the format string function intrinsics
// sc.fsparameter and sc.callinfo into the given module.
//
// Sets the value of FSParameter and FSCallInfo to the relevant intrinsics.
//
void
FormatStringTransform::addFormatStringIntrinsics(Module &M)
{
  Type *int8    = Type::getInt8Ty(M.getContext());
  Type *int32   = Type::getInt32Ty(M.getContext());
  Type *int8ptr = Type::getInt8PtrTy(M.getContext());
  //
  // Build parameter lists.
  //
  vector<Type *> FSPArgs =
    args<Type *>::list(int8ptr, int8ptr, int8ptr, int8);
  vector<Type *> FSCIArgs = args<Type *>::list(int8ptr, int32);
  //
  // Build the function types.
  //
  FunctionType *FSParameterType = FunctionType::get(int8ptr, FSPArgs, false);
  FunctionType *FSCallInfoType  = FunctionType::get(int8ptr, FSCIArgs, true);
  //
  // Check if the functions are already declared.
  //
  Function *FSParameterInModule = M.getFunction("__sc_fsparameter");
  Function *FSCallInfoInModule  = M.getFunction("__sc_fscallinfo");
  if (FSParameterInModule != 0)
    assert((FSParameterInModule->getFunctionType() == FSParameterType ||
      FSParameterInModule->hasLocalLinkage()) &&
      "Intrinsic declared with wrong type!");
  if (FSCallInfoInModule != 0)
    assert((FSCallInfoInModule->getFunctionType() == FSCallInfoType ||
      FSParameterInModule->hasLocalLinkage()) &&
      "Intrinsic declared with wrong type!");
  //
  // Add the function declarations to the module and globally for this pass.
  //
  FSParameter = M.getOrInsertFunction("__sc_fsparameter", FSParameterType);
  FSCallInfo  = M.getOrInsertFunction("__sc_fscallinfo",  FSCallInfoType);
}

//
// Transform all calls of a given function into their secured analogue.
//
// A format string function of the form
//
//   int xprintf(arg1, arg2, ...);
//
// will be transformed into a call of the function of the form
//
//   int pool_xprintf(call_info *, arg1, arg2, ...);
//
// with the call_info * structure containing information about the vararg
// arguments passed into the call. All pointer arguments to the call will
// be wrapped around a pointer_info structure. The space for the call_info
// and pointer_info structures is allocated on the stack.
//
// Inputs:
//  M           - a reference to the current Module
//  name        - the name of the function to transform
//  argc        - the number of (fixed) arguments to the function
//  replacement - the name of the resulting function
//  stat        - a statistic pertaining to the number of transfomations
//                that have been performed
//
// Returns:
//  This function returns true if the module was modified, false otherwise.
//
bool
FormatStringTransform::transform(Module &M,
                                 const char *name,
                                 unsigned argc,
                                 const char *replacement,
                                 Statistic &stat)
{
  Function *f = M.getFunction(name);
  if (f == 0)
    return false;

  //
  // Ensure the function is of the expected type. If not, skip over it.
  //
  FunctionType *fType = f->getFunctionType();
  if (!fType->isVarArg() || fType->getNumParams() != argc)
    return false;

  vector<CallSite> Calls;

  //
  // Locate all the instructions which call the named function.
  //
  for (Function::use_iterator i = f->use_begin(); i != f->use_end(); ++i)
  {
    CallSite C(*i);
    if (!C || C.getCalledFunction() != f)
      continue;
    Calls.push_back(C);
  }

  if (Calls.empty())
    return false;

  FunctionType *rType = xfrmFType(fType, f->getContext());
#ifndef NDEBUG
  Function *found = M.getFunction(replacement);
  assert((found == 0 || found->getFunctionType() == rType
    || found->hasLocalLinkage()) && 
    "Replacement function already declared in module with incorrect type");
#endif

  Value *replacementFunc = M.getOrInsertFunction(replacement, rType);

  //
  // If we get this far, make sure the intrinsics have been declared so we can
  // call them.
  //
  if (FSParameter == 0 || FSCallInfo == 0)
    addFormatStringIntrinsics(M);

  //
  // Iterate over the found call sites and replace them with transformed
  // calls.
  //
  vector<CallSite>::iterator i = Calls.begin(), end = Calls.end();
  for (; i != end; ++i)
  {
    Instruction *OldCall = i->getInstruction();
    CallInst *NewCall = buildSecuredCall(replacementFunc, *i);
    NewCall->insertBefore(OldCall);
    OldCall->replaceAllUsesWith(NewCall);
    //
    // When transforming an invoke instruction, create a branch to the normal
    // label, since the transformed call doesn't throw exceptions.
    //
    if (isa<InvokeInst>(OldCall))
    {
      InvokeInst *Invoke = cast<InvokeInst>(OldCall);
      removeInvokeUnwindPHIs(Invoke);
      BranchInst *Br = BranchInst::Create(Invoke->getNormalDest());
      Invoke->eraseFromParent();
      Br->insertAfter(NewCall);
    }
    else
      OldCall->eraseFromParent();
    ++stat;
  }

  return true;
}

//
// Goes over all the arrays that were allocated as helpers to the intrinsics
// and makes them the proper size.
//
void
FormatStringTransform::fillArraySizes(Module &M)
{
  LLVMContext &C = M.getContext();
  IRBuilder<> builder(C);
  Type *int8ptr = Type::getInt8PtrTy(C);
  Type *int32 = Type::getInt32Ty(C);

  //
  // Make the CallInfo structure allocations the right size.
  //
  for (map<Function *, unsigned>::iterator i = CallInfoWhitelistSizes.begin();
       i != CallInfoWhitelistSizes.end();
       ++i)
  {
    Function *f = i->first;
    unsigned count = i->second;
    Type *CIType = makeCallInfoType(C, count);
    AllocaInst *newAlloc = builder.CreateAlloca(CIType);
    Instruction *newCast = cast<Instruction>(
      builder.CreateBitCast(newAlloc, int8ptr)
    );

    //
    // The CallInfo structure is cast to i8* before being passed into any
    // function calls.
    //
    // The placeholder cast is located in CallInfoStructures.
    //
    Instruction *oldCast  = CallInfoStructures[f];
    Instruction *oldAlloc = cast<Instruction>(oldCast->getOperand(0));

    newAlloc->insertBefore(oldAlloc);
    newCast->insertAfter(newAlloc);
    oldCast->replaceAllUsesWith(newCast);

    oldCast->eraseFromParent();
    oldAlloc->eraseFromParent();
  }

  //
  // Make the PointerInfo structure array allocations the right size.
  //
  for (map<Function *, unsigned>::iterator i = PointerInfoAllocSizes.begin();
       i != PointerInfoAllocSizes.end();
       ++i)
  {
    Function *f = i->first;
    Instruction *oldAlloc = PointerInfoStructures[f];
    Value *sz = ConstantInt::get(int32, i->second);
    AllocaInst *newAlloc = builder.CreateAlloca(PointerInfoType, sz);
    newAlloc->insertBefore(oldAlloc);
    oldAlloc->replaceAllUsesWith(newAlloc);
    oldAlloc->eraseFromParent();
  }

}

//
// Builds a call to fsparameter which registers the given parameter as a
// pointer.
//
// Inputs:
//   arg - the pointer value / instruction pair to register
//
// The function inserts the call to fsparameter before the associated
// instruction.
// Since only one call is needed to fsparameter per pointer / instruction pair,
// the function keeps track of redundant calls to itself and returns the same
// Value each time.
//
// Output:
//   The function returns a Value which is the result of wrapping the pointer
//   parameter using fsparameter. The type is i8 *.
//
Value *
FormatStringTransform::wrapPointerArgument(PointerArgument arg)
{
  //
  // Determine if the value has already been registered for this instruction.
  // If so, return the registered value.
  //
  if (FSParameterCalls.count(arg))
    return FSParameterCalls[arg];

  Instruction *i = arg.first;
  Value *ptr     = arg.second;

  Function *f = i->getParent()->getParent();
  LLVMContext &ctx = f->getContext();
  IRBuilder<> builder(ctx);

  //
  // Otherwise use the next free PointerInfo structure.
  //
  // First determine if the array of PointerInfo structures has already
  // been allocated on the function's stack. If not, do so.
  //
  if (!PointerInfoAllocSizes.count(f))
  {
    Value *zero = ConstantInt::get(Type::getInt32Ty(ctx), 0);
    AllocaInst *allocation = builder.CreateAlloca(PointerInfoType, zero);
    //
    // Allocate the array at the entry point of the function.
    //
    BasicBlock::InstListType &instList =
      i->getParent()->getParent()->getEntryBlock().getInstList();
    instList.insert(instList.begin(), allocation);
    PointerInfoStructures[f] = allocation;
    PointerInfoAllocSizes[f] = 0;
  }

  //
  // This is the index of the array that will be used.
  //
  const unsigned nextStructure = PointerInfoArrayUsage[i]++;
  Type *int8    = Type::getInt8Ty(ctx);
  Type *int8ptr = Type::getInt8PtrTy(ctx);

  //
  // Update the per-function count of the number of pointer_info structures
  // that are used. This used is for allocating the correct size on the stack in
  // fillArraySizes().
  //
  PointerInfoAllocSizes[f] = max(PointerInfoAllocSizes[f], 1 + nextStructure);

  //
  // Index into the next free position in the PointerInfo array.
  //
  Value *array = PointerInfoStructures[f];
  Instruction *gep = cast<Instruction>(
    builder.CreateConstGEP1_32(array, nextStructure)
  );
  Instruction *bitcast = cast<Instruction>(
    builder.CreateBitCast(gep, int8ptr)
  );
  gep->insertBefore(i);
  bitcast->insertBefore(i);

  //
  // Create the fsparameter call and insert it before the given instruction.
  // Also store it for later use if necessary (if the same parameter is
  // registered for the same instruction).
  //
  Value *castedParameter = ptr;
  if (castedParameter->getType() != int8ptr)
  {
    castedParameter = builder.CreateBitCast(ptr, int8ptr);
    if (isa<Instruction>(castedParameter))
      cast<Instruction>(castedParameter)->insertBefore(i);
  }
  vector<Value *> FSArgs(4);
  FSArgs[0] = ConstantPointerNull::get(cast<PointerType>(int8ptr));
  FSArgs[1] = castedParameter;
  FSArgs[2] = bitcast;
  FSArgs[3] = ConstantInt::get(int8, 0);
  CallInst *FSCall = builder.CreateCall(FSParameter, FSArgs);
  FSCall->insertBefore(i);
  FSParameterCalls[arg] = FSCall;

  return FSCall;
}

//
// Builds a call to callinfo which registers information about the given
// call to a format string function.
//
// Inputs:
//  i       - the instruction associated with the call to the format string
//            function
//  vargc   - the number of variable arguments in the call to register
//  PVArguments - every variable pointer argument to the call of the format
//                string function that should be whitelisted
//                 
// This function returns a Value suitable as the first parameter to a
// transformed format string function like pool_printf.
//
Value *
FormatStringTransform::addCallInfo(Instruction *i,
                                   uint32_t vargc,
                                   const set<Value *> &PVArguments)
{
  LLVMContext &ctx = i->getContext();
  IRBuilder<> builder(ctx);
  const unsigned pargc = PVArguments.size();
  Type *int8ptr  = Type::getInt8PtrTy(ctx);

  Function *f = i->getParent()->getParent();
  //
  // Allocate the CallInfo structure at the entry point of the function if
  // necessary. The allocated structure will be a placeholder.
  //
  if (!CallInfoStructures.count(f))
  {
    Value *zero = ConstantInt::get(Type::getInt32Ty(ctx), 0);
    Type *CInfoType = makeCallInfoType(ctx, 0);
    AllocaInst *allocation = builder.CreateAlloca(CInfoType, zero);

    //
    // Place this allocation at the function entry.
    //
    BasicBlock::InstListType &instList =
      i->getParent()->getParent()->getEntryBlock().getInstList();
    instList.insert(instList.begin(), allocation);

    //
    // Bitcast it into (i8 *) because that is the type for which it is used as
    // a parameter to sc.fscallinfo.
    //
    Instruction *bitcast = cast<Instruction>(
      builder.CreateBitCast(allocation, int8ptr)
    );
    bitcast->insertAfter(allocation);

    CallInfoStructures[f]  = bitcast;
    CallInfoWhitelistSizes[f] = 0;
  }

  //
  // Update the per-function count of the max size of the whitelist in the
  // call_info structure. Later fillArraySizes() will allocate a structure
  // with enough space to hold a whitelist for each registered
  // call in the function.
  //
  CallInfoWhitelistSizes[f] = max(CallInfoWhitelistSizes[f], pargc);

  Value *cInfo = CallInfoStructures[f];
  Value *null = ConstantPointerNull::get(cast<PointerType>(int8ptr));
  vector<Value *> Params;

  //
  // Build the parameters to the callinfo call.
  //
  Params.push_back(cInfo);
  Params.push_back(
    ConstantInt::get(Type::getInt32Ty(ctx), vargc)
  );
  Params.insert(Params.end(), PVArguments.begin(), PVArguments.end());
  //
  // Append NULL to terminate the variable argument list and finally build the
  // completed call instruction.
  //
  Params.push_back(null);
  CallInst *c = builder.CreateCall(FSCallInfo, Params);
  c->insertBefore(i);

  //
  // Add to the new call instruction any debugging metadata that the old call
  // had. This will be passed over to the call to the transformed function.
  //
  if (MDNode *DebugMetaData = i->getMetadata("dbg"))
    c->setMetadata("dbg", DebugMetaData);

  return c;
}

//
// Builds a call instruction to newFunc out of the existing call instruction.
// The new call uses the same arguments as the old call, except that pointer
// arguments to the old call are first wrapped using sc.fsparameter before
// being passed into the new call.
//
// Inputs:
//   newFunc - the function to which a call will be built
//   oldCall - a reference to the CallSite to transform
//
// Returns:
//   This function returns a CallInst that replaces the old instruction.
//
CallInst *
FormatStringTransform::buildSecuredCall(Value *newFunc, CallSite &oldCall)
{
  set<Value *> pointerVArgs;
  const unsigned fargc = \
    oldCall.getCalledFunction()->getFunctionType()->getNumParams();
  const unsigned argc  = oldCall.arg_size();
  const unsigned vargc = argc - fargc;
  vector<Value *> NewArgs(1 + argc);
  Instruction *cInst = oldCall.getInstruction();
  //
  // Build the parameters to the new call, creating wrappers with
  // sc.fsparameter when necessary.
  //
  for (unsigned i = 0; i < argc; ++i)
  {
    Value *arg = oldCall.getArgument(i);
    if (!isa<PointerType>(arg->getType()))
      NewArgs[i + 1] = arg;
    else
    {
      Value *wrapped = wrapPointerArgument(PointerArgument(cInst, arg));
      NewArgs[i + 1] = wrapped;
      //
      // If this is a variable pointer argument, it should be registered with
      // the callinfo intrinsic.
      //
      if (i >= fargc)
        pointerVArgs.insert(wrapped);
    }
  }
  //
  // Build the CallInfo structure for the new call.
  //
  NewArgs[0] = addCallInfo(cInst, vargc, pointerVArgs);
  //
  // Construct the new call instruction.
  //
  return CallInst::Create(newFunc, NewArgs);
}

//
// Creates the type of the PointerInfo structure.
// This is defined in FormatStringRuntime.h as
//
//   typedef struct
//   {
//      void *ptr;
//      void *pool;
//      void *bounds[2];
//      uint8_t flags;
//   } pointer_info;
//
// The fields are used as follows:
//  - ptr holds the pointer parameter that was passed.
//  - pool holds the pool that ptr belongs to.
//  - bounds are intended to be filled at runtime with the memory object
//    boundaries of ptr.
//  - flags holds various information about the pointer, regarding completeness
//    etc.
//
Type *
FormatStringTransform::makePointerInfoType(LLVMContext &ctx) const
{
  Type *int8         = Type::getInt8Ty(ctx);
  Type *int8ptr      = Type::getInt8PtrTy(ctx);
  Type *int8ptr_arr2 = ArrayType::get(int8ptr, 2);
  vector<Type *> PointerInfoFields =
    args<Type *>::list(int8ptr, int8ptr, int8ptr_arr2, int8);
  return StructType::get(ctx, PointerInfoFields);
}

//
// Creates the type of the CallInfo structure, with a varying whitelist field
// size.
//
// This type is defined in FormatStringRuntime.h as
//
//   typedef struct
//   {
//      uint32_t vargc;
//      uint32_t tag;
//      uint32_t line_no;
//      const char *source_info;
//      void  *whitelist[1];
//   } call_info;
//
// The fields are used as follows:
//  - vargc is the total number of variable arguments passed in the call.
//  - tag, line_no, source_info hold debug-related information.
//  - whitelist is a variable-sized array of pointers, with the last element
//    in the array being NULL. These pointers are the only values which the
//    wrapper callee will treat as vararg pointer arguments.
//
Type *
FormatStringTransform::makeCallInfoType(LLVMContext &ctx, unsigned argc) const
{
  Type *int32       = Type::getInt32Ty(ctx);
  Type *int8ptr     = Type::getInt8PtrTy(ctx);
  Type *int8ptr_arr = ArrayType::get(int8ptr, 1 + argc);
  vector<Type *> CallInfoFields =
    args<Type *>::list(int32, int32, int32, int8ptr, int8ptr_arr);
  return StructType::get(ctx, CallInfoFields);
}

}
