Skip to content

Commit 255db38

Browse files
authored
Merge pull request #38 from lazor-kit/fix/oob-slot-hash-read
fix(auth): check slot hash index bounds correctly (OOB Read #17)
2 parents b48d940 + 5ba56b6 commit 255db38

3 files changed

Lines changed: 51 additions & 1 deletion

File tree

program/src/auth/secp256r1/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ impl Authenticator for Secp256r1Authenticator {
118118
#[cfg(not(target_os = "solana"))]
119119
{
120120
let _ = signed_payload; // suppress unused warning for non-solana
121+
let _ = discriminator;
121122
hasher = [0u8; 32];
122123
}
123124

program/src/auth/secp256r1/slothashes.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ where
7575
/// Returns the slot hash at the specified index.
7676
#[inline(always)]
7777
pub fn get_slot_hash(&self, index: usize) -> Result<&SlotHash, ProgramError> {
78-
if index > self.get_slothashes_len() as usize {
78+
if index >= self.get_slothashes_len() as usize {
7979
return Err(AuthError::PermissionDenied.into()); // Mapping generic error for simplicity
8080
}
8181
unsafe { Ok(self.get_slot_hash_unchecked(index)) }
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use lazorkit_program::auth::secp256r1::slothashes::SlotHashes;
2+
3+
#[test]
4+
fn test_slot_hashes_oob_read() {
5+
// 1. Setup Mock Data
6+
// num_entries: 2 (u64)
7+
// entry 0: slot 100, hash [1; 32]
8+
// entry 1: slot 99, hash [2; 32]
9+
let mut data = Vec::new();
10+
data.extend_from_slice(&2u64.to_le_bytes()); // len = 2
11+
12+
// Entry 0
13+
data.extend_from_slice(&100u64.to_le_bytes());
14+
data.extend_from_slice(&[1u8; 32]);
15+
16+
// Entry 1
17+
data.extend_from_slice(&99u64.to_le_bytes());
18+
data.extend_from_slice(&[2u8; 32]);
19+
20+
// Interpret data as &[u8]
21+
let data_slice: &[u8] = &data;
22+
23+
// Safety: we constructed data correctly
24+
let slot_hashes = unsafe { SlotHashes::new_unchecked(data_slice) };
25+
26+
// 2. Verify Valid Access
27+
let hash_0 = slot_hashes.get_slot_hash(0).unwrap();
28+
assert_eq!(hash_0.height, 100);
29+
30+
let hash_1 = slot_hashes.get_slot_hash(1).unwrap();
31+
assert_eq!(hash_1.height, 99);
32+
33+
// 3. Verify OOB Access (The Bug)
34+
println!("Trying to access OOB index 2...");
35+
// This call accesses index 2.
36+
// Length is 2.
37+
// Current Buggy Logic: 2 > 2 is FALSE.
38+
// So it PROCEEDS to unsafe code and returns Ok or Panics.
39+
// We expect it to be Err(PermissionDenied).
40+
41+
let result = slot_hashes.get_slot_hash(2);
42+
43+
// If the bug is present, result.is_ok() will be true (or panic).
44+
// If fixed, result.is_err() will be true.
45+
assert!(
46+
result.is_err(),
47+
"Index equal to length should be an error! (OOB Read)"
48+
);
49+
}

0 commit comments

Comments
 (0)