1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
"""
Just an experiment.
"""
import os
from pypy.objspace.std.objspace import StdObjSpace
from pypy.objspace.proxy import patch_space_in_place
from pypy.objspace.thunk import nb_forcing_args
from pypy.interpreter.error import OperationError
from pypy.interpreter import baseobjspace, gateway, executioncontext
from pypy.interpreter.function import Method
from pypy.interpreter.pyframe import PyFrame
from pypy.tool.sourcetools import func_with_new_name
from pypy.rlib.unroll import unrolling_iterable
class W_Tainted(baseobjspace.W_Root):
def __init__(self, w_obj):
self.w_obj = w_obj
## def getdict(self):
## return taint(self.w_obj.getdict())
## def getdictvalue(self, space, attr):
## return taint(self.w_obj.getdictvalue(space, attr))
## def setdictvalue(self, space, attr, w_value):
## return self.w_obj.setdictvalue(space, attr, w_value)
## ...
class W_TaintBomb(baseobjspace.W_Root):
filename = '?'
codename = '?'
codeline = 0
def __init__(self, space, operr):
self.space = space
self.operr = operr
self.record_debug_info()
def record_debug_info(self):
ec = self.space.getexecutioncontext()
frame = ec.gettopframe_nohidden()
if isinstance(frame, PyFrame): # and, in particular, frame != None
self.filename = frame.pycode.co_filename
self.codename = frame.pycode.co_name
self.codeline = frame.get_last_lineno()
if get_debug_level(self.space) > 0:
self.debug_dump()
def debug_dump(self):
os.write(2, 'Taint Bomb from file "%s", line %d, in %s\n %s\n' % (
self.filename, self.codeline, self.codename,
self.operr.errorstr(self.space)))
def explode(self):
#msg = self.operr.errorstr(space)
raise OperationError(self.space.w_TaintError, self.space.w_None)
def taint(w_obj):
"""Return a tainted version of the argument."""
if w_obj is None or isinstance(w_obj, W_Tainted):
return w_obj
else:
return W_Tainted(w_obj)
taint.unwrap_spec = [gateway.W_Root]
app_taint = gateway.interp2app(taint)
def is_tainted(space, w_obj):
"""Return whether the argument is tainted."""
res = isinstance(w_obj, W_Tainted) or isinstance(w_obj, W_TaintBomb)
return space.wrap(res)
app_is_tainted = gateway.interp2app(is_tainted)
def untaint(space, w_expectedtype, w_obj):
"""untaint(expectedtype, tainted_obj) -> obj
Untaint untainted_obj and return it. If the result is not of expectedtype,
raise a type error."""
if (isinstance(w_expectedtype, W_Tainted) or
isinstance(w_expectedtype, W_TaintBomb)):
raise OperationError(space.w_TypeError,
space.wrap("untaint() arg 1 must be an untainted type"))
if not space.is_true(space.isinstance(w_expectedtype, space.w_type)):
raise OperationError(space.w_TypeError,
space.wrap("untaint() arg 1 must be a type"))
if isinstance(w_obj, W_Tainted):
w_obj = w_obj.w_obj
elif isinstance(w_obj, W_TaintBomb):
w_obj.explode()
#if isinstance(w_expectedtype, W_Tainted):
# w_expectedtype = w_expectedtype.w_obj
w_realtype = space.type(w_obj)
if not space.is_w(w_realtype, w_expectedtype):
#msg = "expected an object of type '%s'" % (
# w_expectedtype.getname(space, '?'),)
# #w_realtype.getname(space, '?'))
raise OperationError(space.w_TaintError, space.w_None)
return w_obj
app_untaint = gateway.interp2app(untaint)
# ____________________________________________________________
def taint_atomic_function(space, w_func, args_w):
newargs_w = []
tainted = False
for w_arg in args_w:
if isinstance(w_arg, W_Tainted):
tainted = True
w_arg = w_arg.w_obj
elif isinstance(w_arg, W_TaintBomb):
return w_arg
newargs_w.append(w_arg)
w_newargs = space.newtuple(newargs_w)
try:
w_res = space.call(w_func, w_newargs)
except OperationError, operr:
if not tainted:
raise
return W_TaintBomb(space, operr)
if tainted:
w_res = taint(w_res)
return w_res
app_taint_atomic_function = gateway.interp2app(
taint_atomic_function,
unwrap_spec=[gateway.ObjSpace, gateway.W_Root, 'args_w'])
def taint_atomic(space, w_callable):
"""decorator to make a callable "taint-atomic": if the function is called
with tainted arguments, those are untainted. The result of the function is
tainted again. All exceptions that the callable raises are turned into
taint bombs."""
meth = Method(space, space.w_fn_taint_atomic_function,
w_callable, space.type(w_callable))
return space.wrap(meth)
app_taint_atomic = gateway.interp2app(taint_atomic)
# ____________________________________________________________
executioncontext.ExecutionContext.taint_debug = 0
def taint_debug(space, level):
"""Set the debug level. If the debug level is greater than 0, the creation
of taint bombs will print debug information. For debugging purposes
only!"""
space.getexecutioncontext().taint_debug = level
app_taint_debug = gateway.interp2app(taint_debug,
unwrap_spec=[gateway.ObjSpace, int])
def taint_look(space, w_obj):
"""Print some info about the taintedness of an object. For debugging
purposes only!"""
if isinstance(w_obj, W_Tainted):
info = space.type(w_obj.w_obj).getname(space)
msg = space.str_w(w_obj.w_obj.getrepr(space, info))
msg = 'Taint Box %s\n' % msg
os.write(2, msg)
elif isinstance(w_obj, W_TaintBomb):
w_obj.debug_dump()
else:
os.write(2, 'not tainted\n')
app_taint_look = gateway.interp2app(taint_look)
def get_debug_level(space):
return space.getexecutioncontext().taint_debug
def debug_bomb(space, operr):
ec = space.getexecutioncontext()
filename = '?'
codename = '?'
codeline = 0
frame = ec.gettopframe_nohidden()
if isinstance(frame, PyFrame): # and, in particular, frame != None
filename = frame.pycode.co_filename
codename = frame.pycode.co_name
codeline = frame.get_last_lineno()
os.write(2, 'Taint Bomb in file "%s", line %d, in %s\n %s\n' % (
filename, codeline, codename, operr.errorstr(space)))
# ____________________________________________________________
class TaintSpace(StdObjSpace):
def __init__(self, *args, **kwds):
StdObjSpace.__init__(self, *args, **kwds)
w_dict = self.newdict()
self.setitem(w_dict, self.wrap("__doc__"), self.wrap("""\
Exception that is raised when an operation revealing information on a tainted
object is performed."""))
self.w_TaintError = self.call_function(
self.w_type,
self.wrap("TaintError"),
self.newtuple([self.w_Exception]),
w_dict
)
w___pypy__ = self.getbuiltinmodule("__pypy__")
self.setattr(w___pypy__, self.wrap('taint'),
self.wrap(app_taint))
self.setattr(w___pypy__, self.wrap('is_tainted'),
self.wrap(app_is_tainted))
self.setattr(w___pypy__, self.wrap('untaint'),
self.wrap(app_untaint))
self.w_fn_taint_atomic_function = self.wrap(app_taint_atomic_function)
self.setattr(w___pypy__, self.wrap('taint_atomic'),
self.wrap(app_taint_atomic))
self.setattr(w___pypy__, self.wrap('TaintError'),
self.w_TaintError)
self.setattr(w___pypy__, self.wrap('_taint_debug'),
self.wrap(app_taint_debug))
self.setattr(w___pypy__, self.wrap('_taint_look'),
self.wrap(app_taint_look))
patch_space_in_place(self, 'taint', proxymaker)
# XXX may leak info, perfomance hit, what about taint bombs?
from pypy.objspace.std.typeobject import W_TypeObject
def taint_lookup(w_obj, name):
if isinstance(w_obj, W_Tainted):
w_obj = w_obj.w_obj
w_type = self.type(w_obj)
assert isinstance(w_type, W_TypeObject)
return w_type.lookup(name)
def taint_lookup_in_type_where(w_obj, name):
if isinstance(w_obj, W_Tainted):
w_type = w_obj.w_obj
else:
w_type = w_obj
assert isinstance(w_type, W_TypeObject)
return w_type.lookup_where(name)
self.lookup = taint_lookup
self.lookup_in_type_where = taint_lookup_in_type_where
Space = TaintSpace
def tainted_error(space, name):
#msg = "operation '%s' forbidden on tainted object" % (name,)
raise OperationError(space.w_TaintError, space.w_None)# space.wrap(msg))
RegularMethods = dict.fromkeys(
[name for name, _, _, _ in baseobjspace.ObjSpace.MethodTable])
TaintResultIrregularMethods = dict.fromkeys(
['wrap', 'call_args'] +
[name for name in baseobjspace.ObjSpace.IrregularOpTable
if name.startswith('new')])
def proxymaker(space, name, parentfn):
arity = nb_forcing_args[name]
indices = unrolling_iterable(range(arity))
if name in RegularMethods:
def proxy(*args_w):
newargs_w = ()
tainted = False
for i in indices:
w_arg = args_w[i]
if isinstance(w_arg, W_Tainted):
tainted = True
w_arg = w_arg.w_obj
elif isinstance(w_arg, W_TaintBomb):
return w_arg
newargs_w += (w_arg,)
newargs_w += args_w[arity:]
try:
w_res = parentfn(*newargs_w)
except OperationError, operr:
if not tainted:
raise
return W_TaintBomb(space, operr)
if tainted:
w_res = taint(w_res)
return w_res
elif arity == 0:
return None
else:
def proxy(*args_w):
for i in indices:
w_arg = args_w[i]
if isinstance(w_arg, W_Tainted):
tainted_error(space, name)
elif isinstance(w_arg, W_TaintBomb):
w_arg.explode()
return parentfn(*args_w)
proxy = func_with_new_name(proxy, '%s_proxy' % name)
return proxy
|