diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs
index c9111254082da..ae42ae3baf418 100644
--- a/library/core/src/ffi/c_str.rs
+++ b/library/core/src/ffi/c_str.rs
@@ -94,7 +94,7 @@ use crate::str;
 /// ```
 ///
 /// [str]: prim@str "str"
-#[derive(Hash)]
+#[derive(PartialEq, Eq, Hash)]
 #[stable(feature = "core_c_str", since = "1.64.0")]
 #[rustc_has_incoherent_inherent_impls]
 #[lang = "CStr"]
@@ -104,7 +104,6 @@ use crate::str;
 // want `repr(transparent)` but we don't want it to show up in rustdoc, so we hide it under
 // `cfg(doc)`. This is an ad-hoc implementation of attribute privacy.
 #[repr(transparent)]
-#[allow(clippy::derived_hash_with_manual_eq)]
 pub struct CStr {
     // FIXME: this should not be represented with a DST slice but rather with
     //        just a raw `c_char` along with some form of marker to make
@@ -678,15 +677,9 @@ impl CStr {
     }
 }
 
-#[stable(feature = "rust1", since = "1.0.0")]
-impl PartialEq for CStr {
-    #[inline]
-    fn eq(&self, other: &CStr) -> bool {
-        self.to_bytes().eq(other.to_bytes())
-    }
-}
-#[stable(feature = "rust1", since = "1.0.0")]
-impl Eq for CStr {}
+// `.to_bytes()` representations are compared instead of the inner `[c_char]`s,
+// because `c_char` is `i8` (not `u8`) on some platforms.
+// That is why this is implemented manually and not derived.
 #[stable(feature = "rust1", since = "1.0.0")]
 impl PartialOrd for CStr {
     #[inline]
diff --git a/library/core/tests/ffi.rs b/library/core/tests/ffi.rs
new file mode 100644
index 0000000000000..2b33fbd95f073
--- /dev/null
+++ b/library/core/tests/ffi.rs
@@ -0,0 +1 @@
+mod cstr;
diff --git a/library/core/tests/ffi/cstr.rs b/library/core/tests/ffi/cstr.rs
new file mode 100644
index 0000000000000..9bf4c21a9ab97
--- /dev/null
+++ b/library/core/tests/ffi/cstr.rs
@@ -0,0 +1,15 @@
+use core::ffi::CStr;
+
+#[test]
+fn compares_as_u8s() {
+    let a: &CStr = c"Hello!"; // Starts with ascii
+    let a_bytes: &[u8] = a.to_bytes();
+    assert!((..0b1000_0000).contains(&a_bytes[0]));
+
+    let b: &CStr = c"こんにちは!"; // Starts with non ascii
+    let b_bytes: &[u8] = b.to_bytes();
+    assert!((0b1000_0000..).contains(&b_bytes[0]));
+
+    assert_eq!(Ord::cmp(a, b), Ord::cmp(a_bytes, b_bytes));
+    assert_eq!(PartialOrd::partial_cmp(a, b), PartialOrd::partial_cmp(a_bytes, b_bytes));
+}
diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs
index 83a615fcd8be3..04aaa3f35b7ae 100644
--- a/library/core/tests/lib.rs
+++ b/library/core/tests/lib.rs
@@ -132,6 +132,7 @@ mod clone;
 mod cmp;
 mod const_ptr;
 mod convert;
+mod ffi;
 mod fmt;
 mod future;
 mod hash;