fix(process): classify withheld Darwin environments before parser failures
This commit is contained in:
1 parent
9a68ce57a0
commit
26b033d51f
3 files changed
+83
-22
No files matched your search
@@ -228,6 +228,22 @@ func processRunTokenObservation(
|
||||
parse: Result<ParsedRunToken, RunTokenReadError>,
|
||||
codeSigningFlags: () -> UInt32?
|
||||
) -> RunTokenObservation {
|
||||
// Native inspection restrictions apply even when argv is incomplete or
|
||||
// overwritten, before parsing reaches the environment. Keep this fact at
|
||||
// the native boundary; cleanup still decides whether the process is related.
|
||||
func withheldEnvironment() -> RunTokenObservation? {
|
||||
guard let flags = codeSigningFlags() else { return nil }
|
||||
if (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func unavailableEnvironment(_ reason: String) -> RunTokenObservation {
|
||||
withheldEnvironment() ?? unavailable(pid: pid, reason: reason)
|
||||
}
|
||||
switch parse {
|
||||
case .success(let parsed):
|
||||
if let runId = parsed.runId {
|
||||
@@ -238,40 +254,25 @@ func processRunTokenObservation(
|
||||
if parsed.environmentEntryCount > 0 {
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
|
||||
}
|
||||
let flags = codeSigningFlags()
|
||||
if let flags, (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if let flags, (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
if let withheld = withheldEnvironment() { return withheld }
|
||||
return RunTokenObservation(pid: pid, state: "readable", run_id: nil, reason: nil)
|
||||
case .failure(let failure):
|
||||
switch failure {
|
||||
case .environmentUnavailable:
|
||||
let flags = codeSigningFlags()
|
||||
if let flags, (flags & csPlatformBinary) != 0 {
|
||||
return unavailable(pid: pid, reason: "platform_binary_environment_withheld")
|
||||
}
|
||||
if let flags, (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return unavailable(pid: pid, reason: "environment_unavailable")
|
||||
return unavailableEnvironment("environment_unavailable")
|
||||
case .ambiguousEnvironmentBoundary:
|
||||
if let flags = codeSigningFlags(), (flags & csRestrict) != 0 {
|
||||
return unavailable(pid: pid, reason: "restricted_process_environment_withheld")
|
||||
}
|
||||
return unavailable(pid: pid, reason: "ambiguous_environment_boundary")
|
||||
return unavailableEnvironment("ambiguous_environment_boundary")
|
||||
// These failures already establish that token bytes were available.
|
||||
case .duplicateToken:
|
||||
return unavailable(pid: pid, reason: "duplicate_run_token")
|
||||
case .invalidTokenEncoding:
|
||||
return unavailable(pid: pid, reason: "invalid_run_token_encoding")
|
||||
case .appleVectorUnavailable:
|
||||
return unavailable(pid: pid, reason: "apple_vector_unavailable")
|
||||
return unavailableEnvironment("apple_vector_unavailable")
|
||||
case .invalidBuffer:
|
||||
return unavailable(pid: pid, reason: "malformed_procargs")
|
||||
return unavailableEnvironment("malformed_procargs")
|
||||
case .invalidBufferAt(let stage):
|
||||
return unavailable(pid: pid, reason: "malformed_procargs_\(stage.rawValue)")
|
||||
return unavailableEnvironment("malformed_procargs_\(stage.rawValue)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1317,6 +1317,29 @@ it.skipIf(!onDarwin)(
|
||||
missingAppleBoundaryFailsClosed: true,
|
||||
malformedBuffersRejected: true,
|
||||
invalidPointerSizeFailsClosed: true,
|
||||
incompleteRestrictedArguments: [
|
||||
"malformed_procargs_path_padding",
|
||||
"malformed_procargs_arguments",
|
||||
"apple_vector_unavailable",
|
||||
].map((reason) => [
|
||||
{
|
||||
state: "unavailable",
|
||||
reason: "platform_binary_environment_withheld",
|
||||
},
|
||||
{
|
||||
state: "unavailable",
|
||||
reason: "restricted_process_environment_withheld",
|
||||
},
|
||||
{ state: "unavailable", reason },
|
||||
]),
|
||||
signedReadableTokens: [true, true],
|
||||
signedInvalidTokens: [
|
||||
"duplicate_run_token",
|
||||
"invalid_run_token_encoding",
|
||||
].map((reason) => [
|
||||
{ state: "unavailable", reason },
|
||||
{ state: "unavailable", reason },
|
||||
]),
|
||||
malformedStages: [
|
||||
{ state: "unavailable", reason: "malformed_procargs_header" },
|
||||
{ state: "unavailable", reason: "malformed_procargs_argument_count" },
|
||||
|
||||
+37
@@ -193,6 +193,40 @@ struct ProcessRunTokenParserProbe {
|
||||
parse: .failure(.invalidBuffer),
|
||||
codeSigningFlags: { 0 }
|
||||
)
|
||||
// A restricted native process can expose an incomplete argv layout before
|
||||
// the parser reaches the withheld environment (CI observed mdworker here).
|
||||
let incompleteRestrictedArguments = [
|
||||
RunTokenReadError.invalidBufferAt(.pathPadding),
|
||||
.invalidBufferAt(.arguments),
|
||||
.appleVectorUnavailable,
|
||||
].map { failure in
|
||||
[UInt32(0x0400_0000), UInt32(0x0000_0800), nil].map { flags in
|
||||
let observation = processRunTokenObservation(
|
||||
pid: 1,
|
||||
parse: .failure(failure),
|
||||
codeSigningFlags: { flags }
|
||||
)
|
||||
return ["state": observation.state, "reason": observation.reason ?? ""]
|
||||
}
|
||||
}
|
||||
let signedReadableTokens = [UInt32(0x0400_0000), UInt32(0x0000_0800)].map { flags in
|
||||
let observation = processRunTokenObservation(
|
||||
pid: 1,
|
||||
parse: .success(realToken),
|
||||
codeSigningFlags: { flags }
|
||||
)
|
||||
return observation.state == "readable" && observation.run_id == "owned-token"
|
||||
}
|
||||
let signedInvalidTokens = [RunTokenReadError.duplicateToken, .invalidTokenEncoding].map { failure in
|
||||
[UInt32(0x0400_0000), UInt32(0x0000_0800)].map { flags in
|
||||
let observation = processRunTokenObservation(
|
||||
pid: 1,
|
||||
parse: .failure(failure),
|
||||
codeSigningFlags: { flags }
|
||||
)
|
||||
return ["state": observation.state, "reason": observation.reason ?? ""]
|
||||
}
|
||||
}
|
||||
let duplicateTokenFailsClosed: Bool
|
||||
do {
|
||||
_ = try parseProcessEnvironment(in: fixture(
|
||||
@@ -311,6 +345,9 @@ struct ProcessRunTokenParserProbe {
|
||||
"malformedBuffersRejected": malformedBuffersRejected,
|
||||
"invalidPointerSizeFailsClosed": invalidPointerSizeFailsClosed,
|
||||
"malformedStages": malformedStages,
|
||||
"incompleteRestrictedArguments": incompleteRestrictedArguments,
|
||||
"signedReadableTokens": signedReadableTokens,
|
||||
"signedInvalidTokens": signedInvalidTokens,
|
||||
"malformedObservation": [
|
||||
"state": malformedObservation.state,
|
||||
"reason": malformedObservation.reason as Any? ?? NSNull(),
|
||||
|
||||
Reference in new issue
Block a user