Confused compare function :)

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sun Dec 9 02:39:19 EST 2012


On Sun, 09 Dec 2012 14:22:21 +1100, Chris Angelico wrote:

> On Sun, Dec 9, 2012 at 2:07 PM, Ramchandra Apte <maniandram01 at gmail.com>
> wrote:
>> Not really. I remember a bug saying that only 256 hashes were required
>> of known texts and then the randomization becomes useless.
> 
> That requires that someone be able to get you to hash some text and give
> back the hash. In any case, even if you _are_ dealing with the
> worst-case hash collision attack, all it does is stop a Python
> dictionary from being an exception to the general principle.


Dictionaries never were an exception to the general principle.

Regardless of how cheap or expensive it is, whether it is a constant cost 
or a potentially unbound expense or somewhere in between, the "look" in 
"look before you leap" tests has some cost.

# Look Before You Leap (LBYL)
if condition():
   do_this()
else:
   do_that()


# Easier to Ask Forgiveness than Permission (EAFP)
try:
    do_this()
except ConditionFailed:
    do_that()


Depending on the cost, and how often you have to pay it, either LBYL or 
EAFP will be cheaper. Sometimes you can predict which one will be cheaper 
-- "most of the time, the key will be present" -- sometimes you can't. 
But there's always a choice, and the choice is not always the right 
choice.


But... why are we focusing only on the cost? What about *correctness*?

LBYL suffers from an even more critical problem, never mind the cost of 
the check. That problem is the gap in time between the check and when you 
go to do the actual work. In the fraction of a second between checking 
condition and calling do_this(), the situation might have changed. The 
file that did exist has now been deleted by another program. The key was 
in the dict, and now another thread has deleted it. The network was up, 
and now its down.

These sorts of things are extremely hard to diagnose, extremely hard to 
prevent, and extremely hard to replicate. They lead to race conditions 
and "Time Of Check to Time Of Use" bugs. Many of the more tricky, subtle 
security vulnerabilities are due to TOCTOU bugs.

So, very often, it isn't enough to Look Before You Leap. You *still* have 
to be prepared to Ask Forgiveness:

# not good enough
if os.path.exists(filename):
    f = open(filename)
else:
    handle_missing_file()

# should be
if os.path.exists(filename):
    try:
        f = open(filename)
    except (IOError, OSError):
        handle_missing_file()
else:
    handle_missing_file()


But in that case, what's the point of the initial check?



-- 
Steven



More information about the Python-list mailing list