Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# CPython-suite gap: test_mmap's exhaustive find/rfind sweep (test_find_end,
# test_rfind) never uses an empty pattern — its list is
# [b"o", b"on", b"two", b"ones", b"s"] — so the near-end answer an empty needle
# owes is untested there. The oversized-needle cases below are covered by that
# sweep, and are kept only to hold the two halves of one bound together.
# parity-tests reason: test_mmap is not in the suite gate (still 3 failures and
# 21 errors), so nothing in the vendored suite protects either half today.

"""`mmap.find`/`rfind` over a span that cannot hold the needle report -1.

The scan's upper bound is `span - len(needle)`. Clamping that subtraction at
zero still leaves one index to try, and reading a needle-sized window there
runs off the end of a shorter span, so the interpreter aborts instead of
answering.

The empty needle is the boundary in the other direction, and it is the half
with no oracle in the vendored suite: it matches at the near end of the span —
`start` for `find`, `end` for `rfind` — where a shared "empty or inverted"
guard would fold it into -1 along with the spans that really have no room.

Every case pins the value rather than the absence of a panic, so the fixture
keeps its meaning once the abort is gone.
"""

import mmap

m = mmap.mmap(-1, 4)
m[:] = b"abca"

# The empty needle matches at the near end of the span.
assert m.find(b"") == 0, m.find(b"")
assert m.rfind(b"") == 4, m.rfind(b"")
assert m.find(b"", 2) == 2, m.find(b"", 2)
assert m.rfind(b"", 0, 2) == 2, m.rfind(b"", 0, 2)
assert m.find(b"", 4) == 4, m.find(b"", 4)
assert m.rfind(b"", 4) == 4, m.rfind(b"", 4)
assert m.find(b"", -2) == 2, m.find(b"", -2)
assert m.rfind(b"", -2) == 4, m.rfind(b"", -2)

# An inverted span holds nothing at all, not even the empty needle.
assert m.find(b"", 3, 1) == -1, m.find(b"", 3, 1)
assert m.rfind(b"", 3, 1) == -1, m.rfind(b"", 3, 1)

# The needle is longer than the whole map.
assert m.find(b"abcab") == -1, m.find(b"abcab")
assert m.rfind(b"abcab") == -1, m.rfind(b"abcab")

# The needle fits the map but not the requested span.
assert m.find(b"abc", 2) == -1, m.find(b"abc", 2)
assert m.rfind(b"abc", 2) == -1, m.rfind(b"abc", 2)
assert m.find(b"abc", 0, 2) == -1, m.find(b"abc", 0, 2)
assert m.rfind(b"abc", 0, 2) == -1, m.rfind(b"abc", 0, 2)

# A span exactly the needle's length still has one candidate.
assert m.find(b"bc", 1, 3) == 1, m.find(b"bc", 1, 3)
assert m.rfind(b"bc", 1, 3) == 1, m.rfind(b"bc", 1, 3)

# The ordinary answers, so a bound that returns -1 too eagerly is caught too.
assert m.find(b"a") == 0, m.find(b"a")
assert m.rfind(b"a") == 3, m.rfind(b"a")
assert m.find(b"ca") == 2, m.find(b"ca")
assert m.find(b"a", -1) == 3, m.find(b"a", -1)
assert m.find(b"abca", -10) == 0, m.find(b"abca", -10)

m.close()

# A one-byte map is the smallest span an oversized needle can overrun.
one = mmap.mmap(-1, 1)
one[:] = b"a"
assert one.find(b"ab") == -1, one.find(b"ab")
assert one.rfind(b"ab") == -1, one.rfind(b"ab")
assert one.find(b"") == 0, one.find(b"")
assert one.rfind(b"") == 1, one.rfind(b"")
assert one.find(b"a") == 0, one.find(b"a")
one.close()

