[{"data":1,"prerenderedAt":20},["ShallowReactive",2],{"blog-detail:can-aptos-move-really-avoid-reentrancy-what-function-values-changed-13195c":3},{"category":4,"article":5},null,{"slug":6,"category":7,"categoryLabel":8,"subcategoryLabel":9,"authorName":10,"title":11,"excerpt":12,"contentHtml":13,"coverUrl":14,"publishedAt":15,"updatedAt":16,"readTime":17,"cover":14,"hasCover":18,"coverAlt":19},"can-aptos-move-really-avoid-reentrancy-what-function-values-changed-13195c","research","Research","Industry Research","Olivia","Can Aptos Move Really Avoid Reentrancy? What Function Values Changed","For a long time, many Move developers have operated under a simple assumption: reentrancy is primarily a Solidity problem, and Move largely avoids it by design.","\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">For a long time, many Move developers have operated under a simple assumption: \u003Cstrong>reentrancy is primarily a Solidity problem, and Move largely avoids it by design.\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">That assumption was broadly accurate for earlier versions of Move. Static module dependencies, strict resource semantics, and VM-level protections made the classic Solidity attack pattern difficult to reproduce: an external call re-enters a function before its previous execution has finished, allowing the same state to be manipulated repeatedly.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">With the introduction of \u003Cstrong>Function Values in Aptos Move 2.2\u003C\u002Fstrong>, however, that security model became more nuanced.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Function Values allow functions to be passed around and invoked dynamically. This makes a call path like the following possible:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Module A → Module B → Function Value callback → Module A\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Module A can pass a callback to Module B, and B can execute that callback before A&#39;s original execution has completed. In other words, execution can re-enter Module A while the first invocation is still active.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">At first glance, this looks very similar to traditional reentrancy. But there is an important difference.\u003C\u002Fp>\u003Ch3 class=\"docx-heading docx-size-22\" style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cspan class=\"docx-size-22\">Move Still Has Resource Locking\u003C\u002Fspan>\u003C\u002Fh3>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">When a module is re-entered through a Function Value, the Move VM restricts access to resources defined by that module.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">For example, if Module A is already active and the callback re-enters A, another attempt to access A&#39;s own resource may look like this:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">borrow_global&lt;Balance&gt;(addr);\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">move_from&lt;Balance&gt;(addr);\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">If Balance is defined by the re-entered module, the VM&#39;s resource-locking rules can prevent the second access and abort the transaction.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">This means Function Values do \u003Cstrong>not\u003C\u002Fstrong> simply reintroduce the classic Solidity pattern where an attacker repeatedly withdraws funds before a balance is updated.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">The more interesting security boundary appears one layer deeper.\u003C\u002Fp>\u003Ch3 class=\"docx-heading docx-size-22\" style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cspan class=\"docx-size-22\">Resource Locking Does Not Protect Every Piece of State\u003C\u002Fspan>\u003C\u002Fh3>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Resource locking protects resources belonging to the module being re-entered. It does not automatically freeze every external state dependency involved in the transaction.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Consider a function in Module A with the following execution flow:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Check external state → Execute user-controlled callback → Continue using the earlier check\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Now assume that the callback can modify a resource managed by another module, or otherwise change external state that Module A relies on.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">A simplified flow might look like this:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">1. Module A checks that the user&#39;s limit is 100.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">2. Module A executes an external Function Value.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">3. The callback modifies state related to that limit.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">4. Execution returns to Module A.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-bottom: 24.5pt\">5. Module A continues using the result from step 1.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">The problem is no longer simply “the same balance was withdrawn twice.”\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Instead, the issue becomes a combination of \u003Cstrong>reentrancy, cross-module state manipulation, and TOCTOU — Time of Check to Time of Use\u003C\u002Fstrong>.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Module A made a security decision based on one version of the state, but by the time that decision is acted upon, the underlying state may have changed.\u003C\u002Fp>\u003Ch3 class=\"docx-heading docx-size-22\" style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cspan class=\"docx-size-22\">Function Values Expand the Trust Boundary\u003C\u002Fspan>\u003C\u002Fh3>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">This becomes particularly important when a contract accepts arbitrary Function Values, callbacks, or hooks supplied by external callers.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">In those cases, an audit should not stop at asking:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cstrong>Can this module&#39;s own resources be accessed twice?\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">It should also ask:\u003C\u002Fp>\u003Cul>\u003Cli>What external state can change while the callback is executing?\u003C\u002Fli>\u003Cli>Do permission, balance, price, or quota checks performed before the callback remain valid afterward?\u003C\u002Fli>\u003Cli>Is the Function Value controlled by the caller?\u003C\u002Fli>\u003Cli>Which modules can that callback invoke?\u003C\u002Fli>\u003Cli>Which resources can those modules modify?\u003C\u002Fli>\u003Cli>Does the original function rely on any state that should be revalidated after control returns?\u003C\u002Fli>\u003C\u002Ful>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Where security-sensitive logic depends on mutable external state, critical conditions may need to be checked again at the point of use rather than assuming that a pre-callback validation remains valid.\u003C\u002Fp>\u003Ch3 class=\"docx-heading docx-size-22\" style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cspan class=\"docx-size-22\">The Security Model Has Changed, Not Disappeared\u003C\u002Fspan>\u003C\u002Fh3>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">The right conclusion is not:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cstrong>“Move now has the same reentrancy problem as Solidity.”\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">That would be misleading.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Move&#39;s VM and resource model still prevent many forms of classic reentrancy. What Function Values change is the boundary of what developers and auditors need to reason about.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">Once \u003Cstrong>Function Values, external resources, and pre-callback state checks\u003C\u002Fstrong> appear in the same execution path, reentrancy-related state changes and TOCTOU conditions become part of the threat model.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">For Move developers, the key question is no longer only whether a module can be re-entered.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">It is also:\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">\u003Cstrong>What can change while control is outside the module, and are the assumptions made before that external execution still valid when control returns?\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">That is the security boundary Function Values have introduced into modern Aptos Move.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">At \u003Cstrong>MoveBit\u003C\u002Fstrong>, we review not only isolated functions, but also the state transitions and trust boundaries created by callbacks, Function Values, and cross-module interactions.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">If you&#39;re building on Aptos or other Move-based ecosystems, these are exactly the kinds of edge cases that deserve attention before deployment.\u003C\u002Fp>\u003Cp style=\"line-height: 1.5; margin-top: 0pt; margin-bottom: 24.5pt\">— \u003Cstrong>MoveBit Research\u003C\u002Fstrong>\u003C\u002Fp>","\u002Fmedia\u002Fmovebit\u002F2026\u002F08\u002F2e24434a92f775515edf49221e289d0c.jpg","2026-08-18T03:18:54Z","2026-08-18T11:24:25Z","4 min read",true,"Can Aptos Move Really Avoid Reentrancy? What Function Values Changed cover image",1792635791890]