Skip to content

Commit 81492d6

Browse files
authored
fix: process issues from external security review (#897)
* fix: avoid call stack overflow * fix(engine): throw errors on more allocation failures
1 parent ffbdbac commit 81492d6

File tree

48 files changed

+420
-249
lines changed

Some content is hidden

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

48 files changed

+420
-249
lines changed

nova_vm/src/ecmascript/abstract_operations/operations_on_iterator_objects.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::{
2525
},
2626
engine::{
2727
ScopableCollection, ScopedCollection, VmIteratorRecord,
28-
context::{Bindable, GcScope, bindable_handle},
28+
context::{Bindable, GcScope, NoGcScope, bindable_handle},
2929
rootable::Scopable,
3030
},
3131
heap::{
@@ -609,7 +609,8 @@ pub(crate) fn create_iter_result_object<'a>(
609609
agent: &mut Agent,
610610
value: Value<'a>,
611611
done: bool,
612-
) -> OrdinaryObject<'a> {
612+
gc: NoGcScope<'a, '_>,
613+
) -> JsResult<'a, OrdinaryObject<'a>> {
613614
// 1. Let obj be OrdinaryObjectCreate(%Object.prototype%).
614615
// 2. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
615616
// 3. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
@@ -644,6 +645,7 @@ pub(crate) fn create_iter_result_object<'a>(
644645
},
645646
],
646647
)
648+
.map_err(|err| agent.throw_allocation_exception(err, gc))
647649
}
648650

649651
/// ### [7.4.16 IteratorToList ( iteratorRecord )](https://tc39.es/ecma262/#sec-iteratortolist)

nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2248,7 +2248,7 @@ pub(crate) fn try_copy_data_properties_into_object<'gc, 'b>(
22482248
}
22492249
}
22502250

2251-
TryResult::Continue(OrdinaryObject::create_object(
2251+
let obj = OrdinaryObject::create_object(
22522252
agent,
22532253
Some(
22542254
agent
@@ -2258,7 +2258,13 @@ pub(crate) fn try_copy_data_properties_into_object<'gc, 'b>(
22582258
.into_object(),
22592259
),
22602260
&entries,
2261-
))
2261+
);
2262+
if let Ok(obj) = obj {
2263+
TryResult::Continue(obj)
2264+
} else {
2265+
// We failed to allocate: try run GC.
2266+
TryError::GcError.into()
2267+
}
22622268
}
22632269

22642270
/// ### [7.3.25 CopyDataProperties ( target, source, excludedItems )](https://tc39.es/ecma262/#sec-copydataproperties)
@@ -2327,7 +2333,7 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>(
23272333
i += 1;
23282334
}
23292335

2330-
let object = OrdinaryObject::create_object(
2336+
let object = match OrdinaryObject::create_object(
23312337
agent,
23322338
Some(
23332339
agent
@@ -2337,8 +2343,10 @@ pub(crate) fn copy_data_properties_into_object<'a, 'b>(
23372343
.into_object(),
23382344
),
23392345
&entries,
2340-
)
2341-
.bind(gc.nogc());
2346+
) {
2347+
Ok(obj) => obj,
2348+
Err(err) => return Err(agent.throw_allocation_exception(err, gc.into_nogc())),
2349+
};
23422350

23432351
if broke {
23442352
let _ = keys.drain(..i);

nova_vm/src/ecmascript/builders/ordinary_object_builder.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ pub(super) fn create_intrinsic_backing_object(
361361
let (cap, index) = agent
362362
.heap
363363
.elements
364-
.allocate_keys_with_capacity(properties_count);
364+
.allocate_keys_with_capacity(properties_count)
365+
.expect("Intrinsic object's property allocation failed");
365366
let cap = cap.make_intrinsic();
366367
let keys_memory = agent.heap.elements.get_keys_uninit_raw(cap, index);
367368
for (slot, key) in keys_memory.iter_mut().zip(properties.iter().map(|e| e.0)) {

nova_vm/src/ecmascript/builtins/arguments.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
//!
2727
//! ECMAScript implementations of arguments exotic objects have historically contained an accessor property named "caller". Prior to ECMAScript 2017, this specification included the definition of a throwing "caller" property on ordinary arguments objects. Since implementations do not contain this extension any longer, ECMAScript 2017 dropped the requirement for a throwing "caller" accessor.
2828
29+
use std::collections::TryReserveError;
30+
2931
use ahash::AHashMap;
3032

3133
use crate::{
@@ -124,7 +126,7 @@ pub(crate) fn create_unmapped_arguments_object<'a, 'b>(
124126
agent: &mut Agent,
125127
arguments_list: &ScopedArgumentsList<'b>,
126128
gc: NoGcScope<'a, 'b>,
127-
) -> Object<'a> {
129+
) -> Result<Object<'a>, TryReserveError> {
128130
// 1. Let len be the number of elements in argumentsList.
129131
let len = arguments_list.len(agent);
130132
// SAFETY: GC is not allowed in this scope, and no other scoped values are
@@ -136,11 +138,11 @@ pub(crate) fn create_unmapped_arguments_object<'a, 'b>(
136138
// 2. Let obj be OrdinaryObjectCreate(%Object.prototype%, « [[ParameterMap]] »).
137139
let prototype = agent.current_realm_record().intrinsics().object_prototype();
138140
let mut shape = ObjectShape::get_shape_for_prototype(agent, Some(prototype.into_object()));
139-
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.length.to_property_key());
140-
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.callee.into());
141-
shape = shape.get_child_shape(agent, WellKnownSymbolIndexes::Iterator.into());
141+
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.length.to_property_key())?;
142+
shape = shape.get_child_shape(agent, BUILTIN_STRING_MEMORY.callee.into())?;
143+
shape = shape.get_child_shape(agent, WellKnownSymbolIndexes::Iterator.into())?;
142144
for index in 0..len {
143-
shape = shape.get_child_shape(agent, index.into());
145+
shape = shape.get_child_shape(agent, index.into())?;
144146
}
145147
let obj = OrdinaryObject::create_object_with_shape(agent, shape)
146148
.expect("Failed to create Arguments object storage");
@@ -213,7 +215,7 @@ pub(crate) fn create_unmapped_arguments_object<'a, 'b>(
213215
// [[Configurable]]: false
214216
// }).
215217
// 9. Return obj.
216-
Object::Arguments(obj)
218+
Ok(Object::Arguments(obj))
217219
}
218220

219221
// 10.4.4.7 CreateMappedArgumentsObject ( func, formals, argumentsList, env )

nova_vm/src/ecmascript/builtins/array/abstract_operations.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,11 @@ pub(crate) fn array_create<'a>(
6363
{
6464
None
6565
} else {
66-
Some(OrdinaryObject::create_object(agent, Some(proto), &[]).bind(gc))
66+
Some(
67+
OrdinaryObject::create_object(agent, Some(proto), &[])
68+
.expect("Should perform GC here")
69+
.bind(gc),
70+
)
6771
}
6872
} else {
6973
None

nova_vm/src/ecmascript/builtins/bound_function.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ impl<'a> FunctionInternalProperties<'a> for BoundFunction<'a> {
169169
arguments_list: ArgumentsList,
170170
gc: GcScope<'gc, '_>,
171171
) -> JsResult<'gc, Value<'gc>> {
172+
agent.check_call_depth(gc.nogc()).unbind()?;
172173
let f = self.bind(gc.nogc());
173174
let arguments_list = arguments_list.bind(gc.nogc());
174175
// 1. Let target be F.[[BoundTargetFunction]].
@@ -219,6 +220,7 @@ impl<'a> FunctionInternalProperties<'a> for BoundFunction<'a> {
219220
new_target: Function,
220221
gc: GcScope<'gc, '_>,
221222
) -> JsResult<'gc, Object<'gc>> {
223+
agent.check_call_depth(gc.nogc()).unbind()?;
222224
let arguments_list = arguments_list.bind(gc.nogc());
223225
let new_target = new_target.bind(gc.nogc());
224226
// 1. Let target be F.[[BoundTargetFunction]].

nova_vm/src/ecmascript/builtins/builtin_constructor.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@ pub(crate) fn create_builtin_constructor<'a>(
351351
},
352352
};
353353
let entries = [length_entry, name_entry, prototype_entry];
354-
let backing_object = OrdinaryObject::create_intrinsic_object(agent, args.prototype, &entries);
354+
let backing_object = OrdinaryObject::create_intrinsic_object(agent, args.prototype, &entries)
355+
.expect("Should perform GC here");
355356

