Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion program/src/instructions/extensions/add_timelock/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ impl<'a> TryFrom<&'a [u8]> for AddTimelockData {
fn try_from(data: &'a [u8]) -> Result<Self, Self::Error> {
require_len!(data, Self::LEN);

Ok(Self { extensions_bump: data[0], lock_duration: u64::from_le_bytes(data[1..9].try_into().unwrap()) })
let lock_duration = u64::from_le_bytes(data[1..9].try_into().unwrap());
if lock_duration > i64::MAX as u64 {
return Err(ProgramError::InvalidInstructionData);
}

Ok(Self { extensions_bump: data[0], lock_duration })
}
}

Expand Down
4 changes: 2 additions & 2 deletions program/src/state/extensions/timelock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ impl TimelockData {
return Ok(());
}

let unlock_time =
deposited_at.checked_add(self.lock_duration as i64).ok_or(ProgramError::ArithmeticOverflow)?;
let lock_duration_i64 = i64::try_from(self.lock_duration).map_err(|_| ProgramError::ArithmeticOverflow)?;
let unlock_time = deposited_at.checked_add(lock_duration_i64).ok_or(ProgramError::ArithmeticOverflow)?;
let clock = Clock::get()?;
if clock.unix_timestamp < unlock_time {
return Err(EscrowProgramError::TimelockNotExpired.into());
Expand Down
20 changes: 19 additions & 1 deletion tests/integration-tests/src/test_add_timelock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ fn test_add_timelock_success() {

#[test]
fn test_add_timelock_success_lock_duration_values() {
for lock_duration in [0u64, 1, 60, 3600, 86400, u64::MAX] {
for lock_duration in [0u64, 1, 60, 3600, 86400, i64::MAX as u64] {
let mut ctx = TestContext::new();

let escrow_ix = CreateEscrowFixture::build_valid(&mut ctx);
Expand All @@ -152,3 +152,21 @@ fn test_add_timelock_success_lock_duration_values() {
assert_timelock_extension(&ctx, &extensions_pda, lock_duration);
}
}

#[test]
fn test_add_timelock_rejects_lock_duration_exceeding_i64_max() {
for lock_duration in [i64::MAX as u64 + 1, u64::MAX] {
let mut ctx = TestContext::new();

let escrow_ix = CreateEscrowFixture::build_valid(&mut ctx);
let admin = escrow_ix.signers[0].insecure_clone();
let escrow_seed = escrow_ix.signers[1].pubkey();
escrow_ix.send_expect_success(&mut ctx);

let (escrow_pda, _) = find_escrow_pda(&escrow_seed);

let test_ix = AddTimelockFixture::build_with_escrow(&mut ctx, escrow_pda, admin, lock_duration);
let error = test_ix.send_expect_error(&mut ctx);
assert_instruction_error(error, InstructionError::InvalidInstructionData);
}
}