Harden dynamic HTTP and compiler boundaries
This commit is contained in:
+154
-51
@@ -39,6 +39,7 @@
|
||||
#include <string>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
@@ -55,6 +56,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#include <poll.h>
|
||||
#include "hardened_http_internal.h"
|
||||
|
||||
struct WasmDylinkInfo
|
||||
{
|
||||
@@ -390,8 +392,6 @@ static u64 wasm_socket_connect_bounded(const String& host, u16 port, u64 timeout
|
||||
}
|
||||
if(fd <= 0)
|
||||
return(0);
|
||||
if(context)
|
||||
context->resources.sockets.push_back(fd);
|
||||
return((u64)fd);
|
||||
}
|
||||
|
||||
@@ -622,7 +622,7 @@ static u64 uce_shell_spawn_spec(const DValue& spec)
|
||||
}
|
||||
|
||||
|
||||
static DValue uce_exec_argv_capture(std::vector<String> argv, String input, u64 timeout_ms)
|
||||
static DValue uce_exec_argv_capture(std::vector<String> argv, String input, u64 timeout_ms, size_t output_limit=0, bool clean_env=false)
|
||||
{
|
||||
DValue r; r["exit_code"]=(f64)-1; r["stdout"]=""; r["stderr"]=""; r["timed_out"].set_bool(false);
|
||||
if(argv.empty()) { r["stderr"]="empty argv"; return(r); }
|
||||
@@ -639,7 +639,9 @@ static DValue uce_exec_argv_capture(std::vector<String> argv, String input, u64
|
||||
dup2(inpipe[0],0); dup2(outpipe[1],1); dup2(errpipe[1],2);
|
||||
close(inpipe[0]); close(inpipe[1]); close(outpipe[0]); close(outpipe[1]); close(errpipe[0]); close(errpipe[1]);
|
||||
std::vector<char*> args; for(auto& a: argv) args.push_back((char*)a.c_str()); args.push_back(0);
|
||||
execvp(args[0], args.data()); _exit(127);
|
||||
if(clean_env) { clearenv(); setenv("PATH", "/usr/bin:/bin", 1); execv(args[0], args.data()); }
|
||||
else execvp(args[0], args.data());
|
||||
_exit(127);
|
||||
}
|
||||
if(pid < 0)
|
||||
{
|
||||
@@ -655,8 +657,8 @@ static DValue uce_exec_argv_capture(std::vector<String> argv, String input, u64
|
||||
{
|
||||
if(!exited) { pid_t w=waitpid(pid,&status,WNOHANG); if(w==pid) { exited=true; status_valid=true; } else if(w<0&&errno==ECHILD) { u64 transfer_deadline=wasm_deadline_after_ms(50); do { status_valid=child_exit_status_take(pid,status,child_status_snapshot); if(!status_valid)sched_yield(); } while(!status_valid&&wasm_monotonic_ms()<transfer_deadline); exited=true; if(!status_valid)r["stderr"]=r["stderr"].to_string()+"lost child exit status"; } }
|
||||
if(in_open) { if(input_off<input.size()) { ssize_t n=write(inpipe[1], input.data()+input_off, input.size()-input_off); if(n>0) input_off+=(size_t)n; else if(n<0 && errno!=EINTR && errno!=EAGAIN && errno!=EWOULDBLOCK) { close(inpipe[1]); in_open=false; } } else { close(inpipe[1]); in_open=false; } }
|
||||
char buf[4096]; ssize_t n; while((n=read(outpipe[0],buf,sizeof(buf)))>0) r["stdout"] = r["stdout"].to_string()+String(buf,n); if(n==0&&out_open){close(outpipe[0]);out_open=false;}
|
||||
while((n=read(errpipe[0],buf,sizeof(buf)))>0) r["stderr"] = r["stderr"].to_string()+String(buf,n); if(n==0&&err_open){close(errpipe[0]);err_open=false;}
|
||||
char buf[4096]; ssize_t n; while((n=read(outpipe[0],buf,sizeof(buf)))>0) { if(output_limit && r["stdout"].to_string().size()+(size_t)n>output_limit) { r["output_limited"].set_bool(true); kill(-pid,SIGKILL); kill(pid,SIGKILL); } else r["stdout"] = r["stdout"].to_string()+String(buf,n); } if(n==0&&out_open){close(outpipe[0]);out_open=false;}
|
||||
while((n=read(errpipe[0],buf,sizeof(buf)))>0) { if(output_limit && r["stderr"].to_string().size()+(size_t)n>output_limit) { r["output_limited"].set_bool(true); kill(-pid,SIGKILL); kill(pid,SIGKILL); } else r["stderr"] = r["stderr"].to_string()+String(buf,n); } if(n==0&&err_open){close(errpipe[0]);err_open=false;}
|
||||
if((out_open || err_open || !exited) && wasm_monotonic_ms() >= deadline) { r["timed_out"].set_bool(true); kill(-pid,SIGKILL); kill(pid,SIGKILL); if(!exited) status_valid=waitpid(pid,&status,0)==pid; exited=true; if(in_open){close(inpipe[1]);in_open=false;} if(out_open){close(outpipe[0]);out_open=false;} if(err_open){close(errpipe[0]);err_open=false;} }
|
||||
if(out_open || err_open || !exited) usleep(10000);
|
||||
}
|
||||
@@ -671,8 +673,11 @@ static bool uce_header_name_safe(String name)
|
||||
return(true);
|
||||
}
|
||||
|
||||
static DValue uce_hardened_http_request_value(const DValue& req, u64 timeout_ms, bool keep_worker_process_group=false);
|
||||
|
||||
static DValue uce_http_request_value(const DValue& req)
|
||||
{
|
||||
const DValue* security=req.key("security"); if(hardened_http_security_requested(security)) { u64 requested=req.key("timeout_ms")?req.key("timeout_ms")->to_u64(5000):5000; return(uce_hardened_http_request_value(req,std::max<u64>(1,requested))); }
|
||||
DValue r; r["status"]=(f64)0; r["headers"].set_array(); r["body"]=""; r["error"]="";
|
||||
const DValue* method_value = req.key("method");
|
||||
const DValue* url_value = req.key("url");
|
||||
@@ -706,19 +711,35 @@ static DValue uce_http_request_value(const DValue& req)
|
||||
return(r);
|
||||
}
|
||||
|
||||
static DValue uce_hardened_http_request_value(const DValue& req, u64 timeout_ms, bool keep_worker_process_group)
|
||||
{
|
||||
HardenedHttpHooks hooks;
|
||||
hooks.resolve=[](String host) { std::vector<String> answers; addrinfo hints{}; hints.ai_socktype=SOCK_STREAM; hints.ai_family=AF_UNSPEC; addrinfo* result=0; if(getaddrinfo(host.c_str(), "443", &hints, &result)!=0) return answers; for(addrinfo* p=result;p;p=p->ai_next) { char text[INET6_ADDRSTRLEN]; if(p->ai_family==AF_INET && inet_ntop(AF_INET,&((sockaddr_in*)p->ai_addr)->sin_addr,text,sizeof(text))) answers.push_back(text); else if(p->ai_family==AF_INET6 && inet_ntop(AF_INET6,&((sockaddr_in6*)p->ai_addr)->sin6_addr,text,sizeof(text))) answers.push_back(text); else answers.push_back(""); } freeaddrinfo(result); return answers; };
|
||||
hooks.execute=[keep_worker_process_group](std::vector<String> argv, String input, std::vector<String>, u64 deadline, size_t limit) { return hardened_http_exec_argv_capture(argv,input,deadline,limit,true,!keep_worker_process_group); };
|
||||
return hardened_http_request_internal(req,timeout_ms,hooks);
|
||||
}
|
||||
|
||||
static u64 uce_http_spawn_spec(const DValue& req)
|
||||
{
|
||||
uce_job_reap(); u64 id=uce_job_new("http"); if(!id) return(0);
|
||||
int ready[2]; if(pipe(ready)) { DValue r; r["error"]="pipe failed"; uce_job_finish(id,r,"failed"); return(id); }
|
||||
pid_t pid=fork();
|
||||
if(pid==0) { setsid(); uce_write_text(uce_job_path(id)+"/worker_pid", std::to_string((long long)getpid())); uce_write_text(uce_job_path(id)+"/state", "running"); DValue result=uce_http_request_value(req); uce_job_finish(id,result,result["error"].to_string()==""?"done":"failed"); _exit(0); }
|
||||
if(pid<0) { DValue r; r["error"]="fork failed"; uce_job_finish(id,r,"failed"); return(id); }
|
||||
if(pid==0) { close(ready[0]); if(setsid()<0) _exit(127); char ok='1'; if(write(ready[1],&ok,1)!=1) _exit(127); close(ready[1]); uce_write_text(uce_job_path(id)+"/worker_pid", std::to_string((long long)getpid())); uce_write_text(uce_job_path(id)+"/state", "running"); const DValue* security=req.key("security"); bool hardened=hardened_http_security_requested(security); DValue result=hardened ? uce_hardened_http_request_value(req,req.key("timeout_ms")?std::max<u64>(1,req.key("timeout_ms")->to_u64(5000)):5000,true) : uce_http_request_value(req); uce_job_finish(id,result,result["error"].to_string()==""?"done":"failed"); _exit(0); }
|
||||
close(ready[1]); char ok=0; ssize_t started; do { started=read(ready[0],&ok,1); } while(started<0&&errno==EINTR); close(ready[0]);
|
||||
if(pid<0 || started!=1 || ok!='1') { DValue r; r["error"]="async worker start failed"; uce_job_finish(id,r,"failed"); return(id); }
|
||||
uce_write_text(uce_job_path(id)+"/worker_pid", std::to_string((long long)pid)); uce_write_text(uce_job_path(id)+"/state", "running"); return(id);
|
||||
}
|
||||
|
||||
static DValue uce_job_status_value(u64 id)
|
||||
{
|
||||
DValue r; String dir=uce_job_path(id); r["job_id"]=(f64)id;
|
||||
if(id==0 || !std::filesystem::is_directory(dir)) { r["state"]="missing"; return(r); }
|
||||
bool is_directory = false;
|
||||
if(id != 0)
|
||||
{
|
||||
try { is_directory = std::filesystem::is_directory(dir); }
|
||||
catch(...) { is_directory = false; }
|
||||
}
|
||||
if(!is_directory) { r["state"]="missing"; return(r); }
|
||||
String state=trim(uce_read_text(dir+"/state")); if(state=="") state="pending"; r["state"]=state;
|
||||
r["kind"]=trim(uce_read_text(dir+"/kind")); r["pid"]=(f64)strtoull(uce_read_text(dir+"/worker_pid").c_str(),0,10);
|
||||
r["done"].set_bool(state=="done"||state=="failed"||state=="cancelled");
|
||||
@@ -1868,6 +1889,7 @@ public:
|
||||
bool writable = false;
|
||||
};
|
||||
std::vector<FileHandle> file_handles;
|
||||
std::vector<int> socket_fds;
|
||||
|
||||
struct RequestPerfSnapshot
|
||||
{
|
||||
@@ -2025,7 +2047,9 @@ public:
|
||||
// the wasm-side enforcement of request-scoped DB lifecycle; app code should
|
||||
// never cache these opaque handles across requests.
|
||||
std::vector<SQLite*> sqlite_handles;
|
||||
std::vector<MySQL*> mysql_handles;
|
||||
std::map<u64, MySQL*> mysql_handles;
|
||||
std::set<u64> mysql_task_handles;
|
||||
u64 mysql_next_handle = 1;
|
||||
std::vector<MySQL*> mysql_request_pool;
|
||||
std::vector<MySQL*> mysql_request_owned;
|
||||
#endif
|
||||
@@ -2042,6 +2066,14 @@ public:
|
||||
h.fd = -1;
|
||||
}
|
||||
}
|
||||
for(auto& fd : socket_fds)
|
||||
{
|
||||
if(fd >= 0)
|
||||
{
|
||||
::socket_close((u64)fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
#ifdef UCE_WASM_HOST_CONNECTORS
|
||||
for(auto* db : sqlite_handles)
|
||||
if(db)
|
||||
@@ -3682,15 +3714,20 @@ private:
|
||||
int32_t input_size = args[1].i32();
|
||||
u32 cap = (u32)args[3].i32();
|
||||
int32_t buf = args[2].i32();
|
||||
String stage_key = "crypto_operation";
|
||||
if(!self->hostcall_staged(stage_key, out))
|
||||
if(input_size > 0 && input_size <= 64 * 1024 && self->hostcall_read(args[0].i32(), input_size, encoded) == "" && ucb_decode(encoded, request, &error))
|
||||
{
|
||||
if(input_size > 0 && input_size <= 64 * 1024 && self->hostcall_read(args[0].i32(), input_size, encoded) == "" && ucb_decode(encoded, request, &error))
|
||||
String stage_key = "crypto_operation:" + encoded;
|
||||
if(!self->hostcall_staged(stage_key, out))
|
||||
{
|
||||
response = crypto_operation_native(request);
|
||||
else
|
||||
response["error"] = "invalid_request";
|
||||
out = ucb_encode(response);
|
||||
if(buf == 0) self->hostcall_stage(stage_key, out);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response["error"] = "invalid_request";
|
||||
out = ucb_encode(response);
|
||||
if(buf == 0) self->hostcall_stage(stage_key, out);
|
||||
}
|
||||
if(buf && cap >= out.size()) self->hostcall_write(buf, out);
|
||||
results[0] = Val((int32_t)out.size());
|
||||
@@ -4122,7 +4159,7 @@ private:
|
||||
if(mod == "env" && name == "uce_host_file_truncate")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); results[0]=Val((int32_t)(r!=""&&truncate(r.c_str(),(off_t)args[4].i64())==0)); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_dir_remove")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); bool rec=args[4].i32()!=0; bool ok=false; if(r!="") { if(rec) ok=std::filesystem::remove_all(r)>0; else ok=::rmdir(r.c_str())==0; } results[0]=Val((int32_t)ok); return(std::monostate()); }));
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String path,current; self->hostcall_read(args[0].i32(),args[1].i32(),path); self->hostcall_read(args[2].i32(),args[3].i32(),current); String r=self->resolve_guest_write(path,current); bool rec=args[4].i32()!=0; bool ok=false; if(r!="") { if(rec) { try { ok=std::filesystem::remove_all(r)>0; } catch(...) { ok=false; } } else ok=::rmdir(r.c_str())==0; } results[0]=Val((int32_t)ok); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_temp")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> { String prefix,current; self->hostcall_read(args[0].i32(),args[1].i32(),prefix); self->hostcall_read(args[2].i32(),args[3].i32(),current); u32 cap=(u32)args[5].i32(); int32_t buf=args[4].i32(); String out; String stage_key="file_temp:"+prefix+"\0"+current; if(!self->hostcall_staged(stage_key,out)) { if(prefix=="") prefix="/tmp/uce-temp"; String templ=self->resolve_guest_write(prefix+"XXXXXX",current); if(templ!="") { std::vector<char> t(templ.begin(), templ.end()); t.push_back(0); int fd=mkstemp(t.data()); if(fd>=0) { close(fd); out=t.data(); } } if(buf==0) self->hostcall_stage(stage_key,out); } if(buf&&cap>=out.size()) self->hostcall_write(buf,out); results[0]=Val((int32_t)out.size()); return(std::monostate()); }));
|
||||
if(mod == "env" && name == "uce_host_file_chmod")
|
||||
@@ -4332,9 +4369,15 @@ private:
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_memcache_command")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
if(handle < 1 || handle > self->socket_fds.size() || self->socket_fds[(size_t)handle - 1] < 0)
|
||||
{
|
||||
results[0] = Val((int32_t)0);
|
||||
return(std::monostate());
|
||||
}
|
||||
String command;
|
||||
self->hostcall_read(args[1].i32(), args[2].i32(), command);
|
||||
String key = "memcache:" + std::to_string((u64)args[0].i64()) + ":" + command;
|
||||
String key = "memcache:" + std::to_string(handle) + ":" + command;
|
||||
u32 cap = (u32)args[4].i32();
|
||||
int32_t buf = args[3].i32();
|
||||
String out;
|
||||
@@ -4346,7 +4389,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
u64 socket_fd = (u64)args[0].i64();
|
||||
u64 socket_fd = (u64)self->socket_fds[(size_t)handle - 1];
|
||||
out = wasm_memcache_exchange(socket_fd, command, self->bounded_hostcall_timeout_ms(1000));
|
||||
if(buf == 0)
|
||||
{
|
||||
@@ -4384,41 +4427,60 @@ private:
|
||||
String password = request["password"].to_string();
|
||||
String database = request["database"].to_string();
|
||||
MySQL* db = 0;
|
||||
for(auto* pooled : self->mysql_request_pool)
|
||||
if(pooled && pooled->connection && pooled->request_host == host && pooled->request_username == username && pooled->request_password == password && pooled->request_database == database)
|
||||
{
|
||||
db = pooled;
|
||||
connection_source = "request";
|
||||
break;
|
||||
}
|
||||
bool ok = db != 0;
|
||||
if(!db)
|
||||
bool ok = false;
|
||||
if(task_child_process)
|
||||
{
|
||||
bool reused = false;
|
||||
bool persistent = false;
|
||||
db = self->worker.mysql_checkout(host, username, password, database, reused, persistent);
|
||||
connection_source = reused ? "worker" : "new";
|
||||
ok = db && db->connection;
|
||||
if(ok && db->connection)
|
||||
// task() closes inherited descriptors after fork. Never reuse
|
||||
// request/worker MySQL objects whose sockets were just closed.
|
||||
db = new MySQL();
|
||||
ok = db->connect(host, username, password, database);
|
||||
connection_source = "task";
|
||||
if(ok)
|
||||
{
|
||||
self->mysql_request_pool.push_back(db);
|
||||
if(!persistent)
|
||||
self->mysql_request_owned.push_back(db);
|
||||
self->mysql_request_owned.push_back(db);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(auto* pooled : self->mysql_request_pool)
|
||||
if(pooled && pooled->connection && pooled->request_host == host && pooled->request_username == username && pooled->request_password == password && pooled->request_database == database)
|
||||
{
|
||||
db = pooled;
|
||||
connection_source = "request";
|
||||
break;
|
||||
}
|
||||
ok = db != 0;
|
||||
if(!db)
|
||||
{
|
||||
bool reused = false;
|
||||
bool persistent = false;
|
||||
db = self->worker.mysql_checkout(host, username, password, database, reused, persistent);
|
||||
connection_source = reused ? "worker" : "new";
|
||||
ok = db && db->connection;
|
||||
if(ok)
|
||||
{
|
||||
self->mysql_request_pool.push_back(db);
|
||||
if(!persistent)
|
||||
self->mysql_request_owned.push_back(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
u64 handle = 0;
|
||||
if(ok && db->connection)
|
||||
if(ok && db->connection && self->mysql_next_handle != 0)
|
||||
{
|
||||
db->request_leases++;
|
||||
self->mysql_handles.push_back(db);
|
||||
handle = self->mysql_handles.size();
|
||||
handle = self->mysql_next_handle;
|
||||
self->mysql_next_handle = handle == UINT64_MAX ? 0 : handle + 1;
|
||||
self->mysql_handles[handle] = db;
|
||||
if(task_child_process) self->mysql_task_handles.insert(handle);
|
||||
if(connection_source == "new") self->mysql_connection_open_count++;
|
||||
else if(connection_source == "worker") self->mysql_connection_reuse_count++;
|
||||
else if(connection_source == "request") self->mysql_request_pool_hit_count++;
|
||||
}
|
||||
response["handle"] = (f64)handle;
|
||||
response["error_code"] = (f64)db->_preload_next_error_code;
|
||||
response["statement_info"] = db->error();
|
||||
response["error_code"] = (f64)(handle == 0 ? 2000 : db->_preload_next_error_code);
|
||||
response["statement_info"] = handle == 0 ? String("mysql handle space exhausted") : db->error();
|
||||
if(handle == 0 && db)
|
||||
delete db;
|
||||
}
|
||||
@@ -4430,8 +4492,8 @@ private:
|
||||
else
|
||||
{
|
||||
u64 handle = request["handle"].to_u64();
|
||||
MySQL* db = (handle >= 1 && handle <= self->mysql_handles.size())
|
||||
? self->mysql_handles[(size_t)handle - 1] : 0;
|
||||
auto handle_it = self->mysql_handles.find(handle);
|
||||
MySQL* db = handle_it == self->mysql_handles.end() || (task_child_process && self->mysql_task_handles.find(handle) == self->mysql_task_handles.end()) ? 0 : handle_it->second;
|
||||
if(op == "query" && db)
|
||||
{
|
||||
response["result"] = db->query(request["query"].to_string());
|
||||
@@ -4444,7 +4506,20 @@ private:
|
||||
{
|
||||
if(db->request_leases > 0)
|
||||
db->request_leases--;
|
||||
self->mysql_handles[(size_t)handle - 1] = 0;
|
||||
self->mysql_handles.erase(handle);
|
||||
self->mysql_task_handles.erase(handle);
|
||||
if(task_child_process && db->request_leases == 0)
|
||||
{
|
||||
auto erase_db = [db](auto& pool) { pool.erase(std::remove(pool.begin(), pool.end(), db), pool.end()); };
|
||||
erase_db(self->mysql_request_pool);
|
||||
erase_db(self->mysql_request_owned);
|
||||
delete db;
|
||||
}
|
||||
}
|
||||
else if(op == "query" || op == "disconnect")
|
||||
{
|
||||
response["error_code"] = (f64)2000;
|
||||
response["statement_info"] = "mysql handle is invalid or unavailable in this task";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4486,24 +4561,52 @@ private:
|
||||
String host;
|
||||
self->hostcall_read(args[0].i32(), args[1].i32(), host);
|
||||
u64 fd = wasm_socket_connect_bounded(host, (u16)args[2].i32(), self->bounded_hostcall_timeout_ms(self->worker.cfg.invocation_timeout_ms));
|
||||
results[0] = Val((int64_t)fd);
|
||||
u64 handle = 0;
|
||||
if(fd > 0)
|
||||
{
|
||||
self->socket_fds.push_back((int)fd);
|
||||
handle = self->socket_fds.size();
|
||||
}
|
||||
results[0] = Val((int64_t)handle);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_socket_close")
|
||||
return(add([](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
::socket_close((u64)args[0].i64());
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val>) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
if(handle >= 1 && handle <= self->socket_fds.size())
|
||||
{
|
||||
int& fd = self->socket_fds[(size_t)handle - 1];
|
||||
if(fd >= 0)
|
||||
{
|
||||
::socket_close((u64)fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_socket_write")
|
||||
return(add([self](Caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 handle = (u64)args[0].i64();
|
||||
String data;
|
||||
self->hostcall_read(args[1].i32(), args[2].i32(), data);
|
||||
results[0] = Val(wasm_socket_write_bounded((u64)args[0].i64(), data, self->bounded_hostcall_timeout_ms(self->worker.cfg.invocation_timeout_ms)) ? (int32_t)1 : (int32_t)0);
|
||||
bool ok = false;
|
||||
if(handle >= 1 && handle <= self->socket_fds.size())
|
||||
{
|
||||
int fd = self->socket_fds[(size_t)handle - 1];
|
||||
ok = fd >= 0 && wasm_socket_write_bounded((u64)fd, data, self->bounded_hostcall_timeout_ms(self->worker.cfg.invocation_timeout_ms));
|
||||
}
|
||||
results[0] = Val(ok ? (int32_t)1 : (int32_t)0);
|
||||
return(std::monostate());
|
||||
}));
|
||||
if(mod == "env" && name == "uce_host_socket_read")
|
||||
return(add([self](Caller caller, Span<const Val> args, Span<Val> results) -> Result<std::monostate, Trap> {
|
||||
u64 sockfd = (u64)args[0].i64();
|
||||
u64 handle = (u64)args[0].i64();
|
||||
if(handle < 1 || handle > self->socket_fds.size() || self->socket_fds[(size_t)handle - 1] < 0)
|
||||
{
|
||||
results[0] = Val((int32_t)0);
|
||||
return(std::monostate());
|
||||
}
|
||||
u64 sockfd = (u64)self->socket_fds[(size_t)handle - 1];
|
||||
u32 max_length = (u32)args[1].i32();
|
||||
u32 requested_timeout = (u32)args[2].i32();
|
||||
u64 requested_ms = requested_timeout == 0 ? self->invocation_remaining_ms() : (u64)requested_timeout * 1000;
|
||||
@@ -4511,7 +4614,7 @@ private:
|
||||
int32_t buf = args[3].i32();
|
||||
u32 cap = (u32)args[4].i32();
|
||||
// The size and fetch calls share this key, while the remaining budget may change between them.
|
||||
String key = std::to_string(sockfd) + ":" + std::to_string(max_length) + ":" + std::to_string(requested_timeout);
|
||||
String key = std::to_string(handle) + ":" + std::to_string(max_length) + ":" + std::to_string(requested_timeout);
|
||||
String out;
|
||||
if(buf != 0 && self->staged_socket_read_key == key)
|
||||
{
|
||||
@@ -4578,7 +4681,7 @@ private:
|
||||
// before the hostcall stack unwinds, so `self` points to the child's
|
||||
// copy of this per-request workspace. The parent request can return and
|
||||
// destroy its workspace without invalidating the child copy.
|
||||
u64 task_timeout_ms = timeout > UINT64_MAX / 1000 ? UINT64_MAX : timeout * 1000;
|
||||
u64 task_timeout_ms = timeout == 0 ? UINT64_MAX : (timeout > UINT64_MAX / 1000 ? UINT64_MAX : timeout * 1000);
|
||||
auto run_callback = [self, callback_id, task_timeout_ms]() {
|
||||
String error = self->run_task_callback(callback_id, task_timeout_ms);
|
||||
if(error != "")
|
||||
|
||||
Reference in New Issue
Block a user