From 728c8fdcdb4e9276a7bd0721bad3b5d0cbed67fa Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 17 Jul 2023 21:21:32 -0700 Subject: Implement `process.{stdout, stderr}.{columns, rows, getWindowSize}` --- src/bun.js/bindings/Process.cpp | 55 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) (limited to 'src/bun.js/bindings/Process.cpp') diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index dd5c41c44..a7798bf9f 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -15,6 +15,9 @@ #include #include #include +#include +#include +#include #pragma mark - Node.js Process @@ -109,7 +112,53 @@ JSC_DEFINE_CUSTOM_SETTER(Process_defaultSetter, return true; } -JSC_DECLARE_HOST_FUNCTION(Process_functionNextTick); +static bool getWindowSize(int fd, size_t* width, size_t* height) +{ + struct winsize ws; + int err; + do + err = ioctl(fd, TIOCGWINSZ, &ws); + while (err == -1 && errno == EINTR); + + if (err == -1) + return false; + + *width = ws.ws_col; + *height = ws.ws_row; + + return true; +} + +JSC_DEFINE_HOST_FUNCTION(Process_functionInternalGetWindowSize, + (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSC::VM& vm = globalObject->vm(); + auto argCount = callFrame->argumentCount(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (argCount == 0) { + JSC::throwTypeError(globalObject, throwScope, "getWindowSize requires 2 argument (a file descriptor)"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + int fd = callFrame->uncheckedArgument(0).toInt32(globalObject); + RETURN_IF_EXCEPTION(throwScope, {}); + JSC::JSArray* array = jsDynamicCast(callFrame->uncheckedArgument(1)); + if (!array || array->length() < 2) { + JSC::throwTypeError(globalObject, throwScope, "getWindowSize requires 2 argument (an array)"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + size_t width, height; + if (!getWindowSize(fd, &width, &height)) { + return JSC::JSValue::encode(jsBoolean(false)); + } + + array->putDirectIndex(globalObject, 0, jsNumber(width)); + array->putDirectIndex(globalObject, 1, jsNumber(height)); + + return JSC::JSValue::encode(jsBoolean(true)); +} + JSC_DEFINE_HOST_FUNCTION(Process_functionNextTick, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { @@ -855,9 +904,13 @@ static JSValue constructStdioWriteStream(JSC::JSGlobalObject* globalObject, int { auto& vm = globalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); + JSC::JSFunction* getWindowSizeFunction = JSC::JSFunction::create(vm, globalObject, 2, + String("getWindowSize"_s), Process_functionInternalGetWindowSize, ImplementationVisibility::Public); + JSC::JSFunction* getStdioWriteStream = JSC::JSFunction::create(vm, processObjectInternalsGetStdioWriteStreamCodeGenerator(vm), globalObject); JSC::MarkedArgumentBuffer args; args.append(JSC::jsNumber(fd)); + args.append(getWindowSizeFunction); auto clientData = WebCore::clientData(vm); JSC::CallData callData = JSC::getCallData(getStdioWriteStream); -- cgit v1.2.3 From b760d1da30c343a98600f8693b5455e00e3f47c5 Mon Sep 17 00:00:00 2001 From: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> Date: Mon, 17 Jul 2023 23:02:33 -0700 Subject: Fix potential crash in process.dlopen() --- src/bun.js/bindings/Process.cpp | 22 +++++++++++++++++----- test/js/node/process/process.test.js | 10 ++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src/bun.js/bindings/Process.cpp') diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index a7798bf9f..6c58c94dd 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -230,23 +230,35 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, auto argCount = callFrame->argumentCount(); if (argCount < 2) { - JSC::throwTypeError(globalObject, scope, "dlopen requires 2 arguments"_s); return JSC::JSValue::encode(JSC::JSValue {}); } JSC::JSValue moduleValue = callFrame->uncheckedArgument(0); - if (!moduleValue.isObject()) { + JSC::JSObject* moduleObject = jsDynamicCast(moduleValue); + if (UNLIKELY(!moduleObject)) { JSC::throwTypeError(globalObject, scope, "dlopen requires an object as first argument"_s); return JSC::JSValue::encode(JSC::JSValue {}); } - JSC::Identifier exportsSymbol = JSC::Identifier::fromString(vm, "exports"_s); - JSC::JSObject* exports = moduleValue.getObject()->getIfPropertyExists(globalObject, exportsSymbol).getObject(); + + JSValue exports = moduleObject->getIfPropertyExists(globalObject, builtinNames(vm).exportsPublicName()); + RETURN_IF_EXCEPTION(scope, {}); + + if (UNLIKELY(!exports)) { + JSC::throwTypeError(globalObject, scope, "dlopen requires an object with an exports property"_s); + return JSC::JSValue::encode(JSC::JSValue {}); + } + + globalObject->pendingNapiModule = exports; + if (exports.isCell()) { + vm.writeBarrier(globalObject, exports.asCell()); + } WTF::String filename = callFrame->uncheckedArgument(1).toWTFString(globalObject); + RETURN_IF_EXCEPTION(scope, {}); + CString utf8 = filename.utf8(); - globalObject->pendingNapiModule = exports; void* handle = dlopen(utf8.data(), RTLD_LAZY); if (!handle) { diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 91479108f..539a92f2c 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -478,3 +478,13 @@ for (const stub of emptyArrayStubs) { expect(process[stub]).toHaveLength(0); }); } + +it("dlopen args parsing", () => { + expect(() => process.dlopen({ module: "42" }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: 42 }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: { exports: "42" } }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: { exports: 42 } }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: Symbol() }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: { exports: Symbol("123") } }, "/tmp/not-found.so")).toThrow(); + expect(() => process.dlopen({ module: { exports: Symbol("123") } }, Symbol("badddd"))).toThrow(); +}); -- cgit v1.2.3