Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vec: with_capacity: check for overflow #10417

Merged
merged 1 commit into from
Nov 11, 2013
Merged

vec: with_capacity: check for overflow #10417

merged 1 commit into from
Nov 11, 2013

Conversation

emberian
Copy link
Member

Fixes #10271

let alloc = capacity * mem::nonzero_size_of::<T>();
let ptr = malloc_raw(alloc + mem::size_of::<Vec<()>>()) as *mut Vec<()>;
let alloc = capacity.checked_mul(&mem::nonzero_size_of::<T>())
.expect("with_capacity: vector size overflowed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a failure, or an abort? (Also, does the managed branch need checks too?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reserve doesn't need checks, see line 1414. reserve_raw in at_vec also checks for overflow.

@@ -186,7 +186,11 @@ pub fn with_capacity<T>(capacity: uint) -> ~[T] {
vec
} else {
let alloc = capacity * mem::nonzero_size_of::<T>();
let ptr = malloc_raw(alloc + mem::size_of::<Vec<()>>()) as *mut Vec<()>;
let size = alloc + mem::size_of::<Vec<()>>();
if alloc / mem::nonzero_size_of::<T>() != capacity || size < alloc {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this check incorrect, because the header is included in alloc?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The header comes from size_of::<Vec<()>>. This is the same code used
for overflow handling elsewhere. cc @thestinger

On Mon, Nov 11, 2013 at 6:23 AM, Huon Wilson notifications@gh.neting.ccwrote:

In src/libstd/vec.rs:

@@ -186,7 +186,11 @@ pub fn with_capacity(capacity: uint) -> ~[T] {
vec
} else {
let alloc = capacity * mem::nonzero_size_of::();

  •        let ptr = malloc_raw(alloc + mem::size_of::<Vec<()>>()) as *mut Vec<()>;
    
  •        let size = alloc + mem::size_of::<Vec<()>>();
    
  •        if alloc / mem::nonzero_size_of::<T>() != capacity || size < alloc {
    

Isn't this check incorrect, because the header is included in alloc?


Reply to this email directly or view it on GitHubhttps://github.com//pull/10417/files#r7556668
.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, of course... it's alloc / ... not size / ....

@bill-myers
Copy link
Contributor

@cmr Division is slow (it may take 20-100x the time of other arithmetic ops depending on CPU), so it seems a bad idea to use it.

Why not just use checked_mul() and then checked_add() which are generally far faster, and also make the intent totally clear?

@emberian
Copy link
Member Author

@thestinger ? I imagine it's because rust simply didn't have checked_*

On Mon, Nov 11, 2013 at 8:39 AM, bill-myers notifications@gh.neting.ccwrote:

@cmr https://github.com/cmr Division is slow (it may take 20-100x the
time of other arithmetic ops depending on CPU), so it seems a bad idea to
use it.

Why not just use checked_add() and checked_mul(), which are generally far
faster?


Reply to this email directly or view it on GitHubhttps://github.com//pull/10417#issuecomment-28200053
.

bors added a commit that referenced this pull request Nov 11, 2013
@bors bors closed this Nov 11, 2013
@bors bors merged commit a46b2b8 into rust-lang:master Nov 11, 2013
@huonw
Copy link
Member

huonw commented Nov 11, 2013

@bill-myers the division is by a compile-time constant, so it will almost certainly be optimised to some combination of a shift and a multiplication (but only when optimisations are on).

@bill-myers
Copy link
Contributor

@huonw It's still going to be less efficient, since optimizing a division by non-power-of-two constant to a multiplication is not that easy (that is, you need a multiplication by multiplicative inverse and a loop, or an extending multiplication, shifts and other tricks); with checked_mul() you just check the carry flag or that the high part of extended multiplication is 0

@huonw
Copy link
Member

huonw commented Nov 12, 2013

@bill-myers that is true (i.e. it becomes a short series of multiplications + shifts... in fact, just checking now, LLVM optimises n / 3 to a single multiplication with a single shift), but the context here is the following operation is either an allocation or task failure, either of which are relatively expensive (and optimising in the case of task failure is certainly not relevant).

flip1995 pushed a commit to flip1995/rust that referenced this pull request Apr 23, 2023
Suppress the triggering of some lints in derived structures

Fixes rust-lang#10185
Fixes rust-lang#10417

For `integer_arithmetic`, `arithmetic_side_effects` and `shadow_reuse`.

* ~~Not sure how to test these use-cases so feel free to point any method or any related PR.~~

---

changelog: FP: [`integer_arithmetic`], [`arithmetic_side_effects`]: No longer lint inside proc macros
[rust-lang#10203](rust-lang/rust-clippy#10203)
<!-- changelog_checked -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

integer overflow in vec::with_capacity
4 participants