Skip to Content.
Sympa Menu

svadev - Re: [svadev] Documentation

svadev AT lists.siebelschool.illinois.edu

Subject: Svadev mailing list

List archive

Re: [svadev] Documentation


Chronological Thread 
  • From: John Criswell <criswell AT uiuc.edu>
  • To: Jean-Baptiste Tristan <jean.baptiste.tristan AT gmail.com>
  • Cc: "svadev AT cs.uiuc.edu" <svadev AT cs.uiuc.edu>
  • Subject: Re: [svadev] Documentation
  • Date: Thu, 11 Mar 2010 15:11:01 -0600
  • List-archive: <http://lists.cs.uiuc.edu/pipermail/svadev>
  • List-id: <svadev.cs.uiuc.edu>
  • Organization: University of Illinois

Jean-Baptiste Tristan wrote:
On Wed, Mar 10, 2010 at 2:48 PM, John Criswell
<criswell AT uiuc.edu>
wrote:
Jean-Baptiste Tristan wrote:
I was expecting to be able to apply SAFEcode on the example in figure
2 a of the PLDI 06
paper and match the result (in figure 4) with what I obtain.

Note that SAFECode in the PLDI 2006 publication used automatic pool
allocation. Using the default options to sc, automatic pool allocation is
not used, so you will only see one pool.

Automatic pool allocation hasn't been used in awhile, so it is experiencing
some bitrot issues. Improving pool allocation's quality of implementation
is on our TODO list.
With the input code

int **x, *y, *z, ***w, u;
x = (int **) malloc(4);
y = (int *) malloc (4);
z = (int *) malloc(4);

*x = y;
*y = 5;
free(z);
*z = 10;

u = *z;
w = (int ***) z;
*w = x;

return 0;



SAFEcode gives me :


