#!/bin/bash
#
# pre: we are in the top-level directory created by ps_analyze.sh
#
#     fix bug -- something is wrong with positive inference when the fn we're looking for 
#                is not even supported
#
# Find possible libc functions in CGC binaries 
#     specified in $LIBC_SEARCH_SPEC
#

ORIG_VARIANT_ID=$1
TESTABLE=a.ncexe.cinderella     

LIBC_SEARCH_SPEC=$PEASOUP_HOME/tools/cinderella.spec

$SECURITY_TRANSFORMS_HOME/libIRDB/test/clone.exe $ORIG_VARIANT_ID clone.id
cloneid=`cat clone.id`

TRUE_MALLOC=malloc.true.functions

# prep the binary for testing
#     pin all functions
#     splice-in our testing loop into the target program
$SECURITY_TRANSFORMS_HOME/tools/cinderella/cinderella_prep.exe $cloneid

# get list of all functions in binary
# for stripped binary, this will typically be of the form:
#    sub_80004fde
$SECURITY_TRANSFORMS_HOME/tools/cgclibc/display_functions.exe $cloneid | grep "^function" | cut -d' ' -f2 > cinderella.functions.all 

# pass 1: statically get possible candidates for malloc/free
#$SECURITY_TRANSFORMS_HOME/tools/cgclibc/cgclibc.exe $ORIG_VARIANT_ID > cinderella.static.pass1
#grep "positive malloc" cinderella.static.pass1 > cinderella.static.pass1.malloc
#grep "positive free" cinderella.static.pass1 > cinderella.static.pass1.free

# produce a zipr'd version so that we can dynamically test behavior
echo "Cinderella: Produce zipr'ed test version: id: $cloneid"
$ZIPR_INSTALL/bin/zipr.exe -v $cloneid -c $ZIPR_INSTALL/bin/callbacks.inferfn.exe -j $PS_OBJCOPY
mv b.out.addseg $TESTABLE

#----------------------------------------------------------------
# We now have a Zipr'd binary in which we inserted a testing loop
# Dynamically test for libc functions
#----------------------------------------------------------------

# Look for potential libc functions in the binary
# TODO: fixme: specify output inference file here
$PEASOUP_HOME/tools/do_prince.sh $cloneid `pwd`/$TESTABLE $LIBC_SEARCH_SPEC cinderella.functions.all

echo "CINDERELLA TODO: rename all libc functions detected: prepend to cinderella namespace, i.e., cinderella::strcpy, cinderella::memcpy"

#
# Look for the true malloc
#
#grep -i "positive malloc" cinderella.static.pass1.malloc | cut -d' ' -f4 > malloc.addresses
#$PEASOUP_HOME/tools/do_prince.sh $cloneid `pwd`/$TESTABLE $PEASOUP_HOME/tools/cinderella.malloc.spec malloc.addresses
#
#
# At this point, we have found a whole bunch of libc functions via
# dynamic testing
#
# Chances are, we have more than one choice for malloc(), so look
# for the true malloc()
#

TMP=tmp.$$

echo "CINDERELLA PASS1: simply intersects static + dynamic"
$SECURITY_TRANSFORMS_HOME/tools/cgclibc/cgclibc.exe $ORIG_VARIANT_ID --positive-inferences cinderella.inferences.positive --negative-inferences cinderella.inferences.negative > cinderella.static.pass1
count_malloc=`grep "^static positive malloc" cinderella.static.pass1 | wc -l`
if [ "$count_malloc" = "0" ]; then
	echo "No dynamic memory allocation in this program"
	exit 0
elif [ "$count_malloc" = "1" ]; then
	grep -i "positive malloc" cinderella.static.pass1 | cut -d' ' -f4 > $TRUE_MALLOC
	echo "CINDERELLA: pass 1: detected true malloc"
	cat $TRUE_MALLOC
	echo "CINDERELLA TODO: rename detected malloc fn to cinderella::malloc"
	exit 0
fi

