July 7th, 2022
Ferran Celades, a security researcher at Halborn, discovered and reported a vulnerability in how the Secure Cadence update to Cadence manages resources, during the Halborn-Dapper Labs audit engagement. In some cases, Cadence, the smart contract programming language of Flow blockchain, inappropriately allowed the access and use of destroyed resources.
Cadence has the concept of resources, which are single-use types that can only exist in a single location at a time. After the resource is declared and created, it must be either moved or destroyed after use.
If resources aren’t properly moved or destroyed, they are considered “lost”. If a lost resource exists within a program, then it will not be allowed to execute. Also, attempts to use a destroyed reference will also block the program’s execution.
References in Cadence can point to resources and allow access to fields and call functions on that object. If a reference points to a destroyed resource, it will be unwrapped to that resource and the program will not allow execution.
With the Secure Cadence milestone in June 2022, it is possible to create references to optional variables. These optional variables are checked to see if they hold a value — i.e. are not nil — and then unwrapped to the underlying value.
If a value is present, a reference to the value is created; if not, nil is returned.
However, when a resource was destroyed, the interpreter did not invalidate references to it, making it possible to access fields and call functions on a destroyed resource.
The proof of concept that we submitted to the Cadence team, shown below, demonstrates this issue: It retrieves an item from a dictionary, creates an optional reference, and, in this case, the reference r1 is a key to the dictionary. The variable ref still holds a valid reference to the resource even after the resource and dictionary have been destroyed, allowing access to the resource that should no longer exist.
The Cadence interpreter has been updated so that when a reference to an optional is created, the type of the inner value is checked. If it is a resource, it is tracked throughout its lifetime. Once the referenced resource is destroyed, also all references to it are invalidated, so they cannot be accessed anymore.