Skip to content

Commit 6bb778e

Browse files
committed
Fix panic in sort_by
1 parent d459736 commit 6bb778e

File tree

2 files changed

+45
-21
lines changed

2 files changed

+45
-21
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ Breaking Changes
99

1010
* `stdweb` support is removed. The feature flag `stdweb` is also removed. Use `wasm-bindgen` instead.
1111

12+
Bug fixes
13+
---------
14+
15+
* (Fuzzing) Fixed panic when using `sort` on an array with a comparer function that does not implement a [total order](https://en.wikipedia.org/wiki/Total_order).
16+
1217
Enhancements
1318
------------
1419

src/packages/array_basic.rs

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,6 +1514,8 @@ pub mod array_functions {
15141514
}
15151515
/// Sort the array based on applying the `comparer` function.
15161516
///
1517+
/// The `comparer` function must implement a [total order](https://en.wikipedia.org/wiki/Total_order).
1518+
///
15171519
/// # Function Parameters
15181520
///
15191521
/// * `element1`: copy of the current array element to compare
@@ -1534,6 +1536,11 @@ pub mod array_functions {
15341536
///
15351537
/// Any other return value type will yield unpredictable order.
15361538
///
1539+
/// # Errors
1540+
///
1541+
/// If the `comparer` function does not implement a [total order](https://en.wikipedia.org/wiki/Total_order),
1542+
/// an error is returned.
1543+
///
15371544
/// # Example
15381545
///
15391546
/// ```rhai
@@ -1550,27 +1557,32 @@ pub mod array_functions {
15501557
return Ok(());
15511558
}
15521559

1553-
array.sort_by(|x, y| {
1554-
comparer
1555-
.call_raw(&ctx, None, [x.clone(), y.clone()])
1556-
.ok()
1557-
.and_then(|v| {
1558-
v.as_int()
1559-
.or_else(|_| v.as_bool().map(|v| if v { -1 } else { 1 }))
1560-
.ok()
1561-
})
1562-
.map_or_else(
1563-
|| x.type_id().cmp(&y.type_id()),
1564-
|v| match v {
1565-
v if v > 0 => Ordering::Greater,
1566-
v if v < 0 => Ordering::Less,
1567-
0 => Ordering::Equal,
1568-
_ => unreachable!("v is {}", v),
1569-
},
1570-
)
1571-
});
1572-
1573-
Ok(())
1560+
// `slice::sort_by` may panic if the comparer function does not implement a total order.
1561+
// Catch this instead.
1562+
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
1563+
array.sort_by(|x, y| {
1564+
comparer
1565+
.call_raw(&ctx, None, [x.clone(), y.clone()])
1566+
.ok()
1567+
.and_then(|v| {
1568+
v.as_int()
1569+
.or_else(|_| v.as_bool().map(|v| if v { -1 } else { 1 }))
1570+
.ok()
1571+
})
1572+
.map_or_else(
1573+
|| x.type_id().cmp(&y.type_id()),
1574+
|v| match v {
1575+
v if v > 0 => Ordering::Greater,
1576+
v if v < 0 => Ordering::Less,
1577+
0 => Ordering::Equal,
1578+
_ => unreachable!("v is {}", v),
1579+
},
1580+
)
1581+
});
1582+
}))
1583+
.map_err(|_| {
1584+
ERR::ErrorRuntime("error in comparer for sorting".into(), Position::NONE).into()
1585+
})
15741586
}
15751587
/// Sort the array.
15761588
///
@@ -1796,6 +1808,8 @@ pub mod array_functions {
17961808
}
17971809
/// Sort the array based on applying the `comparer` function and return it as a new array.
17981810
///
1811+
/// The `comparer` function must implement a [total order](https://en.wikipedia.org/wiki/Total_order).
1812+
///
17991813
/// # Function Parameters
18001814
///
18011815
/// * `element1`: copy of the current array element to compare
@@ -1816,6 +1830,11 @@ pub mod array_functions {
18161830
///
18171831
/// Any other return value type will yield unpredictable order.
18181832
///
1833+
/// # Errors
1834+
///
1835+
/// If the `comparer` function does not implement a [total order](https://en.wikipedia.org/wiki/Total_order),
1836+
/// an error is returned.
1837+
///
18191838
/// # Example
18201839
///
18211840
/// ```rhai

0 commit comments

Comments
 (0)