SlideShare uma empresa Scribd logo
1 de 25
Baixar para ler offline
Коварный CodeType,
или от segfault'а
к работающему коду
Андрей Захаревич
Дмитрий Алимов
С чего все началось
# Plain functions
if inspect.isroutine(obj):
if inspect.ismethod(obj):
has_self = True
argspec = inspect.getargspec(obj)
# Class constructors
elif inspect.isclass(obj):
try:
argspec = inspect.getargspec(obj.__new__)
except (AttributeError, TypeError):
argspec = inspect.getargspec(obj.__init__)
has_self = True
# obj objects
elif hasattr(obj, '__call__'):
method = obj.__call__
argspec = inspect.getargspec(method)
has_self = inspect.ismethod(method)
return argspec.args[1:] if has_self else args
Проблема
● Моя функция принимает переменное число аргументов (*args)
● Число передаваемых аргументов я узнаю в рантайме
● Объект функции в это время уже создан
Что же делать?
Метапрограммирование!
Ленивый вариант
compile('def func(a, b): f(a, b)', 'func.py', 'exec')
Не спортивно
Идем на stackoverflow
import types
def create_function(name, args):
def y(): pass
y_code = types.CodeType(args,
y.func_code.co_nlocals,
y.func_code.co_stacksize,
y.func_code.co_flags,
y.func_code.co_code,
y.func_code.co_consts,
y.func_code.co_names,
y.func_code.co_varnames,
y.func_code.co_filename,
name,
y.func_code.co_firstlineno,
y.func_code.co_lnotab)
return types.FunctionType(y_code, y.func_globals, name)
myfunc = create_function('myfunc', 3)
myfunc(1, 2, 3, 4)
# TypeError: myfunc() takes exactly 3 arguments (4 given)
Кажется должно работать
def create_function(name, nargs):
def y(*args):
print args
y_code = types.CodeType(nargs,
y.func_code.co_nlocals,
# Параметры
)
return types.FunctionType(y_code, y.func_globals,
name)
myfunc = create_function('myfunc', 3)
myfunc(1, 2, 3)
Но нет
[1] 8514 segmentation fault python func.py
Еще попытка
class Meta(type):
def __new__(mcls, name, bases, attrs):
nargs = 3
varnames = tuple(['self'] + [str(i) for i in range(nargs)])
ret_code = types.CodeType(
nargs + 1,
attrs['ret_func'].func_code.co_nlocals,
attrs['ret_func'].func_code.co_stacksize,
attrs['ret_func'].func_code.co_flags,
attrs['ret_func'].func_code.co_code,
attrs['ret_func'].func_code.co_consts,
attrs['ret_func'].func_code.co_names,
varnames,
attrs['ret_func'].func_code.co_filename,
'call',
attrs['ret_func'].func_code.co_firstlineno,
attrs['ret_func'].func_code.co_lnotab)
attrs['call'] = types.FunctionType(
ret_code,
attrs['ret_func'].func_globals, 'call')
return super(Meta, mcls).__new__(mcls, name, bases, attrs)
class CustomCall(object):
__metaclass__ = Meta
def __call__(self, *args):
self.call(*args)
def ret_func(self):
print self.call.__name__
obj = CustomCall()
obj.call(1, 2, 3)
# Если
закомментировать, то
все упадет
obj(1, 2, 3)
Еще попытка
class Meta(type):
def __new__(mcls, name, bases, attrs):
nargs = 3
varnames = tuple(['self'] + [str(i) for i in range(nargs)])
ret_code = types.CodeType(
nargs + 1,
attrs['ret_func'].func_code.co_nlocals,
attrs['ret_func'].func_code.co_stacksize,
attrs['ret_func'].func_code.co_flags,
attrs['ret_func'].func_code.co_code,
attrs['ret_func'].func_code.co_consts,
attrs['ret_func'].func_code.co_names,
varnames,
attrs['ret_func'].func_code.co_filename,
'call',
attrs['ret_func'].func_code.co_firstlineno,
attrs['ret_func'].func_code.co_lnotab)
attrs['call'] = types.FunctionType(
ret_code,
attrs['ret_func'].func_globals, 'call')
return super(Meta, mcls).__new__(mcls, name, bases, attrs)
class CustomCall(object):
__metaclass__ = Meta
def __call__(self, *args):
self.call(*args)
def ret_func(self):
print self.call.__name__
obj = CustomCall()
obj.call(1, 2, 3)
# Если
закомментировать, то
все упадет
obj(1, 2, 3)
Заглянем в Disassembler
5D09E429 |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10] ; ESI = frame
5D09E42D |. 8B7D 14 MOV EDI,DWORD PTR SS:[EBP+14] ; EDI = *args
5D09E430 |. 8B5C24 18 MOV EBX,DWORD PTR SS:[ESP+18] ; EBX = argcount
5D09E434 |. 81C6 38010000 ADD ESI,0x138 ; ESI += 0x138 (f_localsplus)
5D09E43A |. 2BFE SUB EDI,ESI ; EDI -= ESI
5D09E43C |. 8D6424 00 LEA ESP,[ESP] ; NOP
5D09E440 |> 8B0437 /MOV EAX,DWORD PTR DS:[ESI+EDI] ; EAX = *(ESI + EDI)
5D09E443 |. FF00 |INC DWORD PTR DS:[EAX] ; Py_INCREF(*EAX)
5D09E445 |. 8B0E |MOV ECX,DWORD PTR DS:[ESI] ; ECX = *ESI
5D09E447 |. 8906 |MOV DWORD PTR DS:[ESI],EAX ; *ESI = EAX
5D09E449 |. 85C9 |TEST ECX,ECX ; test ECX:
5D09E44B |.- 74 11 |JZ SHORT 5D09E45E ; if 0: goto 5D09E45E
5D09E44D |. 8301 FF |ADD DWORD PTR DS:[ECX],-1 ; else: Py_XDECREF(*ECX)
5D09E450 |.- 75 0C |JNZ SHORT 5D09E45E ; if not 0: goto 5D09E45E
5D09E452 |. 8B41 04 |MOV EAX,DWORD PTR DS:[ECX+4] ; ECX->ob_type
5D09E455 |. 51 |PUSH ECX ; save fastlocals[i] to stack
5D09E456 |. 8B48 18 |MOV ECX,DWORD PTR DS:[EAX+18] ; ECX = ob_type->tp_dealloc
5D09E459 |. FFD1 |CALL ECX ; call ob_type->tp_dealloc()
5D09E45B |. 83C4 04 |ADD ESP,4 ; restore ESP
5D09E45E |> 83C6 04 |ADD ESI,4 ; ESI += 4
5D09E461 |. 83EB 01 |SUB EBX,1 ; EBX -= 1
5D09E464 |.- 75 DA JNZ SHORT 5D09E440 ;
01eb3030 00000001 ob_refcnt
01eb3034 5886d830 python27!PyFrame_Type
01eb3038 00000002 ob_size
01eb303c 023b6b30 f_back
01eb3040 023a6b18 f_code
01eb3044 01e790c0 f_builtins
01eb3048 01e8aa50 f_globals
01eb304c 00000000 f_locals
01eb3050 01eb316c f_valuestack
01eb3054 01eb316c f_stacktop
01eb3058 00000000 f_trace
01eb305c 00000000 f_exc_type
01eb3060 00000000 f_exc_value
01eb3064 00000000 f_exc_traceback
01eb3068 01f72cf0 f_tstate
01eb306c ffffffff f_lasti
01eb3070 0000002f f_lineno
01eb3074 00000000 f_iblock
01eb3078 baadf00d f_blockstack[20] = {b_type,
01eb307c baadf00d b_handler
01eb3080 baadf00d b_level}
...
01eb3168 023bf550 f_localsplus // frame + 0x138
01eb316c 01f7d8a0
01eb3170 baadf00d
01eb3174 baadf00d
Что в памяти
PyObject *
PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
PyObject **args, int argcount, PyObject **kws, int kwcount,
PyObject **defs, int defcount, PyObject *closure) {
...
n = co->co_argcount;
for (i = 0; i < n; i++) {
x = args[i];
Py_INCREF(x);
SETLOCAL(i, x);
}
...
https://github.com/python/cpython/blob/2.7/Python/ceval.c
#define Py_INCREF(op) ( 
_Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA 
((PyObject*)(op))->ob_refcnt++)
#define GETLOCAL(i) (fastlocals[i])
#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); 
GETLOCAL(i) = value; 
Py_XDECREF(tmp); } while (0)
#define Py_XDECREF(op) do { if ((op) == NULL); else Py_DECREF(op); } while (0)
#define Py_DECREF(op) 
do { 
if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA 
--((PyObject*)(op))->ob_refcnt != 0) 
_Py_CHECK_REFCNT(op) 
else 
_Py_Dealloc((PyObject *)(op)); 
} while (0)
#define _Py_Dealloc(op) ( 
_Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA 
(*Py_TYPE(op)->tp_dealloc)((PyObject *)(op)))
#endif /* !Py_TRACE_REFS */
https://github.com/python/cpython/blob/2.7/Include/object.h
typedef struct _frame {
PyObject_VAR_HEAD
struct _frame *f_back; /* previous frame, or NULL */
PyCodeObject *f_code; /* code segment */
PyObject *f_builtins; /* builtin symbol table (PyDictObject) */
PyObject *f_globals; /* global symbol table (PyDictObject) */
PyObject *f_locals; /* local symbol table (any mapping) */
PyObject **f_valuestack; /* points after the last local */
PyObject **f_stacktop;
PyObject *f_trace; /* Trace function */
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
PyThreadState *f_tstate;
int f_lasti; /* Last instruction if called */
int f_lineno; /* Current line number */
int f_iblock; /* index in f_blockstack */
PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */
PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */
} PyFrameObject;
https://github.com/python/cpython/blob/2.7/Include/frameobject.h
ncells = PyTuple_GET_SIZE(code->co_cellvars);
nfrees = PyTuple_GET_SIZE(code->co_freevars);
extras = code->co_stacksize + code->co_nlocals + ncells + nfrees;
if (free_list == NULL) {
f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras);
...
https://github.com/python/cpython/blob/2.7/Objects/frameobject.c
co_cellvars – is a tuple with the names of local variables referenced by nested functions;
co_freevars – names of variables in the function, defined in an enclosing function scope;
Память для f_localsplus
Проблема найдена
ret_code = types.CodeType(
nargs + 1,
attrs['ret_func'].func_code.co_nlocals,
attrs['ret_func'].func_code.co_stacksize,
attrs['ret_func'].func_code.co_flags,
attrs['ret_func'].func_code.co_code,
attrs['ret_func'].func_code.co_consts,
attrs['ret_func'].func_code.co_names,
varnames,
attrs['ret_func'].func_code.co_filename,
'call',
attrs['ret_func'].func_code.co_firstlineno,
attrs['ret_func'].func_code.co_lnotab,
(), # freevars
() # cellvars
)
Попробуем применить
def create_func(fun, nargs):
def wrapper():
args = map(itemgetter(1),
sorted(((var_name, arg) for (var_name, arg)
in locals().items() if var_name != 'fun'),
key=itemgetter(0)))
fun(*args)
varnames = tuple(str(i) for i in range(0, nargs))
ret_code = types.CodeType(nargs,
wrapper.func_code.co_nlocals + nargs,
# Параметры
)
return types.FunctionType(ret_code, wrapper.func_globals,
wrapper.func_code.co_name)
def test_func(*args):
print args
fun = create_func(test_func, 3)
fun(1, 2, 3)
Как-то не очень
Traceback (most recent call last):
File "func.py", line 38, in <module>
fun(1,2,3)
File "func.py", line 16, in wrapper
fun(*args)
SystemError: Objects/cellobject.c:24: bad argument to internal function
Заработало!
def create_func(fun, nargs):
def wrapper(fun):
args = map(itemgetter(1),
sorted(((var_name, arg) for (var_name, arg)
in locals().items() if var_name != 'fun'),
key=itemgetter(0)))
fun(*args)
varnames = tuple(['fun'] + [str(i) for i in range(0, nargs)])
ret_code = types.CodeType(nargs + 1,
wrapper.func_code.co_nlocals + nargs,
# Параметры
)
return partial(types.FunctionType(ret_code, wrapper.func_globals,
wrapper.func_code.co_name), fun)
def test_func(*args):
print args
fun = create_func(test_func, 3)
fun(1, 2, 3)
Что-то потеряли
>>> dir(wrapper.func_code)
['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename',
'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals',
'co_stacksize', 'co_varnames']
Добавим параметр
ret_code = types.CodeType(nargs,
wrapper.func_code.co_nlocals + nargs,
# Параметры
wrapper.func_code.co_freevars)
return types.FunctionType(ret_code, wrapper.func_globals,
wrapper.func_code.co_name)
>>>Traceback (most recent call last):
File "func.py", line 39, in <module>
fun = create_func(test_func, 3)
File "func.py", line 33, in create_func
return types.FunctionType(ret_code, wrapper.func_globals,
wrapper.func_code.co_name)
TypeError: arg 5 (closure) must be tuple
def create_func(fun, nargs):
def wrapper():
args = map(itemgetter(1),
sorted(((var_name, arg) for (var_name, arg)
in locals().items() if var_name != 'fun'),
key=itemgetter(0)))
fun(*args)
varnames = tuple([str(i) for i in range(0, nargs)])
ret_code = types.CodeType(nargs,
wrapper.func_code.co_nlocals + nargs,
# Параметры
wrapper.func_code.co_freevars)
return types.FunctionType(ret_code, wrapper.func_globals,
wrapper.func_code.co_name, None,
wrapper.func_closure)
def test_func(*args):
print args
fun = create_func(test_func, 3)
fun(1, 2, 3)
И наконец, замыкания работают!
… почти
from operator import itemgetter
def wrapper():
print locals()
args = map(itemgetter(1),
sorted(((var_name, arg) for (var_name, arg)
in locals().items() if var_name != 'fun'),
key=itemgetter(0)))
print locals()
fun(*args)
{'1': 2, '0': 1, '2': 3, 'fun': <function test_func at 0x10cd62398>}
{'1': 2, '0': [1, 2, 3], '2': 3, 'fun': <function test_func at 0x10cd62398>}
Спасибо за внимание!
Вопросы?
SPb Python Interest Group
https://telegram.me/spbpython

