[pyar] Peligrosidad de un lambda

Claudio Freire klaussfreire en gmail.com
Vie Oct 8 11:34:46 ART 2010


2010/10/7 Hystrix <ego en hystrix.com.ar>

> 2010/10/7 Claudio Freire <klaussfreire en gmail.com>:
> > La pregunta original, leela, estaba libre de contexto. Como los parsers.
>
> Agrego un poco de contexto porque aunque la discusion se volvio
> interesante, sigo con la duda original.
> La intencion era permitir que el usuario pudiera tener una funcion
> programable, es decir, que pueda definir la formula que se usa para
> calcular el resultado de una operacion.
> Esto surge de una simulacion muy basica que saque de un libro:
> Hay un club con 100 miembros, cada uno de una casta (0-9), estos
> miembros se juntan de a pares, se aplica una formula que recibe como
> entrada las castas de cada uno y el resultado es la casta nueva del
> primero del par. La idea era poder modificar la formula que calcula la
> casta nueva sin tener que editar el codigo del programa.
> Por eso se me ocurrio, muy ingenuamente, usar codigo metido en un
> lambda. Obviamente eso no sirve, no al menos como me hubiera gustado.
> Entonces, se puede darle al usuario una forma de modificar cierta
> parte del programa sin exponerse a cosas obvias?
>

Depende de cuánta funcionalidad necesites en esas fórmulas.

Si sólo necesitás lógica básica, podrías utilizar compile() con un poco de
análisis de bytecode.
¿cómo?

Bueno, la única forma que se me ocurre de hacer algo peligroso es con el
opcode LOAD_GLOBAL, IMPORT_X, EXEC, etc...

Validar que una función no ejecute esos bytecodes es relativamente sencillo
(buscando el opcode, que es un único byte, en f.func_code.co_code).

Ejemplo:

>>> formula = raw_input('lambda a,b: ')
lambda a,b: max(a,b)
>>> formula
'max(a,b)'
>>> f = eval('lambda a,b: ' + formula)
>>> f.func_code.co_code
't\x00\x00|\x00\x00|\x01\x00\x83\x02\x00S'

Estoy asumiendo que, como pregunté antes, no hay forma de ejecutar código
con eval('lambda a,b: ' + formula). Que parece sensato, eval sólo admite
expresiones, y ya lo forcé a una expresión lambda (que no ejecuta código,
sólo crea una función).

Habría que demostrar eso, pero creo que es válido.

>>> import opcode
>>> opcode.opmap['LOAD_GLOBAL']
116

Ese es el opcode para LOAD_GLOBAL. En realidad hay que buscar otros también,
podés fijarte en la documentación todos los opcodes malos. No creo que un
lambda pueda hacer import, pero por las dudas lo agregaría.

>>> chr(opcode.opmap['LOAD_GLOBAL']) in f.func_code.co_code
True

Oops... mi función es insegura. Porque toma un global ('max'), y no sé qué
podrá contener ese global.

Eso quiere decir que si querés que pueda acceder a una función, tenés que
darle permiso:

>>> f = eval('lambda a,b,max=max: ' + formula)
>>> chr(opcode.opmap['LOAD_GLOBAL']) in f.func_code.co_code
False
>>>

Ahí está seguro. Le dejé usar max (poniéndolo en el scope local del lambda).

Así podés darle acceso a módulos enteros (math... asumiendo que darle acceso
a math es seguro) si querés.

Dudo que puedas darle acceso a un módulo entero y estar 100% seguro, pero la
idea está.

Creo que es una idea copada :-)
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20101008/853b1e7a/attachment.html>


More information about the pyar mailing list