{
"package": "win-handles-sys",
"description": "Sabotages for win-handles-sys's duplicate_handle_with_access and Access, which were win-sync-sys's duplicate module until DI-14.8.3 moved them. Run with tools/run-sabotage.ps1; see tools/README-sabotage.md for the format.",
"notCoveredHere": "DuplicateHandle's failure other than for a mask Windows refuses is absent: it fails only when the process cannot get another handle, which no test can produce on demand. duplicate.rs says so at the definition. A handle closed twice is not injected for close_handle: the second close would act on a value another thread may have been given, and the sweep would be measuring that and not a test.",
"testArgs": ["-p", "win-handles-sys", "--lib", "--locked"],
"sabotages": [
{
"name": "WS-1+.5: the duplicate ignores the access asked for",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "The access is the whole point. A duplicate that gets none cannot be waited on, which the wait tests see, and cannot set, which the modify-state test sees.",
"find": [" DuplicateAccess::Chosen(mask) => (mask.bits(), 0),\n"],
"replace": [" DuplicateAccess::Chosen(_) => (0, 0),\n"]
},
{
"name": "WS-1+.5: the duplicate keeps the source's access",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "DUPLICATE_SAME_ACCESS (2) makes the call ignore the mask, so every duplicate would be a full-access clone: the narrowing, which is the reason for the function, silently not happening. The synchronize-only tests see it as a set that succeeds.",
"find": [" DuplicateAccess::Chosen(mask) => (mask.bits(), 0),\n"],
"replace": [" DuplicateAccess::Chosen(mask) => (mask.bits(), DUPLICATE_SAME_ACCESS),\n"]
},
{
"name": "WS-1+.5: the duplicate is inheritable",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "A handle that a child process inherits is one the caller did not decide to hand over; the documented contract is that it is not inheritable.",
"find": [" Inheritance::NotInheritable => FALSE,\n"],
"replace": [" Inheritance::NotInheritable => TRUE,\n"]
},
{
"name": "WS-1+.5: a refused duplicate is reported as success",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "The error path is reached by a mask Windows refuses. Swallowing it would hand back a handle that is not one.",
"find": [" if ok == FALSE {\n return Err(io::Error::last_os_error());\n"],
"replace": [" if false {\n return Err(io::Error::last_os_error());\n"]
},
{
"name": "WS-1+.5: combining access masks intersects them",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "A mask built with | that comes out as the common bits asks for nothing: the modify-state-and-synchronize duplicates cannot set or cannot wait.",
"find": [" Self(self.0 | other.0)\n"],
"replace": [" Self(self.0 & other.0)\n"]
},
{
"name": "WS-1+.5: the SYNCHRONIZE constant is the modify-state right",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "The named right the wait-only handle is built from. Wrong, the handle can set and cannot wait.",
"find": [" pub const SYNCHRONIZE: Self = Self(SYNCHRONIZATION_SYNCHRONIZE);\n"],
"replace": [" pub const SYNCHRONIZE: Self = Self(EVENT_MODIFY_STATE);\n"]
},
{
"name": "CONTROL: the duplicate's failure is tested against 0 rather than FALSE",
"file": "src/duplicate.rs",
"expect": "survives",
"why": "The manifest checking itself on the new module: FALSE is 0, so this is an equivalent change on code every duplicate runs. If it comes back caught, something is reacting to text rather than behaviour.",
"find": [" if ok == FALSE {\n return Err(io::Error::last_os_error());\n"],
"replace": [" if ok == 0 {\n return Err(io::Error::last_os_error());\n"]
},
{
"name": "DI-14.8.4: the query reports no flags",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "What the call wrote is the answer; a query that always said none would report every inheritable handle as not.",
"find": [" Ok(HandleFlags(flags))\n"],
"replace": [" Ok(HandleFlags(0))\n"]
},
{
"name": "DI-14.8.4: the inherit flag is the protect bit",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "Each flag is the Win32 bit it is named for; swapped, an inheritable handle would read as protected.",
"find": [" pub const INHERIT: Self = Self(HANDLE_FLAG_INHERIT);\n"],
"replace": [" pub const INHERIT: Self = Self(HANDLE_FLAG_PROTECT_FROM_CLOSE);\n"]
},
{
"name": "DI-14.8.4: the protect flag is the inherit bit",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "The same for the other flag.",
"find": [" pub const PROTECT_FROM_CLOSE: Self = Self(HANDLE_FLAG_PROTECT_FROM_CLOSE);\n"],
"replace": [" pub const PROTECT_FROM_CLOSE: Self = Self(HANDLE_FLAG_INHERIT);\n"]
},
{
"name": "DI-14.8.4: containing is overlapping",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "A set contains another only if it has every flag of it; a set that overlaps it has some.",
"find": [" self.0 & other.0 == other.0\n"],
"replace": [" self.0 & other.0 != 0\n"]
},
{
"name": "DI-14.8.4: combining flags intersects them",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "Combining is a union, as for the other masks of this workspace.",
"find": [" Self(self.0 | other.0)\n"],
"replace": [" Self(self.0 & other.0)\n"]
},
{
"name": "DI-14.8.4: BLIND SPOT: a refused query is reported as flags",
"file": "src/handle_information.rs",
"expect": "survives",
"why": "GetHandleInformation fails only for a handle that is not open, which AsHandle rules out, so no test can make it fail. The definition says so.",
"find": [" if ok == FALSE {\n return Err(io::Error::last_os_error());\n }\n Ok(HandleFlags(flags))\n"],
"replace": [" if ok == 12345 {\n return Err(io::Error::last_os_error());\n }\n Ok(HandleFlags(flags))\n"]
},
{
"name": "WH-1.2: the mask is not passed to the call",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "The mask is what names the flags to change: passed as zero, nothing is ever set or cleared.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), 0, flags.0) };"]
},
{
"name": "WH-1.2: the flags are not passed to the call",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "The flags give each flag in the mask its new value: passed as zero, a flag can only be cleared, never set.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, 0) };"]
},
{
"name": "WH-1.2: the mask and the flags are passed in each other's places",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "The call takes the mask before the flags; swapped, a flag is set only if it is also what is to be cleared, and a clear changes nothing.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), flags.0, mask.0) };"]
},
{
"name": "WH-1.2: a refused call is reported as success",
"file": "src/handle_information.rs",
"expect": "caught",
"why": "A failure of the call is its error: a handle the platform refuses, which a test can hold as the current process's pseudo-handle, must not read as set.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };", " if ok == FALSE {"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };", " if false {"]
},
{
"name": "WH-1.2 control: only the flags in the mask are passed as flags",
"file": "src/handle_information.rs",
"expect": "survives",
"why": "A control: the platform applies flags & mask, so passing that is the same observable by another spelling, which no test can tell apart. A sweep that reports it caught is reporting everything caught and proves nothing.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0 & mask.0) };"]
},
{
"name": "WH-1.2: BLIND SPOT: a bit Windows does not name is narrowed away before the call",
"file": "src/handle_information.rs",
"expect": "survives",
"why": "The crate does not narrow what the call is given (HandleFlags), but the platform accepts a bit it does not name and gives it no effect, so a narrowing before the call looks the same from outside. The claim is documented and measured (WH-D-3) and cannot be pinned by a test.",
"find": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0, flags.0) };"],
"replace": [" let ok = unsafe { SetHandleInformation(handle.as_handle().as_raw_handle(), mask.0 & 3, flags.0 & 3) };"]
},
{
"name": "WH-1.3: the answer is inverted",
"file": "src/compare.rs",
"expect": "caught",
"why": "TRUE is that the two handles name one object; read the other way, every duplicate is a different object and every pair of different ones is the same.",
"find": [" same != FALSE"],
"replace": [" same == FALSE"]
},
{
"name": "WH-1.3: every pair is the same object",
"file": "src/compare.rs",
"expect": "caught",
"why": "A comparison that cannot say no cannot tell a handle from another object's.",
"find": [" same != FALSE"],
"replace": [" let _ = same;", " true"]
},
{
"name": "WH-1.3: no pair is the same object",
"file": "src/compare.rs",
"expect": "caught",
"why": "A comparison that cannot say yes cannot find a duplicate, nor a handle to the current process.",
"find": [" same != FALSE"],
"replace": [" let _ = same;", " false"]
},
{
"name": "WH-1.3: the first handle is compared with itself",
"file": "src/compare.rs",
"expect": "caught",
"why": "The second handle is the other half of the question: with it dropped, every handle is the same object as whatever it is asked about.",
"find": [" second.as_handle().as_raw_handle(),"],
"replace": [" first.as_handle().as_raw_handle(),"]
},
{
"name": "WH-1.3 control: the call's result is tested against a literal zero",
"file": "src/compare.rs",
"expect": "survives",
"why": "A control: FALSE is zero, so the same test by another spelling, which no test can tell apart. A sweep that reports it caught is reporting everything caught and proves nothing.",
"find": [" same != FALSE"],
"replace": [" same != 0"]
},
{
"name": "WH-1.4: a failed close is reported as success",
"file": "src/close.rs",
"expect": "caught",
"why": "The point of the function is the result a drop throws away: a close the platform refused, which a handle protected from close is, must be an error.",
"find": [" if unsafe { CloseHandle(raw) } == FALSE {"],
"replace": [" if unsafe { CloseHandle(raw) } == FALSE && false {"]
},
{
"name": "WH-1.4: the handle is not closed",
"file": "src/close.rs",
"expect": "caught",
"why": "A close that does nothing and says it worked leaks every handle it is given; the last handle to a named object is what shows it, since the object is not removed.",
"find": [" if unsafe { CloseHandle(raw) } == FALSE {"],
"replace": [" if { let _ = raw; 1 } == FALSE {"]
},
{
"name": "WH-1.4: every close is reported as a failure",
"file": "src/close.rs",
"expect": "caught",
"why": "A close that succeeded is Ok: reported as failed, a caller cannot tell a handle that closed from one that did not.",
"find": [" if unsafe { CloseHandle(raw) } == FALSE {"],
"replace": [" if unsafe { CloseHandle(raw) } == FALSE || true {"]
},
{
"name": "WH-1.4: the error is not the call's",
"file": "src/close.rs",
"expect": "caught",
"why": "The code the call set is the whole of what a caller can learn of why it failed.",
"find": [" return Err(io::Error::last_os_error());"],
"replace": [" return Err(io::Error::from(io::ErrorKind::Other));"]
},
{
"name": "WH-1.4 control: the call's result is tested against a literal zero",
"file": "src/close.rs",
"expect": "survives",
"why": "A control: FALSE is zero, so the same test by another spelling, which no test can tell apart. A sweep that reports it caught is reporting everything caught and proves nothing.",
"find": [" if unsafe { CloseHandle(raw) } == FALSE {"],
"replace": [" if unsafe { CloseHandle(raw) } == 0 {"]
},
{
"name": "WH-2.1: a same-access duplicate gets no access",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "DUPLICATE_SAME_ACCESS with the flag dropped and the mask zero is a handle that can do nothing: a same-access duplicate that cannot wait, which every same-access test sees.",
"find": [" DuplicateAccess::Same => (0, DUPLICATE_SAME_ACCESS),\n"],
"replace": [" DuplicateAccess::Same => (0, 0),\n"]
},
{
"name": "WH-2.1: an inheritable duplicate is not",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "Asking for Inheritable and getting a handle a child does not receive hands the child nothing, silently: the flag read back from the duplicate is how it is seen.",
"find": [" Inheritance::Inheritable => TRUE,\n"],
"replace": [" Inheritance::Inheritable => FALSE,\n"]
},
{
"name": "WH-2.1: the with-access form keeps the source's access",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "The wrapper that names a mask must pass it on. Passing Same instead makes every narrowed duplicate a full-access clone, which the synchronize-only tests see as a set that succeeds.",
"find": [" DuplicateAccess::Chosen(access),\n Inheritance::NotInheritable,\n"],
"replace": [" DuplicateAccess::Same,\n Inheritance::NotInheritable,\n"]
},
{
"name": "WH-2.1: the with-access form is inheritable",
"file": "src/duplicate.rs",
"expect": "caught",
"why": "The wrapper's documented contract is a duplicate that is not inheritable; the not-inheritable test reads the flag back through it.",
"find": [" DuplicateAccess::Chosen(access),\n Inheritance::NotInheritable,\n"],
"replace": [" DuplicateAccess::Chosen(access),\n Inheritance::Inheritable,\n"]
},
{
"name": "CONTROL: the mask given with the same-access flag is not zero",
"file": "src/duplicate.rs",
"expect": "survives",
"why": "DUPLICATE_SAME_ACCESS makes DuplicateHandle ignore the mask (the page for the call says so), so a nonzero mask beside the flag is the same observable by another spelling. If it comes back caught, something reacts to the text and not to the behaviour.",
"find": [" DuplicateAccess::Same => (0, DUPLICATE_SAME_ACCESS),\n"],
"replace": [" DuplicateAccess::Same => (1, DUPLICATE_SAME_ACCESS),\n"]
}
]
}