356357
// 13. Return func.
357358
agent

nova_vm/src/ecmascript/builtins/builtin_function.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,8 @@ fn builtin_call_or_construct<'gc>(
665665
new_target: Option<Function>,
666666
gc: GcScope<'gc, '_>,
667667
) -> JsResult<'gc, Value<'gc>> {
668+
agent.check_call_depth(gc.nogc()).unbind()?;
669+
668670
let f = f.bind(gc.nogc());
669671
let this_argument = this_argument.bind(gc.nogc());
670672
let arguments_list = arguments_list.bind(gc.nogc());
@@ -812,11 +814,10 @@ pub fn create_builtin_function<'a>(
812814
configurable: true,
813815
},
814816
};
815-
Some(OrdinaryObject::create_object(
816-
agent,
817-
Some(prototype),
818-
&[length_entry, name_entry],
819-
))
817+
Some(
818+
OrdinaryObject::create_object(agent, Some(prototype), &[length_entry, name_entry])
819+
.expect("Should perform GC here"),
820+
)
820821
}
821822
} else {
822823
None

nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_abstract_operations.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ fn async_generator_complete_step(
127127
agent.set_current_realm(realm);
128128
}
129129
// iii. Let iteratorResult be CreateIteratorResultObject(value, done).
130-
let iterator_result = create_iter_result_object(agent, value, done);
130+
let iterator_result =
131+
create_iter_result_object(agent, value, done, gc).expect("Should perform GC here");
131132
// iv. Set the running execution context's Realm to oldRealm.
132133
if set_realm {
133134
agent.set_current_realm(old_realm);
@@ -136,7 +137,7 @@ fn async_generator_complete_step(
136137
} else {
137138
// c. Else,
138139
// i. Let iteratorResult be CreateIteratorResultObject(value, done).
139-
create_iter_result_object(agent, value, done)
140+
create_iter_result_object(agent, value, done, gc).expect("Should perform GC here")
140141
};
141142
// d. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
142143
unwrap_try(promise_capability.try_resolve(agent, iterator_result.into_value(), gc));

nova_vm/src/ecmascript/builtins/control_abstraction_objects/async_generator_objects/async_generator_prototype.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ impl AsyncGeneratorPrototype {
8686
// 6. If state is completed, then
8787
if state.is_completed() {
8888
// a. Let iteratorResult be CreateIteratorResultObject(undefined, true).
89-
let iterator_result = create_iter_result_object(agent, Value::Undefined, true);
89+
let iterator_result =
90+
create_iter_result_object(agent, Value::Undefined, true, gc.nogc())
91+
.unbind()?
92+
.bind(gc.nogc());
9093
// b. Perform ! Call(promiseCapability.[[Resolve]], undefined, « iteratorResult »).
9194
promise_capability
9295
.unbind()

0 commit comments

Comments
 (0)