Skip to content

Commit 27f87c6

Browse files
committed
std: Fix overflow of HashMap's capacity
1 parent ae7342a commit 27f87c6

File tree

2 files changed

+70
-32
lines changed

2 files changed

+70
-32
lines changed

src/libstd/collections/hashmap/table.rs

+49-32
Original file line numberDiff line numberDiff line change
@@ -526,32 +526,45 @@ fn test_rounding() {
526526
assert_eq!(round_up_to_next(5, 4), 8);
527527
}
528528

529-
// Returns a tuple of (minimum required malloc alignment, hash_offset,
530-
// key_offset, val_offset, array_size), from the start of a mallocated array.
531-
fn calculate_offsets(
532-
hash_size: uint, hash_align: uint,
533-
keys_size: uint, keys_align: uint,
534-
vals_size: uint, vals_align: uint) -> (uint, uint, uint, uint, uint) {
529+
// Returns a tuple of (key_offset, val_offset),
530+
// from the start of a mallocated array.
531+
fn calculate_offsets(hashes_size: uint,
532+
keys_size: uint, keys_align: uint,
533+
vals_align: uint)
534+
-> (uint, uint) {
535+
let keys_offset = round_up_to_next(hashes_size, keys_align);
536+
let end_of_keys = keys_offset + keys_size;
535537

536-
let hash_offset = 0;
537-
let end_of_hashes = hash_offset + hash_size;
538+
let vals_offset = round_up_to_next(end_of_keys, vals_align);
538539

539-
let keys_offset = round_up_to_next(end_of_hashes, keys_align);
540-
let end_of_keys = keys_offset + keys_size;
540+
(keys_offset, vals_offset)
541+
}
541542

542-
let vals_offset = round_up_to_next(end_of_keys, vals_align);
543-
let end_of_vals = vals_offset + vals_size;
543+
// Returns a tuple of (minimum required malloc alignment, hash_offset,
544+
// array_size), from the start of a mallocated array.
545+
fn calculate_allocation(hash_size: uint, hash_align: uint,
546+
keys_size: uint, keys_align: uint,
547+
vals_size: uint, vals_align: uint)
548+
-> (uint, uint, uint) {
549+
let hash_offset = 0;
550+
let (_, vals_offset) = calculate_offsets(hash_size,
551+
keys_size, keys_align,
552+
vals_align);
553+
let end_of_vals = vals_offset + vals_size;
544554

545555
let min_align = cmp::max(hash_align, cmp::max(keys_align, vals_align));
546556

547-
(min_align, hash_offset, keys_offset, vals_offset, end_of_vals)
557+
(min_align, hash_offset, end_of_vals)
548558
}
549559

550560
#[test]
551561
fn test_offset_calculation() {
552-
assert_eq!(calculate_offsets(128, 8, 15, 1, 4, 4 ), (8, 0, 128, 144, 148));
553-
assert_eq!(calculate_offsets(3, 1, 2, 1, 1, 1 ), (1, 0, 3, 5, 6));
554-
assert_eq!(calculate_offsets(6, 2, 12, 4, 24, 8), (8, 0, 8, 24, 48));
562+
assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4), (8, 0, 148));
563+
assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 0, 6));
564+
assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 0, 48));
565+
assert_eq!(calculate_offsets(128, 15, 1, 4), (128, 144));
566+
assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5));
567+
assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24));
555568
}
556569

557570
impl<K, V> RawTable<K, V> {
@@ -566,12 +579,11 @@ impl<K, V> RawTable<K, V> {
566579
marker: marker::CovariantType,
567580
};
568581
}
569-
let hashes_size = capacity.checked_mul(&size_of::<u64>())
570-
.expect("capacity overflow");
571-
let keys_size = capacity.checked_mul(&size_of::< K >())
572-
.expect("capacity overflow");
573-
let vals_size = capacity.checked_mul(&size_of::< V >())
574-
.expect("capacity overflow");
582+
// No need for `checked_mul` before a more restrictive check performed
583+
// later in this method.
584+
let hashes_size = capacity * size_of::<u64>();
585+
let keys_size = capacity * size_of::< K >();
586+
let vals_size = capacity * size_of::< V >();
575587

576588
// Allocating hashmaps is a little tricky. We need to allocate three
577589
// arrays, but since we know their sizes and alignments up front,
@@ -581,12 +593,19 @@ impl<K, V> RawTable<K, V> {
581593
// This is great in theory, but in practice getting the alignment
582594
// right is a little subtle. Therefore, calculating offsets has been
583595
// factored out into a different function.
584-
let (malloc_alignment, hash_offset, _, _, size) =
585-
calculate_offsets(
596+
let (malloc_alignment, hash_offset, size) =
597+
calculate_allocation(
586598
hashes_size, min_align_of::<u64>(),
587599
keys_size, min_align_of::< K >(),
588600
vals_size, min_align_of::< V >());
589601

602+
// One check for overflow that covers calculation and rounding of size.
603+
let size_of_bucket = size_of::<u64>().checked_add(&size_of::<K>()).unwrap()
604+
.checked_add(&size_of::<V>()).unwrap();
605+
assert!(size >= capacity.checked_mul(&size_of_bucket)
606+
.expect("capacity overflow"),
607+
"capacity overflow");
608+
590609
let buffer = allocate(size, malloc_alignment);
591610

592611
let hashes = buffer.offset(hash_offset as int) as *mut u64;
@@ -603,12 +622,10 @@ impl<K, V> RawTable<K, V> {
603622
let hashes_size = self.capacity * size_of::<u64>();
604623
let keys_size = self.capacity * size_of::<K>();
605624

606-
let keys_offset = (hashes_size + min_align_of::<K>() - 1) & !(min_align_of::<K>() - 1);
607-
let end_of_keys = keys_offset + keys_size;
608-
609-
let vals_offset = (end_of_keys + min_align_of::<V>() - 1) & !(min_align_of::<V>() - 1);
610-
611625
let buffer = self.hashes as *mut u8;
626+
let (keys_offset, vals_offset) = calculate_offsets(hashes_size,
627+
keys_size, min_align_of::<K>(),
628+
min_align_of::<V>());
612629

613630
unsafe {
614631
RawBucket {
@@ -866,9 +883,9 @@ impl<K, V> Drop for RawTable<K, V> {
866883
let hashes_size = self.capacity * size_of::<u64>();
867884
let keys_size = self.capacity * size_of::<K>();
868885
let vals_size = self.capacity * size_of::<V>();
869-
let (align, _, _, _, size) = calculate_offsets(hashes_size, min_align_of::<u64>(),
870-
keys_size, min_align_of::<K>(),
871-
vals_size, min_align_of::<V>());
886+
let (align, _, size) = calculate_allocation(hashes_size, min_align_of::<u64>(),
887+
keys_size, min_align_of::<K>(),
888+
vals_size, min_align_of::<V>());
872889

873890
unsafe {
874891
deallocate(self.hashes as *mut u8, size, align);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:capacity overflow
12+
13+
use std::collections::hashmap::HashMap;
14+
use std::uint;
15+
use std::mem::size_of;
16+
17+
fn main() {
18+
let threshold = uint::MAX / size_of::<(u64, u64, u64)>();
19+
let mut h = HashMap::<u64, u64>::with_capacity(threshold + 100);
20+
h.insert(0, 0);
21+
}

0 commit comments

Comments
 (0)