dslab-epfl / klint

Repository for the "Automated Verification of Network Function Binaries" paper (NSDI'22).
MIT License
8 stars 5 forks source link

Cleaner contracts #10

Open SolalPirelli opened 1 year ago

SolalPirelli commented 1 year ago

@tharvik this is a quick example of what I meant in our discussion yesterday as what index_pool's contract could look like instead of the current angr-dependent mess:

from klint import assume, any_bool, any_int, Map, sizeof
from klint.types import size_t, time_t, TIME_MAX, SIZE_MAX

# ideally we'd be able to use "not", "and", "or" but that requires patching the Python interpreter so... probably not
# instead, ~, &, | it is...

# I wonder if those doc comments are even needed? perhaps only optionally for the names of the function
# if they don't follow some convention (like struct_name_method_name), the parameters can probably be inferred?

class IndexPool:
    def __init__(self, size, expiration_time):
        """struct index_pool* index_pool_alloc(size_t size, time_t expiration_time)"""
        assert(size * sizeof(time_t) <= SIZE_MAX)
        self.size = size
        self.expiration_time = expiration_time
        self.items = Map(size_t, time_t)

    def borrow(self, time):
        """bool index_pool_borrow(struct index_pool* pool, time_t time, size_t* out_index, bool* out_used)"""
        assert(time != TIME_MAX)
        result = any_bool()
        index = any_int()
        used = any_bool()
        if self.items.length() == self.size:
            if self.items.forall(lambda k, v: time < self.expiration_time | (time - self.expiration_time) <= v):
                assume(~result)
            else:
                assume(result)
                assume(used)
        else:
             assume(result)
        if result:
            assume(index < self.size)
            if used:
                assume(self.items.contains(index))
                assume(time >= self.expiration_time)
                assume((time - self.expiration_time) > self.items.get(index))
            else:
                assume(~self.items.contains(index))
            self.items.set(index, time)
        return (result, index, used)

    def refresh(self, time, index):
        """void index_pool_refresh(struct index_pool* pool, time_t time, size_t index)"""
        assert(time != TIME_MAX)
        assert(index < self.size)
        self.items.set(index, time)

    def used(self, time, index):
        """"bool index_pool_used(struct index_pool* pool, time_t time, size_t index)"""
        value = self.items.get(index)
        if value is None:
            return False
        else:
            return time < self.expiration_time | (time - self.expiration_time) <= value

    def return(self, index):
        """void index_pool_return(struct index_pool* pool, size_t index)"""
        assert(index < self.size)
        self.items.remove(index)

Obviously just an example, I'm not even 100% sure it has all of the info necessary and only that, but I thought it'd be useful to write a clean version since the angr-specific one is a bit of a pain to understand.