Skip to content

Commit 91a7022

Browse files
Fix potential div by zero
1 parent 034b5c0 commit 91a7022

8 files changed

Lines changed: 37 additions & 46 deletions

File tree

mtorrent-cli/tests/test_complete_torrent.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ fn get_piece_info(metainfo: &Metainfo) -> data::PieceInfo {
342342
.or_else(|| metainfo.files().map(|it| it.map(|(len, _path)| len).sum()))
343343
.unwrap();
344344

345-
data::PieceInfo::new(metainfo.pieces().unwrap(), metainfo.piece_length().unwrap(), total_length)
345+
data::PieceInfo::new(metainfo.pieces(), metainfo.piece_length().unwrap(), total_length).unwrap()
346346
}
347347

348348
impl Peer for Seeder {

mtorrent-core/src/data/block_accountant.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ mod tests {
201201
use std::iter;
202202

203203
fn piece_info() -> Rc<PieceInfo> {
204-
Rc::new(PieceInfo::new(iter::empty(), 3, 256))
204+
Rc::new(PieceInfo::new(iter::repeat_n(&[0u8; 20], 86), 3, 256).unwrap())
205205
}
206206

207207
#[test]

mtorrent-core/src/data/piece_info.rs

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::data::Error;
2+
use std::io;
23

34
/// Helper for obtaining various information about pieces of a torrent.
45
#[derive(Debug)]
@@ -11,21 +12,32 @@ pub struct PieceInfo {
1112
impl PieceInfo {
1213
/// Create new [`PieceInfo`] from an iterator over 20-bytes slices representing SHA-1 hashes of
1314
/// the pieces of a torrent.
14-
pub fn new<'a, I: Iterator<Item = &'a [u8]>>(
15+
pub fn new<'a, I: Iterator<Item = &'a [u8; 20]>>(
1516
piece_it: I,
1617
piece_length: usize,
1718
total_length: usize,
18-
) -> Self {
19-
fn to_20_byte_array(slice: &[u8]) -> [u8; 20] {
20-
let mut ret = [0u8; 20];
21-
ret.copy_from_slice(slice);
22-
ret
19+
) -> Result<Self, Error> {
20+
if piece_length == 0 {
21+
return Err(
22+
io::Error::new(io::ErrorKind::InvalidInput, "piece length cannot be zero").into()
23+
);
2324
}
24-
PieceInfo {
25-
pieces: piece_it.map(to_20_byte_array).collect(),
25+
if total_length == 0 {
26+
return Err(
27+
io::Error::new(io::ErrorKind::InvalidInput, "total length cannot be zero").into()
28+
);
29+
}
30+
let pieces: Vec<[u8; 20]> = piece_it.cloned().collect();
31+
if pieces.len() != total_length.div_ceil(piece_length) {
32+
return Err(
33+
io::Error::new(io::ErrorKind::InvalidInput, "unexpected number of pieces").into()
34+
);
35+
}
36+
Ok(PieceInfo {
37+
pieces,
2638
piece_length,
2739
total_length,
28-
}
40+
})
2941
}
3042

3143
/// Get global offset relative to the start of the entire torrent (a single entity possibly
@@ -78,7 +90,7 @@ mod tests {
7890

7991
#[test]
8092
fn last_incomplete_piece_is_handled_correctly() {
81-
let p = PieceInfo::new(std::iter::repeat_n([0u8; 20].as_slice(), 3), 5, 12);
93+
let p = PieceInfo::new(std::iter::repeat_n(&[0u8; 20], 3), 5, 12).unwrap();
8294
assert_eq!(3, p.piece_count());
8395
assert_eq!(5, p.piece_len(0));
8496
assert_eq!(5, p.piece_len(1));

mtorrent-core/src/input/metainfo.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,11 @@ impl Metainfo {
105105
}
106106

107107
/// 20-byte SHA-1 hash values, one per piece.
108-
pub fn pieces(&self) -> Option<impl Iterator<Item = &[u8]>> {
109-
if let Some(benc::Element::ByteString(data)) = self.info.get("pieces") {
110-
Some(data.chunks_exact(20))
111-
} else {
112-
None
113-
}
108+
pub fn pieces(&self) -> impl Iterator<Item = &[u8; 20]> {
109+
self.info.get("pieces").into_iter().flat_map(|e| match e {
110+
benc::Element::ByteString(data) => data.as_chunks::<20>().0,
111+
_ => &[],
112+
})
114113
}
115114

116115
/// Length of the file in bytes for single file torrents.
@@ -223,7 +222,7 @@ mod tests {
223222
let piece_length = info.piece_length().unwrap();
224223
assert_eq!(2_097_152, piece_length, "piece length: {piece_length}");
225224

226-
let piece_count = info.pieces().unwrap().count();
225+
let piece_count = info.pieces().count();
227226
assert_eq!(/* 13360 / 20 */ 668, piece_count);
228227

229228
let length = info.length();
@@ -411,7 +410,7 @@ mod tests {
411410
info.name().unwrap()
412411
);
413412
assert_eq!(1048576, info.piece_length().unwrap());
414-
assert_eq!(28040 / 20, info.pieces().unwrap().count());
413+
assert_eq!(28040 / 20, info.pieces().count());
415414
assert_eq!(data.len(), info.size());
416415

417416
assert!(info.files().is_none());

mtorrent-utils/src/connect_throttle.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ pub struct ConnectThrottle {
4343
impl ConnectThrottle {
4444
/// Create new throttle that allows up to `max_connections` concurrent permits, and keeps track
4545
/// of `remembered_peers` last addresses that have been issued a permit (regardless of whether
46-
/// the permit is still alive or not).
46+
/// the permit is still alive or not). Both `max_connections` and `remembered_peers` must be
47+
/// non-zero.
4748
pub fn new(max_connections: usize, remembered_peers: usize) -> Self {
4849
Self {
4950
connected_peers: Rc::new(sealed::Set::with_capacity(max_connections)),

mtorrent-utils/src/net.rs

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,7 @@ pub fn bind_to_interface<'s>(socket: impl Into<SockRef<'s>>, interface: &str) ->
165165
let interface = std::ffi::CString::new(interface)?;
166166
let idx = unsafe { libc::if_nametoindex(interface.as_ptr()) };
167167
let Some(idx) = std::num::NonZeroU32::new(idx) else {
168-
// If the index is 0, check errno and return an I/O error.
169-
return Err(io::Error::new(
170-
io::ErrorKind::InvalidInput,
171-
"error converting interface name to index",
172-
));
168+
return Err(io::Error::new(io::ErrorKind::NotFound, "interface not found"));
173169
};
174170

175171
let Ok(local_addr) = socket.local_addr() else {
@@ -295,23 +291,6 @@ mod tests {
295291
assert_eq!(SocketAddrV6BytesIter(&data).collect::<Vec<_>>(), addrs);
296292
}
297293

298-
#[cfg(not(windows))]
299-
#[test]
300-
fn test_network_interfaces() {
301-
for i in 0..10 {
302-
let mut name_buf = [0u8; 128];
303-
let name = unsafe { libc::if_indextoname(i, name_buf.as_mut_ptr() as *mut i8) };
304-
if name.is_null() {
305-
continue;
306-
}
307-
let name = unsafe { std::ffi::CStr::from_ptr(name) }.to_string_lossy();
308-
println!("Interface {}: {}", i, name);
309-
310-
let idx = unsafe { libc::if_nametoindex(name_buf.as_ptr() as *const i8) };
311-
assert_eq!(idx, i);
312-
}
313-
}
314-
315294
#[test]
316295
fn test_get_bind_addr() {
317296
let iface = if cfg!(target_os = "windows") {

mtorrent/src/ops/ctx.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,13 @@ impl MainCtx {
150150
move || io::Error::new(io::ErrorKind::InvalidData, s)
151151
}
152152
let pieces = Rc::new(data::PieceInfo::new(
153-
metainfo.pieces().ok_or_else(make_error("no pieces in metainfo"))?,
153+
metainfo.pieces(),
154154
metainfo.piece_length().ok_or_else(make_error("no piece length in metainfo"))?,
155155
metainfo
156156
.length()
157157
.or_else(|| metainfo.files().map(|it| it.map(|(len, _path)| len).sum()))
158158
.ok_or_else(make_error("no total length in metainfo"))?,
159-
));
159+
)?);
160160
let accountant = data::BlockAccountant::new(pieces.clone());
161161
let piece_tracker = data::PieceTracker::new(pieces.piece_count());
162162
let ctx = Self {

mtorrent/src/ops/peer/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ async fn run_listening_seeder(
129129
join!(content_storage_server.run(), meta_storage_server.run());
130130
});
131131

132-
let piece_count = metainfo.pieces().unwrap().count();
132+
let piece_count = metainfo.pieces().count();
133133
let mut local_id = [0u8; 20];
134134
local_id[..6].copy_from_slice("seeder".as_bytes());
135135

0 commit comments

Comments
 (0)