#include "node_wasi.h" #include "base_object-inl.h" #include "debug_utils-inl.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node.h" #include "node_errors.h" #include "node_mem-inl.h" #include "permission/permission.h" #include "util-inl.h" #include "uv.h" #include "uvwasi.h" namespace node { namespace wasi { template inline void Debug(const WASI& wasi, Args&&... args) { Debug(wasi.env(), DebugCategory::WASI, std::forward(args)...); } #define CHECK_BOUNDS_OR_RETURN(mem_size, offset, buf_size) \ do { \ if (!uvwasi_serdes_check_bounds((offset), (mem_size), (buf_size))) { \ return UVWASI_EOVERFLOW; \ } \ } while (0) using v8::Array; using v8::ArrayBuffer; using v8::BigInt; using v8::CFunction; using v8::Context; using v8::Exception; using v8::FastApiCallbackOptions; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; using v8::HandleScope; using v8::Integer; using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::Object; using v8::Signature; using v8::String; using v8::Uint32; using v8::Value; using v8::WasmMemoryObject; static MaybeLocal WASIException(Local context, int errorno, const char* syscall) { Isolate* isolate = context->GetIsolate(); Environment* env = Environment::GetCurrent(context); CHECK_NOT_NULL(env); const char* err_name = uvwasi_embedder_err_code_to_string(errorno); Local js_code = OneByteString(isolate, err_name); Local js_syscall = OneByteString(isolate, syscall); Local js_msg = js_code; js_msg = String::Concat(isolate, js_msg, FIXED_ONE_BYTE_STRING(isolate, ", ")); js_msg = String::Concat(isolate, js_msg, js_syscall); Local e; if (!Exception::Error(js_msg)->ToObject(context).ToLocal(&e)) return MaybeLocal(); if (e->Set(context, env->errno_string(), Integer::New(isolate, errorno)).IsNothing() || e->Set(context, env->code_string(), js_code).IsNothing() || e->Set(context, env->syscall_string(), js_syscall).IsNothing()) { return MaybeLocal(); } return e; } WASI::WASI(Environment* env, Local object, uvwasi_options_t* options) : BaseObject(env, object) { MakeWeak(); alloc_info_ = MakeAllocator(); options->allocator = &alloc_info_; int err = uvwasi_init(&uvw_, options); if (err != UVWASI_ESUCCESS) { Local exception; CHECK( WASIException(env->context(), err, "uvwasi_init").ToLocal(&exception)); env->isolate()->ThrowException(exception); } } WASI::~WASI() { uvwasi_destroy(&uvw_); CHECK_EQ(current_uvwasi_memory_, 0); } void WASI::MemoryInfo(MemoryTracker* tracker) const { tracker->TrackField("memory", memory_); tracker->TrackFieldWithSize("uvwasi_memory", current_uvwasi_memory_); } void WASI::CheckAllocatedSize(size_t previous_size) const { CHECK_GE(current_uvwasi_memory_, previous_size); } void WASI::IncreaseAllocatedSize(size_t size) { current_uvwasi_memory_ += size; } void WASI::DecreaseAllocatedSize(size_t size) { current_uvwasi_memory_ -= size; } void WASI::New(const FunctionCallbackInfo& args) { CHECK(args.IsConstructCall()); CHECK_EQ(args.Length(), 4); CHECK(args[0]->IsArray()); CHECK(args[1]->IsArray()); CHECK(args[2]->IsArray()); CHECK(args[3]->IsArray()); Environment* env = Environment::GetCurrent(args); THROW_IF_INSUFFICIENT_PERMISSIONS( env, permission::PermissionScope::kWASI, ""); Local context = env->context(); Local argv = args[0].As(); const uint32_t argc = argv->Length(); uvwasi_options_t options; uvwasi_options_init(&options); Local stdio = args[3].As(); CHECK_EQ(stdio->Length(), 3); options.in = stdio->Get(context, 0).ToLocalChecked()-> Int32Value(context).FromJust(); options.out = stdio->Get(context, 1).ToLocalChecked()-> Int32Value(context).FromJust(); options.err = stdio->Get(context, 2).ToLocalChecked()-> Int32Value(context).FromJust(); options.fd_table_size = 3; options.argc = argc; options.argv = const_cast(argc == 0 ? nullptr : new char*[argc]); for (uint32_t i = 0; i < argc; i++) { auto arg = argv->Get(context, i).ToLocalChecked(); CHECK(arg->IsString()); node::Utf8Value str(env->isolate(), arg); options.argv[i] = strdup(*str); CHECK_NOT_NULL(options.argv[i]); } Local env_pairs = args[1].As(); const uint32_t envc = env_pairs->Length(); options.envp = const_cast(new char*[envc + 1]); for (uint32_t i = 0; i < envc; i++) { auto pair = env_pairs->Get(context, i).ToLocalChecked(); CHECK(pair->IsString()); node::Utf8Value str(env->isolate(), pair); options.envp[i] = strdup(*str); CHECK_NOT_NULL(options.envp[i]); } options.envp[envc] = nullptr; Local preopens = args[2].As(); CHECK_EQ(preopens->Length() % 2, 0); options.preopenc = preopens->Length() / 2; options.preopens = Calloc(options.preopenc); int index = 0; for (uint32_t i = 0; i < preopens->Length(); i += 2) { auto mapped = preopens->Get(context, i).ToLocalChecked(); auto real = preopens->Get(context, i + 1).ToLocalChecked(); CHECK(mapped->IsString()); CHECK(real->IsString()); node::Utf8Value mapped_path(env->isolate(), mapped); node::Utf8Value real_path(env->isolate(), real); options.preopens[index].mapped_path = strdup(*mapped_path); CHECK_NOT_NULL(options.preopens[index].mapped_path); options.preopens[index].real_path = strdup(*real_path); CHECK_NOT_NULL(options.preopens[index].real_path); index++; } new WASI(env, args.This(), &options); if (options.argv != nullptr) { for (uint32_t i = 0; i < argc; i++) free(const_cast(options.argv[i])); delete[] options.argv; } for (uint32_t i = 0; options.envp[i]; i++) free(const_cast(options.envp[i])); delete[] options.envp; if (options.preopens != nullptr) { for (uint32_t i = 0; i < options.preopenc; i++) { free(const_cast(options.preopens[i].mapped_path)); free(const_cast(options.preopens[i].real_path)); } free(options.preopens); } } template void WASI::WasiFunction::SetFunction( Environment* env, const char* name, Local tmpl) { auto c_function = CFunction::Make(FastCallback); Local t = FunctionTemplate::New(env->isolate(), SlowCallback, Local(), Local(), sizeof...(Args), v8::ConstructorBehavior::kThrow, v8::SideEffectType::kHasSideEffect, &c_function); const v8::NewStringType type = v8::NewStringType::kInternalized; Local name_string = String::NewFromUtf8(env->isolate(), name, type).ToLocalChecked(); tmpl->PrototypeTemplate()->Set(name_string, t); t->SetClassName(name_string); } namespace { template inline R EinvalError(); template <> inline uint32_t EinvalError() { return UVWASI_EINVAL; } template <> inline void EinvalError() {} } // namespace template R WASI::WasiFunction::FastCallback( Local receiver, Args... args, // NOLINTNEXTLINE(runtime/references) This is V8 api. FastApiCallbackOptions& options) { WASI* wasi = reinterpret_cast(BaseObject::FromJSObject(receiver)); if (UNLIKELY(wasi == nullptr)) return EinvalError(); Isolate* isolate = receiver->GetIsolate(); HandleScope scope(isolate); if (wasi->memory_.IsEmpty()) { THROW_ERR_WASI_NOT_STARTED(isolate); return EinvalError(); } Local ab = wasi->memory_.Get(isolate)->Buffer(); size_t mem_size = ab->ByteLength(); char* mem_data = static_cast(ab->Data()); CHECK_NOT_NULL(mem_data); return F(*wasi, {mem_data, mem_size}, args...); } namespace { template static bool CheckType(Local v); template static VT ConvertType(Local V); template <> bool CheckType(Local value) { return value->IsUint32(); } template <> uint32_t ConvertType(Local value) { return value.As()->Value(); } template <> bool CheckType(Local value) { return value->IsBigInt(); } template <> uint64_t ConvertType(Local value) { Local js_value = value.As(); bool lossless; return js_value->Uint64Value(&lossless); } template <> bool CheckType(Local value) { return value->IsBigInt(); } template <> int64_t ConvertType(Local value) { Local js_value = value.As(); bool lossless; return js_value->Int64Value(&lossless); } template bool CheckTypes(const FunctionCallbackInfo& info, int i, T) { return CheckType(info[i]); } template bool CheckTypes(const FunctionCallbackInfo& info, int i, T arg, Ts... args) { if (!CheckTypes(info, i, arg)) return false; return CheckTypes(info, i + 1, args...); } template bool CheckTypes(const FunctionCallbackInfo& info) { return CheckTypes(info, 0, Args()...); } template <> bool CheckTypes(const FunctionCallbackInfo& info) { return true; } template ::value, bool> = true> inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, WasmMemory memory) { args.GetReturnValue().Set( F(*wasi, memory, ConvertType(args[Indices])...)); } template ::value, bool> = true> inline void CallAndSetReturn(std::index_sequence, const FunctionCallbackInfo& args, WASI* wasi, WasmMemory memory) { F(*wasi, memory, ConvertType(args[Indices])...); } } // namespace template void WASI::WasiFunction::SlowCallback( const FunctionCallbackInfo& args) { if (args.Length() != sizeof...(Args)) { args.GetReturnValue().Set(UVWASI_EINVAL); return; } if (!CheckTypes(args)) { args.GetReturnValue().Set(UVWASI_EINVAL); return; } WASI* wasi; ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); if (wasi->memory_.IsEmpty()) { THROW_ERR_WASI_NOT_STARTED(Environment::GetCurrent(args)); return; } Local memory = PersistentToLocal::Strong(wasi->memory_); Local ab = memory->Buffer(); size_t mem_size = ab->ByteLength(); char* mem_data = static_cast(ab->Data()); CHECK_NOT_NULL(mem_data); CallAndSetReturn( std::make_index_sequence{}, args, wasi, {mem_data, mem_size}); } template static void SetFunction(R (*f)(WASI&, WasmMemory, Args...), Environment* env, const char* name, Local tmpl) { WASI::WasiFunction::SetFunction(env, name, tmpl); } uint32_t WASI::ArgsGet(WASI& wasi, WasmMemory memory, uint32_t argv_offset, uint32_t argv_buf_offset) { Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset); CHECK_BOUNDS_OR_RETURN(memory.size, argv_buf_offset, wasi.uvw_.argv_buf_size); CHECK_BOUNDS_OR_RETURN( memory.size, argv_offset, wasi.uvw_.argc * UVWASI_SERDES_SIZE_uint32_t); std::vector argv(wasi.uvw_.argc); char* argv_buf = &memory.data[argv_buf_offset]; uvwasi_errno_t err = uvwasi_args_get(&wasi.uvw_, argv.data(), argv_buf); if (err == UVWASI_ESUCCESS) { for (size_t i = 0; i < wasi.uvw_.argc; i++) { uint32_t offset = static_cast(argv_buf_offset + (argv[i] - argv[0])); uvwasi_serdes_write_uint32_t( memory.data, argv_offset + (i * UVWASI_SERDES_SIZE_uint32_t), offset); } } return err; } uint32_t WASI::ArgsSizesGet(WASI& wasi, WasmMemory memory, uint32_t argc_offset, uint32_t argv_buf_offset) { Debug(wasi, "args_sizes_get(%d, %d)\n", argc_offset, argv_buf_offset); CHECK_BOUNDS_OR_RETURN(memory.size, argc_offset, UVWASI_SERDES_SIZE_size_t); CHECK_BOUNDS_OR_RETURN( memory.size, argv_buf_offset, UVWASI_SERDES_SIZE_size_t); uvwasi_size_t argc; uvwasi_size_t argv_buf_size; uvwasi_errno_t err = uvwasi_args_sizes_get(&wasi.uvw_, &argc, &argv_buf_size); if (err == UVWASI_ESUCCESS) { uvwasi_serdes_write_size_t(memory.data, argc_offset, argc); uvwasi_serdes_write_size_t(memory.data, argv_buf_offset, argv_buf_size); } return err; } uint32_t WASI::ClockResGet(WASI& wasi, WasmMemory memory, uint32_t clock_id, uint32_t resolution_ptr) { Debug(wasi, "clock_res_get(%d, %d)\n", clock_id, resolution_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, resolution_ptr, UVWASI_SERDES_SIZE_timestamp_t); uvwasi_timestamp_t resolution; uvwasi_errno_t err = uvwasi_clock_res_get(&wasi.uvw_, clock_id, &resolution); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_timestamp_t(memory.data, resolution_ptr, resolution); return err; } uint32_t WASI::ClockTimeGet(WASI& wasi, WasmMemory memory, uint32_t clock_id, uint64_t precision, uint32_t time_ptr) { Debug(wasi, "clock_time_get(%d, %d, %d)\n", clock_id, precision, time_ptr); CHECK_BOUNDS_OR_RETURN(memory.size, time_ptr, UVWASI_SERDES_SIZE_timestamp_t); uvwasi_timestamp_t time; uvwasi_errno_t err = uvwasi_clock_time_get(&wasi.uvw_, clock_id, precision, &time); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_timestamp_t(memory.data, time_ptr, time); return err; } uint32_t WASI::EnvironGet(WASI& wasi, WasmMemory memory, uint32_t environ_offset, uint32_t environ_buf_offset) { Debug(wasi, "environ_get(%d, %d)\n", environ_offset, environ_buf_offset); CHECK_BOUNDS_OR_RETURN( memory.size, environ_buf_offset, wasi.uvw_.env_buf_size); CHECK_BOUNDS_OR_RETURN(memory.size, environ_offset, wasi.uvw_.envc * UVWASI_SERDES_SIZE_uint32_t); std::vector environment(wasi.uvw_.envc); char* environ_buf = &memory.data[environ_buf_offset]; uvwasi_errno_t err = uvwasi_environ_get(&wasi.uvw_, environment.data(), environ_buf); if (err == UVWASI_ESUCCESS) { for (size_t i = 0; i < wasi.uvw_.envc; i++) { uint32_t offset = static_cast( environ_buf_offset + (environment[i] - environment[0])); uvwasi_serdes_write_uint32_t( memory.data, environ_offset + (i * UVWASI_SERDES_SIZE_uint32_t), offset); } } return err; } uint32_t WASI::EnvironSizesGet(WASI& wasi, WasmMemory memory, uint32_t envc_offset, uint32_t env_buf_offset) { Debug(wasi, "environ_sizes_get(%d, %d)\n", envc_offset, env_buf_offset); CHECK_BOUNDS_OR_RETURN(memory.size, envc_offset, UVWASI_SERDES_SIZE_size_t); CHECK_BOUNDS_OR_RETURN( memory.size, env_buf_offset, UVWASI_SERDES_SIZE_size_t); uvwasi_size_t envc; uvwasi_size_t env_buf_size; uvwasi_errno_t err = uvwasi_environ_sizes_get(&wasi.uvw_, &envc, &env_buf_size); if (err == UVWASI_ESUCCESS) { uvwasi_serdes_write_size_t(memory.data, envc_offset, envc); uvwasi_serdes_write_size_t(memory.data, env_buf_offset, env_buf_size); } return err; } uint32_t WASI::FdAdvise(WASI& wasi, WasmMemory, uint32_t fd, uint64_t offset, uint64_t len, uint32_t advice) { Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice); return uvwasi_fd_advise(&wasi.uvw_, fd, offset, len, advice); } uint32_t WASI::FdAllocate( WASI& wasi, WasmMemory, uint32_t fd, uint64_t offset, uint64_t len) { Debug(wasi, "fd_allocate(%d, %d, %d)\n", fd, offset, len); return uvwasi_fd_allocate(&wasi.uvw_, fd, offset, len); } uint32_t WASI::FdClose(WASI& wasi, WasmMemory, uint32_t fd) { Debug(wasi, "fd_close(%d)\n", fd); return uvwasi_fd_close(&wasi.uvw_, fd); } uint32_t WASI::FdDatasync(WASI& wasi, WasmMemory, uint32_t fd) { Debug(wasi, "fd_datasync(%d)\n", fd); return uvwasi_fd_datasync(&wasi.uvw_, fd); } uint32_t WASI::FdFdstatGet(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t buf) { Debug(wasi, "fd_fdstat_get(%d, %d)\n", fd, buf); CHECK_BOUNDS_OR_RETURN(memory.size, buf, UVWASI_SERDES_SIZE_fdstat_t); uvwasi_fdstat_t stats; uvwasi_errno_t err = uvwasi_fd_fdstat_get(&wasi.uvw_, fd, &stats); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_fdstat_t(memory.data, buf, &stats); return err; } uint32_t WASI::FdFdstatSetFlags(WASI& wasi, WasmMemory, uint32_t fd, uint32_t flags) { Debug(wasi, "fd_fdstat_set_flags(%d, %d)\n", fd, flags); return uvwasi_fd_fdstat_set_flags(&wasi.uvw_, fd, flags); } uint32_t WASI::FdFdstatSetRights(WASI& wasi, WasmMemory, uint32_t fd, uint64_t fs_rights_base, uint64_t fs_rights_inheriting) { Debug(wasi, "fd_fdstat_set_rights(%d, %d, %d)\n", fd, fs_rights_base, fs_rights_inheriting); return uvwasi_fd_fdstat_set_rights( &wasi.uvw_, fd, fs_rights_base, fs_rights_inheriting); } uint32_t WASI::FdFilestatGet(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t buf) { Debug(wasi, "fd_filestat_get(%d, %d)\n", fd, buf); CHECK_BOUNDS_OR_RETURN(memory.size, buf, UVWASI_SERDES_SIZE_filestat_t); uvwasi_filestat_t stats; uvwasi_errno_t err = uvwasi_fd_filestat_get(&wasi.uvw_, fd, &stats); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_filestat_t(memory.data, buf, &stats); return err; } uint32_t WASI::FdFilestatSetSize(WASI& wasi, WasmMemory, uint32_t fd, uint64_t st_size) { Debug(wasi, "fd_filestat_set_size(%d, %d)\n", fd, st_size); return uvwasi_fd_filestat_set_size(&wasi.uvw_, fd, st_size); } uint32_t WASI::FdFilestatSetTimes(WASI& wasi, WasmMemory, uint32_t fd, uint64_t st_atim, uint64_t st_mtim, uint32_t fst_flags) { Debug(wasi, "fd_filestat_set_times(%d, %d, %d, %d)\n", fd, st_atim, st_mtim, fst_flags); return uvwasi_fd_filestat_set_times( &wasi.uvw_, fd, st_atim, st_mtim, fst_flags); } uint32_t WASI::FdPread(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t iovs_ptr, uint32_t iovs_len, uint64_t offset, uint32_t nread_ptr) { Debug(wasi, "uvwasi_fd_pread(%d, %d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, offset, nread_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, iovs_ptr, iovs_len * UVWASI_SERDES_SIZE_iovec_t); CHECK_BOUNDS_OR_RETURN(memory.size, nread_ptr, UVWASI_SERDES_SIZE_size_t); std::vector iovs(iovs_len); uvwasi_errno_t err; err = uvwasi_serdes_readv_iovec_t( memory.data, memory.size, iovs_ptr, iovs.data(), iovs_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t nread; err = uvwasi_fd_pread(&wasi.uvw_, fd, iovs.data(), iovs_len, offset, &nread); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, nread_ptr, nread); return err; } uint32_t WASI::FdPrestatGet(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t buf) { Debug(wasi, "fd_prestat_get(%d, %d)\n", fd, buf); CHECK_BOUNDS_OR_RETURN(memory.size, buf, UVWASI_SERDES_SIZE_prestat_t); uvwasi_prestat_t prestat; uvwasi_errno_t err = uvwasi_fd_prestat_get(&wasi.uvw_, fd, &prestat); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_prestat_t(memory.data, buf, &prestat); return err; } uint32_t WASI::FdPrestatDirName(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t path_ptr, uint32_t path_len) { Debug(wasi, "fd_prestat_dir_name(%d, %d, %d)\n", fd, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); return uvwasi_fd_prestat_dir_name( &wasi.uvw_, fd, &memory.data[path_ptr], path_len); } uint32_t WASI::FdPwrite(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t iovs_ptr, uint32_t iovs_len, uint64_t offset, uint32_t nwritten_ptr) { Debug(wasi, "uvwasi_fd_pwrite(%d, %d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, offset, nwritten_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, iovs_ptr, iovs_len * UVWASI_SERDES_SIZE_ciovec_t); CHECK_BOUNDS_OR_RETURN(memory.size, nwritten_ptr, UVWASI_SERDES_SIZE_size_t); std::vector iovs(iovs_len); uvwasi_errno_t err; err = uvwasi_serdes_readv_ciovec_t( memory.data, memory.size, iovs_ptr, iovs.data(), iovs_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t nwritten; err = uvwasi_fd_pwrite( &wasi.uvw_, fd, iovs.data(), iovs_len, offset, &nwritten); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, nwritten_ptr, nwritten); return err; } uint32_t WASI::FdRead(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t iovs_ptr, uint32_t iovs_len, uint32_t nread_ptr) { Debug(wasi, "fd_read(%d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, nread_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, iovs_ptr, iovs_len * UVWASI_SERDES_SIZE_iovec_t); CHECK_BOUNDS_OR_RETURN(memory.size, nread_ptr, UVWASI_SERDES_SIZE_size_t); std::vector iovs(iovs_len); uvwasi_errno_t err; err = uvwasi_serdes_readv_iovec_t( memory.data, memory.size, iovs_ptr, iovs.data(), iovs_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t nread; err = uvwasi_fd_read(&wasi.uvw_, fd, iovs.data(), iovs_len, &nread); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, nread_ptr, nread); return err; } uint32_t WASI::FdReaddir(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t buf_ptr, uint32_t buf_len, uint64_t cookie, uint32_t bufused_ptr) { Debug(wasi, "uvwasi_fd_readdir(%d, %d, %d, %d, %d)\n", fd, buf_ptr, buf_len, cookie, bufused_ptr); CHECK_BOUNDS_OR_RETURN(memory.size, buf_ptr, buf_len); CHECK_BOUNDS_OR_RETURN(memory.size, bufused_ptr, UVWASI_SERDES_SIZE_size_t); uvwasi_size_t bufused; uvwasi_errno_t err = uvwasi_fd_readdir( &wasi.uvw_, fd, &memory.data[buf_ptr], buf_len, cookie, &bufused); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, bufused_ptr, bufused); return err; } uint32_t WASI::FdRenumber(WASI& wasi, WasmMemory, uint32_t from, uint32_t to) { Debug(wasi, "fd_renumber(%d, %d)\n", from, to); return uvwasi_fd_renumber(&wasi.uvw_, from, to); } uint32_t WASI::FdSeek(WASI& wasi, WasmMemory memory, uint32_t fd, int64_t offset, uint32_t whence, uint32_t newoffset_ptr) { Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, newoffset_ptr, UVWASI_SERDES_SIZE_filesize_t); uvwasi_filesize_t newoffset; uvwasi_errno_t err = uvwasi_fd_seek(&wasi.uvw_, fd, offset, whence, &newoffset); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_filesize_t(memory.data, newoffset_ptr, newoffset); return err; } uint32_t WASI::FdSync(WASI& wasi, WasmMemory, uint32_t fd) { Debug(wasi, "fd_sync(%d)\n", fd); return uvwasi_fd_sync(&wasi.uvw_, fd); } uint32_t WASI::FdTell(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t offset_ptr) { Debug(wasi, "fd_tell(%d, %d)\n", fd, offset_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, offset_ptr, UVWASI_SERDES_SIZE_filesize_t); uvwasi_filesize_t offset; uvwasi_errno_t err = uvwasi_fd_tell(&wasi.uvw_, fd, &offset); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_filesize_t(memory.data, offset_ptr, offset); return err; } uint32_t WASI::FdWrite(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t iovs_ptr, uint32_t iovs_len, uint32_t nwritten_ptr) { Debug(wasi, "fd_write(%d, %d, %d, %d)\n", fd, iovs_ptr, iovs_len, nwritten_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, iovs_ptr, iovs_len * UVWASI_SERDES_SIZE_ciovec_t); CHECK_BOUNDS_OR_RETURN(memory.size, nwritten_ptr, UVWASI_SERDES_SIZE_size_t); std::vector iovs(iovs_len); uvwasi_errno_t err; err = uvwasi_serdes_readv_ciovec_t( memory.data, memory.size, iovs_ptr, iovs.data(), iovs_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t nwritten; err = uvwasi_fd_write(&wasi.uvw_, fd, iovs.data(), iovs_len, &nwritten); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, nwritten_ptr, nwritten); return err; } uint32_t WASI::PathCreateDirectory(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t path_ptr, uint32_t path_len) { Debug(wasi, "path_create_directory(%d, %d, %d)\n", fd, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); uvwasi_errno_t err = uvwasi_path_create_directory( &wasi.uvw_, fd, &memory.data[path_ptr], path_len); return err; } uint32_t WASI::PathFilestatGet(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t flags, uint32_t path_ptr, uint32_t path_len, uint32_t buf_ptr) { Debug(wasi, "path_filestat_get(%d, %d, %d)\n", fd, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, buf_ptr, UVWASI_SERDES_SIZE_filestat_t); uvwasi_filestat_t stats; uvwasi_errno_t err = uvwasi_path_filestat_get( &wasi.uvw_, fd, flags, &memory.data[path_ptr], path_len, &stats); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_filestat_t(memory.data, buf_ptr, &stats); return err; } uint32_t WASI::PathFilestatSetTimes(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t flags, uint32_t path_ptr, uint32_t path_len, uint64_t st_atim, uint64_t st_mtim, uint32_t fst_flags) { Debug(wasi, "path_filestat_set_times(%d, %d, %d, %d, %d, %d, %d)\n", fd, flags, path_ptr, path_len, st_atim, st_mtim, fst_flags); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); return uvwasi_path_filestat_set_times(&wasi.uvw_, fd, flags, &memory.data[path_ptr], path_len, st_atim, st_mtim, fst_flags); } uint32_t WASI::PathLink(WASI& wasi, WasmMemory memory, uint32_t old_fd, uint32_t old_flags, uint32_t old_path_ptr, uint32_t old_path_len, uint32_t new_fd, uint32_t new_path_ptr, uint32_t new_path_len) { Debug(wasi, "path_link(%d, %d, %d, %d, %d, %d, %d)\n", old_fd, old_flags, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, old_path_ptr, old_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, new_path_ptr, new_path_len); return uvwasi_path_link(&wasi.uvw_, old_fd, old_flags, &memory.data[old_path_ptr], old_path_len, new_fd, &memory.data[new_path_ptr], new_path_len); } uint32_t WASI::PathOpen(WASI& wasi, WasmMemory memory, uint32_t dirfd, uint32_t dirflags, uint32_t path_ptr, uint32_t path_len, uint32_t o_flags, uint64_t fs_rights_base, uint64_t fs_rights_inheriting, uint32_t fs_flags, uint32_t fd_ptr) { Debug(wasi, "path_open(%d, %d, %d, %d, %d, %d, %d, %d, %d)\n", dirfd, dirflags, path_ptr, path_len, o_flags, fs_rights_base, fs_rights_inheriting, fs_flags, fd_ptr); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, fd_ptr, UVWASI_SERDES_SIZE_fd_t); uvwasi_fd_t fd; uvwasi_errno_t err = uvwasi_path_open(&wasi.uvw_, dirfd, dirflags, &memory.data[path_ptr], path_len, static_cast(o_flags), fs_rights_base, fs_rights_inheriting, static_cast(fs_flags), &fd); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, fd_ptr, fd); return err; } uint32_t WASI::PathReadlink(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t path_ptr, uint32_t path_len, uint32_t buf_ptr, uint32_t buf_len, uint32_t bufused_ptr) { Debug(wasi, "path_readlink(%d, %d, %d, %d, %d, %d)\n", fd, path_ptr, path_len, buf_ptr, buf_len, bufused_ptr); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, buf_ptr, buf_len); CHECK_BOUNDS_OR_RETURN(memory.size, bufused_ptr, UVWASI_SERDES_SIZE_size_t); uvwasi_size_t bufused; uvwasi_errno_t err = uvwasi_path_readlink(&wasi.uvw_, fd, &memory.data[path_ptr], path_len, &memory.data[buf_ptr], buf_len, &bufused); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, bufused_ptr, bufused); return err; } uint32_t WASI::PathRemoveDirectory(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t path_ptr, uint32_t path_len) { Debug(wasi, "path_remove_directory(%d, %d, %d)\n", fd, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); return uvwasi_path_remove_directory( &wasi.uvw_, fd, &memory.data[path_ptr], path_len); } uint32_t WASI::PathRename(WASI& wasi, WasmMemory memory, uint32_t old_fd, uint32_t old_path_ptr, uint32_t old_path_len, uint32_t new_fd, uint32_t new_path_ptr, uint32_t new_path_len) { Debug(wasi, "path_rename(%d, %d, %d, %d, %d, %d)\n", old_fd, old_path_ptr, old_path_len, new_fd, new_path_ptr, new_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, old_path_ptr, old_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, new_path_ptr, new_path_len); return uvwasi_path_rename(&wasi.uvw_, old_fd, &memory.data[old_path_ptr], old_path_len, new_fd, &memory.data[new_path_ptr], new_path_len); } uint32_t WASI::PathSymlink(WASI& wasi, WasmMemory memory, uint32_t old_path_ptr, uint32_t old_path_len, uint32_t fd, uint32_t new_path_ptr, uint32_t new_path_len) { Debug(wasi, "path_symlink(%d, %d, %d, %d, %d)\n", old_path_ptr, old_path_len, fd, new_path_ptr, new_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, old_path_ptr, old_path_len); CHECK_BOUNDS_OR_RETURN(memory.size, new_path_ptr, new_path_len); return uvwasi_path_symlink(&wasi.uvw_, &memory.data[old_path_ptr], old_path_len, fd, &memory.data[new_path_ptr], new_path_len); } uint32_t WASI::PathUnlinkFile(WASI& wasi, WasmMemory memory, uint32_t fd, uint32_t path_ptr, uint32_t path_len) { Debug(wasi, "path_unlink_file(%d, %d, %d)\n", fd, path_ptr, path_len); CHECK_BOUNDS_OR_RETURN(memory.size, path_ptr, path_len); return uvwasi_path_unlink_file( &wasi.uvw_, fd, &memory.data[path_ptr], path_len); } uint32_t WASI::PollOneoff(WASI& wasi, WasmMemory memory, uint32_t in_ptr, uint32_t out_ptr, uint32_t nsubscriptions, uint32_t nevents_ptr) { Debug(wasi, "poll_oneoff(%d, %d, %d, %d)\n", in_ptr, out_ptr, nsubscriptions, nevents_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, in_ptr, nsubscriptions * UVWASI_SERDES_SIZE_subscription_t); CHECK_BOUNDS_OR_RETURN( memory.size, out_ptr, nsubscriptions * UVWASI_SERDES_SIZE_event_t); CHECK_BOUNDS_OR_RETURN(memory.size, nevents_ptr, UVWASI_SERDES_SIZE_size_t); std::vector in(nsubscriptions); std::vector out(nsubscriptions); for (uint32_t i = 0; i < nsubscriptions; ++i) { uvwasi_serdes_read_subscription_t(memory.data, in_ptr, &in[i]); in_ptr += UVWASI_SERDES_SIZE_subscription_t; } uvwasi_size_t nevents; uvwasi_errno_t err = uvwasi_poll_oneoff( &wasi.uvw_, in.data(), out.data(), nsubscriptions, &nevents); if (err == UVWASI_ESUCCESS) { uvwasi_serdes_write_size_t(memory.data, nevents_ptr, nevents); for (uint32_t i = 0; i < nsubscriptions; ++i) { uvwasi_serdes_write_event_t(memory.data, out_ptr, &out[i]); out_ptr += UVWASI_SERDES_SIZE_event_t; } } return err; } void WASI::ProcExit(WASI& wasi, WasmMemory, uint32_t code) { Debug(wasi, "proc_exit(%d)\n", code); uvwasi_proc_exit(&wasi.uvw_, code); } uint32_t WASI::ProcRaise(WASI& wasi, WasmMemory, uint32_t sig) { Debug(wasi, "proc_raise(%d)\n", sig); return uvwasi_proc_raise(&wasi.uvw_, sig); } uint32_t WASI::RandomGet(WASI& wasi, WasmMemory memory, uint32_t buf_ptr, uint32_t buf_len) { Debug(wasi, "random_get(%d, %d)\n", buf_ptr, buf_len); CHECK_BOUNDS_OR_RETURN(memory.size, buf_ptr, buf_len); return uvwasi_random_get(&wasi.uvw_, &memory.data[buf_ptr], buf_len); } uint32_t WASI::SchedYield(WASI& wasi, WasmMemory) { Debug(wasi, "sched_yield()\n"); return uvwasi_sched_yield(&wasi.uvw_); } uint32_t WASI::SockAccept(WASI& wasi, WasmMemory memory, uint32_t sock, uint32_t flags, uint32_t fd_ptr) { Debug(wasi, "sock_accept(%d, %d, %d)\n", sock, flags, fd_ptr); uvwasi_fd_t fd; uvwasi_errno_t err = uvwasi_sock_accept(&wasi.uvw_, sock, flags, &fd); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, fd_ptr, fd); return err; } uint32_t WASI::SockRecv(WASI& wasi, WasmMemory memory, uint32_t sock, uint32_t ri_data_ptr, uint32_t ri_data_len, uint32_t ri_flags, uint32_t ro_datalen_ptr, uint32_t ro_flags_ptr) { Debug(wasi, "sock_recv(%d, %d, %d, %d, %d, %d)\n", sock, ri_data_ptr, ri_data_len, ri_flags, ro_datalen_ptr, ro_flags_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, ri_data_ptr, ri_data_len * UVWASI_SERDES_SIZE_iovec_t); CHECK_BOUNDS_OR_RETURN(memory.size, ro_datalen_ptr, 4); CHECK_BOUNDS_OR_RETURN(memory.size, ro_flags_ptr, 4); std::vector ri_data(ri_data_len); uvwasi_errno_t err = uvwasi_serdes_readv_iovec_t( memory.data, memory.size, ri_data_ptr, ri_data.data(), ri_data_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t ro_datalen; uvwasi_roflags_t ro_flags; err = uvwasi_sock_recv(&wasi.uvw_, sock, ri_data.data(), ri_data_len, ri_flags, &ro_datalen, &ro_flags); if (err == UVWASI_ESUCCESS) { uvwasi_serdes_write_size_t(memory.data, ro_datalen_ptr, ro_datalen); uvwasi_serdes_write_roflags_t(memory.data, ro_flags_ptr, ro_flags); } return err; } uint32_t WASI::SockSend(WASI& wasi, WasmMemory memory, uint32_t sock, uint32_t si_data_ptr, uint32_t si_data_len, uint32_t si_flags, uint32_t so_datalen_ptr) { Debug(wasi, "sock_send(%d, %d, %d, %d, %d)\n", sock, si_data_ptr, si_data_len, si_flags, so_datalen_ptr); CHECK_BOUNDS_OR_RETURN( memory.size, si_data_ptr, si_data_len * UVWASI_SERDES_SIZE_ciovec_t); CHECK_BOUNDS_OR_RETURN( memory.size, so_datalen_ptr, UVWASI_SERDES_SIZE_size_t); std::vector si_data(si_data_len); uvwasi_errno_t err = uvwasi_serdes_readv_ciovec_t( memory.data, memory.size, si_data_ptr, si_data.data(), si_data_len); if (err != UVWASI_ESUCCESS) { return err; } uvwasi_size_t so_datalen; err = uvwasi_sock_send( &wasi.uvw_, sock, si_data.data(), si_data_len, si_flags, &so_datalen); if (err == UVWASI_ESUCCESS) uvwasi_serdes_write_size_t(memory.data, so_datalen_ptr, so_datalen); return err; } uint32_t WASI::SockShutdown(WASI& wasi, WasmMemory, uint32_t sock, uint32_t how) { Debug(wasi, "sock_shutdown(%d, %d)\n", sock, how); return uvwasi_sock_shutdown(&wasi.uvw_, sock, how); } void WASI::_SetMemory(const FunctionCallbackInfo& args) { WASI* wasi; ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); CHECK_EQ(args.Length(), 1); if (!args[0]->IsWasmMemoryObject()) { return node::THROW_ERR_INVALID_ARG_TYPE( wasi->env(), "\"instance.exports.memory\" property must be a WebAssembly.Memory " "object"); } wasi->memory_.Reset(wasi->env()->isolate(), args[0].As()); } static void InitializePreview1(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); Isolate* isolate = env->isolate(); Local tmpl = NewFunctionTemplate(isolate, WASI::New); tmpl->InstanceTemplate()->SetInternalFieldCount(WASI::kInternalFieldCount); #define V(F, name) \ SetFunction(WASI::F, env, name, tmpl); V(ArgsGet, "args_get") V(ArgsSizesGet, "args_sizes_get") V(ClockResGet, "clock_res_get") V(ClockTimeGet, "clock_time_get") V(EnvironGet, "environ_get") V(EnvironSizesGet, "environ_sizes_get") V(FdAdvise, "fd_advise") V(FdAllocate, "fd_allocate") V(FdClose, "fd_close") V(FdDatasync, "fd_datasync") V(FdFdstatGet, "fd_fdstat_get") V(FdFdstatSetFlags, "fd_fdstat_set_flags") V(FdFdstatSetRights, "fd_fdstat_set_rights") V(FdFilestatGet, "fd_filestat_get") V(FdFilestatSetSize, "fd_filestat_set_size") V(FdFilestatSetTimes, "fd_filestat_set_times") V(FdPread, "fd_pread") V(FdPrestatGet, "fd_prestat_get") V(FdPrestatDirName, "fd_prestat_dir_name") V(FdPwrite, "fd_pwrite") V(FdRead, "fd_read") V(FdReaddir, "fd_readdir") V(FdRenumber, "fd_renumber") V(FdSeek, "fd_seek") V(FdSync, "fd_sync") V(FdTell, "fd_tell") V(FdWrite, "fd_write") V(PathCreateDirectory, "path_create_directory") V(PathFilestatGet, "path_filestat_get") V(PathFilestatSetTimes, "path_filestat_set_times") V(PathLink, "path_link") V(PathOpen, "path_open") V(PathReadlink, "path_readlink") V(PathRemoveDirectory, "path_remove_directory") V(PathRename, "path_rename") V(PathSymlink, "path_symlink") V(PathUnlinkFile, "path_unlink_file") V(PollOneoff, "poll_oneoff") V(ProcExit, "proc_exit") V(ProcRaise, "proc_raise") V(RandomGet, "random_get") V(SchedYield, "sched_yield") V(SockAccept, "sock_accept") V(SockRecv, "sock_recv") V(SockSend, "sock_send") V(SockShutdown, "sock_shutdown") #undef V SetInstanceMethod(isolate, tmpl, "_setMemory", WASI::_SetMemory); SetConstructorFunction(context, target, "WASI", tmpl); } } // namespace wasi } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL(wasi, node::wasi::InitializePreview1)