-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Detect expressions where size_of::<T>()
is an argument to ptr::copy/ptr::copy_nonoverlapping and T
is the type of the pointee.
#6381
Comments
Should this also check for std::mem::size_of_val? |
Can I take this issue? I'm not sure how complex it is but I feel like I can probably do it |
@nico-abram Yeah sure go ahead. Make sure you take a look at the documentation in the |
I have a basic form of this that just checks for the count parameter being size_of or size_of_val, or a series of multiplications which involves size_of or size_of_val (With the pointee type of the copy) seemingly working. Is checking for multiplications fine? Should I also check for any other binary expressions? (Like divisions) |
It's probably dubious to use it in any case but you should definitely check for |
Yeah that's the first case I looked for. This is what I have right now for checking that: fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<TyM<'tcx>> {
match &expr.kind {
ExprKind::Call(ref count_func, func_args) => {
if_chain! {
if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::MEM_SIZE_OF) || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL);
then {
cx.typeck_results().node_substs(count_func.hir_id).types().next()
} else {
None
}
}
},
ExprKind::Binary(op, left, right) if &BinOpKind::Mul == &op.node || &BinOpKind::Div == &op.node => {
get_size_of_ty(cx, &*left).or_else(|| get_size_of_ty(cx, &*right))
},
_ => None,
}
} With these test cases: const SIZE: usize = 128;
const HALF_SIZE: usize = SIZE / 2;
const DOUBLE_SIZE: usize = SIZE * 2;
let mut x = [2u8; SIZE];
let mut y = [2u8; SIZE];
// Count is size_of (Should trigger the lint)
unsafe { copy_nonoverlapping::<u8>(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>()) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0])) };
// Count expression involving multiplication of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * SIZE) };
// Count expression involving nested multiplications of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * HALF_SIZE * 2) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), HALF_SIZE * size_of_val(&x[0]) * 2) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE * HALF_SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * HALF_SIZE * 2) };
// Count expression involving divisions of size_of (Should trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * DOUBLE_SIZE / 2) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE / 2 * size_of_val(&x[0])) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), DOUBLE_SIZE * size_of::<u8>() / 2) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&x[0]) * DOUBLE_SIZE / 2) };
// No size_of calls (Should not trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), SIZE) };
// Different types for pointee and size_of (Should not trigger the lint)
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>() / 2 * SIZE) };
unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of::<u16>() / 2 * SIZE) };
unsafe { copy(x.as_ptr(), y.as_mut_ptr(), size_of_val(&0u16) / 2 * SIZE) }; I think I'll open a PR soon |
What it does
What does this lint do?
Detects expressions where
size_of::<T>()
is used in the size argument toptr::copy
/ptr::copy_nonoverlapping
/ etc, whereT
is the type of the pointers passed in as arguments.It should not fire if
T
is a different type, as it's common and correct to use this for byte copies.Note:
This can happen because the docs for this function say:
Which is slightly inaccurate, since
memcpy
takes the size in bytes despite accepting pointers of any type (in C, anyway — in C++ you'll have to explicitly cast to coerce tovoid*
).(TODO(me): file a docs bug about this)rust-lang/rust#79430Categories (optional)
clippy::correctness
probably.What is the advantage of the recommended code over the original code
copy_nonoverlapping
already incorporatessize_of::<T>()
into the offset calculation, so it avoids reads/writes out of bounds, probably.Drawbacks
It's possible the original code isn't wrong, but if so it would be highly confusing.
Example
Assuming
a
andb
are pointers toT
:Could be written as:
The text was updated successfully, but these errors were encountered: