Skip to content

Commit 57685c3

Browse files
committed
Enforce a bounds check on HashTable accesses.
Without this bounds check a malformed ELF file could trigger an out-of-bounds read. Fixes #86
1 parent f747ecf commit 57685c3

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

src/hash.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
1+
use core::mem;
2+
13
use symbol_table::Entry;
2-
use zero::Pod;
4+
use zero::{read, Pod};
35

46
#[derive(Clone, Copy, Debug)]
57
#[repr(C)]
6-
pub struct HashTable {
8+
struct HashTableInner {
79
bucket_count: u32,
810
chain_count: u32,
911
first_bucket: u32,
1012
}
1113

12-
unsafe impl Pod for HashTable {}
14+
const OFFSET_OF_FIRST_BUCKET: usize = mem::offset_of!(HashTableInner, first_bucket);
15+
16+
#[derive(Clone, Copy, Debug)]
17+
pub struct HashTable<'a> {
18+
inner: &'a HashTableInner,
19+
bounds: usize, // In number of u32s
20+
}
21+
22+
unsafe impl Pod for HashTableInner {}
1323

1424
pub fn hash(input: &str) -> u32 {
1525
let mut result = 0;
@@ -24,26 +34,35 @@ pub fn hash(input: &str) -> u32 {
2434
result
2535
}
2636

27-
impl HashTable {
37+
impl<'a> HashTable<'a> {
38+
pub(crate) fn read(data: &'a [u8]) -> HashTable<'a> {
39+
HashTable {
40+
inner: read(&data[0..12]),
41+
bounds: (data.len() - OFFSET_OF_FIRST_BUCKET) / mem::size_of::<u32>(),
42+
}
43+
}
44+
2845
pub fn get_bucket(&self, index: u32) -> u32 {
29-
assert!(index < self.bucket_count);
46+
assert!(index < self.inner.bucket_count);
47+
assert!((index as usize) < self.bounds);
3048
unsafe {
31-
let ptr = (&self.first_bucket as *const u32).offset(index as isize);
49+
let ptr = (&self.inner.first_bucket as *const u32).offset(index as isize);
3250
*ptr
3351
}
3452
}
3553

3654
pub fn get_chain(&self, index: u32) -> u32 {
37-
assert!(index < self.chain_count);
38-
let index = self.bucket_count + index;
55+
assert!(index < self.inner.chain_count);
56+
let index = self.inner.bucket_count + index;
57+
assert!((index as usize) < self.bounds);
3958
unsafe {
40-
let ptr = (&self.first_bucket as *const u32).offset(index as isize);
59+
let ptr = (&self.inner.first_bucket as *const u32).offset(index as isize);
4160
*ptr
4261
}
4362
}
4463

45-
pub fn lookup<'a, F>(&'a self, _name: &str, _f: F) -> &'a dyn Entry
46-
where F: Fn(&'a dyn Entry) -> bool
64+
pub fn lookup<F>(&'a self, _name: &str, _f: F) -> &'a dyn Entry
65+
where F: Fn(&dyn Entry) -> bool
4766
{
4867
// TODO
4968
unimplemented!();

src/sections.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ impl<'a> SectionHeader<'a> {
165165
}
166166
ShType::Hash => {
167167
let data = self.raw_data(elf_file);
168-
SectionData::HashTable(read(&data[0..12]))
168+
SectionData::HashTable(HashTable::read(data))
169169
}
170170
}))
171171
}
@@ -366,7 +366,7 @@ pub enum SectionData<'a> {
366366
Rel64(&'a [Rel<P64>]),
367367
Dynamic32(&'a [Dynamic<P32>]),
368368
Dynamic64(&'a [Dynamic<P64>]),
369-
HashTable(&'a HashTable),
369+
HashTable(HashTable<'a>),
370370
}
371371

372372
#[derive(Debug)]

0 commit comments

Comments
 (0)