Mais conteúdo relacionado

Mais procurados

Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
Abed Bukhari
 
The Evolution of Async-Programming (SD 2.0, JavaScript)
The Evolution of Async-Programming (SD 2.0, JavaScript)The Evolution of Async-Programming (SD 2.0, JavaScript)
The Evolution of Async-Programming (SD 2.0, JavaScript)
jeffz
 
Javascript Uncommon Programming
Javascript Uncommon ProgrammingJavascript Uncommon Programming
Javascript Uncommon Programming
jeffz
 

Mais procurados (20)

Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
 
The Evolution of Async-Programming (SD 2.0, JavaScript)
The Evolution of Async-Programming (SD 2.0, JavaScript)The Evolution of Async-Programming (SD 2.0, JavaScript)
The Evolution of Async-Programming (SD 2.0, JavaScript)
 
The Ring programming language version 1.5.3 book - Part 88 of 184
The Ring programming language version 1.5.3 book - Part 88 of 184The Ring programming language version 1.5.3 book - Part 88 of 184
The Ring programming language version 1.5.3 book - Part 88 of 184
 
One definition rule - что это такое, и как с этим жить
One definition rule - что это такое, и как с этим житьOne definition rule - что это такое, и как с этим жить
One definition rule - что это такое, и как с этим жить
 
