9 ms·
Abusing python can really save your life sometimes. I think my favorite recent hack has been accessing and MODIFYING callers variable, due to inability to get i
by Demiurge 9y ago
Abusing python can really save your life sometimes. I think my favorite recent hack has been accessing and MODIFYING callers variable, due to inability to get it passed through the Django module to the callback.
def invite_accepted(sender, email, **kwargs):
import sys
caller = sys._getframe(2)
request = caller.f_locals['request']
request.session['invite_accepted'] = True
- Waterluvian 9y agoI love how everything is available at runtime. Python's "we are all consenting adults here" philosophy has been super useful in a pinch. I was monkey patching an external library in JavaScript today, rather than having to fork the thing for one slight modification, and thought about this philosophy again.
- Demiurge 9y agoOh yeah, the first time I monkey patched a method, it was an "aha" moment. I really understood the 'self' variable, and class as variable, and python just clicked all of a sudden for me.
- yen223 9y agoAs a person who has to maintain such systems, I really hate this aspect of Python.
- Waterluvian 9y agoI empathize. But I'm not convinced sloppy python is any worse than any other language used sloppily. Your beef is likely with the reality of doing business and shipping a real product.
- yen223 9y agoThe effort required to unwind these technical debts is actively inhibiting our ability to deliver real value. That's the reality we've got. Sloppy Python is particularly bad, because Python gives you a massive footgun to play with. Like you could write functions that radically alter its behaviour depending on where it's getting called - that's similar to the hack that the parent post was talking about. It's easy to build systems that are coupled in really weird and unexpected ways, and that makes reasoning about code much harder than it needs to be.
- ageofwant 9y agoThe reality is that Python provided you with a paying job, because it carried you to where you are. The effort and sheer dumb luck involved in keeping a business alive to the point that some dev can complain about technical debt is a very underappreciated luxury. Not that you are wrong, just saying...
- scaryclam 9y agoThat's not really the languages fault though. I've seen some amazingly bad Java, Python, JavaScript, PHP and bash. Be cross that technical debt was introduced and not paid back rather than at the tools used to do it.
- ghusbands 9y agoMost languages don't facilitate directly accessing the variables of other stack frames. Of the languages you listed, only Python does that (though bash has equivalent terrible bits), so that in particular _is_ Python's fault.
- SerLava 9y agoIn other words, not everyone involved is consenting to the things they're doing at runtime.
- progval 9y agoit's also a pain for compilers
- stochastic_monk 9y agoIf Python is interpreted, what’s the pain? Do you mean static compilers like Cython/pybind11/Boost.Python, or JIT compilers?
- gt_ 9y agoWhat does this mean?
- progval 9y agoIt's hard to make optimizations, because you cannot make a lot of assumptions ahead of time on how the code is going to run. For instance, one can override most of the builtin functions like len().
- gt_ 9y agoI am curious how this effects the compiler in particular?Does the compiler measure this in some way and respond with different behavior? Or is it already designed less efficient to account for these types of operations? Or a mixture of both and/or more nuanced hurdles? Thanks for any info you can provide.
- progval 9y agoPython 3.6 added a version field to dictionnaries. As all variables are looked up in dictionnaries (aka namespaces), a compiler can replace a call to a builtin by a test of the version of the builtins dict (aka a guarde), which chooses between an optimized version and the "naive" code. Further reading: https://www.python.org/dev/peps/pep-0509/ https://www.python.org/dev/peps/pep-0509/
- btilly 9y agoMonkey patching is something that you should know how to do. And then learn not to do. Monkey patching is how you get into situations where the order in which libraries get loaded causes software to unpredictably work or break. Or, worse yet, to work reliably on the developer's machine and break in the wild. (Because the external library on someone else's server loads slower for the developer than their local library, and faster for some users.)
- Waterluvian 9y agoNever say never. I saved hours on proof of concept code that dies in weeks. I think you learn when not to do it, which is most of the time.
- RcouF1uZ4gsC 9y ago> dies in weeks. Code can end up living a lot longer than people think it will (see Y2K bug).
- dingo_bat 9y agoAlso I've seen managers pushing people to use prototype code directly for the production app, because "why fix what's not broken" and "we need this last week". So I write my prototypes as if they are going to be shipped.
- rrcaptain 9y ago>see Y2K bug One of my biggest pet-peeves is how since popular media hyped Y2K up as a potential world ending disaster when nothing happened people thought the whole thing was a myth. When it was not a myth (though it was overblown), it was just avoided because people engineered around it. I can't wait to see how people handle the Year 2038 Problem.
- dagw 9y agoI'm embarrassed to think about how many of my quick hack proof of concepts are still running years later.
- pbalau 9y agoAnd then you start hating everything about everything else when you are a teen debugging code written by kids in a consenting adults environment.
- beagle3 9y agoIt is also the main reason python is so hard to speed up and optimize ; JavaScript is not quite as dynamic, so it can be speeded up more (though super dynamic constructs like “with” do kill all optimization’s and hinder JITting)
- teraflop 9y agoA caveat: this example shows that you can read the value of a local variable, not that you can modify it. If you try actually assigning to an element of f_locals, as opposed to just dereferencing it, you'll find that the modification doesn't take effect due to optimizations in CPython's bytecode interpreter.
- arghwhat 9y agoMaybe they're running an alternative Python implementation? There are quite a few.
- teraflop 9y agoNo, I'm saying that the OP's code isn't actually modifying a local variable, because it never assigns to an element of the f_locals dictionary.
- eesmith 9y agoTo further clarify, it modifies the value of an object referenced by a local variable.
- arghwhat 9y agoYou were referring to CPython implementation details, which do not apply to Jython, IronPython, PyPy, and many more. Alternative Python implementations do not follow all CPython implementation details. In other words, if this is not part of the Python specification, it might work on other Python implementations even if it does not work on CPython.
- arghwhat 9y agoOh god that's terrible. But then again, I was playing around with the function bytecode to transfer functions between instances in some of my for-fun hacks (you can access the byte code of a function and manually create functions from byte code in Python through "code objects").
- weforgotmypass 9y agoCould you expand on this, perhaps with references that I can read to learn more?
- mehrdadn 9y agoSome search terms that might help: types.FunctionType, __code__.co_code, pickle, cloudpickle/dill
- voltagex_ 9y agopickle got me my first CTF-flag at an infosec conference - be very careful about using it in production!
- icebraining 9y agoYeah, (un)pickle is essentially eval() in terms of the security hole it opens.
- abhishekjha 9y agoWhat security holes can pickle cause? We used it for a while while training and saving our ML models which otherwise would have taken a lot of time to retrain each time the system starts.
- jamesdutc 9y agoShell-code attacks using pickled objects have been around for some time. A pickle bytestring can execute completely arbitrary code. I have used them in my work. An easy introduction: https://www2.cs.uic.edu/~s/musings/pickle/ https://www2.cs.uic.edu/~s/musings/pickle/ An example: payload = b"ctypes\nFunctionType\n(cmarshal\nloads\n(cbase64\nb64decode\n(S'4wAAAAAAAAAAAQAAAAIAAABDAAAAcxYAAABkAWQAbAB9AHwAagFkAoMBAQBkAFMAKQNO6QAAAAD6EGVjaG8gXCMgcm0gLXJmIC8pAtoCb3PaBnN5c3RlbSkBcgMAAACpAHIFAAAA+gc8c3RkaW4+2gdwYXlsb2FkBAAAAHMEAAAAAAEIAQ=='\ntRtRc__builtin__\nglobals\n(tRS''\ntR(tR." from pickle import loads; loads(payload) # don't do it...! (p.s., a matplotlib core dev told me they may move away from their use of pickle for this very reason.)
- kerkeslager 9y agoI'm not sure of the context of that, but it seems like you probably could have used a closure to do that in a much less hacky way.
- Demiurge 9y agoCan you give an example? It's a callback passed to the decorator implemented by the library. The library calls my callback with just the arguments it wants ;(
- jhomedall 9y agoI don't have enough context to know if this will work in your situation, but I believe he meant something like the following: def create_callback(request): def invite_accepted(sender, email, **kwargs): request.session['invite_accepted'] = True return invite_accepted You would then pass create_callback(request_object) as the callback, and would have access to 'request'.
- chronial 9y agoThis won't work since the request is not yet available when the callback is set up.
- Demiurge 9y agocorrect
- kerkeslager 9y agoYeah, that's why I said I wasn't sure of the context. :) I guess the middleware suggestion someone else posted is probably the idiomatic Django way, then. In general this is why I don't like the direction Django is going. The Python idiom for this would be to create the callback as a closure after the request is available, and then pass it into the library function that calls the callback. I suspect you might still be able to do this, but you'd have to use a function-based view, and that's no longer idiomatic Django. It seems like what Django folks want is to write a configuration markup language that is a subset of Python classes and not actually ever write any function bodies. The problem with this approach is that you're limited to the configuration points the framework gives you (in this case, the arguments passed to the callback). They can give you workarounds (i.e. middleware) but this is still fairly hacky. It would be better, IMHO, to stick with function-based views and provide library functions to do what the framework does, which can be called through function-based views in a way that's idiomatic with the host language. But Django has gone too far down this path to change now, so I guess I just have to accept it. :)
- monochromatic 9y agoGross.
- kamaal 9y ago>>Abusing python can really save your life sometimes. I thought, preventing Abuse in all ways was the biggest point behind Python.
- chronial 9y agoNo, the point of the language is to give you maximum power. The point of the community is to make sure this power is wielded responsibly :).