mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Forbid closing stdio from quickjs-libc (#576)
Intrinsically dangerous because it leaves the std{in,out,err} C globals in an undefined state.
This commit is contained in:
parent
ddabcf5e93
commit
27715a46bb
2 changed files with 31 additions and 13 deletions
|
@ -878,15 +878,19 @@ static JSClassID js_std_file_class_id;
|
|||
|
||||
typedef struct {
|
||||
FILE *f;
|
||||
BOOL close_in_finalizer;
|
||||
BOOL is_popen;
|
||||
} JSSTDFile;
|
||||
|
||||
static BOOL is_stdio(FILE *f)
|
||||
{
|
||||
return f == stdin || f == stdout || f == stderr;
|
||||
}
|
||||
|
||||
static void js_std_file_finalizer(JSRuntime *rt, JSValue val)
|
||||
{
|
||||
JSSTDFile *s = JS_GetOpaque(val, js_std_file_class_id);
|
||||
if (s) {
|
||||
if (s->f && s->close_in_finalizer) {
|
||||
if (s->f && !is_stdio(s->f)) {
|
||||
#if !defined(__wasi__)
|
||||
if (s->is_popen)
|
||||
pclose(s->f);
|
||||
|
@ -914,9 +918,7 @@ static JSValue js_std_strerror(JSContext *ctx, JSValue this_val,
|
|||
return JS_NewString(ctx, strerror(err));
|
||||
}
|
||||
|
||||
static JSValue js_new_std_file(JSContext *ctx, FILE *f,
|
||||
BOOL close_in_finalizer,
|
||||
BOOL is_popen)
|
||||
static JSValue js_new_std_file(JSContext *ctx, FILE *f, BOOL is_popen)
|
||||
{
|
||||
JSSTDFile *s;
|
||||
JSValue obj;
|
||||
|
@ -928,7 +930,6 @@ static JSValue js_new_std_file(JSContext *ctx, FILE *f,
|
|||
JS_FreeValue(ctx, obj);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
s->close_in_finalizer = close_in_finalizer;
|
||||
s->is_popen = is_popen;
|
||||
s->f = f;
|
||||
JS_SetOpaque(obj, s);
|
||||
|
@ -971,7 +972,7 @@ static JSValue js_std_open(JSContext *ctx, JSValue this_val,
|
|||
JS_FreeCString(ctx, mode);
|
||||
if (!f)
|
||||
return JS_NULL;
|
||||
return js_new_std_file(ctx, f, TRUE, FALSE);
|
||||
return js_new_std_file(ctx, f, FALSE);
|
||||
fail:
|
||||
JS_FreeCString(ctx, filename);
|
||||
JS_FreeCString(ctx, mode);
|
||||
|
@ -1008,7 +1009,7 @@ static JSValue js_std_popen(JSContext *ctx, JSValue this_val,
|
|||
JS_FreeCString(ctx, mode);
|
||||
if (!f)
|
||||
return JS_NULL;
|
||||
return js_new_std_file(ctx, f, TRUE, TRUE);
|
||||
return js_new_std_file(ctx, f, TRUE);
|
||||
fail:
|
||||
JS_FreeCString(ctx, filename);
|
||||
JS_FreeCString(ctx, mode);
|
||||
|
@ -1043,7 +1044,7 @@ static JSValue js_std_fdopen(JSContext *ctx, JSValue this_val,
|
|||
JS_FreeCString(ctx, mode);
|
||||
if (!f)
|
||||
return JS_NULL;
|
||||
return js_new_std_file(ctx, f, TRUE, FALSE);
|
||||
return js_new_std_file(ctx, f, FALSE);
|
||||
fail:
|
||||
JS_FreeCString(ctx, mode);
|
||||
return JS_EXCEPTION;
|
||||
|
@ -1059,7 +1060,7 @@ static JSValue js_std_tmpfile(JSContext *ctx, JSValue this_val,
|
|||
js_set_error_object(ctx, argv[0], f ? 0 : errno);
|
||||
if (!f)
|
||||
return JS_NULL;
|
||||
return js_new_std_file(ctx, f, TRUE, FALSE);
|
||||
return js_new_std_file(ctx, f, FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1122,6 +1123,8 @@ static JSValue js_std_file_close(JSContext *ctx, JSValue this_val,
|
|||
return JS_EXCEPTION;
|
||||
if (!s->f)
|
||||
return JS_ThrowTypeError(ctx, "invalid file handle");
|
||||
if (is_stdio(s->f))
|
||||
return JS_ThrowTypeError(ctx, "cannot close stdio");
|
||||
#if !defined(__wasi__)
|
||||
if (s->is_popen)
|
||||
err = js_get_errno(pclose(s->f));
|
||||
|
@ -1643,9 +1646,9 @@ static int js_std_init(JSContext *ctx, JSModuleDef *m)
|
|||
|
||||
JS_SetModuleExportList(ctx, m, js_std_funcs,
|
||||
countof(js_std_funcs));
|
||||
JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE, FALSE));
|
||||
JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE, FALSE));
|
||||
JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE, FALSE));
|
||||
JS_SetModuleExport(ctx, m, "in", js_new_std_file(ctx, stdin, FALSE));
|
||||
JS_SetModuleExport(ctx, m, "out", js_new_std_file(ctx, stdout, FALSE));
|
||||
JS_SetModuleExport(ctx, m, "err", js_new_std_file(ctx, stderr, FALSE));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -289,6 +289,20 @@ function test_timeout_order()
|
|||
function d() { assert(s === "abc"); } // not "acb"
|
||||
}
|
||||
|
||||
function test_stdio_close()
|
||||
{
|
||||
for (const f of [std.in, std.out, std.err]) {
|
||||
let caught = false;
|
||||
try {
|
||||
f.close();
|
||||
} catch (e) {
|
||||
assert(/cannot close stdio/.test(e.message));
|
||||
caught = true;
|
||||
}
|
||||
assert(caught);
|
||||
}
|
||||
}
|
||||
|
||||
test_printf();
|
||||
test_file1();
|
||||
test_file2();
|
||||
|
@ -299,3 +313,4 @@ test_os();
|
|||
test_interval();
|
||||
test_timeout();
|
||||
test_timeout_order();
|
||||
test_stdio_close();
|
||||
|
|
Loading…
Reference in a new issue