Skip to content

Commit 58994d7

Browse files
authored
tlv-account-resolution: Always demote signer flag (#164)
* tlv-account-resolution: Always demote signer flag #### Problem As described at solana-program/transfer-hook#83, there's just too many ways for signers to be potentially abused during transfer hooks. #### Summary of changes Demote all accounts to non-signer when resolving from an extra account metas list. * Review feedback
1 parent bf6041a commit 58994d7

File tree

1 file changed

+44
-33
lines changed

1 file changed

+44
-33
lines changed

tlv-account-resolution/src/state.rs

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,28 +33,25 @@ fn account_info_to_meta(account_info: &AccountInfo) -> AccountMeta {
3333

3434
/// De-escalate an account meta if necessary
3535
fn de_escalate_account_meta(account_meta: &mut AccountMeta, account_metas: &[AccountMeta]) {
36-
// This is a little tricky to read, but the idea is to see if
37-
// this account is marked as writable or signer anywhere in
38-
// the instruction at the start. If so, DON'T escalate it to
39-
// be a writer or signer in the CPI
36+
// This is a little tricky to read, but checks if this account is marked as
37+
// writable in the instruction. If it's read-only, de-escalate it to read-only
38+
// in the CPI.
4039
let maybe_highest_privileges = account_metas
4140
.iter()
4241
.filter(|&x| x.pubkey == account_meta.pubkey)
43-
.map(|x| (x.is_signer, x.is_writable))
44-
.reduce(|acc, x| (acc.0 || x.0, acc.1 || x.1));
42+
.map(|x| x.is_writable)
43+
.reduce(|acc, x| acc || x);
4544
// If `Some`, then the account was found somewhere in the instruction
46-
if let Some((is_signer, is_writable)) = maybe_highest_privileges {
47-
if !is_signer && is_signer != account_meta.is_signer {
48-
// Existing account is *NOT* a signer already, but the CPI
49-
// wants it to be, so de-escalate to not be a signer
50-
account_meta.is_signer = false;
51-
}
45+
if let Some(is_writable) = maybe_highest_privileges {
5246
if !is_writable && is_writable != account_meta.is_writable {
5347
// Existing account is *NOT* writable already, but the CPI
5448
// wants it to be, so de-escalate to not be writable
5549
account_meta.is_writable = false;
5650
}
5751
}
52+
53+
// Always mark an account as a non-signer
54+
account_meta.is_signer = false;
5855
}
5956

6057
/// Stateless helper for storing additional accounts required for an
@@ -400,6 +397,20 @@ mod tests {
400397
}
401398
}
402399

400+
/// Helper to convert an `AccountInfo` to an `AccountMeta`
401+
fn account_info_to_meta_non_signer(account_info: &AccountInfo) -> AccountMeta {
402+
AccountMeta {
403+
pubkey: *account_info.key,
404+
is_signer: false,
405+
is_writable: account_info.is_writable,
406+
}
407+
}
408+
409+
fn de_escalate_signer(mut account_meta: AccountMeta) -> AccountMeta {
410+
account_meta.is_signer = false;
411+
account_meta
412+
}
413+
403414
#[tokio::test]
404415
async fn init_with_metas() {
405416
let metas = [
@@ -426,7 +437,7 @@ mod tests {
426437

427438
let check_metas = metas
428439
.iter()
429-
.map(|e| AccountMeta::try_from(e).unwrap())
440+
.map(|e| de_escalate_signer(AccountMeta::try_from(e).unwrap()))
430441
.collect::<Vec<_>>();
431442

432443
assert_eq!(instruction.accounts, check_metas,);
@@ -525,9 +536,9 @@ mod tests {
525536

526537
// Convert to `AccountMeta` to check instruction
527538
let check_metas = [
528-
account_info_to_meta(&account_infos[0]),
529-
account_info_to_meta(&account_infos[1]),
530-
account_info_to_meta(&account_infos[2]),
539+
account_info_to_meta_non_signer(&account_infos[0]),
540+
account_info_to_meta_non_signer(&account_infos[1]),
541+
account_info_to_meta_non_signer(&account_infos[2]),
531542
AccountMeta::new(check_required_pda, false),
532543
];
533544

@@ -614,10 +625,10 @@ mod tests {
614625
)
615626
.0;
616627
let check_metas = [
617-
ix_account1,
618-
ix_account2,
619-
extra_meta1,
620-
extra_meta2,
628+
ix_account1, // not de-escalated since it's not extra
629+
ix_account2, // not de-escalated since it's not extra
630+
de_escalate_signer(extra_meta1),
631+
de_escalate_signer(extra_meta2),
621632
AccountMeta::new(check_extra_meta3_pubkey, false),
622633
AccountMeta::new(check_extra_meta4_pubkey, false),
623634
];
@@ -743,10 +754,10 @@ mod tests {
743754
)
744755
.0;
745756
let check_metas = [
746-
extra_meta1,
747-
extra_meta2,
748-
extra_meta3,
749-
extra_meta4,
757+
de_escalate_signer(extra_meta1),
758+
de_escalate_signer(extra_meta2),
759+
de_escalate_signer(extra_meta3),
760+
de_escalate_signer(extra_meta4),
750761
AccountMeta::new(check_extra_meta5_pubkey, false),
751762
AccountMeta::new(check_extra_meta6_pubkey, false),
752763
];
@@ -940,7 +951,7 @@ mod tests {
940951

941952
let test_ix_check_metas = account_infos
942953
.iter()
943-
.map(account_info_to_meta)
954+
.map(account_info_to_meta_non_signer)
944955
.collect::<Vec<_>>();
945956
assert_eq!(instruction.accounts, test_ix_check_metas,);
946957

@@ -986,10 +997,10 @@ mod tests {
986997
.0;
987998

988999
let test_other_ix_check_metas = vec![
989-
extra_meta1,
990-
extra_meta2,
991-
extra_meta3,
992-
extra_meta4,
1000+
de_escalate_signer(extra_meta1),
1001+
de_escalate_signer(extra_meta2),
1002+
de_escalate_signer(extra_meta3),
1003+
de_escalate_signer(extra_meta4),
9931004
AccountMeta::new(check_extra_meta5_pubkey, false),
9941005
AccountMeta::new(check_extra_meta6_pubkey, false),
9951006
AccountMeta::new(instruction_key_data_pubkey_arg, false),
@@ -1471,7 +1482,7 @@ mod tests {
14711482
];
14721483
let check_metas_1 = updated_metas_1
14731484
.iter()
1474-
.map(|e| AccountMeta::try_from(e).unwrap())
1485+
.map(|e| de_escalate_signer(AccountMeta::try_from(e).unwrap()))
14751486
.collect::<Vec<_>>();
14761487
update_and_assert_metas(program_id, &mut buffer, &updated_metas_1, &check_metas_1).await;
14771488

@@ -1483,7 +1494,7 @@ mod tests {
14831494
];
14841495
let check_metas_2 = updated_metas_2
14851496
.iter()
1486-
.map(|e| AccountMeta::try_from(e).unwrap())
1497+
.map(|e| de_escalate_signer(AccountMeta::try_from(e).unwrap()))
14871498
.collect::<Vec<_>>();
14881499
update_and_assert_metas(program_id, &mut buffer, &updated_metas_2, &check_metas_2).await;
14891500

@@ -1492,7 +1503,7 @@ mod tests {
14921503
[ExtraAccountMeta::new_with_pubkey(&Pubkey::new_unique(), true, true).unwrap()];
14931504
let check_metas_3 = updated_metas_3
14941505
.iter()
1495-
.map(|e| AccountMeta::try_from(e).unwrap())
1506+
.map(|e| de_escalate_signer(AccountMeta::try_from(e).unwrap()))
14961507
.collect::<Vec<_>>();
14971508
update_and_assert_metas(program_id, &mut buffer, &updated_metas_3, &check_metas_3).await;
14981509

@@ -1521,7 +1532,7 @@ mod tests {
15211532
)
15221533
.0;
15231534
let check_metas_4 = [
1524-
AccountMeta::new(seed_pubkey, true),
1535+
AccountMeta::new(seed_pubkey, false),
15251536
AccountMeta::new(simple_pda, false),
15261537
];
15271538

0 commit comments

Comments
 (0)