call void @__sc_dbg_poolargvregister(i32 %argc, i8** %argv)
%sizetmp = mul i32 1, 4 ; <i32> [#uses=2]
%call29 = call i8* @__sc_dbg_src_poolalloc(%PoolDescriptor*
@__poolalloc_GlobalPool, i32 %sizetmp, i32 0, i8* getelementptr
inbounds ([5 x i8]* @sourcefile, i32 0, i32 0), i32 1) ; <i8*>
[#uses=2]
%call2.casted = bitcast i8* %call29 to i8* ; <i8*> [#uses=1]
%__poolalloc_GlobalPool.casted8 = bitcast %PoolDescriptor*
@__poolalloc_GlobalPool to i8* ; <i8*> [#uses=1]
call void @__sc_dbg_src_poolregister(i8*
%__poolalloc_GlobalPool.casted8, i8* %call2.casted, i32 %sizetmp, i32
6, i8* getelementptr inbounds ([5 x i8]* @sourcefile7, i32 0, i32 0),
i32 7)
%0 = bitcast i8* %call29 to i8* ; <i8*> [#uses=1]
%conv = bitcast i8* %0 to i32** ; <i32**> [#uses=2]
%sizetmp3 = mul i32 1, 4 ; <i32> [#uses=2]
%call1410 = call i8* @__sc_dbg_src_poolalloc(%PoolDescriptor*
@__poolalloc_GlobalPool, i32 %sizetmp3, i32 1, i8* getelementptr
inbounds ([5 x i8]* @sourcefile2, i32 0, i32 0), i32 2) ; <i8*>
[#uses=2]
%call14.casted = bitcast i8* %call1410 to i8* ; <i8*> [#uses=1]
%__poolalloc_GlobalPool.casted7 = bitcast %PoolDescriptor*
@__poolalloc_GlobalPool to i8* ; <i8*> [#uses=1]
call void @__sc_dbg_src_poolregister(i8*
%__poolalloc_GlobalPool.casted7, i8* %call14.casted, i32 %sizetmp3,
i32 5, i8* getelementptr inbounds ([5 x i8]* @sourcefile6, i32 0, i32
0), i32 6)
%1 = bitcast i8* %call1410 to i8* ; <i8*> [#uses=1]
%conv2 = bitcast i8* %1 to i32* ; <i32*> [#uses=2]
%sizetmp5 = mul i32 1, 4 ; <i32> [#uses=2]
%call3611 = call i8* @__sc_dbg_src_poolalloc(%PoolDescriptor*
@__poolalloc_GlobalPool, i32 %sizetmp5, i32 2, i8* getelementptr
inbounds ([5 x i8]* @sourcefile3, i32 0, i32 0), i32 3) ; <i8*>
[#uses=3]
%call36.casted = bitcast i8* %call3611 to i8* ; <i8*> [#uses=1]
%__poolalloc_GlobalPool.casted = bitcast %PoolDescriptor*
@__poolalloc_GlobalPool to i8* ; <i8*> [#uses=1]
call void @__sc_dbg_src_poolregister(i8*
%__poolalloc_GlobalPool.casted, i8* %call36.casted, i32 %sizetmp5, i32
4, i8* getelementptr inbounds ([5 x i8]* @sourcefile5, i32 0, i32 0),
i32 5)
%2 = bitcast i8* %call3611 to i8* ; <i8*> [#uses=1]
%conv4 = bitcast i8* %2 to i32* ; <i32*> [#uses=4]
store i32* %conv2, i32** %conv
store i32 5, i32* %conv2
%conv8 = bitcast i32* %conv4 to i8* ; <i8*> [#uses=1]
call void @__sc_dbg_poolunregister_debug(i8* bitcast
(%PoolDescriptor* @__poolalloc_GlobalPool to i8*), i8* %call3611, i32
7, i8* getelementptr inbounds ([5 x i8]* @sourcefile8, i32 0, i32 0),
i32 8)
call void @__sc_dbg_src_poolfree(%PoolDescriptor*
@__poolalloc_GlobalPool, i8* %conv8, i32 3, i8* getelementptr inbounds
([5 x i8]* @sourcefile4, i32 0, i32 0), i32 4)
store i32 10, i32* %conv4
%tmp11 = load i32* %conv4 ; <i32> [#uses=0]
%conv13 = bitcast i32* %conv4 to i32*** ; <i32***> [#uses=1]
store i32** %conv, i32*** %conv13
ret i32 0


And I have difficulties to find the pool information that may be used
to perform type verification
or the poolinit, pooldestroy operations.

The pool is explicitly passed into the poolalloc and poolfree calls (named
__sc_dbg_src_poolalloc and __sc_dbg_src_poolfree, respectively). I do not
see the call to poolinit(); I believe calls to poolinit() for global (i.e.,
context insensitive) pools are done in a constructor function instead of in
the main() function (because C++ global variables may have constructors that
are called before main(), so the pools that they use must be initialized
before main() too).

Yes, OK.

%PoolDescriptor = type [92 x i8*]
@__poolalloc_GlobalPool = global %PoolDescriptor zeroinitializer ;
<%PoolDescriptor*> [#uses=9]

Having only one pool doesn't change the safety guarantees, it only
results in more runtime checks
and an increased memory consumption that what would be needed with a
better pool allocation. Is that right ?
It maintains some guarantees but violates others.

In terms of memory safety, using a single global pool still ensures that loads and stores access valid memory objects and array/structure indexing (GEPs) don't index past the beginning or end of an object. The pass that converts the allocations into pool allocations on a global pool (poolalloc/lib/PoolAllocate/PASimple.cpp) adjusts the points-to analysis so that all heap objects are type-unknown; in DSA parlance, the nodes in the points-to graph are "folded" or "collapsed." For preventing exploits or reporting memory bugs, this incurs more overhead (due to more run-time checks) but is still effective.

However, SAFECode's guarantee that pointers always access objects in the correct points-to set (as computed by DSA) can be violated. This is because the PASimple.cpp pass does not correctly merge all nodes in the points-to graph into a single node. While a program is guaranteed to access an object of the expected type, that object may belong to a different points-to set than what DSA determined.

-- John T.





Archive powered by MHonArc 2.6.16.

Top of Page