win-handles-sys 0.1.1

Memory-safe Rust over the Win32 handle and object API: duplicating a handle with the access kept or chosen and its inheritance chosen, with a typed access mask, and reading and setting a handle's flags, asking whether two handles name one object, and closing a handle with its result. Thin over Win32, adding no policy.
Documentation
{
  "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"]
    }
  ]
}