Skip to content

Commit ac550f0

Browse files
committed
Enable the max object nesting check
1 parent d4fdb8f commit ac550f0

File tree

5 files changed

+126
-22
lines changed

5 files changed

+126
-22
lines changed

aptos-move/aptos-release-builder/src/components/feature_flags.rs

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ pub enum FeatureFlag {
103103
ConcurrentFungibleAssets,
104104
RefundableBytes,
105105
ObjectCodeDeployment,
106+
MaxObjectNestingCheck,
106107
}
107108

108109
fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
@@ -264,6 +265,7 @@ impl From<FeatureFlag> for AptosFeatureFlag {
264265
FeatureFlag::ConcurrentFungibleAssets => AptosFeatureFlag::CONCURRENT_FUNGIBLE_ASSETS,
265266
FeatureFlag::RefundableBytes => AptosFeatureFlag::REFUNDABLE_BYTES,
266267
FeatureFlag::ObjectCodeDeployment => AptosFeatureFlag::OBJECT_CODE_DEPLOYMENT,
268+
FeatureFlag::MaxObjectNestingCheck => AptosFeatureFlag::MAX_OBJECT_NESTING_CHECK,
267269
}
268270
}
269271
}
@@ -348,6 +350,7 @@ impl From<AptosFeatureFlag> for FeatureFlag {
348350
AptosFeatureFlag::CONCURRENT_FUNGIBLE_ASSETS => FeatureFlag::ConcurrentFungibleAssets,
349351
AptosFeatureFlag::REFUNDABLE_BYTES => FeatureFlag::RefundableBytes,
350352
AptosFeatureFlag::OBJECT_CODE_DEPLOYMENT => FeatureFlag::ObjectCodeDeployment,
353+
AptosFeatureFlag::MAX_OBJECT_NESTING_CHECK => FeatureFlag::MaxObjectNestingCheck,
351354
}
352355
}
353356
}

aptos-move/framework/aptos-framework/sources/object.move

