Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python Library Calls Produce Unexpected Engine Instrumentation #108

Open
pashneal opened this issue Apr 18, 2020 · 1 comment
Open

Python Library Calls Produce Unexpected Engine Instrumentation #108

pashneal opened this issue Apr 18, 2020 · 1 comment
Labels
engine engine-breaker-$$$ Winner of the Battlehack 2020 engine breaker prize!

Comments

@pashneal
Copy link

pashneal commented Apr 18, 2020

import math
import random
def turn():
    
    #costs 1 bytecode regardless of size
    z = "a,,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,aaa"
    #costs 3 bytcode regardless of string size
    z = z.replace("a","thisIsATest")

    #costs 5 byte code regardless of number
    a = math.factorial(100000)

    #costs 2 bytecode regardless of z size
    f = z.split(",")

    #costs 1 bytecode regardless of size
    ordered="1,9,8,7,2,4,6,9,1,0,4,3,b,j,1,0,9,3,4,1,9,3,4,b,j,h,1,3,4,6,h,v,j,a,o,s,y,d,0,8,7,f,a,d,b,j,h,g,d,f,g,6,7,a,9,8,a,7,h,9,a,7,8,h,7,a,+,9,j,5,m,a,a,s,d,f,8,t,1,7,8,s,f,1,g,8,7,g,1,n,8,7,a,1,n,6,g,4,1,n,s,6,h,6,s,4,d,v,6,b,7,8,6,v,7,6,d,6,5,h,n,s,4,v,n,4,5,6,f,v,b,n,5,6,x,4,j,g,6,8,6,m,t,6,8,m,k,s,9,8,,,6,8,7,,,d,j,5,5,s,t,g,h,4,+,8,s,t,9,8,8,7,6,8,7,m,h,6,s,4,6,8,s,6,r,4,m,s,6,8,y,m,s,h,6,8,s,n,7,4,h,s,9,8,r,7,8,m,k,s,9,8,h,4,7,g,s,m,5,6,r,6,7,8,s,y,h,a,9,8,r,8,6,,,4,a,s,r,5,g,9,+,n,7,r,6,9,a,r,4,9,s,#"

    # cost 4 bytecode regardless of size
    if (ordered.find("X") == -1): pass

    # cost 2 bytecode regardless of size
    ordered = ordered.split(",")
    
    #costs 2 bytcode regardless of ordered size
    random.shuffle(ordered)

    #costs 2 bytecode 
    ordered.pop(0)

    #costs 2 bytecode regardless of ordered size
    a = "".join(ordered)

    #costs 2 bytecode regardless of size
    a = ordered.index("#")
    
   

It seems that the bytecode counter fails to jump into python standard library calls. Thus many O(n) operations are seen as O(1). The above results are not exhaustive, many more string, dict, set, and list operations exist that potentially exhibit the same behavior. A user can do orders of magnitudes more expensive operations than instrumented and get away with it.

It seems that sorting behaves as expected for lists

Some more areas to consider listed below
Note: I did not yet test to see the time complexity of the following operations but I have strong suspicion that they misbehave.

######string operations
######I'm not sure if all of these are O(n)
center()
count()
encode()
index()
isalnum()
isalpha()
isdecimal()
isdigit()
islower()
isnumeric()
isspace()
isupper()
join()
ljust()
lower()
lstrip()
maketrans()
partition()
rfind()
rindex()
rjust()
rpartition()
rsplit()
rstrip()
splitlines()
startswith()
strip()
swapcase()
title()
translate()
upper()
zfill()
######set operations
issubset()
issuperset()
isintersection()
isunion()
difference()
copy()
######dict operations
copy()
items()
keys()
values()
######list 
extend()
insert()
remove()
pop()
index()
count()
reverse()
@pashneal pashneal changed the title Python Library Function Produce Unexpected Engine Instrumentation Python Library Calls Produce Unexpected Engine Instrumentation Apr 18, 2020
@pashneal
Copy link
Author

A malicious player that is losing can call math.factorial(100000000) to slow down or break the engine and prevent match resolution.

@arvid220u arvid220u added engine-breaker-$$$ Winner of the Battlehack 2020 engine breaker prize! and removed 🤑? labels Apr 24, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
engine engine-breaker-$$$ Winner of the Battlehack 2020 engine breaker prize!
Projects
None yet
Development

No branches or pull requests

2 participants