{"id":6555,"date":"2018-04-13T16:04:32","date_gmt":"2018-04-13T08:04:32","guid":{"rendered":"https:\/\/kyle.ai\/blog\/?p=6555"},"modified":"2018-04-14T14:04:37","modified_gmt":"2018-04-14T06:04:37","slug":"%e5%ae%89%e5%85%a8%e5%9c%b0%e4%bd%bf%e7%94%a8python%e4%b8%ad%e7%9a%84eval","status":"publish","type":"post","link":"https:\/\/kyle.ai\/blog\/6555.html","title":{"rendered":"\u5b89\u5168\u5730\u4f7f\u7528Python\u4e2d\u7684eval"},"content":{"rendered":"<p>python\u4e2d\u7684eval\u51fd\u6570\u975e\u5e38\u5f3a\u5927\uff0c\u53ef\u4ee5\u6267\u884c\u8f93\u5165\u7684\u5b57\u7b26\u4e32\u52a8\u6001\u4ee3\u7801\uff0c\u4f8b\u5982\uff1a<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; eval(&quot;1+2&quot;)\r\n3\r\n&gt;&gt;&gt; eval(&quot;&#x5B;x for x in range(10)]&quot;)\r\n&#x5B;0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\r\n\r\n&gt;&gt;&gt; import os\r\n&gt;&gt;&gt; eval(&quot;os.system('whoami')&quot;)\r\nkyle\r\n0\r\n<\/pre>\n<p>eval \u53ea\u80fd\u6267\u884c Python \u7684\u8868\u8fbe\u5f0f\u7c7b\u578b\u7684\u4ee3\u7801\uff0c\u4e0d\u80fd\u76f4\u63a5\u7528\u5b83\u8fdb\u884c import \u64cd\u4f5c\uff0c\u4f46 exec \u53ef\u4ee5\u3002\u5982\u679c\u975e\u8981\u4f7f\u7528 eval \u8fdb\u884c import\uff0c\u5219\u4f7f\u7528__import__\uff1a<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; eval(&quot;__import__('os').system('whoami')&quot;)\r\n<\/pre>\n<p>eval \u53ea\u63a5\u53d7\u4e00\u4e2a\u8868\u8fbe\u5f0f\u5b57\u7b26\u4e32\u4ee3\u7801\uff0c\u5e76\u8fd4\u56de\u6267\u884c\u7ed3\u679c\uff0c\u800c exec \u53ef\u4ee5\u63a5\u53d7\u4e00\u4e2a\u4ee3\u7801\u5757\uff0c\u91cc\u9762\u53ef\u4ee5\u6709\u5faa\u73af\uff0c\u53ef\u4ee5\u6709\u7c7b\u3001\u51fd\u6570\u5b9a\u4e49\u7b49\u7b49\uff0cexec\u6267\u884c\u8fd9\u4e2a\u4ee3\u7801\u5757\uff0c\u5e76\u6c38\u8fdc\u8fd4\u56de None\u3002<\/p>\n<p>\u5173\u4e8e\u66f4\u591a eval exec \u548c compile \u7684\u533a\u522b\uff0c\u53ef\u4ee5\u53c2\u8003\u8fd9\u4e2a\u5e16\u5b50\uff1a<a href=\"https:\/\/stackoverflow.com\/questions\/2220699\/whats-the-difference-between-eval-exec-and-compile-in-python\">https:\/\/stackoverflow.com\/questions\/2220699\/whats-the-difference-between-eval-exec-and-compile-in-python<\/a><\/p>\n<p>\u6b63\u56e0\u4e3aeval\u529f\u80fd\u7684\u5f3a\u5927\uff0c\u5f88\u6709\u53ef\u80fd\u8f93\u5165\u7684\u53c2\u6570\u542b\u6709\u6076\u610f\u5229\u7528\u7684\u4ee3\u7801\uff0c\u9020\u6210\u6f0f\u6d1e\uff0c\u4f8b\u5982\u7528\u6237\u8f93\u5165\u7684\u5b57\u7b26\u4e32\u662f\u8fd9\u6837\u7684<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n__import__('os').system('dir')  \r\n<\/pre>\n<p>\u90a3\u4e48 eval() \u4e4b\u540e\uff0c\u4f60\u4f1a\u53d1\u73b0\uff0c\u5f53\u524d\u76ee\u5f55\u6587\u4ef6\u90fd\u4f1a\u5c55\u73b0\u5728\u7528\u6237\u524d\u9762\u3002\u6216\u8005<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nopen('\u6587\u4ef6\u540d').read()  \r\n__import__('os').system('rm -rf \/')  \r\n<\/pre>\n<p>\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u4f20\u9012 __builtins__ \u53c2\u6570\u6765\u9650\u5236\u5185\u7f6e\u65b9\u6cd5\u7684\u4f7f\u7528\uff0c\u5982<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nIn &#x5B;2]: print eval(&quot;__import__('os').remove('file')&quot;, {&quot;__builtins__&quot;: {}})\r\n---------------------------------------------------------------------------\r\nNameError                                 Traceback (most recent call last)\r\n&lt;ipython-input-2-ca308c631e67&gt; in &lt;module&gt;()\r\n----&gt; 1 print eval(&quot;__import__('os').remove('file')&quot;, {&quot;__builtins__&quot;: {}})\r\n\r\n&lt;string&gt; in &lt;module&gt;()\r\n\r\nNameError: name '__import__' is not defined\r\n<\/pre>\n<p>\u4e0d\u8fc7\u8bbe\u7f6e __builtins__ \u4e5f\u5e76\u4e0d\u4fdd\u8bc1\u7edd\u5bf9\u5b89\u5168\uff0c\u4f8b\u5982\u4e0b\u9762\u8fd9\u6bb5\u4ee3\u7801\u5219\u662f\u9000\u51fa\u89e3\u91ca\u5668\uff1a<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&gt;&gt;&gt; s = &quot;&quot;&quot;\r\n... &#x5B;\r\n...     c for c in\r\n...     ().__class__.__bases__&#x5B;0].__subclasses__()\r\n...     if c.__name__ == &quot;Quitter&quot;\r\n... ]&#x5B;0](0)()\r\n... &quot;&quot;&quot;\r\n&gt;&gt;&gt; eval(s, {'__builtins__':{}})\r\n\r\nD:\\&gt;\r\n<\/pre>\n<p>().__class__.__bases__[0].__subclasses__()<\/p>\n<p>CPU \u6076\u610f\u653b\u51fb\u7684\u4f8b\u5b50\uff0c\u6267\u884c\u4e0b\u9762\u8fd9\u4e2aeval\u4f1a\u8ba9\u4f60\u7684cpu\u957f\u671f100%\u8d1f\u8f7d\u8fd0\u884c\uff0c\u6574\u4e2a\u7a0b\u5e8f\u50f5\u6b7b\u3002<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\neval(&quot;2**9999999999**9999999&quot;)\r\n<\/pre>\n<p>\u8fd8\u6709\u5185\u5b58\u653b\u51fb\u7684\u4f8b\u5b50\uff0c\u5728 python 2 \u4e2d\u8fd0\u884c<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\neval('(1,' * 100 + ')' * 100)\r\n<\/pre>\n<p>\u4f1a\u62a5 memory error\u7684\u9519\u8bef\uff1a<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nIn &#x5B;3]: eval('(1,' * 100 + ')' * 100)\r\ns_push: parser stack overflow\r\n---------------------------------------------------------------------------\r\nMemoryError Traceback (most recent call last)\r\nin ()\r\n----&gt; 1 eval('(1,' * 100 + ')' * 100)\r\n\r\nMemoryError:\r\n<\/pre>\n<p>\u5728 python 2 \u4e2d\u8fd0\u884c\u4e0b\u9762\u8fd9\u4e2aeval\uff0c\u4f1a\u51fa\u73b0 Segmentation fault (core dumped) \u7684\u9519\u8bef<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\nprint eval('(lambda i: &#x5B;i for i in ((i, 1) for j in range(1000000))]&#x5B;-1])(1)')\r\n<\/pre>\n<p>\u90a3\u4e48\u5982\u4f55\u4fdd\u62a4eval\u65b9\u6cd5\uff0c\u5bf9\u5b83\u7684\u53c2\u6570\u8fdb\u884c\u5b89\u5168\u8fdb\u6821\u9a8c\u5462\uff1f\u6211\u5728\u7f51\u4e0a\u770b\u5230\u4e86\u4e24\u79cd\u601d\u8def\u3002<\/p>\n<p>\u7b2c\u4e00\u4e2a\u65b9\u6cd5\u6765\u81ea\u4e8e <a href=\"https:\/\/opensourcehacker.com\/2014\/10\/29\/safe-evaluation-of-math-expressions-in-pure-python\/\">https:\/\/opensourcehacker.com\/2014\/10\/29\/safe-evaluation-of-math-expressions-in-pure-python\/<\/a><\/p>\n<blockquote><p>We use the Python compile() function to prepare the Python expression to be the bytecode for evaling. Then, we actually don\u2019t eval(). Instead we use custom opcode handlers and evaluate opcodes in a loop one after another. There cannot be a sandbox escape, because opcodes having should functionality are not implemented. Because compile() does the job of generating the microcode, we are saved from the headache of writing a custom parser. Python dis module helps us minimize the code needed for a stack-based virtual machine.<\/p><\/blockquote>\n<p>\u610f\u601d\u5c31\u662f\u4e0d\u76f4\u63a5\u6267\u884ceval\uff0c\u800c\u662f\u5148\u7528compile\u5f97\u5230\u5b57\u8282\u7801\uff0c\u518d\u81ea\u5df1\u5199\u65b9\u6cd5\u6765\u4e00\u6b65\u6b65\u5730\u89e3\u6790\u6267\u884c\u5b57\u8282\u7801\u4e2d\u7684\u903b\u8f91\u3002<\/p>\n<p>\u81f3\u4e8eCPU\u653b\u51fb\uff0c\u53ef\u4ee5\u5148eval\u8fc7\u7a0b\u653e\u5230\u4e00\u4e2a\u5b50\u8fdb\u7a0b\u4e2d\u53bb\u6267\u884c\uff0c\u5e76\u4e14\u9650\u5236\u4f4f\u5b50\u8fdb\u7a0b\u8fd0\u884c\u7684\u65f6\u957f\uff0c\u5982\u679c\u8d85\u65f6\u4e86\uff0c\u5c31\u628a\u5b83kill\u6389\uff0c\u7531\u4e8epython\u7684GIL\u9501\u9650\u5236\uff0c\u6211\u4eec\u4e0d\u80fd\u4f7f\u7528\u5b50\u7ebf\u7a0b\u6765\u6740\u6389CPU\u5360\u7528\u7684\u8d85\u65f6\u903b\u8f91\uff0c\u6240\u4ee5\u53ea\u80fd\u7528\u8fdb\u7a0b\u6a21\u5f0f\u3002<\/p>\n<blockquote><p>We do this using Python\u2019s multiprocess module. The calculation is run in a separate child process. This process is terminated if it doesn\u2019t finish timely. In our case, we\u2019ll give 100 milliseconds for the expression calculations to finish.<\/p>\n<p>As a side note we threads didn\u2019t work here because it turned out compile() does not release GIL and thus thread.start() never returns if thread.run() contains a complex compile() \u2013 the original thread does not get GIL back.<\/p><\/blockquote>\n<p>\u5b8c\u6574\u65b9\u6848\u7684\u4ee3\u7801\u5b9e\u73b0\u53ef\u4ee5\u67e5\u770b\uff1a<a href=\"https:\/\/gist.github.com\/miohtama\/34a83d870a14aa7e580d\">https:\/\/gist.github.com\/miohtama\/34a83d870a14aa7e580d<\/a>\uff0c\u6587\u672b\u4e5f\u628a\u4ee3\u7801\u9644\u4e0a\u3002<\/p>\n<p>\u7b2c\u4e8c\u79cd\u65b9\u6cd5\u6765\u81ea\u4e8e\u5e93 Evalidate\uff0c\u53c2\u8003\uff1a<a href=\"http:\/\/evalidate.readthedocs.io\/en\/latest\/\">http:\/\/evalidate.readthedocs.io\/en\/latest\/<\/a><\/p>\n<p>\u6211\u4e2a\u4eba\u6bd4\u8f83\u559c\u6b22\u8fd9\u4e2a\u65b9\u6cd5\uff0c\u4ee3\u7801\u5b9e\u73b0\u4e5f\u5f88\u7b80\u77ed\uff0c\u601d\u8def\u662f\u901a\u8fc7ast\u5e93\u89e3\u6790\u4ee3\u7801\u7684\u8bed\u6cd5\u6811\uff0c\u518d\u9010\u4e00\u5206\u6790\u8bed\u6cd5\u6811\u662f\u4e0d\u662f\u5408\u6cd5\u7684\uff0c\u6211\u4eec\u53ef\u4ee5\u9650\u5236\u54ea\u4e9b\u8bed\u6cd5\u53ef\u4ee5\u4f7f\u7528\u3002<\/p>\n<p>\u6bd4\u5982\u8981\u89e3\u51b3 eval(&#8220;2**9999999999**9999999&#8221;) \u5c31\u53ef\u4ee5\u9650\u5236\u4e58\u65b9\u8fd0\u7b97\uff0c\u800c\u4e58\u65b9\u8fd0\u7b97\u5bf9\u5e94\u7684ast\u6811\u540d\u79f0\u4e3a Pow\uff0cAST\u6811\u5bf9\u5e94\u7684\u540d\u79f0\u6587\u6863\u53ef\u4ee5\u53c2\u8003 <a href=\"https:\/\/greentreesnakes.readthedocs.io\/en\/latest\/nodes.html\">https:\/\/greentreesnakes.readthedocs.io\/en\/latest\/nodes.html<\/a><\/p>\n<p>\u5b8c\u6574\u4ee3\u7801\u5982\u4e0b<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n\r\n#!\/usr\/bin\/python\r\n\r\n# from http:\/\/evalidate.readthedocs.io\/en\/latest\/\r\n&quot;&quot;&quot;Safe user-supplied python expression evaluation.&quot;&quot;&quot;\r\n\r\nimport ast\r\nimport sys\r\n\r\nversion = '0.6'\r\n\r\n\r\nclass SafeAST(ast.NodeVisitor):\r\n\r\n    &quot;&quot;&quot;AST-tree walker class.&quot;&quot;&quot;\r\n\r\n    allowed = {}\r\n\r\n    def __init__(self, safenodes=None, addnodes=None):\r\n        &quot;&quot;&quot;create whitelist of allowed operations.&quot;&quot;&quot;\r\n        if safenodes is not None:\r\n            self.allowed = safenodes\r\n        else:\r\n            '''Nodes doc: https:\/\/greentreesnakes.readthedocs.io\/en\/latest\/nodes.html'''\r\n            # 123, 'asdf'\r\n            values = &#x5B;'Num', 'Str']\r\n            # any expression\r\n            expression = &#x5B;'Expression']\r\n            # == ...\r\n            compare = &#x5B;'Compare', 'Eq', 'NotEq', 'Gt', 'GtE', 'Lt', 'LtE']\r\n            # variable name\r\n            variables = &#x5B;'Name', 'Load']\r\n            binop = &#x5B;'BinOp']\r\n            arithmetics = &#x5B;'Add', 'Sub', 'Div']\r\n            subscript = &#x5B;'Subscript', 'Index']  # person&#x5B;'name']\r\n            boolop = &#x5B;'BoolOp', 'And', 'Or', 'UnaryOp', 'Not']  # True and True\r\n            inop = &#x5B;&quot;In&quot;]  # &quot;aaa&quot; in i&#x5B;'list']\r\n            ifop = &#x5B;&quot;IfExp&quot;] # for if expressions, like: expr1 if expr2 else expr3\r\n            nameconst = &#x5B;&quot;NameConstant&quot;] # for True and False constants\r\n\r\n            self.allowed = expression + values + compare + variables + binop + \\\r\n                arithmetics + subscript + boolop + inop + ifop + nameconst\r\n\r\n        if addnodes is not None:\r\n            self.allowed = self.allowed + addnodes\r\n\r\n    def generic_visit(self, node):\r\n        &quot;&quot;&quot;Check node, rais exception is node is not in whitelist.&quot;&quot;&quot;\r\n        if type(node).__name__ in self.allowed:\r\n            ast.NodeVisitor.generic_visit(self, node)\r\n        else:\r\n            raise ValueError(\r\n                &quot;Operaton type {optype} is not allowed&quot;.format(\r\n                    optype=type(node).__name__))\r\n\r\n\r\ndef evalidate(expression, safenodes=None, addnodes=None):\r\n    &quot;&quot;&quot;Validate expression.\r\n\r\n    return node if it passes our checks\r\n    or pass exception from SafeAST visit.\r\n    &quot;&quot;&quot;\r\n    node = ast.parse(expression, '&lt;usercode&gt;', 'eval')\r\n\r\n    v = SafeAST(safenodes, addnodes)\r\n    v.visit(node)\r\n    return node\r\n\r\n\r\ndef safeeval(src, context={}, safenodes=None, addnodes=None):\r\n    &quot;&quot;&quot;C-style simplified wrapper, eval() replacement.&quot;&quot;&quot;\r\n    try:\r\n        node = evalidate(src, safenodes, addnodes)\r\n    except Exception as e:\r\n        return (False, &quot;Validation error: &quot;+e.__str__())\r\n\r\n    try:\r\n        code = compile(node, '&lt;usercode&gt;', 'eval')\r\n    except Exception as e:\r\n        return (False, &quot;Compile error: &quot;+e.__str__())\r\n\r\n    try:\r\n        wcontext = context.copy()\r\n        result = eval(code, wcontext)\r\n    except Exception as e:\r\n        et, ev, erb = sys.exc_info()\r\n        return False, &quot;Runtime error ({}): {}&quot;.format(type(e).__name__, ev)\r\n\r\n    return (True, result)\r\n\r\n\r\nif __name__ == '__main__':\r\n\r\n    books = &#x5B;\r\n        {\r\n            'title': 'The Sirens of Titan',\r\n            'author': 'Kurt Vonnegut',\r\n            'stock': 10,\r\n            'price': 9.71\r\n        },\r\n        {\r\n            'title': 'Cat\\'s Cradle',\r\n            'author': 'Kurt Vonnegut',\r\n            'stock': 2,\r\n            'price': 4.23\r\n        },\r\n        {\r\n            'title': 'Chapaev i Pustota',\r\n            'author': 'Victor Pelevin',\r\n            'stock': 0,\r\n            'price': 21.33\r\n        },\r\n        {\r\n            'title': 'Gone Girl',\r\n            'author': 'Gillian Flynn',\r\n            'stock': 5,\r\n            'price': 8.97\r\n        },\r\n    ]\r\n\r\n    #src = 'stock&gt;= (5 if price&lt;9 else 0)' src = 'stock&gt;5 or price&gt;10'\r\n\r\n    for book in books:\r\n        success, result = safeeval(src, book)\r\n        if success:\r\n            if result:\r\n                print(book)\r\n        else:\r\n            print(&quot;ERR: &quot;, result)\r\n\r\n    print safeeval(&quot;os.system('whoami')&quot;)\r\n    print safeeval('import os')\r\n    print safeeval(&quot;__import__('os').system('dir')  &quot;)\r\n    print safeeval('a + (3 if 3&gt;b else b) \/ c', {'a':5, 'b':1, 'c':2})\r\n    print safeeval('a + b**3', {'a':5, 'b':1, 'c':2})\r\n\r\n<\/pre>\n<p>\u65b9\u6cd5\u4e00\u4ee3\u7801\u5982\u4e0b<\/p>\n<pre class=\"brush: python; title: ; notranslate\" title=\"\">\r\n&quot;&quot;&quot;&quot;\r\n    The orignal author: Alexer \/ #python.fi\r\n&quot;&quot;&quot;\r\n\r\nimport opcode\r\nimport dis\r\nimport sys\r\nimport multiprocessing\r\nimport time\r\n\r\n# Python 3 required\r\nassert sys.version_info&#x5B;0] == 3, &quot;No country for old snakes&quot;\r\n\r\n\r\nclass UnknownSymbol(Exception):\r\n    &quot;&quot;&quot; There was a function or constant in the expression we don't support. &quot;&quot;&quot;\r\n\r\n\r\nclass BadValue(Exception):\r\n    &quot;&quot;&quot; The user tried to input dangerously big value. &quot;&quot;&quot;\r\n\r\n    MAX_ALLOWED_VALUE = 2**63\r\n\r\n\r\nclass BadCompilingInput(Exception):\r\n    &quot;&quot;&quot; The user tried to input something which might cause compiler to slow down. &quot;&quot;&quot;\r\n\r\n\r\nclass TimeoutException(Exception):\r\n    &quot;&quot;&quot; It took too long to compile and execute. &quot;&quot;&quot;\r\n\r\n\r\nclass RunnableProcessing(multiprocessing.Process):\r\n    &quot;&quot;&quot; Run a function in a child process.\r\n    Pass back any exception received.\r\n    &quot;&quot;&quot;\r\n    def __init__(self, func, *args, **kwargs):\r\n        self.queue = multiprocessing.Queue(maxsize=1)\r\n        args = (func,) + args\r\n        multiprocessing.Process.__init__(self, target=self.run_func, args=args, kwargs=kwargs)\r\n\r\n    def run_func(self, func, *args, **kwargs):\r\n        try:\r\n            result = func(*args, **kwargs)\r\n            self.queue.put((True, result))\r\n        except Exception as e:\r\n            self.queue.put((False, e))\r\n\r\n    def done(self):\r\n        return self.queue.full()\r\n\r\n    def result(self):\r\n        return self.queue.get()\r\n\r\n\r\ndef timeout(seconds, force_kill=True):\r\n    &quot;&quot;&quot; Timeout decorator using Python multiprocessing.\r\n    Courtesy of http:\/\/code.activestate.com\/recipes\/577853-timeout-decorator-with-multiprocessing\/\r\n    &quot;&quot;&quot;\r\n    def wrapper(function):\r\n        def inner(*args, **kwargs):\r\n            now = time.time()\r\n            proc = RunnableProcessing(function, *args, **kwargs)\r\n            proc.start()\r\n            proc.join(seconds)\r\n            if proc.is_alive():\r\n                if force_kill:\r\n                    proc.terminate()\r\n                runtime = time.time() - now\r\n                raise TimeoutException('timed out after {0} seconds'.format(runtime))\r\n            assert proc.done()\r\n            success, result = proc.result()\r\n            if success:\r\n                return result\r\n            else:\r\n                raise result\r\n        return inner\r\n    return wrapper\r\n\r\n\r\ndef disassemble(co):\r\n    &quot;&quot;&quot; Loop through Python bytecode and match instructions  with our internal opcodes.\r\n    :param co: Python code object\r\n    &quot;&quot;&quot;\r\n    code = co.co_code\r\n    n = len(code)\r\n    i = 0\r\n    extended_arg = 0\r\n    result = &#x5B;]\r\n    while i &lt; n: op = code&#x5B;i] curi = i i = i+1 if op &gt;= dis.HAVE_ARGUMENT:\r\n            # Python 2\r\n            # oparg = ord(code&#x5B;i]) + ord(code&#x5B;i+1])*256 + extended_arg\r\n            oparg = code&#x5B;i] + code&#x5B;i+1] * 256 + extended_arg\r\n            extended_arg = 0\r\n            i = i+2\r\n            if op == dis.EXTENDED_ARG:\r\n                # Python 2\r\n                #extended_arg = oparg*65536L\r\n                extended_arg = oparg*65536\r\n        else:\r\n            oparg = None\r\n\r\n        # print(opcode.opname&#x5B;op])\r\n\r\n        opv = globals()&#x5B;opcode.opname&#x5B;op].replace('+', '_')](co, curi, i, op, oparg)\r\n\r\n        result.append(opv)\r\n\r\n    return result\r\n\r\n# For the opcodes see dis.py\r\n# (Copy-paste)\r\n# https:\/\/docs.python.org\/2\/library\/dis.html\r\n\r\nclass Opcode:\r\n    &quot;&quot;&quot; Base class for out internal opcodes. &quot;&quot;&quot;\r\n    args = 0\r\n    pops = 0\r\n    pushes = 0\r\n    def __init__(self, co, i, nexti, op, oparg):\r\n        self.co = co\r\n        self.i = i\r\n        self.nexti = nexti\r\n        self.op = op\r\n        self.oparg = oparg\r\n\r\n    def get_pops(self):\r\n        return self.pops\r\n\r\n    def get_pushes(self):\r\n        return self.pushes\r\n\r\n    def touch_value(self, stack, frame):\r\n        assert self.pushes == 0\r\n        for i in range(self.pops):\r\n            stack.pop()\r\n\r\n\r\nclass OpcodeArg(Opcode):\r\n    args = 1\r\n\r\n\r\nclass OpcodeConst(OpcodeArg):\r\n    def get_arg(self):\r\n        return self.co.co_consts&#x5B;self.oparg]\r\n\r\n\r\nclass OpcodeName(OpcodeArg):\r\n    def get_arg(self):\r\n        return self.co.co_names&#x5B;self.oparg]\r\n\r\n\r\nclass POP_TOP(Opcode):\r\n    &quot;&quot;&quot;Removes the top-of-stack (TOS) item.&quot;&quot;&quot;\r\n    pops = 1\r\n    def touch_value(self, stack, frame):\r\n        stack.pop()\r\n\r\n\r\nclass DUP_TOP(Opcode):\r\n    &quot;&quot;&quot;Duplicates the reference on top of the stack.&quot;&quot;&quot;\r\n    # XXX: +-1\r\n    pops = 1\r\n    pushes = 2\r\n    def touch_value(self, stack, frame):\r\n        stack&#x5B;-1:] = 2 * stack&#x5B;-1:]\r\n\r\n\r\nclass ROT_TWO(Opcode):\r\n    &quot;&quot;&quot;Swaps the two top-most stack items.&quot;&quot;&quot;\r\n    pops = 2\r\n    pushes = 2\r\n    def touch_value(self, stack, frame):\r\n        stack&#x5B;-2:] = stack&#x5B;-2:]&#x5B;::-1]\r\n\r\n\r\nclass ROT_THREE(Opcode):\r\n    &quot;&quot;&quot;Lifts second and third stack item one position up, moves top down to position three.&quot;&quot;&quot;\r\n    pops = 3\r\n    pushes = 3\r\n    direct = True\r\n    def touch_value(self, stack, frame):\r\n        v3, v2, v1 = stack&#x5B;-3:]\r\n        stack&#x5B;-3:] = &#x5B;v1, v3, v2]\r\n\r\n\r\nclass ROT_FOUR(Opcode):\r\n    &quot;&quot;&quot;Lifts second, third and forth stack item one position up, moves top down to position four.&quot;&quot;&quot;\r\n    pops = 4\r\n    pushes = 4\r\n    direct = True\r\n    def touch_value(self, stack, frame):\r\n        v4, v3, v2, v1 = stack&#x5B;-3:]\r\n        stack&#x5B;-3:] = &#x5B;v1, v4, v3, v2]\r\n\r\n\r\nclass UNARY(Opcode):\r\n    &quot;&quot;&quot;Unary Operations take the top of the stack, apply the operation, and push the result back on the stack.&quot;&quot;&quot;\r\n    pops = 1\r\n    pushes = 1\r\n\r\n\r\nclass UNARY_POSITIVE(UNARY):\r\n    &quot;&quot;&quot;Implements TOS = +TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        stack&#x5B;-1] = +stack&#x5B;-1]\r\n\r\n\r\nclass UNARY_NEGATIVE(UNARY):\r\n    &quot;&quot;&quot;Implements TOS = -TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        stack&#x5B;-1] = -stack&#x5B;-1]\r\n\r\n\r\nclass BINARY(Opcode):\r\n    &quot;&quot;&quot;Binary operations remove the top of the stack (TOS) and the second top-most stack item (TOS1) from the stack. \r\nThey perform the operation, and put the result back on the stack.&quot;&quot;&quot;\r\n    pops = 2\r\n    pushes = 1\r\n\r\n\r\nclass BINARY_POWER(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 ** TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        print(TOS1, TOS)\r\n        if abs(TOS1) &gt; BadValue.MAX_ALLOWED_VALUE or abs(TOS) &gt; BadValue.MAX_ALLOWED_VALUE:\r\n            raise BadValue(&quot;The value for exponent was too big&quot;)\r\n\r\n        stack&#x5B;-2:] = &#x5B;TOS1 ** TOS]\r\n\r\n\r\nclass BINARY_MULTIPLY(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 * TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 * TOS]\r\n\r\n\r\nclass BINARY_DIVIDE(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 \/ TOS when from __future__ import division is not in effect.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 \/ TOS]\r\n\r\n\r\nclass BINARY_MODULO(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 % TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 % TOS]\r\n\r\n\r\nclass BINARY_ADD(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 + TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 + TOS]\r\n\r\n\r\nclass BINARY_SUBTRACT(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 - TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 - TOS]\r\n\r\n\r\nclass BINARY_FLOOR_DIVIDE(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 \/\/ TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 \/\/ TOS]\r\n\r\n\r\nclass BINARY_TRUE_DIVIDE(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 \/ TOS when from __future__ import division is in effect.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 \/ TOS]\r\n\r\n\r\nclass BINARY_LSHIFT(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 &lt;&lt; TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 &lt;&lt; TOS] class BINARY_RSHIFT(BINARY): &quot;&quot;&quot;Implements TOS = TOS1 &gt;&gt; TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 &gt;&gt; TOS]\r\n\r\n\r\nclass BINARY_AND(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 &amp; TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 &amp; TOS]\r\n\r\n\r\nclass BINARY_XOR(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 ^ TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 ^ TOS]\r\n\r\n\r\nclass BINARY_OR(BINARY):\r\n    &quot;&quot;&quot;Implements TOS = TOS1 | TOS.&quot;&quot;&quot;\r\n    def touch_value(self, stack, frame):\r\n        TOS1, TOS = stack&#x5B;-2:]\r\n        stack&#x5B;-2:] = &#x5B;TOS1 | TOS]\r\n\r\n\r\nclass RETURN_VALUE(Opcode):\r\n    &quot;&quot;&quot;Returns with TOS to the caller of the function.&quot;&quot;&quot;\r\n    pops = 1\r\n    final = True\r\n    def touch_value(self, stack, frame):\r\n        value = stack.pop()\r\n        return value\r\n\r\n\r\nclass LOAD_CONST(OpcodeConst):\r\n    &quot;&quot;&quot;Pushes co_consts&#x5B;consti] onto the stack.&quot;&quot;&quot; # consti\r\n    pushes = 1\r\n    def touch_value(self, stack, frame):\r\n        # XXX moo: Validate type\r\n        value = self.get_arg()\r\n        assert isinstance(value, (int, float))\r\n        stack.append(value)\r\n\r\n\r\nclass LOAD_NAME(OpcodeName):\r\n    &quot;&quot;&quot;Pushes the value associated with co_names&#x5B;namei] onto the stack.&quot;&quot;&quot; # namei\r\n    pushes = 1\r\n    def touch_value(self, stack, frame):\r\n        # XXX moo: Get name from dict of valid variables\/functions\r\n        name = self.get_arg()\r\n        if name not in frame:\r\n            raise UnknownSymbol(&quot;Does not know symbol {}&quot;.format(name))\r\n        stack.append(frame&#x5B;name])\r\n\r\n\r\nclass CALL_FUNCTION(OpcodeArg):\r\n    &quot;&quot;&quot;Calls a function. The low byte of argc indicates the number of positional parameters, the high byte the number of keyword parameters. \r\nOn the stack, the opcode finds the keyword parameters first. For each keyword argument,\r\n the value is on top of the key. Below the keyword parameters, the positional parameters are on the stack, \r\nwith the right-most parameter on top. Below the parameters, the function object to call is on the stack. Pops all function arguments,\r\n and the function itself off the stack, and pushes the return value.&quot;&quot;&quot; # argc\r\n    pops = None\r\n    pushes = 1\r\n\r\n    def get_pops(self):\r\n        args = self.oparg &amp; 0xff\r\n        kwargs = (self.oparg &gt;&gt; 8) &amp; 0xff\r\n        return 1 + args + 2 * kwargs\r\n\r\n    def touch_value(self, stack, frame):\r\n        argc = self.oparg &amp; 0xff\r\n        kwargc = (self.oparg &gt;&gt; 8) &amp; 0xff\r\n        assert kwargc == 0\r\n        if argc &gt; 0:\r\n            args = stack&#x5B;-argc:]\r\n            stack&#x5B;:] = stack&#x5B;:-argc]\r\n        else:\r\n            args = &#x5B;]\r\n        func = stack.pop()\r\n\r\n        assert func in frame.values(), &quot;Uh-oh somebody injected bad function. This does not happen.&quot;\r\n\r\n        result = func(*args)\r\n        stack.append(result)\r\n\r\n\r\ndef check_for_pow(expr):\r\n    &quot;&quot;&quot; Python evaluates power operator during the compile time if its on constants.\r\n    You can do CPU \/ memory burning attack with ``2**999999999999999999999**9999999999999``.\r\n    We mainly care about memory now, as we catch timeoutting in any case.\r\n    We just disable pow and do not care about it.\r\n    &quot;&quot;&quot;\r\n    if &quot;**&quot; in expr:\r\n        raise BadCompilingInput(&quot;Power operation is not allowed&quot;)\r\n\r\n\r\ndef _safe_eval(expr, functions_and_constants={}, check_compiling_input=True):\r\n    &quot;&quot;&quot; Evaluate a Pythonic math expression and return the output as a string.\r\n    The expr is limited to 1024 characters \/ 1024 operations\r\n    to prevent CPU burning or memory stealing.\r\n    :param functions_and_constants: Supplied &quot;built-in&quot; data for evaluation\r\n    &quot;&quot;&quot;\r\n\r\n    # Some safety checks\r\n    assert len(expr) &lt; 1024\r\n\r\n    # Check for potential bad compiler input\r\n    if check_compiling_input:\r\n        check_for_pow(expr)\r\n\r\n    # Compile Python source code to Python code for eval()\r\n    code = compile(expr, '', 'eval')\r\n\r\n    # Dissect bytecode back to Python opcodes\r\n    ops = disassemble(code)\r\n    assert len(ops) &lt; 1024\r\n\r\n    stack = &#x5B;]\r\n    for op in ops:\r\n        value = op.touch_value(stack, functions_and_constants)\r\n\r\n    return value\r\n\r\n\r\n@timeout(0.1)\r\ndef safe_eval_timeout(expr, functions_and_constants={}, check_compiling_input=True):\r\n    &quot;&quot;&quot; Hardered compile + eval for long running maths.\r\n    Mitigate against CPU burning attacks.\r\n    If some nasty user figures out a way around our limitations to make really really slow calculations.\r\n    &quot;&quot;&quot;\r\n    return _safe_eval(expr, functions_and_constants, check_compiling_input)\r\n\r\n\r\nif __name__ == &quot;__main__&quot;:\r\n\r\n    # Run some self testing\r\n\r\n    def test_eval(expected_result, *args):\r\n        result = safe_eval_timeout(*args)\r\n        if result != expected_result:\r\n            raise AssertionError(&quot;Got: {} expected: {}&quot;.format(result, expected_result))\r\n\r\n    test_eval(2, &quot;1+1&quot;)\r\n    test_eval(2, &quot;1 + 1&quot;)\r\n    test_eval(3, &quot;a + b&quot;, dict(a=1, b=2))\r\n    test_eval(2, &quot;max(1, 2)&quot;, dict(max=max))\r\n    test_eval(2, &quot;max(a, b)&quot;, dict(a=1, b=2, max=max))\r\n    test_eval(3, &quot;max(a, c, b)&quot;, dict(a=1, b=2, c=3, max=max))\r\n    test_eval(3, &quot;max(a, max(c, b))&quot;, dict(a=1, b=2, c=3, max=max))\r\n    test_eval(&quot;2&quot;, &quot;str(1 + 1)&quot;, dict(str=str))\r\n    test_eval(2.5, &quot;(a + b) \/ c&quot;, dict(a=4, b=1, c=2))\r\n\r\n    try:\r\n        test_eval(None, &quot;max(1, 0)&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except UnknownSymbol:\r\n        pass\r\n\r\n    # CPU burning\r\n    try:\r\n        test_eval(None, &quot;2**999999999999999999999**9999999999&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except BadCompilingInput:\r\n        pass\r\n\r\n    # CPU burning, see out timeoutter works\r\n    try:\r\n        safe_eval_timeout(&quot;2**999999999999999999999**9999999999&quot;, check_compiling_input=False)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except TimeoutException:\r\n        pass\r\n\r\n    try:\r\n        test_eval(None, &quot;1 \/ 0&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except ZeroDivisionError:\r\n        pass\r\n\r\n    try:\r\n        test_eval(None, &quot;(((((((((((((((()&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except SyntaxError:\r\n        #    for i in range(0, 100):\r\n        #      ^\r\n        # SyntaxError: invalid synta\r\n        pass\r\n\r\n    try:\r\n        test_eval(None, &quot;&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except SyntaxError:\r\n        # SyntaxError: unexpected EOF while parsing\r\n        pass\r\n\r\n    # compile() should not allow multiline stuff\r\n    # http:\/\/stackoverflow.com\/q\/12698028\/315168\r\n    try:\r\n        test_eval(None, &quot;for i in range(0, 100):\\n    pass&quot;, dict(i=-1))\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except SyntaxError:\r\n        #    for i in range(0, 100):\r\n        #      ^\r\n        # SyntaxError: invalid synta\r\n        pass\r\n\r\n    # No functions allowed\r\n    try:\r\n        test_eval(None, &quot;lamdba x: x+1&quot;)\r\n        raise AssertionError(&quot;Should not be reached&quot;)\r\n    except SyntaxError:\r\n        # SyntaxError: unexpected EOF while parsing\r\n        pass\r\n\r\n<\/pre>\n<p>\u53c2\u8003\u6765\u6e90\uff1a<\/p>\n<ul>\n<li>https:\/\/github.com\/aosabook\/500lines\/blob\/master\/template-engine\/code\/templite.py<\/li>\n<li>https:\/\/github.com\/greysign\/pysec\/blob\/master\/safeeval.py<\/li>\n<li>http:\/\/rinige.com\/index.php\/archives\/571\/<\/li>\n<li>http:\/\/evalidate.readthedocs.io\/en\/latest\/<\/li>\n<li>https:\/\/opensourcehacker.com\/2014\/10\/29\/safe-evaluation-of-math-expressions-in-pure-python\/<\/li>\n<li>https:\/\/gist.github.com\/miohtama\/34a83d870a14aa7e580d<\/li>\n<li>https:\/\/greentreesnakes.readthedocs.io\/en\/latest\/nodes.html<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>python\u4e2d\u7684eval\u51fd\u6570\u975e\u5e38\u5f3a\u5927\uff0c\u53ef\u4ee5\u6267\u884c\u8f93\u5165\u7684\u5b57\u7b26\u4e32\u52a8\u6001\u4ee3\u7801\uff0c\u4f8b\u5982\uff1a &gt;&gt;&gt; eval [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"class_list":["post-6555","post","type-post","status-publish","format-standard","hentry","category-code_related"],"_links":{"self":[{"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/posts\/6555","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/comments?post=6555"}],"version-history":[{"count":4,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/posts\/6555\/revisions"}],"predecessor-version":[{"id":6559,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/posts\/6555\/revisions\/6559"}],"wp:attachment":[{"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/media?parent=6555"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/categories?post=6555"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kyle.ai\/blog\/wp-json\/wp\/v2\/tags?post=6555"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}