Metaprogramming and Reflection in Common Lisp
Metaprogramming and Reflection in Common LispMetaprogramming and Reflection in Common Lisp
Metaprogramming and Reflection in Common Lisp
 
Welcome to Modern C++
Welcome to Modern C++Welcome to Modern C++
Welcome to Modern C++
 
Pro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScriptPro typescript.ch03.Object Orientation in TypeScript
Pro typescript.ch03.Object Orientation in TypeScript
 
Namespaces
NamespacesNamespaces
Namespaces
 
Java, Up to Date Sources
Java, Up to Date SourcesJava, Up to Date Sources
Java, Up to Date Sources
 
Basic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python ProgrammersBasic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python Programmers
 
响应式编程及框架
响应式编程及框架响应式编程及框架
响应式编程及框架
 
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
Самые вкусные баги из игрового кода: как ошибаются наши коллеги-программисты ...
 
Writing good std::future&lt;c++>
Writing good std::future&lt;c++>Writing good std::future&lt;c++>
Writing good std::future&lt;c++>
 
Message in a bottle
Message in a bottleMessage in a bottle
Message in a bottle
 
Javascript Uncommon Programming
Javascript Uncommon ProgrammingJavascript Uncommon Programming
Javascript Uncommon Programming
 
E:\Plp 2009 2\Plp 9
E:\Plp 2009 2\Plp 9E:\Plp 2009 2\Plp 9
E:\Plp 2009 2\Plp 9
 