#
# Use dominator heuristic to find malloc
#    potential mallocs (dynamic): D = {A, B, C}
#    potential mallocs (static) : S = {X, Y, A, B, C}
#
#    F = D intersect S = {A, B, C}
#    call graph: A --> B --> C            ==>     A is malloc
#    call graph: A --> B --> C, A --> C   ==>     A is malloc
#
# Warning: static analyses must use the original variant id
#          as the clone id has all its functions pinned down so that zipr
#          doesn't move them. but pinning down functions will interfere
#          with the static analysis pass
#
echo "CINDERELLA PASS2: intersect dynamic and static analyses for malloc / turn on --dominator"
grep -i "positive malloc" cinderella.static.pass1 > $TMP
$SECURITY_TRANSFORMS_HOME/tools/cgclibc/cgclibc.exe $ORIG_VARIANT_ID --positive-inferences $TMP --negative-inferences cinderella.inferences.negative --dominator > cinderella.static.pass2
count_malloc=`grep "^static positive malloc" cinderella.static.pass2 | wc -l`
count_free=`grep "^static positive free" cinderella.static.pass2 | wc -l`

if [ "$count_malloc" = "1" ]; then
	grep -i "positive malloc" cinderella.static.pass2 | cut -d' ' -f4 > $TRUE_MALLOC
	echo "CINDERELLA: pass 2: detected true malloc"
	cat $TRUE_MALLOC
	echo "CINDERELLA TODO: rename detected malloc fn to cinderella::malloc"
	exit 0
fi

#
# Haven't yet found the true malloc/free
# Use simple clustering heuristic
#
if [ "$count_malloc" != "1" ] || [ "$count_free" != "1" ] ; then
	echo "CINDERELLA PASS3: with restrictions on malloc / turn on --dominator"
	grep -i "positive malloc" cinderella.static.pass2 > $TMP
	$SECURITY_TRANSFORMS_HOME/tools/cgclibc/cgclibc.exe $ORIG_VARIANT_ID --positive-inferences $TMP --negative-inferences cinderella.inferences.negative --dominator > cinderella.static.pass3
	count_malloc=`grep "^static positive malloc" cinderella.static.pass3 | wc -l`
	count_free=`grep "^static positive free" cinderella.static.pass3 | wc -l`
fi

echo "CINDERELLA: PASS3: #mallocs: $count_malloc  #frees: $count_free"

if [ "$count_malloc" = "1" ]; then
	grep -i "positive malloc" cinderella.static.pass3 | cut -d' ' -f4 > $TRUE_MALLOC
	echo "CINDERELLA: pass 3: detected true malloc"
	cat $TRUE_MALLOC
	echo "CINDERELLA TODO: rename detected malloc fn to cinderella::malloc"
	exit 0
fi

echo "CINDERELLA PASS3: with restrictions on malloc / turn on --dominator and --cluster"
grep -i "positive malloc" cinderella.static.pass3 > $TMP
$SECURITY_TRANSFORMS_HOME/tools/cgclibc/cgclibc.exe $ORIG_VARIANT_ID --positive-inferences $TMP --negative-inferences cinderella.inferences.negative --dominator --cluster > cinderella.static.pass4
count_malloc=`grep "^static positive malloc" cinderella.static.pass4 | wc -l`
count_free=`grep "^static positive free" cinderella.static.pass4 | wc -l`

echo "CINDERELLA: PASS4: #mallocs: $count_malloc  #frees: $count_free"
if [ "$count_malloc" = "1" ]; then
	grep -i "positive malloc" cinderella.static.pass4 | cut -d' ' -f4 > $TRUE_MALLOC
	echo "CINDERELLA: pass 4: detected true malloc"
	cat $TRUE_MALLOC
	echo "CINDERELLA TODO: rename detected malloc fn to cinderella::malloc"
	exit 0
fi

echo "CINDERELLA: TODO: handle realloc() and calloc()"
exit 0

#
# if we pin down malloc and free correctly
# let's look for realloc and/or calloc
#
if [ "$count_malloc" = "1" ];then
	if [ "$count_free" = "1" ];then
		echo "CINDERELLA SUCCESS: true malloc() and free() found"

		#
		# Look for calloc/realloc
		# @todo: We should exclude all functions already discovered here to speed this up
		#
		echo "CINDERELLA SUCCESS: look for realloc"
		$PEASOUP_HOME/tools/do_prince.sh $cloneid `pwd`/$TESTABLE $PEASOUP_HOME/tools/cinderella.realloc.spec malloc.addresses $TRUE_MALLOC

		# @todo: fix this, not working at all
		echo "CINDERELLA SUCCESS: look for calloc"
		$PEASOUP_HOME/tools/do_prince.sh $cloneid `pwd`/$TESTABLE $PEASOUP_HOME/tools/cinderella.calloc.spec malloc.addresses $TRUE_MALLOC

echo "CINDERELLA TODO: if successful, rename detected calloc and realloc fns to cinderella::calloc, cinderella::realloc"
	fi
fi

exit 0