1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

fix(runtime): ChildProcess::kill() doesn't require additional perms (#15339)

Fixes #15217.
This commit is contained in:
Nayeem Rahman 2023-05-11 13:53:45 +01:00 committed by GitHub
parent 20c42286f8
commit 2ba9ccc1ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 13 deletions

View file

@ -3435,6 +3435,11 @@ itest!(test_and_bench_are_noops_in_run {
output_str: Some(""), output_str: Some(""),
}); });
itest!(spawn_kill_permissions {
args: "run --quiet --unstable --allow-run=deno spawn_kill_permissions.ts",
output_str: Some(""),
});
itest!(followup_dyn_import_resolved { itest!(followup_dyn_import_resolved {
args: "run --unstable --allow-read run/followup_dyn_import_resolves/main.ts", args: "run --unstable --allow-read run/followup_dyn_import_resolves/main.ts",
output: "run/followup_dyn_import_resolves/main.ts.out", output: "run/followup_dyn_import_resolves/main.ts.out",

View file

@ -0,0 +1,6 @@
const child = new Deno.Command("deno", {
args: ["eval", "await new Promise(r => setTimeout(r, 2000))"],
stdout: "null",
stderr: "null",
}).spawn();
child.kill("SIGTERM");

View file

@ -867,3 +867,21 @@ Deno.test(
} }
}, },
); );
Deno.test(
{ permissions: { run: true, read: true } },
async function commandKillAfterStatus() {
const command = new Deno.Command(Deno.execPath(), {
args: ["help"],
stdout: "null",
stderr: "null",
});
const child = command.spawn();
await child.status;
assertThrows(
() => child.kill(),
TypeError,
"Child process has already terminated.",
);
},
);

View file

@ -200,6 +200,7 @@ function collectOutput(readableStream) {
class ChildProcess { class ChildProcess {
#rid; #rid;
#waitPromiseId; #waitPromiseId;
#waitComplete = false;
#unrefed = false; #unrefed = false;
#pid; #pid;
@ -268,8 +269,8 @@ class ChildProcess {
const waitPromise = core.opAsync("op_spawn_wait", this.#rid); const waitPromise = core.opAsync("op_spawn_wait", this.#rid);
this.#waitPromiseId = waitPromise[promiseIdSymbol]; this.#waitPromiseId = waitPromise[promiseIdSymbol];
this.#status = PromisePrototypeThen(waitPromise, (res) => { this.#status = PromisePrototypeThen(waitPromise, (res) => {
this.#rid = null;
signal?.[abortSignal.remove](onAbort); signal?.[abortSignal.remove](onAbort);
this.#waitComplete = true;
return res; return res;
}); });
} }
@ -317,10 +318,10 @@ class ChildProcess {
} }
kill(signo = "SIGTERM") { kill(signo = "SIGTERM") {
if (this.#rid === null) { if (this.#waitComplete) {
throw new TypeError("Child process has already terminated."); throw new TypeError("Child process has already terminated.");
} }
ops.op_kill(this.#pid, signo, "Deno.Child.kill()"); ops.op_spawn_kill(this.#rid, signo);
} }
ref() { ref() {

View file

@ -2,6 +2,7 @@
use super::check_unstable; use super::check_unstable;
use crate::permissions::PermissionsContainer; use crate::permissions::PermissionsContainer;
use deno_core::error::type_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::op; use deno_core::op;
use deno_core::serde_json; use deno_core::serde_json;
@ -106,6 +107,7 @@ deno_core::extension!(
op_spawn_child, op_spawn_child,
op_spawn_wait, op_spawn_wait,
op_spawn_sync, op_spawn_sync,
op_spawn_kill,
deprecated::op_run, deprecated::op_run,
deprecated::op_run_status, deprecated::op_run_status,
deprecated::op_kill, deprecated::op_kill,
@ -115,7 +117,9 @@ deno_core::extension!(
}, },
); );
struct ChildResource(tokio::process::Child); /// Second member stores the pid separately from the RefCell. It's needed for
/// `op_spawn_kill`, where the RefCell is borrowed mutably by `op_spawn_wait`.
struct ChildResource(RefCell<tokio::process::Child>, u32);
impl Resource for ChildResource { impl Resource for ChildResource {
fn name(&self) -> Cow<str> { fn name(&self) -> Cow<str> {
@ -302,7 +306,9 @@ fn spawn_child(
.take() .take()
.map(|stderr| state.resource_table.add(ChildStderrResource::from(stderr))); .map(|stderr| state.resource_table.add(ChildStderrResource::from(stderr)));
let child_rid = state.resource_table.add(ChildResource(child)); let child_rid = state
.resource_table
.add(ChildResource(RefCell::new(child), pid));
Ok(Child { Ok(Child {
rid: child_rid, rid: child_rid,
@ -328,17 +334,18 @@ async fn op_spawn_wait(
state: Rc<RefCell<OpState>>, state: Rc<RefCell<OpState>>,
rid: ResourceId, rid: ResourceId,
) -> Result<ChildStatus, AnyError> { ) -> Result<ChildStatus, AnyError> {
#![allow(clippy::await_holding_refcell_ref)]
let resource = state let resource = state
.borrow_mut() .borrow_mut()
.resource_table .resource_table
.take::<ChildResource>(rid)?; .get::<ChildResource>(rid)?;
Rc::try_unwrap(resource) let result = resource.0.try_borrow_mut()?.wait().await?.try_into();
.ok() state
.unwrap() .borrow_mut()
.0 .resource_table
.wait() .close(rid)
.await? .expect("shouldn't have closed until now");
.try_into() result
} }
#[op] #[op]
@ -366,6 +373,19 @@ fn op_spawn_sync(
}) })
} }
#[op]
fn op_spawn_kill(
state: &mut OpState,
rid: ResourceId,
signal: String,
) -> Result<(), AnyError> {
if let Ok(child_resource) = state.resource_table.get::<ChildResource>(rid) {
deprecated::kill(child_resource.1 as i32, &signal)?;
return Ok(());
}
Err(type_error("Child process has already terminated."))
}
mod deprecated { mod deprecated {
use super::*; use super::*;