Node.js behind: V8 and its optimizations
Node.js behind: V8 and its optimizationsNode.js behind: V8 and its optimizations
Node.js behind: V8 and its optimizations
 
Антон Бикинеев, Reflection in C++Next
Антон Бикинеев,  Reflection in C++NextАнтон Бикинеев,  Reflection in C++Next
Антон Бикинеев, Reflection in C++Next
 
第二回CTF勉強会資料
第二回CTF勉強会資料第二回CTF勉強会資料
第二回CTF勉強会資料
 
C++ L05-Functions
C++ L05-FunctionsC++ L05-Functions
C++ L05-Functions
 

Semelhante a Коварный code type ITGM #9

Bytes in the Machine: Inside the CPython interpreter
Bytes in the Machine: Inside the CPython interpreterBytes in the Machine: Inside the CPython interpreter
Bytes in the Machine: Inside the CPython interpreter
akaptur
 
Unit 4
Unit 4Unit 4
Unit 4
siddr
 
Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)
ujihisa
 
This is my code- #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
This is my code-  #include -llvm-IR-LegacyPassManager-h- #include -llv.pdfThis is my code-  #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
This is my code- #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
EricvtJFraserr
 
C-Sharp Arithmatic Expression Calculator
C-Sharp Arithmatic Expression CalculatorC-Sharp Arithmatic Expression Calculator
C-Sharp Arithmatic Expression Calculator
Neeraj Kaushik
 
