Add precondition checks to ptr::offset, ptr::add, ptr::sub · qinheping/verify-rust-std@8293e74 (original) (raw)

`@@ -395,6 +395,36 @@ impl<T: ?Sized> *const T {

`

395

395

`where

`

396

396

`T: Sized,

`

397

397

`{

`

``

398

`+

#[inline]

`

``

399

`+

const fn runtime_offset_nowrap(this: *const (), count: isize, size: usize) -> bool {

`

``

400

`+

#[inline]

`

``

401

`+

fn runtime(this: *const (), count: isize, size: usize) -> bool {

`

``

402

`` +

// We know size <= isize::MAX so the as cast here is not lossy.

``

``

403

`+

let Some(byte_offset) = count.checked_mul(size as isize) else {

`

``

404

`+

return false;

`

``

405

`+

};

`

``

406

`+

let (_, overflow) = this.addr().overflowing_add_signed(byte_offset);

`

``

407

`+

!overflow

`

``

408

`+

}

`

``

409

+

``

410

`+

const fn comptime(_: *const (), _: isize, _: usize) -> bool {

`

``

411

`+

true

`

``

412

`+

}

`

``

413

+

``

414

`+

// We can use const_eval_select here because this is only for UB checks.

`

``

415

`+

intrinsics::const_eval_select((this, count, size), comptime, runtime)

`

``

416

`+

}

`

``

417

+

``

418

`+

ub_checks::assert_unsafe_precondition!(

`

``

419

`+

check_language_ub,

`

``

420

`+

"ptr::offset requires the address calculation to not overflow",

`

``

421

`+

(

`

``

422

`+

this: *const () = self as *const (),

`

``

423

`+

count: isize = count,

`

``

424

`+

size: usize = size_of::(),

`

``

425

`+

) => runtime_offset_nowrap(this, count, size)

`

``

426

`+

);

`

``

427

+

398

428

`` // SAFETY: the caller must uphold the safety contract for offset.

``

399

429

`unsafe { intrinsics::offset(self, count) }

`

400

430

`}

`

`@@ -726,7 +756,6 @@ impl<T: ?Sized> *const T {

`

726

756

`true

`

727

757

`}

`

728

758

``

729

``

`-

#[allow(unused_unsafe)]

`

730

759

` intrinsics::const_eval_select((this, origin), comptime, runtime)

`

731

760

`}

`

732

761

``

`@@ -858,6 +887,34 @@ impl<T: ?Sized> *const T {

`

858

887

`where

`

859

888

`T: Sized,

`

860

889

`{

`

``

890

`+

#[inline]

`

``

891

`+

const fn runtime_add_nowrap(this: *const (), count: usize, size: usize) -> bool {

`

``

892

`+

#[inline]

`

``

893

`+

fn runtime(this: *const (), count: usize, size: usize) -> bool {

`

``

894

`+

let Some(byte_offset) = count.checked_mul(size) else {

`

``

895

`+

return false;

`

``

896

`+

};

`

``

897

`+

let (_, overflow) = this.addr().overflowing_add(byte_offset);

`

``

898

`+

byte_offset <= (isize::MAX as usize) && !overflow

`

``

899

`+

}

`

``

900

+

``

901

`+

const fn comptime(_: *const (), _: usize, _: usize) -> bool {

`

``

902

`+

true

`

``

903

`+

}

`

``

904

+

``

905

`+

intrinsics::const_eval_select((this, count, size), comptime, runtime)

`

``

906

`+

}

`

``

907

+

``

908

`+

ub_checks::assert_unsafe_precondition!(

`

``

909

`+

check_language_ub,

`

``

910

`+

"ptr::add requires that the address calculation does not overflow",

`

``

911

`+

(

`

``

912

`+

this: *const () = self as *const (),

`

``

913

`+

count: usize = count,

`

``

914

`+

size: usize = size_of::(),

`

``

915

`+

) => runtime_add_nowrap(this, count, size)

`

``

916

`+

);

`

``

917

+

861

918

`` // SAFETY: the caller must uphold the safety contract for offset.

``

862

919

`unsafe { intrinsics::offset(self, count) }

`

863

920

`}

`

`@@ -936,14 +993,41 @@ impl<T: ?Sized> *const T {

`

936

993

`where

`

937

994

`T: Sized,

`

938

995

`{

`

``

996

`+

#[inline]

`

``

997

`+

const fn runtime_sub_nowrap(this: *const (), count: usize, size: usize) -> bool {

`

``

998

`+

#[inline]

`

``

999

`+

fn runtime(this: *const (), count: usize, size: usize) -> bool {

`

``

1000

`+

let Some(byte_offset) = count.checked_mul(size) else {

`

``

1001

`+

return false;

`

``

1002

`+

};

`

``

1003

`+

byte_offset <= (isize::MAX as usize) && this.addr() >= byte_offset

`

``

1004

`+

}

`

``

1005

+

``

1006

`+

const fn comptime(_: *const (), _: usize, _: usize) -> bool {

`

``

1007

`+

true

`

``

1008

`+

}

`

``

1009

+

``

1010

`+

intrinsics::const_eval_select((this, count, size), comptime, runtime)

`

``

1011

`+

}

`

``

1012

+

``

1013

`+

ub_checks::assert_unsafe_precondition!(

`

``

1014

`+

check_language_ub,

`

``

1015

`+

"ptr::sub requires that the address calculation does not overflow",

`

``

1016

`+

(

`

``

1017

`+

this: *const () = self as *const (),

`

``

1018

`+

count: usize = count,

`

``

1019

`+

size: usize = size_of::(),

`

``

1020

`+

) => runtime_sub_nowrap(this, count, size)

`

``

1021

`+

);

`

``

1022

+

939

1023

`if T::IS_ZST {

`

940

1024

`// Pointer arithmetic does nothing when the pointee is a ZST.

`

941

1025

`self

`

942

1026

`} else {

`

943

1027

`` // SAFETY: the caller must uphold the safety contract for offset.

``

944

1028

`` // Because the pointee is not a ZST, that means that count is

``

945

1029

`` // at most isize::MAX, and thus the negation cannot overflow.

``

946

``

`-

unsafe { self.offset((count as isize).unchecked_neg()) }

`

``

1030

`+

unsafe { intrinsics::offset(self, intrinsics::unchecked_sub(0, count as isize)) }

`

947

1031

`}

`

948

1032

`}

`

949

1033

``