print("OK")
16 changes: 8 additions & 8 deletions pyre/pyre-interpreter/src/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub(crate) unsafe fn backing_exports_incref(buffer: &pyre_object::buffer::Buffer
// releasing the view it came from would let `close`/`resize`
// unmap while this one still reads the mapping.
let _ = w_obj;
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
if crate::module::mmap::interp_mmap::is_mmap(*w_obj) {
crate::module::mmap::interp_mmap::mmap_exports_incref(*w_obj);
}
Expand Down Expand Up @@ -114,7 +114,7 @@ pub(crate) unsafe fn buffer_export_incref(obj: PyObjectRef) -> bool {
pyre_object::memoryview::w_memoryview_exports_incref(obj);
return true;
}
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
if crate::module::mmap::interp_mmap::is_mmap(obj) {
crate::module::mmap::interp_mmap::mmap_exports_incref(obj);
return true;
Expand All @@ -137,7 +137,7 @@ pub(crate) unsafe fn buffer_export_decref(obj: PyObjectRef) {
} else if pyre_object::memoryview::is_w_memoryview(obj) {
pyre_object::memoryview::w_memoryview_exports_decref(obj);
} else {
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
crate::module::mmap::interp_mmap::mmap_exports_decref(obj);
}
}
Expand Down Expand Up @@ -394,7 +394,7 @@ pub(crate) fn w_memoryview_new_simple_with_owner(

/// Build the `W_MMap.readbuf_w`/`writebuf_w` view: one contiguous external
/// byte window whose owner remains the mmap object.
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
unsafe fn w_memoryview_new_mmap(
w_obj: PyObjectRef,
address: usize,
Expand Down Expand Up @@ -657,7 +657,7 @@ fn w_memoryview_new_with_flags_impl(
// keeps its zero-copy window and derived geometry.
return Ok(w_memoryview_new_derived(w_obj, |v| v.clone()));
}
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
if let Some(view) = crate::module::mmap::interp_mmap::mmap_buffer_view(w_obj) {
let (address, length, readonly) = view?;
return Ok(w_memoryview_new_mmap(w_obj, address, length, readonly));
Expand Down Expand Up @@ -1781,7 +1781,7 @@ fn memoryview_repr(args: &[PyObjectRef]) -> Result<PyObjectRef, crate::PyError>

/// Drop an mmap-backed view's export directly, bypassing any Python-callable
/// release. Returns `true` when it handled an mmap backing.
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
unsafe fn release_external_backing(backing: PyObjectRef) -> bool {
if crate::module::mmap::interp_mmap::is_mmap(backing) {
unsafe { crate::module::mmap::interp_mmap::mmap_exports_decref(backing) };
Expand All @@ -1790,7 +1790,7 @@ unsafe fn release_external_backing(backing: PyObjectRef) -> bool {
false
}

#[cfg(not(all(unix, not(feature = "sandbox"))))]
#[cfg(not(all(any(unix, windows), not(feature = "sandbox"))))]
unsafe fn release_external_backing(_backing: PyObjectRef) -> bool {
false
}
Expand Down Expand Up @@ -15547,7 +15547,7 @@ unsafe fn fileio_writebuf(
obj,
));
}
#[cfg(all(unix, not(feature = "sandbox")))]
#[cfg(all(any(unix, windows), not(feature = "sandbox")))]
if let Some(view) = crate::module::mmap::interp_mmap::mmap_buffer_view(obj) {
let (address, length, readonly) = view?;
if !readonly {
Expand Down
6 changes: 5 additions & 1 deletion pyre/pyre-interpreter/src/jit_fnaddr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,11 @@ pub fn jit_trace_fnaddrs() -> Vec<(&'static str, i64)> {
// module itself is gated at `module/mod.rs:80`; the row has to carry
// both or a sandbox build on Linux satisfies `unix` with the module
// configured out.
#[cfg(all(unix, not(target_arch = "wasm32"), not(feature = "sandbox")))]
#[cfg(all(
any(unix, windows),
not(target_arch = "wasm32"),
not(feature = "sandbox")
))]
{
let mmap_type: fn() -> pyre_object::PyObjectRef =
crate::module::mmap::interp_mmap::mmap_type;
Expand Down
Loading
Loading