Hacking Parse.y with ujihisa
Hacking Parse.y with ujihisaHacking Parse.y with ujihisa
Hacking Parse.y with ujihisa
ujihisa
 

Semelhante a Коварный code type ITGM #9 (20)

Bytes in the Machine: Inside the CPython interpreter
Bytes in the Machine: Inside the CPython interpreterBytes in the Machine: Inside the CPython interpreter
Bytes in the Machine: Inside the CPython interpreter
 
Improving Java performance at JBCNConf 2015
Improving Java performance at JBCNConf 2015Improving Java performance at JBCNConf 2015
Improving Java performance at JBCNConf 2015
 
Improving Android Performance at Droidcon UK 2014
Improving Android Performance at Droidcon UK 2014Improving Android Performance at Droidcon UK 2014
Improving Android Performance at Droidcon UK 2014
 
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
 
Как работает LLVM бэкенд в C#. Егор Богатов ➠ CoreHard Autumn 2019
Как работает LLVM бэкенд в C#. Егор Богатов ➠ CoreHard Autumn 2019Как работает LLVM бэкенд в C#. Егор Богатов ➠ CoreHard Autumn 2019
Как работает LLVM бэкенд в C#. Егор Богатов ➠ CoreHard Autumn 2019
 
OpenMP
OpenMPOpenMP
OpenMP
 
Unit 4
Unit 4Unit 4
Unit 4
 
How to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJITHow to add an optimization for C# to RyuJIT
How to add an optimization for C# to RyuJIT
 
Bsides
BsidesBsides
Bsides
 
Introduction to Debuggers
Introduction to DebuggersIntroduction to Debuggers
Introduction to Debuggers
 
Marat-Slides
Marat-SlidesMarat-Slides
Marat-Slides
 
3
33
3
 
삼성 바다 앱개발 실패 노하우 2부
삼성 바다 앱개발 실패 노하우 2부삼성 바다 앱개발 실패 노하우 2부
삼성 바다 앱개발 실패 노하우 2부
 
10 Catalyst Tips
10 Catalyst Tips10 Catalyst Tips
10 Catalyst Tips
 
Groovy
GroovyGroovy
Groovy
 
Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)Hacking parse.y (RubyKansai38)
Hacking parse.y (RubyKansai38)
 
NYU hacknight, april 6, 2016
NYU hacknight, april 6, 2016NYU hacknight, april 6, 2016
NYU hacknight, april 6, 2016
 
This is my code- #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
This is my code-  #include -llvm-IR-LegacyPassManager-h- #include -llv.pdfThis is my code-  #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
This is my code- #include -llvm-IR-LegacyPassManager-h- #include -llv.pdf
 
C-Sharp Arithmatic Expression Calculator
C-Sharp Arithmatic Expression CalculatorC-Sharp Arithmatic Expression Calculator
C-Sharp Arithmatic Expression Calculator
 
Hacking Parse.y with ujihisa
Hacking Parse.y with ujihisaHacking Parse.y with ujihisa
Hacking Parse.y with ujihisa
 

Último

+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
Health
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
masabamasaba
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
mohitmore19
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 

Último (20)

%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
W01_panagenda_Navigating-the-Future-with-The-Hitchhikers-Guide-to-Notes-and-D...
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
%+27788225528 love spells in Boston Psychic Readings, Attraction spells,Bring...
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
Announcing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK SoftwareAnnouncing Codolex 2.0 from GDK Software
Announcing Codolex 2.0 from GDK Software
 
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
%+27788225528 love spells in Vancouver Psychic Readings, Attraction spells,Br...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
TECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service providerTECUNIQUE: Success Stories: IT Service provider
TECUNIQUE: Success Stories: IT Service provider
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
%in kaalfontein+277-882-255-28 abortion pills for sale in kaalfontein
 
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...Chinsurah Escorts ☎️8617697112  Starting From 5K to 15K High Profile Escorts ...
Chinsurah Escorts ☎️8617697112 Starting From 5K to 15K High Profile Escorts ...
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 

