Skip to content

Avoid unreachable integer underflow check in CStr::count_bytes() - #163005

Merged
rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
Darksonn:cstr-count-bytes
Sep 21, 2026
Merged

rust-bors[bot] merged 1 commit into
rust-lang:mainfrom
Darksonn:cstr-count-bytes

Conversation

@Darksonn

@Darksonn Darksonn commented Sep 19, 2026 •

Copy link
Copy Markdown
Member

Follow up to #162930.

cc @clarfonthey, @hanna-kruppe

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-libs Relevant to the library team, which will review and decide on the PR/issue. labels Sep 19, 2026
@rustbot

rustbot commented Sep 19, 2026

Copy link
Copy Markdown
Collaborator

r? @Mark-Simulacrum

rustbot has assigned @Mark-Simulacrum.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: libs
  • libs expanded to 12 candidates
  • Random selection from JohnTitor, LawnGnome, Mark-Simulacrum, clarfonthey, jhpratt

@hanna-kruppe

hanna-kruppe commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

This looks sound, but can you say a bit about what the benefit is? In #162912 you mentioned "A colleague of mine was trying to remove all panics in a Linux kernel driver" -- is this also intended to help eliminate an overflow check? I'm asking both because we should have something more reason than "we can" for using unsafe and because we should have a codegen test capturing that reason (or at least a reasonable proxy for it).

@Zalathar

This comment was marked as resolved.

@clarfonthey

Copy link
Copy Markdown
Contributor

FWIW, the technically-valid functionality of NonZero - 1 is something I've used a lot and have wanted to add a dedicated method for. But in this case it wouldn't help since we're dealing with slice lengths.

@Darksonn

Darksonn commented Sep 19, 2026 •

Copy link
Copy Markdown
Member Author

This is not some random function. CStr::from_ptr(ptr).count_bytes() is the way to invoke strlen from Rust code.

I do in general think it's worth it for core vocabulary types such as CStr to avoid useless panicking paths. As I mentioned in #162912, one reason is that they make it easier to find bugs in your code by listing all places where the final binary calls a panicking function.

I suppose there is a safe implementation:

self.to_bytes().len()

Of course since to_bytes_with_nul already contains an equivalent hint::assert_unchecked, it's not any safer.

@Darksonn Darksonn changed the title Avoid integer underflow in CStr::count_bytes() Avoid unreachable integer underflow check in CStr::count_bytes() Sep 19, 2026
@Darksonn

Copy link
Copy Markdown
Member Author

Well, the codegen test is a good call. Interestingly enough, neither computing the length .unchecked_sub(1) or via self.to_bytes().len() lets LLVM know that the length is less than isize::MAX, and the tests/codegen-llvm/cstr-len-plus-one.rs test added by #162930 fails if modified to use s.count_bytes() instead of s.bytes().count().

Of course the isize::MAX bound is a separate concern from the subtraction underflow.

@hanna-kruppe

Copy link
Copy Markdown
Contributor

I do in general think it's worth it for core vocabulary types such as CStr to avoid useless panicking paths.

