Page MenuHome

Proof of concept restricted Python evaluation (using bytecode evaluation)
AcceptedPublic

Authored by Campbell Barton (campbellbarton) on Mar 19 2016, 12:04 PM.

Details

Summary

This patch doesn't make any changes to how drivers work, it just inspects Python's code object and its byte-codes, failing when any *unsupported* operations are used.

How it works

Restrict Name Access

First all names are checked, they must be in our white-list, that includes...

  • driver namespace (including math, mathutils.noise)
  • driver variable names.
  • our own whitelist of __builtins__.

This means you can't access import, open, exec and eval.

However restricting names isn't enough, since you can do tricks like...

max.__class__.__class__.__subclasses__(max.__class__.__class__)[0].__new__.__globals__['__builtins__']['open']("/somefile")

Restrict Byte-Code Use

As well as restricting names, this patch checks byte-codes and disallows many operations, including import and attribute access.

This means you can do math expressions, with variables - but not very much more.

Tested with glass-half file, and all expressions pass the test.

What Works?

The following expression works

sin(a) / cos(b) ** 2

This doesn't (no attribute access)

sin(a.b) / cos(b) ** 2

Also not (no access to getattr or import) ....

getattr(__import__("os"), "unlink")("/path")

The tricky part is ensuring that this really is secure. To test this more easily, I've made a project on github [0] that implements this in Python which is easier to test.

[0]: https://github.com/ideasman42/py_safe_math_eval

Diff Detail

Repository
rB Blender
Branch
TEMP-D1862-UPDATE
Build Status
Buildable 1764
Build 1764: arc lint + arc unit

Event Timeline

Campbell Barton (campbellbarton) retitled this revision from to Check bytecode before executing py-drivers (possible security feature).
  • More comprehensive list of whitelisted builtins
Campbell Barton (campbellbarton) retitled this revision from Check bytecode before executing py-drivers (possible security feature) to Proof of concept restricted Python evaluation (using bytecode evaluation).Mar 19 2016, 12:12 PM

How do you ensure new insafe opcodes in Python do not cause problems? Or, how do you make sure new opcodes in Python which are safe do not cause drivers to fail?

Since the whole reason we have this is to keep people safe while allowing the majority of drivers to function, I think it's better to err on the side of caution. Any new opcodes that didn't exist before are likely to be for new features in Python. So, old drivers aren't likely to break, while new ones could be fixed on a case by case basis - assuming that the need actually arose.

(I'm guessing here though that these opcodes don't change much between Python releases, especially between things like 3.4 to 3.5. At least for the kinds of basic ops needed for the basic math equations used in driver expressions, it's unlikely that there would suddenly be a whole lot more to add or change.)

You can't predict how optimizer will place opcodes. It is possible that old expression will use new opcodes due to optimizer. Perhaps.

@Sergey Sharybin (sergey), while its possible new python uses opcodes differently.
the current method whitelists opcodes, so wost case new expressions fail in the future.
Since we control the Python version - we can manage this - and CPython are not changing this area very often.

One thing that *could* go wrong is a bytecode that currently safe, becomes unsafe.
(while this seems unlikely, its possible).

Very important code style feedback ;)

Since this is something we've discussed with Campbell here, LGTM. Will help opening files from internet for depsgraph debug purposes.

source/blender/python/intern/bpy_driver.c
58

Indent nested preprocessor.

This revision is now accepted and ready to land.Jun 17 2018, 7:04 PM