+111-20
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
module aptos_framework::object {
1818
use std::bcs;
1919
use std::error;
20+
use std::features;
2021
use std::hash;
2122
use std::signer;
2223
use std::vector;
@@ -532,18 +533,11 @@ module aptos_framework::object {
532533

533534
let current_address = object.owner;
534535
let count = 0;
535-
while ({
536-
spec {
537-
invariant count < MAXIMUM_OBJECT_NESTING;
538-
invariant forall i in 0..count:
539-
exists<ObjectCore>(current_address) && global<ObjectCore>(current_address).allow_ungated_transfer;
540-
// invariant forall i in 0..count:
541-
// current_address == get_transfer_address(global<ObjectCore>(destination).owner, i);
536+
while (owner != current_address) {
537+
count = count + 1;
538+
if (features::max_object_nesting_check_enabled()) {
539+
assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING))
542540
};
543-
owner != current_address
544-
}) {
545-
let count = count + 1;
546-
assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING));
547541
// At this point, the first object exists and so the more likely case is that the
548542
// object's owner is not an object. So we return a more sensible error.
549543
assert!(
@@ -623,16 +617,11 @@ module aptos_framework::object {
623617
let current_address = object.owner;
624618

625619
let count = 0;
626-
while ({
627-
spec {
628-
invariant count < MAXIMUM_OBJECT_NESTING;
629-
invariant forall i in 0..count:
630-
owner != current_address && exists<ObjectCore>(current_address);
620+
while (owner != current_address) {
621+
count = count + 1;
622+
if (features::max_object_nesting_check_enabled()) {
623+
assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING))
631624
};
632-
owner != current_address
633-
}) {
634-
let count = count + 1;
635-
assert!(count < MAXIMUM_OBJECT_NESTING, error::out_of_range(EMAXIMUM_NESTING));
636625
if (!exists<ObjectCore>(current_address)) {
637626
return false
638627
};
@@ -816,4 +805,106 @@ module aptos_framework::object {
816805
let (_, hero) = create_hero(creator);
817806
unburn(creator, hero);
818807
}
808+
809+
#[test_only]
810+
inline fun create_simple_object(creator: &signer, seed: vector<u8>): Object<ObjectCore> {
811+
object_from_constructor_ref<ObjectCore>(&create_named_object(creator, seed))
812+
}
813+
814+
#[test(creator = @0x123)]
815+
#[expected_failure(abort_code = 131078, location = Self)]
816+
fun test_exceeding_maximum_object_nesting_owns_should_fail(creator: &signer) acquires ObjectCore {
817+
let feature = features::get_max_object_nesting_check_feature();
818+
let fx = account::create_signer_for_test(@0x1);
819+
features::change_feature_flags(&fx, vector[feature], vector[]);
820+
821+
let obj1 = create_simple_object(creator, b"1");
822+
let obj2 = create_simple_object(creator, b"2");
823+
let obj3 = create_simple_object(creator, b"3");
824+
let obj4 = create_simple_object(creator, b"4");
825+
let obj5 = create_simple_object(creator, b"5");
826+
let obj6 = create_simple_object(creator, b"6");
827+
let obj7 = create_simple_object(creator, b"7");
828+
let obj8 = create_simple_object(creator, b"8");
829+
let obj9 = create_simple_object(creator, b"9");
830+
831+
transfer(creator, obj1, object_address(&obj2));
832+
transfer(creator, obj2, object_address(&obj3));
833+
transfer(creator, obj3, object_address(&obj4));
834+
transfer(creator, obj4, object_address(&obj5));
835+
transfer(creator, obj5, object_address(&obj6));
836+
transfer(creator, obj6, object_address(&obj7));
837+
transfer(creator, obj7, object_address(&obj8));
838+
transfer(creator, obj8, object_address(&obj9));
839+
840+
assert!(owns(obj9, signer::address_of(creator)), 1);
841+
assert!(owns(obj8, signer::address_of(creator)), 1);
842+
assert!(owns(obj7, signer::address_of(creator)), 1);
843+
assert!(owns(obj6, signer::address_of(creator)), 1);
844+
assert!(owns(obj5, signer::address_of(creator)), 1);
845+
assert!(owns(obj4, signer::address_of(creator)), 1);
846+
assert!(owns(obj3, signer::address_of(creator)), 1);
847+
assert!(owns(obj2, signer::address_of(creator)), 1);
848+
849+
// Calling `owns` should fail as the nesting is too deep.
850+
assert!(owns(obj1, signer::address_of(creator)), 1);
851+
}
852+
853+
#[test(creator = @0x123)]
854+
#[expected_failure(abort_code = 131078, location = Self)]
855+
fun test_exceeding_maximum_object_nesting_transfer_should_fail(creator: &signer) acquires ObjectCore {
856+
let feature = features::get_max_object_nesting_check_feature();
857+
let fx = account::create_signer_for_test(@0x1);
858+
features::change_feature_flags(&fx, vector[feature], vector[]);
859+
860+
let obj1 = create_simple_object(creator, b"1");
861+
let obj2 = create_simple_object(creator, b"2");
862+
let obj3 = create_simple_object(creator, b"3");
863+
let obj4 = create_simple_object(creator, b"4");
864+
let obj5 = create_simple_object(creator, b"5");
865+
let obj6 = create_simple_object(creator, b"6");
866+
let obj7 = create_simple_object(creator, b"7");
867+
let obj8 = create_simple_object(creator, b"8");
868+
let obj9 = create_simple_object(creator, b"9");
869+
870+
transfer(creator, obj1, object_address(&obj2));
871+
transfer(creator, obj2, object_address(&obj3));
872+
transfer(creator, obj3, object_address(&obj4));
873+
transfer(creator, obj4, object_address(&obj5));
874+
transfer(creator, obj5, object_address(&obj6));
875+
transfer(creator, obj6, object_address(&obj7));
876+
transfer(creator, obj7, object_address(&obj8));
877+
transfer(creator, obj8, object_address(&obj9));
878+
879+
// This should fail as the nesting is too deep.
880+
transfer(creator, obj1, @0x1);
881+
}
882+
883+
#[test(creator = @0x123)]
884+
#[expected_failure(abort_code = 131078, location = Self)]
885+
fun test_cyclic_ownership_transfer_should_fail(creator: &signer) acquires ObjectCore {
886+
let feature = features::get_max_object_nesting_check_feature();
887+
let fx = account::create_signer_for_test(@0x1);
888+
features::change_feature_flags(&fx, vector[feature], vector[]);
889+
890+
let obj1 = create_simple_object(creator, b"1");
891+
// This creates a cycle (self-loop) in ownership.
892+
transfer(creator, obj1, object_address(&obj1));
893+
// This should fails as the ownership is cyclic.
894+
transfer(creator, obj1, object_address(&obj1));
895+
}
896+
897+
#[test(creator = @0x123)]
898+
#[expected_failure(abort_code = 131078, location = Self)]
899+
fun test_cyclic_ownership_owns_should_fail(creator: &signer) acquires ObjectCore {
900+
let feature = features::get_max_object_nesting_check_feature();
901+
let fx = account::create_signer_for_test(@0x1);
902+
features::change_feature_flags(&fx, vector[feature], vector[]);
903+
904+
let obj1 = create_simple_object(creator, b"1");
905+
// This creates a cycle (self-loop) in ownership.
906+
transfer(creator, obj1, object_address(&obj1));
907+
// This should fails as the ownership is cyclic.
908+
let _ = owns(obj1, signer::address_of(creator));
909+
}
819910
}

aptos-move/framework/aptos-framework/sources/object.spec.move

+1-2
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ spec aptos_framework::object {
4747
///
4848
spec module {
4949
pragma aborts_if_is_strict;
50-
//ghost variable
51-
global g_roll: u8;
5250
}
5351

5452
spec fun spec_exists_at<T: key>(object: address): bool;
@@ -504,6 +502,7 @@ spec aptos_framework::object {
504502
}
505503

506504
spec owns<T: key>(object: Object<T>, owner: address): bool {
505+
pragma aborts_if_is_partial;
507506
let current_address_0 = object.inner;
508507
let object_0 = global<ObjectCore>(current_address_0);
509508
let current_address = object_0.owner;

aptos-move/framework/move-stdlib/sources/configs/features.move

+10
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,20 @@ module std::features {
394394

395395
/// Whether deploying to objects is enabled.
396396
const OBJECT_CODE_DEPLOYMENT: u64 = 52;
397+
397398
public fun is_object_code_deployment_enabled(): bool acquires Features {
398399
is_enabled(OBJECT_CODE_DEPLOYMENT)
399400
}
400401

402+
/// Whether checking the maximum object nesting is enabled.
403+
const MAX_OBJECT_NESTING_CHECK: u64 = 53;
404+
405+
public fun get_max_object_nesting_check_feature(): u64 { MAX_OBJECT_NESTING_CHECK }
406+
407+
public fun max_object_nesting_check_enabled(): bool acquires Features {
408+
is_enabled(MAX_OBJECT_NESTING_CHECK)
409+
}
410+
401411
// ============================================================================================
402412
// Feature Flag Implementation
403413

types/src/on_chain_config/aptos_features.rs

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ pub enum FeatureFlag {
6060
CONCURRENT_FUNGIBLE_ASSETS = 50,
6161
REFUNDABLE_BYTES = 51,
6262
OBJECT_CODE_DEPLOYMENT = 52,
63+
MAX_OBJECT_NESTING_CHECK = 53,
6364
}
6465

6566
/// Representation of features on chain as a bitset.

0 commit comments

Comments
 (0)