I am sympathetic to this, as long as it doesn't impose undue maintenance burden. But for overflow checks in particular, I'm a bit skeptical because we don't ship the standard library with overflow checks enabled. It's possible to build the standard library like this (and maybe it'll become more common as build-std matures), but today it's a rare configuration that is rarely benchmarked and rarely optimized for.

I guess this PR and earlier ones are evidence that this is starting to change, but today there's probably many other places that have overflow checks. So if this is going to lead to a steady stream of PRs to find and eliminate such overflow checks, that seems like a bigger change that we should have a discussion in the libs team about at some point (e.g., how much do we care about this vs. maintainability, how do we catch regressions and prioritize them, should we have tests and CI jobs that exercise this directly, etc.).

@Darksonn

Copy link
Copy Markdown
Member Author

Huh. I had not realized that he ran into this overflow check due to how Linux builds core. I thought this was how it worked for everyone. Back when I was working on the target modifiers RFC, it was explained to me that the stdlib has a way to inherit overflow checks, and that this is why -Coverflow-checks works without build-std.

@hanna-kruppe

hanna-kruppe commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

Sorry for my sloppy phrasing, the standard library does have ways to inherit overflow checks, but they're opt-in and fairly targeted (see overflow_checks intrinsic, and I think there's also something magic about using the primitive's Add etc. impls in generic code also used by impl Add for $Int etc). By volume, most arithmetic operators under library/* do not inherit overflow checks, including #[inline] methods like CStr::count_bytes: https://rust.godbolt.org/z/Mr8frq5cc

@Darksonn

Copy link
Copy Markdown
Member Author

I opened a discussion about overflow checks on zulip:

#t-libs > Future of overflow checks in the standard library @ 💬

@Mark-Simulacrum Mark-Simulacrum left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

pub const fn count_bytes(&self) -> usize {
self.inner.len() - 1
// SAFETY: This length includes the nul-terminator, so it's at least one.
unsafe { self.inner.len().unchecked_sub(1) }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think I'm OK with this. An alternative could be to try to_bytes().len(), which technically should/could avoid the panic (to_bytes_with_nul, which it calls, has an assert_unchecked(!empty)).

But the added indirection doesn't seem like it has a ton of value and probably hurts optimization (at least in terms of how long it takes to compile).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

assert_unchecked does actually hurt a lot more than it helps, so, the unchecked sub should just help convey the length information properly, honestly.

@rust-bors

rust-bors Bot commented Sep 20, 2026

Copy link
Copy Markdown
Contributor

📌 Commit ae2ecf5 has been approved by Mark-Simulacrum

It is now in the queue for this repository.

@rust-bors rust-bors Bot added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 20, 2026
Zalathar added a commit to Zalathar/rust that referenced this pull request Sep 21, 2026
…Simulacrum

Avoid unreachable integer underflow check in `CStr::count_bytes()`

Follow up to rust-lang#162930.

cc @clarfonthey, @hanna-kruppe
rust-bors Bot pushed a commit that referenced this pull request Sep 21, 2026
Rollup of 15 pull requests

Successful merges:

 - #161051 (When error from local macro, include macro def span)
 - #162750 (add case mapping fast paths for Latin-1)
 - #162831 (Do not continue past `rustc_resolve` when encountering duplicated items)
 - #162835 (rustdoc: account for nested parens and split text events in bare urls lint)
 - #163044 (Report runtime range endpoints for runtime values)
 - #163062 (Use x30 register name with LLVM 23+)
 - #158102 (When compiling without a specified `--edition`, emit a note)
 - #162984 (Windows: don't error if `access_mode` is set on `OpenOptions`)
 - #163005 (Avoid unreachable integer underflow check in `CStr::count_bytes()`)
 - #163019 (Prepare for the introduction of forced keywords (`k#`))
 - #163020 (Dir: fix fallback impl for remove_dir)
 - #163047 (Use verbose suggestion for `mut binding` instead of `&mut binding`)
 - #163075 (Fix ArgAttributes mismatches in ABI UI tests for LoongArch64 and RiscV64)
 - #163079 (enable `f128` from `u64`/`i64` test)
 - #163082 (Remove `TypeChecker::root_cx`)
JonathanBrouwer added a commit to JonathanBrouwer/rust that referenced this pull request Sep 21, 2026
…Simulacrum

Avoid unreachable integer underflow check in `CStr::count_bytes()`

Follow up to rust-lang#162930.

cc @clarfonthey, @hanna-kruppe
rust-bors Bot pushed a commit that referenced this pull request Sep 21, 2026
…uwer

Rollup of 18 pull requests

Successful merges:

 - #161051 (When error from local macro, include macro def span)
 - #161629 (Streamline `StateDiffCollector`)
 - #162750 (add case mapping fast paths for Latin-1)
 - #162831 (Do not continue past `rustc_resolve` when encountering duplicated items)
 - #162835 (rustdoc: account for nested parens and split text events in bare urls lint)
 - #162952 (Depend on lockfiles to prevent GC of the current session)
 - #163044 (Report runtime range endpoints for runtime values)
 - #163062 (Use x30 register name with LLVM 23+)
 - #158102 (When compiling without a specified `--edition`, emit a note)
 - #162939 (Declare multi-kind constants for MacroKinds)
 - #162984 (Windows: don't error if `access_mode` is set on `OpenOptions`)
 - #163005 (Avoid unreachable integer underflow check in `CStr::count_bytes()`)
 - #163019 (Prepare for the introduction of forced keywords (`k#`))
 - #163020 (Dir: fix fallback impl for remove_dir)
 - #163047 (Use verbose suggestion for `mut binding` instead of `&mut binding`)
 - #163075 (Fix ArgAttributes mismatches in ABI UI tests for LoongArch64 and RiscV64)
 - #163079 (enable `f128` from `u64`/`i64` test)
 - #163082 (Remove `TypeChecker::root_cx`)
rust-bors Bot pushed a commit that referenced this pull request Sep 21, 2026
…uwer

Rollup of 19 pull requests

Successful merges:

 - #161051 (When error from local macro, include macro def span)
 - #161629 (Streamline `StateDiffCollector`)
 - #162750 (add case mapping fast paths for Latin-1)
 - #162831 (Do not continue past `rustc_resolve` when encountering duplicated items)
 - #162835 (rustdoc: account for nested parens and split text events in bare urls lint)
 - #162952 (Depend on lockfiles to prevent GC of the current session)
 - #163044 (Report runtime range endpoints for runtime values)
 - #163062 (Use x30 register name with LLVM 23+)
 - #163091 (miri subtree update)
 - #162939 (Declare multi-kind constants for MacroKinds)
 - #162984 (Windows: don't error if `access_mode` is set on `OpenOptions`)
 - #163005 (Avoid unreachable integer underflow check in `CStr::count_bytes()`)
 - #163019 (Prepare for the introduction of forced keywords (`k#`))
 - #163020 (Dir: fix fallback impl for remove_dir)
 - #163041 (Be more explicit on suggestion type without changing how they are rendered)
 - #163047 (Use verbose suggestion for `mut binding` instead of `&mut binding`)
 - #163075 (Fix ArgAttributes mismatches in ABI UI tests for LoongArch64 and RiscV64)
 - #163079 (enable `f128` from `u64`/`i64` test)
 - #163082 (Remove `TypeChecker::root_cx`)
@rust-bors
rust-bors Bot merged commit 67974b6 into rust-lang:main Sep 21, 2026
13 checks passed
@rustbot rustbot added this to the 1.100.0 milestone Sep 21, 2026
rust-bors Bot pushed a commit that referenced this pull request Sep 21, 2026
Rollup merge of #163005 - Darksonn:cstr-count-bytes, r=Mark-Simulacrum

Avoid unreachable integer underflow check in `CStr::count_bytes()`

Follow up to #162930.

cc @clarfonthey, @hanna-kruppe
pull Bot pushed a commit to xtqqczze/rust-lang-miri that referenced this pull request Sep 22, 2026
…uwer

Rollup of 19 pull requests

Successful merges:

 - rust-lang/rust#161051 (When error from local macro, include macro def span)
 - rust-lang/rust#161629 (Streamline `StateDiffCollector`)
 - rust-lang/rust#162750 (add case mapping fast paths for Latin-1)
 - rust-lang/rust#162831 (Do not continue past `rustc_resolve` when encountering duplicated items)
 - rust-lang/rust#162835 (rustdoc: account for nested parens and split text events in bare urls lint)
 - rust-lang/rust#162952 (Depend on lockfiles to prevent GC of the current session)
 - rust-lang/rust#163044 (Report runtime range endpoints for runtime values)
 - rust-lang/rust#163062 (Use x30 register name with LLVM 23+)
 - rust-lang/rust#163091 (miri subtree update)
 - rust-lang/rust#162939 (Declare multi-kind constants for MacroKinds)
 - rust-lang/rust#162984 (Windows: don't error if `access_mode` is set on `OpenOptions`)
 - rust-lang/rust#163005 (Avoid unreachable integer underflow check in `CStr::count_bytes()`)
 - rust-lang/rust#163019 (Prepare for the introduction of forced keywords (`k#`))
 - rust-lang/rust#163020 (Dir: fix fallback impl for remove_dir)
 - rust-lang/rust#163041 (Be more explicit on suggestion type without changing how they are rendered)
 - rust-lang/rust#163047 (Use verbose suggestion for `mut binding` instead of `&mut binding`)
 - rust-lang/rust#163075 (Fix ArgAttributes mismatches in ABI UI tests for LoongArch64 and RiscV64)
 - rust-lang/rust#163079 (enable `f128` from `u64`/`i64` test)
 - rust-lang/rust#163082 (Remove `TypeChecker::root_cx`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-libs Relevant to the library team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants