Skip to content

Commit 8a2e456

Browse files
committed
Harden JVM runtime correctness
1 parent af4f6f8 commit 8a2e456

54 files changed

Lines changed: 1911 additions & 354 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ async-trait = { workspace = true }
4242
bytemuck = { workspace = true }
4343

4444
anyhow = { workspace = true }
45+
tracing = { workspace = true }
4546
tracing-subscriber = { version = "^0.3", features = ["env-filter"] }
4647

4748
jvm = { workspace = true }
@@ -53,7 +54,7 @@ java_runtime = { workspace = true }
5354
test_utils = { workspace = true }
5455

5556
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
56-
tokio = { workspace = true, features = ["rt-multi-thread"] }
57+
tokio = { workspace = true, features = ["rt-multi-thread", "time"] }
5758

5859
[target.'cfg(target_arch = "wasm32")'.dependencies]
59-
tokio = { workspace = true, features = ["rt"] }
60+
tokio = { workspace = true, features = ["rt", "time"] }

classfile/src/attribute.rs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ pub struct CodeAttributeExceptionTable {
1919

2020
impl CodeAttributeExceptionTable {
2121
pub fn parse<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Self> {
22-
map((be_u16, be_u16, be_u16, be_u16), |(start_pc, end_pc, handler_pc, catch_type)| {
22+
map_res((be_u16, be_u16, be_u16, be_u16), |(start_pc, end_pc, handler_pc, catch_type)| {
2323
let catch_type = if catch_type != 0 {
24-
let index = constant_pool.get(&catch_type).unwrap().class_name_index();
25-
Some(constant_pool.get(&index).unwrap().utf8())
24+
let index = constant_pool.get(&catch_type).and_then(ConstantPoolItem::class_name_index).ok_or(())?;
25+
Some(constant_pool.get(&index).and_then(ConstantPoolItem::utf8).ok_or(())?)
2626
} else {
2727
None
2828
};
2929

30-
Self {
30+
Ok::<_, ()>(Self {
3131
start_pc,
3232
end_pc,
3333
handler_pc,
3434
catch_type,
35-
}
35+
})
3636
})
3737
.parse(data)
3838
}
@@ -52,7 +52,7 @@ impl AttributeInfoCode {
5252
(
5353
be_u16,
5454
be_u16,
55-
map(flat_map(be_u32, take), |x: &[u8]| Self::parse_code(x, constant_pool)),
55+
map_res(flat_map(be_u32, take), |x: &[u8]| Self::parse_code(x, constant_pool)),
5656
length_count(be_u16, |x| CodeAttributeExceptionTable::parse(x, constant_pool)),
5757
length_count(be_u16, |x| AttributeInfo::parse(x, constant_pool)),
5858
),
@@ -67,22 +67,21 @@ impl AttributeInfoCode {
6767
.parse(data)
6868
}
6969

70-
fn parse_code(code: &[u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> BTreeMap<u32, Opcode> {
70+
fn parse_code(code: &[u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> Result<BTreeMap<u32, Opcode>, ()> {
7171
let mut result = BTreeMap::new();
7272

7373
let mut data = code;
74-
loop {
74+
while !data.is_empty() {
7575
let offset = unsafe { data.as_ptr().offset_from(code.as_ptr()) } as usize;
76-
if let Ok((remaining, opcode)) = Opcode::parse(data, offset, constant_pool) {
77-
result.insert(offset as _, opcode);
78-
79-
data = remaining;
80-
} else {
81-
break;
76+
let (remaining, opcode) = Opcode::parse(data, offset, constant_pool).map_err(|_| ())?;
77+
if remaining.len() >= data.len() {
78+
return Err(());
8279
}
80+
result.insert(offset as _, opcode);
81+
data = remaining;
8382
}
8483

85-
result
84+
Ok(result)
8685
}
8786
}
8887

@@ -114,8 +113,8 @@ impl LocalVariableTableEntry {
114113
(
115114
be_u16,
116115
be_u16,
117-
map(be_u16, |x| constant_pool.get(&x).unwrap().utf8()),
118-
map(be_u16, |x| constant_pool.get(&x).unwrap().utf8()),
116+
map_res(be_u16, |x| constant_pool.get(&x).and_then(ConstantPoolItem::utf8).ok_or(())),
117+
map_res(be_u16, |x| constant_pool.get(&x).and_then(ConstantPoolItem::utf8).ok_or(())),
119118
be_u16,
120119
),
121120
|(start_pc, length, name, descriptor, index)| Self {
@@ -152,7 +151,10 @@ pub enum AttributeInfo {
152151
impl AttributeInfo {
153152
pub fn parse<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Self> {
154153
map_res(
155-
(map(be_u16, |x| constant_pool.get(&x).unwrap().utf8()), flat_map(be_u32, take)),
154+
(
155+
map_res(be_u16, |x| constant_pool.get(&x).and_then(ConstantPoolItem::utf8).ok_or(())),
156+
flat_map(be_u32, take),
157+
),
156158
|(name, info): (_, &[u8])| {
157159
Ok::<_, nom::Err<_>>(match name.as_str() {
158160
"ConstantValue" => AttributeInfo::ConstantValue(Self::parse_constant_value(info, constant_pool)?.1),
@@ -180,11 +182,11 @@ impl AttributeInfo {
180182
}
181183

182184
fn parse_source_file<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Arc<String>> {
183-
map(be_u16, |x| constant_pool.get(&x).unwrap().utf8()).parse(data)
185+
map_res(be_u16, |x| constant_pool.get(&x).and_then(ConstantPoolItem::utf8).ok_or(())).parse(data)
184186
}
185187

186188
fn parse_constant_value<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], ConstantPoolReference> {
187-
map(be_u16, |x| ConstantPoolReference::from_constant_pool(constant_pool, x as _)).parse(data)
189+
map_res(be_u16, |x| ConstantPoolReference::from_constant_pool(constant_pool, x).ok_or(())).parse(data)
188190
}
189191

190192
fn parse_local_variable_table<'a>(

classfile/src/class.rs

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,45 @@ use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec};
22

33
use nom::{
44
IResult, Parser,
5+
error::{Error, ErrorKind},
56
multi::length_count,
67
number::complete::{be_u16, be_u32},
78
};
89

910
use java_constants::ClassAccessFlags;
1011

11-
use crate::{attribute::AttributeInfo, constant_pool::ConstantPoolItem, field::FieldInfo, interface::parse_interface, method::MethodInfo};
12+
use crate::{
13+
ClassFileError, attribute::AttributeInfo, constant_pool::ConstantPoolItem, field::FieldInfo, interface::parse_interface, method::MethodInfo,
14+
};
1215

1316
fn parse_this_class<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Arc<String>> {
1417
let (data, this_class) = be_u16(data)?;
15-
let class_name_index = constant_pool.get(&this_class).unwrap().class_name_index();
18+
let class_name_index = constant_pool
19+
.get(&this_class)
20+
.and_then(ConstantPoolItem::class_name_index)
21+
.ok_or_else(|| nom::Err::Error(Error::new(data, ErrorKind::Verify)))?;
22+
let class_name = constant_pool
23+
.get(&class_name_index)
24+
.and_then(ConstantPoolItem::utf8)
25+
.ok_or_else(|| nom::Err::Error(Error::new(data, ErrorKind::Verify)))?;
1626

17-
Ok((data, constant_pool.get(&class_name_index).unwrap().utf8()))
27+
Ok((data, class_name))
1828
}
1929

2030
fn parse_super_class<'a>(data: &'a [u8], constant_pool: &BTreeMap<u16, ConstantPoolItem>) -> IResult<&'a [u8], Option<Arc<String>>> {
2131
let (data, super_class) = be_u16(data)?;
2232

2333
let super_class = if super_class != 0 {
24-
let class_name_index = constant_pool.get(&super_class).unwrap().class_name_index();
25-
Some(constant_pool.get(&class_name_index).unwrap().utf8())
34+
let class_name_index = constant_pool
35+
.get(&super_class)
36+
.and_then(ConstantPoolItem::class_name_index)
37+
.ok_or_else(|| nom::Err::Error(Error::new(data, ErrorKind::Verify)))?;
38+
Some(
39+
constant_pool
40+
.get(&class_name_index)
41+
.and_then(ConstantPoolItem::utf8)
42+
.ok_or_else(|| nom::Err::Error(Error::new(data, ErrorKind::Verify)))?,
43+
)
2644
} else {
2745
None
2846
};
@@ -80,12 +98,18 @@ impl ClassInfo {
8098
))
8199
}
82100

83-
pub fn parse(file: &[u8]) -> Option<Self> {
84-
let (remaining, result) = Self::parse_info(file).ok()?;
101+
pub fn parse(file: &[u8]) -> Result<Self, ClassFileError> {
102+
let (remaining, result) = Self::parse_info(file).map_err(|_| ClassFileError::InvalidFormat)?;
85103
if !remaining.is_empty() {
86-
return None;
104+
return Err(ClassFileError::InvalidFormat);
105+
}
106+
if result.major_version < 45 {
107+
return Err(ClassFileError::InvalidFormat);
108+
}
109+
if result.major_version > 70 {
110+
return Err(ClassFileError::UnsupportedVersion(result.major_version));
87111
}
88112

89-
Some(result)
113+
Ok(result)
90114
}
91115
}

0 commit comments

Comments
 (0)