Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit f68ff75

Browse files
committed
backport #479 to v2
1 parent 51dc1ae commit f68ff75

File tree

3 files changed

+51
-28
lines changed

3 files changed

+51
-28
lines changed

docs/src/token.md

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,6 @@ Account's owner via the `Revoke` instruction.
247247

248248
### Multisignatures
249249

250-
Warning: SPL Token v2's multisignature validation is compromised and should not
251-
be used. Validation will erroneously pass a single valid signer signs m times.
252-
For more information:
253-
https://github.com/solana-labs/solana-program-library/issues/477
254-
255250
M of N multisignatures are supported and can be used in place of Mint
256251
authorities or Account owners or delegates. Multisignature authorities must be
257252
initialized with the `InitializeMultisig` instruction. Initialization specifies

token/program/src/instruction.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,11 @@ pub enum TokenInstruction {
5656
InitializeAccount,
5757
/// Initializes a multisignature account with N provided signers.
5858
///
59-
/// Warning, this instruction is compromised:
60-
/// https://github.com/solana-labs/solana-program-library/issues/477
61-
///
6259
/// Multisignature accounts can used in place of any single owner/delegate accounts in any
6360
/// token instruction that require an owner/delegate to be present. The variant field represents the
6461
/// number of signers (M) required to validate this multisignature account.
6562
///
66-
/// The `DangerInitializeMultisig` instruction requires no signers and MUST be included within
63+
/// The `InitializeMultisig` instruction requires no signers and MUST be included within
6764
/// the same Transaction as the system program's `CreateInstruction` that creates the account
6865
/// being initialized. Otherwise another party can acquire ownership of the uninitialized account.
6966
///
@@ -72,7 +69,7 @@ pub enum TokenInstruction {
7269
/// 0. `[writable]` The multisignature account to initialize.
7370
/// 1. `[]` Rent sysvar
7471
/// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11.
75-
DangerInitializeMultisig {
72+
InitializeMultisig {
7673
/// The number of signers (M) required to validate this multisignature account.
7774
m: u8,
7875
},
@@ -353,7 +350,7 @@ impl TokenInstruction {
353350
1 => Self::InitializeAccount,
354351
2 => {
355352
let &m = rest.get(0).ok_or(InvalidInstruction)?;
356-
Self::DangerInitializeMultisig { m }
353+
Self::InitializeMultisig { m }
357354
}
358355
3 | 4 | 7 | 8 => {
359356
let amount = rest
@@ -449,7 +446,7 @@ impl TokenInstruction {
449446
Self::pack_pubkey_option(freeze_authority, &mut buf);
450447
}
451448
Self::InitializeAccount => buf.push(1),
452-
&Self::DangerInitializeMultisig { m } => {
449+
&Self::InitializeMultisig { m } => {
453450
buf.push(2);
454451
buf.push(m);
455452
}
@@ -624,8 +621,8 @@ pub fn initialize_account(
624621
})
625622
}
626623

627-
/// Creates a `DangerInitializeMultisig` instruction.
628-
pub fn danger_initialize_multisig(
624+
/// Creates a `InitializeMultisig` instruction.
625+
pub fn initialize_multisig(
629626
token_program_id: &Pubkey,
630627
multisig_pubkey: &Pubkey,
631628
signer_pubkeys: &[&Pubkey],
@@ -637,7 +634,7 @@ pub fn danger_initialize_multisig(
637634
{
638635
return Err(ProgramError::MissingRequiredSignature);
639636
}
640-
let data = TokenInstruction::DangerInitializeMultisig { m }.pack();
637+
let data = TokenInstruction::InitializeMultisig { m }.pack();
641638

642639
let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len());
643640
accounts.push(AccountMeta::new(*multisig_pubkey, false));
@@ -1083,7 +1080,7 @@ mod test {
10831080
let unpacked = TokenInstruction::unpack(&expect).unwrap();
10841081
assert_eq!(unpacked, check);
10851082

1086-
let check = TokenInstruction::DangerInitializeMultisig { m: 1 };
1083+
let check = TokenInstruction::InitializeMultisig { m: 1 };
10871084
let packed = check.pack();
10881085
let expect = Vec::from([2u8, 1]);
10891086
assert_eq!(packed, expect);

token/program/src/processor.rs

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
use crate::{
66
error::TokenError,
7-
instruction::{is_valid_signer_index, AuthorityType, TokenInstruction},
7+
instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS},
88
option::COption,
99
pack::{IsInitialized, Pack},
1010
state::{Account, AccountState, Mint, Multisig},
@@ -99,8 +99,8 @@ impl Processor {
9999
Ok(())
100100
}
101101

102-
/// Processes a [DangerInitializeMultisig](enum.TokenInstruction.html) instruction.
103-
pub fn process_danger_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
102+
/// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction.
103+
pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
104104
let account_info_iter = &mut accounts.iter();
105105
let multisig_info = next_account_info(account_info_iter)?;
106106
let multisig_info_data_len = multisig_info.data_len();
@@ -637,9 +637,9 @@ impl Processor {
637637
info!("Instruction: InitializeAccount");
638638
Self::process_initialize_account(accounts)
639639
}
640-
TokenInstruction::DangerInitializeMultisig { m } => {
641-
info!("Instruction: DangerInitializeMultisig");
642-
Self::process_danger_initialize_multisig(accounts, m)
640+
TokenInstruction::InitializeMultisig { m } => {
641+
info!("Instruction: InitializeMultisig");
642+
Self::process_initialize_multisig(accounts, m)
643643
}
644644
TokenInstruction::Transfer { amount } => {
645645
info!("Instruction: Transfer");
@@ -714,14 +714,18 @@ impl Processor {
714714
{
715715
let multisig = Multisig::unpack(&owner_account_info.data.borrow())?;
716716
let mut num_signers = 0;
717+
let mut matched = [false; MAX_SIGNERS];
717718
for signer in signers.iter() {
718-
if multisig.signers[0..multisig.n as usize].contains(signer.key) {
719+
for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
720+
if key == signer.key && !matched[position] {
719721
if !signer.is_signer {
720722
return Err(ProgramError::MissingRequiredSignature);
721723
}
724+
matched[position] = true;
722725
num_signers += 1;
723726
}
724727
}
728+
}
725729
if num_signers < multisig.m {
726730
return Err(ProgramError::MissingRequiredSignature);
727731
}
@@ -3600,8 +3604,7 @@ mod tests {
36003604
assert_eq!(
36013605
Err(TokenError::NotRentExempt.into()),
36023606
do_process_instruction(
3603-
danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1)
3604-
.unwrap(),
3607+
initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
36053608
vec![
36063609
&mut multisig_account,
36073610
&mut rent_sysvar,
@@ -3615,7 +3618,7 @@ mod tests {
36153618
// single signer
36163619
let account_info_iter = &mut signer_accounts.iter_mut();
36173620
do_process_instruction(
3618-
danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
3621+
initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(),
36193622
vec![
36203623
&mut multisig_account,
36213624
&mut rent_sysvar,
@@ -3627,7 +3630,7 @@ mod tests {
36273630
// multiple signer
36283631
let account_info_iter = &mut signer_accounts.iter_mut();
36293632
do_process_instruction(
3630-
danger_initialize_multisig(
3633+
initialize_multisig(
36313634
&program_id,
36323635
&multisig_delegate_key,
36333636
&signer_key_refs,
@@ -4087,7 +4090,7 @@ mod tests {
40874090
{
40884091
let mut multisig =
40894092
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
4090-
multisig.m = 2; // TODO 11?
4093+
multisig.m = 11;
40914094
multisig.n = 11;
40924095
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
40934096
}
@@ -4097,6 +4100,34 @@ mod tests {
40974100
Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
40984101
);
40994102
signers[5].is_signer = true;
4103+
4104+
// 11:11, single signer signs multiple times
4105+
{
4106+
let mut signer_lamports = 0;
4107+
let mut signer_data = vec![];
4108+
let signers = vec![
4109+
AccountInfo::new(
4110+
&signer_keys[5],
4111+
true,
4112+
false,
4113+
&mut signer_lamports,
4114+
&mut signer_data,
4115+
&program_id,
4116+
false,
4117+
Epoch::default(),
4118+
);
4119+
MAX_SIGNERS + 1
4120+
];
4121+
let mut multisig =
4122+
Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
4123+
multisig.m = 11;
4124+
multisig.n = 11;
4125+
Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
4126+
assert_eq!(
4127+
Err(ProgramError::MissingRequiredSignature),
4128+
Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
4129+
);
4130+
}
41004131
}
41014132

41024133
#[test]

0 commit comments

Comments
 (0)