Коварный code type ITGM #9

  • 1. Коварный CodeType, или от segfault'а к работающему коду Андрей Захаревич Дмитрий Алимов
  • 2. С чего все началось # Plain functions if inspect.isroutine(obj): if inspect.ismethod(obj): has_self = True argspec = inspect.getargspec(obj) # Class constructors elif inspect.isclass(obj): try: argspec = inspect.getargspec(obj.__new__) except (AttributeError, TypeError): argspec = inspect.getargspec(obj.__init__) has_self = True # obj objects elif hasattr(obj, '__call__'): method = obj.__call__ argspec = inspect.getargspec(method) has_self = inspect.ismethod(method) return argspec.args[1:] if has_self else args
  • 3. Проблема ● Моя функция принимает переменное число аргументов (*args) ● Число передаваемых аргументов я узнаю в рантайме ● Объект функции в это время уже создан Что же делать?
  • 5. Ленивый вариант compile('def func(a, b): f(a, b)', 'func.py', 'exec') Не спортивно
  • 6. Идем на stackoverflow import types def create_function(name, args): def y(): pass y_code = types.CodeType(args, y.func_code.co_nlocals, y.func_code.co_stacksize, y.func_code.co_flags, y.func_code.co_code, y.func_code.co_consts, y.func_code.co_names, y.func_code.co_varnames, y.func_code.co_filename, name, y.func_code.co_firstlineno, y.func_code.co_lnotab) return types.FunctionType(y_code, y.func_globals, name) myfunc = create_function('myfunc', 3) myfunc(1, 2, 3, 4) # TypeError: myfunc() takes exactly 3 arguments (4 given)
  • 7. Кажется должно работать def create_function(name, nargs): def y(*args): print args y_code = types.CodeType(nargs, y.func_code.co_nlocals, # Параметры ) return types.FunctionType(y_code, y.func_globals, name) myfunc = create_function('myfunc', 3) myfunc(1, 2, 3)
  • 8. Но нет [1] 8514 segmentation fault python func.py
  • 9. Еще попытка class Meta(type): def __new__(mcls, name, bases, attrs): nargs = 3 varnames = tuple(['self'] + [str(i) for i in range(nargs)]) ret_code = types.CodeType( nargs + 1, attrs['ret_func'].func_code.co_nlocals, attrs['ret_func'].func_code.co_stacksize, attrs['ret_func'].func_code.co_flags, attrs['ret_func'].func_code.co_code, attrs['ret_func'].func_code.co_consts, attrs['ret_func'].func_code.co_names, varnames, attrs['ret_func'].func_code.co_filename, 'call', attrs['ret_func'].func_code.co_firstlineno, attrs['ret_func'].func_code.co_lnotab) attrs['call'] = types.FunctionType( ret_code, attrs['ret_func'].func_globals, 'call') return super(Meta, mcls).__new__(mcls, name, bases, attrs) class CustomCall(object): __metaclass__ = Meta def __call__(self, *args): self.call(*args) def ret_func(self): print self.call.__name__ obj = CustomCall() obj.call(1, 2, 3) # Если закомментировать, то все упадет obj(1, 2, 3)
  • 10. Еще попытка class Meta(type): def __new__(mcls, name, bases, attrs): nargs = 3 varnames = tuple(['self'] + [str(i) for i in range(nargs)]) ret_code = types.CodeType( nargs + 1, attrs['ret_func'].func_code.co_nlocals, attrs['ret_func'].func_code.co_stacksize, attrs['ret_func'].func_code.co_flags, attrs['ret_func'].func_code.co_code, attrs['ret_func'].func_code.co_consts, attrs['ret_func'].func_code.co_names, varnames, attrs['ret_func'].func_code.co_filename, 'call', attrs['ret_func'].func_code.co_firstlineno, attrs['ret_func'].func_code.co_lnotab) attrs['call'] = types.FunctionType( ret_code, attrs['ret_func'].func_globals, 'call') return super(Meta, mcls).__new__(mcls, name, bases, attrs) class CustomCall(object): __metaclass__ = Meta def __call__(self, *args): self.call(*args) def ret_func(self): print self.call.__name__ obj = CustomCall() obj.call(1, 2, 3) # Если закомментировать, то все упадет obj(1, 2, 3)
  • 11. Заглянем в Disassembler 5D09E429 |. 8B7424 10 MOV ESI,DWORD PTR SS:[ESP+10] ; ESI = frame 5D09E42D |. 8B7D 14 MOV EDI,DWORD PTR SS:[EBP+14] ; EDI = *args 5D09E430 |. 8B5C24 18 MOV EBX,DWORD PTR SS:[ESP+18] ; EBX = argcount 5D09E434 |. 81C6 38010000 ADD ESI,0x138 ; ESI += 0x138 (f_localsplus) 5D09E43A |. 2BFE SUB EDI,ESI ; EDI -= ESI 5D09E43C |. 8D6424 00 LEA ESP,[ESP] ; NOP 5D09E440 |> 8B0437 /MOV EAX,DWORD PTR DS:[ESI+EDI] ; EAX = *(ESI + EDI) 5D09E443 |. FF00 |INC DWORD PTR DS:[EAX] ; Py_INCREF(*EAX) 5D09E445 |. 8B0E |MOV ECX,DWORD PTR DS:[ESI] ; ECX = *ESI 5D09E447 |. 8906 |MOV DWORD PTR DS:[ESI],EAX ; *ESI = EAX 5D09E449 |. 85C9 |TEST ECX,ECX ; test ECX: 5D09E44B |.- 74 11 |JZ SHORT 5D09E45E ; if 0: goto 5D09E45E 5D09E44D |. 8301 FF |ADD DWORD PTR DS:[ECX],-1 ; else: Py_XDECREF(*ECX) 5D09E450 |.- 75 0C |JNZ SHORT 5D09E45E ; if not 0: goto 5D09E45E 5D09E452 |. 8B41 04 |MOV EAX,DWORD PTR DS:[ECX+4] ; ECX->ob_type 5D09E455 |. 51 |PUSH ECX ; save fastlocals[i] to stack 5D09E456 |. 8B48 18 |MOV ECX,DWORD PTR DS:[EAX+18] ; ECX = ob_type->tp_dealloc 5D09E459 |. FFD1 |CALL ECX ; call ob_type->tp_dealloc() 5D09E45B |. 83C4 04 |ADD ESP,4 ; restore ESP 5D09E45E |> 83C6 04 |ADD ESI,4 ; ESI += 4 5D09E461 |. 83EB 01 |SUB EBX,1 ; EBX -= 1 5D09E464 |.- 75 DA JNZ SHORT 5D09E440 ;
  • 12. 01eb3030 00000001 ob_refcnt 01eb3034 5886d830 python27!PyFrame_Type 01eb3038 00000002 ob_size 01eb303c 023b6b30 f_back 01eb3040 023a6b18 f_code 01eb3044 01e790c0 f_builtins 01eb3048 01e8aa50 f_globals 01eb304c 00000000 f_locals 01eb3050 01eb316c f_valuestack 01eb3054 01eb316c f_stacktop 01eb3058 00000000 f_trace 01eb305c 00000000 f_exc_type 01eb3060 00000000 f_exc_value 01eb3064 00000000 f_exc_traceback 01eb3068 01f72cf0 f_tstate 01eb306c ffffffff f_lasti 01eb3070 0000002f f_lineno 01eb3074 00000000 f_iblock 01eb3078 baadf00d f_blockstack[20] = {b_type, 01eb307c baadf00d b_handler 01eb3080 baadf00d b_level} ... 01eb3168 023bf550 f_localsplus // frame + 0x138 01eb316c 01f7d8a0 01eb3170 baadf00d 01eb3174 baadf00d Что в памяти
  • 13. PyObject * PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, PyObject **defs, int defcount, PyObject *closure) { ... n = co->co_argcount; for (i = 0; i < n; i++) { x = args[i]; Py_INCREF(x); SETLOCAL(i, x); } ... https://github.com/python/cpython/blob/2.7/Python/ceval.c #define Py_INCREF(op) ( _Py_INC_REFTOTAL _Py_REF_DEBUG_COMMA ((PyObject*)(op))->ob_refcnt++) #define GETLOCAL(i) (fastlocals[i]) #define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); GETLOCAL(i) = value; Py_XDECREF(tmp); } while (0)
  • 14. #define Py_XDECREF(op) do { if ((op) == NULL); else Py_DECREF(op); } while (0) #define Py_DECREF(op) do { if (_Py_DEC_REFTOTAL _Py_REF_DEBUG_COMMA --((PyObject*)(op))->ob_refcnt != 0) _Py_CHECK_REFCNT(op) else _Py_Dealloc((PyObject *)(op)); } while (0) #define _Py_Dealloc(op) ( _Py_INC_TPFREES(op) _Py_COUNT_ALLOCS_COMMA (*Py_TYPE(op)->tp_dealloc)((PyObject *)(op))) #endif /* !Py_TRACE_REFS */ https://github.com/python/cpython/blob/2.7/Include/object.h
  • 15. typedef struct _frame { PyObject_VAR_HEAD struct _frame *f_back; /* previous frame, or NULL */ PyCodeObject *f_code; /* code segment */ PyObject *f_builtins; /* builtin symbol table (PyDictObject) */ PyObject *f_globals; /* global symbol table (PyDictObject) */ PyObject *f_locals; /* local symbol table (any mapping) */ PyObject **f_valuestack; /* points after the last local */ PyObject **f_stacktop; PyObject *f_trace; /* Trace function */ PyObject *f_exc_type, *f_exc_value, *f_exc_traceback; PyThreadState *f_tstate; int f_lasti; /* Last instruction if called */ int f_lineno; /* Current line number */ int f_iblock; /* index in f_blockstack */ PyTryBlock f_blockstack[CO_MAXBLOCKS]; /* for try and loop blocks */ PyObject *f_localsplus[1]; /* locals+stack, dynamically sized */ } PyFrameObject; https://github.com/python/cpython/blob/2.7/Include/frameobject.h
  • 16. ncells = PyTuple_GET_SIZE(code->co_cellvars); nfrees = PyTuple_GET_SIZE(code->co_freevars); extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; if (free_list == NULL) { f = PyObject_GC_NewVar(PyFrameObject, &PyFrame_Type, extras); ... https://github.com/python/cpython/blob/2.7/Objects/frameobject.c co_cellvars – is a tuple with the names of local variables referenced by nested functions; co_freevars – names of variables in the function, defined in an enclosing function scope; Память для f_localsplus
  • 17. Проблема найдена ret_code = types.CodeType( nargs + 1, attrs['ret_func'].func_code.co_nlocals, attrs['ret_func'].func_code.co_stacksize, attrs['ret_func'].func_code.co_flags, attrs['ret_func'].func_code.co_code, attrs['ret_func'].func_code.co_consts, attrs['ret_func'].func_code.co_names, varnames, attrs['ret_func'].func_code.co_filename, 'call', attrs['ret_func'].func_code.co_firstlineno, attrs['ret_func'].func_code.co_lnotab, (), # freevars () # cellvars )
  • 18. Попробуем применить def create_func(fun, nargs): def wrapper(): args = map(itemgetter(1), sorted(((var_name, arg) for (var_name, arg) in locals().items() if var_name != 'fun'), key=itemgetter(0))) fun(*args) varnames = tuple(str(i) for i in range(0, nargs)) ret_code = types.CodeType(nargs, wrapper.func_code.co_nlocals + nargs, # Параметры ) return types.FunctionType(ret_code, wrapper.func_globals, wrapper.func_code.co_name) def test_func(*args): print args fun = create_func(test_func, 3) fun(1, 2, 3)
  • 19. Как-то не очень Traceback (most recent call last): File "func.py", line 38, in <module> fun(1,2,3) File "func.py", line 16, in wrapper fun(*args) SystemError: Objects/cellobject.c:24: bad argument to internal function
  • 20. Заработало! def create_func(fun, nargs): def wrapper(fun): args = map(itemgetter(1), sorted(((var_name, arg) for (var_name, arg) in locals().items() if var_name != 'fun'), key=itemgetter(0))) fun(*args) varnames = tuple(['fun'] + [str(i) for i in range(0, nargs)]) ret_code = types.CodeType(nargs + 1, wrapper.func_code.co_nlocals + nargs, # Параметры ) return partial(types.FunctionType(ret_code, wrapper.func_globals, wrapper.func_code.co_name), fun) def test_func(*args): print args fun = create_func(test_func, 3) fun(1, 2, 3)
  • 21. Что-то потеряли >>> dir(wrapper.func_code) ['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']
  • 22. Добавим параметр ret_code = types.CodeType(nargs, wrapper.func_code.co_nlocals + nargs, # Параметры wrapper.func_code.co_freevars) return types.FunctionType(ret_code, wrapper.func_globals, wrapper.func_code.co_name) >>>Traceback (most recent call last): File "func.py", line 39, in <module> fun = create_func(test_func, 3) File "func.py", line 33, in create_func return types.FunctionType(ret_code, wrapper.func_globals, wrapper.func_code.co_name) TypeError: arg 5 (closure) must be tuple
  • 23. def create_func(fun, nargs): def wrapper(): args = map(itemgetter(1), sorted(((var_name, arg) for (var_name, arg) in locals().items() if var_name != 'fun'), key=itemgetter(0))) fun(*args) varnames = tuple([str(i) for i in range(0, nargs)]) ret_code = types.CodeType(nargs, wrapper.func_code.co_nlocals + nargs, # Параметры wrapper.func_code.co_freevars) return types.FunctionType(ret_code, wrapper.func_globals, wrapper.func_code.co_name, None, wrapper.func_closure) def test_func(*args): print args fun = create_func(test_func, 3) fun(1, 2, 3) И наконец, замыкания работают!
  • 24. … почти from operator import itemgetter def wrapper(): print locals() args = map(itemgetter(1), sorted(((var_name, arg) for (var_name, arg) in locals().items() if var_name != 'fun'), key=itemgetter(0))) print locals() fun(*args) {'1': 2, '0': 1, '2': 3, 'fun': <function test_func at 0x10cd62398>} {'1': 2, '0': [1, 2, 3], '2': 3, 'fun': <function test_func at 0x10cd62398>}
  • 25. Спасибо за внимание! Вопросы? SPb Python Interest Group https://telegram.me/spbpython