mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Contract process resource limits. (#243)
This commit is contained in:
@@ -138,6 +138,9 @@ struct hp_round_limits_config
|
||||
size_t user_input_bytes;
|
||||
size_t user_output_bytes;
|
||||
size_t npl_output_bytes;
|
||||
size_t proc_cpu_seconds;
|
||||
size_t proc_mem_bytes;
|
||||
size_t proc_ofd_count;
|
||||
};
|
||||
|
||||
struct hp_config
|
||||
@@ -505,7 +508,8 @@ int hp_update_config(const struct hp_config *config)
|
||||
if (!config->npl || strlen(config->npl) == 0 || (strcmp(config->npl, "public") != 0 && strcmp(config->npl, "private")) != 0)
|
||||
__HP_UPDATE_CONFIG_ERROR("Invalid npl flag. Valid values: public|private");
|
||||
|
||||
if (config->round_limits.user_input_bytes < 0 || config->round_limits.user_output_bytes < 0 || config->round_limits.npl_output_bytes < 0)
|
||||
if (config->round_limits.user_input_bytes < 0 || config->round_limits.user_output_bytes < 0 || config->round_limits.npl_output_bytes < 0 ||
|
||||
config->round_limits.proc_cpu_seconds < 0 || config->round_limits.proc_mem_bytes < 0 || config->round_limits.proc_ofd_count < 0)
|
||||
__HP_UPDATE_CONFIG_ERROR("Invalid round limits.");
|
||||
|
||||
const int fd = open(PATCH_FILE_PATH, O_RDWR);
|
||||
@@ -634,7 +638,8 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config)
|
||||
|
||||
// Top-level field values.
|
||||
|
||||
const char *json_string = " \"bin_path\": \"%s\",\n \"bin_args\": \"%s\",\n \"roundtime\": %s,\n \"consensus\": \"%s\",\n \"npl\": \"%s\",\n";
|
||||
const char *json_string = " \"bin_path\": \"%s\",\n \"bin_args\": \"%s\",\n \"roundtime\": %s,\n"
|
||||
" \"consensus\": \"%s\",\n \"npl\": \"%s\",\n";
|
||||
|
||||
char roundtime_str[16];
|
||||
sprintf(roundtime_str, "%d", config->roundtime);
|
||||
@@ -656,16 +661,27 @@ int __hp_write_to_patch_file(const int fd, const struct hp_config *config)
|
||||
|
||||
// Round limits field valies.
|
||||
|
||||
const char *round_limits_json = " \"round_limits\": {\n \"user_input_bytes\": %s,\n \"user_output_bytes\": %s,\n \"npl_output_bytes\": %s\n }\n}";
|
||||
const char *round_limits_json = " \"round_limits\": {\n"
|
||||
" \"user_input_bytes\": %s,\n \"user_output_bytes\": %s,\n \"npl_output_bytes\": %s,\n"
|
||||
" \"proc_cpu_seconds\": %s,\n \"proc_mem_bytes\": %s,\n \"proc_ofd_count\": %s\n }\n}";
|
||||
|
||||
char user_input_bytes_str[20], user_output_bytes_str[20], npl_output_bytes_str[20],
|
||||
proc_cpu_seconds_str[20], proc_mem_bytes_str[20], proc_ofd_count_str[20];
|
||||
|
||||
char user_input_bytes_str[20], user_output_bytes_str[20], npl_output_bytes_str[20];
|
||||
sprintf(user_input_bytes_str, "%" PRIu64, config->round_limits.user_input_bytes);
|
||||
sprintf(user_output_bytes_str, "%" PRIu64, config->round_limits.user_output_bytes);
|
||||
sprintf(npl_output_bytes_str, "%" PRIu64, config->round_limits.npl_output_bytes);
|
||||
|
||||
const size_t round_limits_json_len = 119 + strlen(user_input_bytes_str) + strlen(user_output_bytes_str) + strlen(npl_output_bytes_str);
|
||||
sprintf(proc_cpu_seconds_str, "%" PRIu64, config->round_limits.proc_cpu_seconds);
|
||||
sprintf(proc_mem_bytes_str, "%" PRIu64, config->round_limits.proc_mem_bytes);
|
||||
sprintf(proc_ofd_count_str, "%" PRIu64, config->round_limits.proc_ofd_count);
|
||||
|
||||
const size_t round_limits_json_len = 205 + strlen(user_input_bytes_str) + strlen(user_output_bytes_str) + strlen(npl_output_bytes_str) +
|
||||
strlen(proc_cpu_seconds_str) + strlen(proc_mem_bytes_str) + strlen(proc_ofd_count_str);
|
||||
char round_limits_buf[round_limits_json_len];
|
||||
sprintf(round_limits_buf, round_limits_json, user_input_bytes_str, user_output_bytes_str, npl_output_bytes_str);
|
||||
sprintf(round_limits_buf, round_limits_json,
|
||||
user_input_bytes_str, user_output_bytes_str, npl_output_bytes_str,
|
||||
proc_cpu_seconds_str, proc_mem_bytes_str, proc_ofd_count_str);
|
||||
iov_vec[4].iov_base = round_limits_buf;
|
||||
iov_vec[4].iov_len = round_limits_json_len;
|
||||
|
||||
@@ -769,6 +785,18 @@ void __hp_populate_patch_from_json_object(struct hp_config *config, const struct
|
||||
{
|
||||
__HP_ASSIGN_UINT64(config->round_limits.npl_output_bytes, sub_ele);
|
||||
}
|
||||
else if (strcmp(sub_ele->name->string, "proc_cpu_seconds") == 0)
|
||||
{
|
||||
__HP_ASSIGN_UINT64(config->round_limits.proc_cpu_seconds, sub_ele);
|
||||
}
|
||||
else if (strcmp(sub_ele->name->string, "proc_mem_bytes") == 0)
|
||||
{
|
||||
__HP_ASSIGN_UINT64(config->round_limits.proc_mem_bytes, sub_ele);
|
||||
}
|
||||
else if (strcmp(sub_ele->name->string, "proc_ofd_count") == 0)
|
||||
{
|
||||
__HP_ASSIGN_UINT64(config->round_limits.proc_ofd_count, sub_ele);
|
||||
}
|
||||
sub_ele = sub_ele->next;
|
||||
} while (sub_ele);
|
||||
}
|
||||
|
||||
@@ -144,7 +144,8 @@ class PatchConfig {
|
||||
throw "Invalid consensus flag configured in patch file. Valid values: public|private";
|
||||
if (config.npl != "public" && config.npl != "private")
|
||||
throw "Invalid npl flag configured in patch file. Valid values: public|private";
|
||||
if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0)
|
||||
if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0 ||
|
||||
config.round_limits.proc_cpu_seconds < 0 || config.round_limits.proc_mem_bytes < 0 || config.round_limits.proc_ofd_count < 0)
|
||||
throw "Invalid round limits.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,6 +859,9 @@ namespace conf
|
||||
round_limits.insert_or_assign("user_input_bytes", contract.round_limits.user_input_bytes);
|
||||
round_limits.insert_or_assign("user_output_bytes", contract.round_limits.user_output_bytes);
|
||||
round_limits.insert_or_assign("npl_output_bytes", contract.round_limits.npl_output_bytes);
|
||||
round_limits.insert_or_assign("proc_cpu_seconds", contract.round_limits.proc_cpu_seconds);
|
||||
round_limits.insert_or_assign("proc_mem_bytes", contract.round_limits.proc_mem_bytes);
|
||||
round_limits.insert_or_assign("proc_ofd_count", contract.round_limits.proc_ofd_count);
|
||||
jdoc.insert_or_assign("round_limits", round_limits);
|
||||
}
|
||||
|
||||
@@ -945,6 +948,9 @@ namespace conf
|
||||
contract.round_limits.user_input_bytes = jdoc["round_limits"]["user_input_bytes"].as<size_t>();
|
||||
contract.round_limits.user_output_bytes = jdoc["round_limits"]["user_output_bytes"].as<size_t>();
|
||||
contract.round_limits.npl_output_bytes = jdoc["round_limits"]["npl_output_bytes"].as<size_t>();
|
||||
contract.round_limits.proc_cpu_seconds = jdoc["round_limits"]["proc_cpu_seconds"].as<size_t>();
|
||||
contract.round_limits.proc_mem_bytes = jdoc["round_limits"]["proc_mem_bytes"].as<size_t>();
|
||||
contract.round_limits.proc_ofd_count = jdoc["round_limits"]["proc_ofd_count"].as<size_t>();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
||||
@@ -90,6 +90,9 @@ namespace conf
|
||||
size_t user_input_bytes = 0; // Max contract input bytes per user per round.
|
||||
size_t user_output_bytes = 0; // Max contract output bytes per user per round.
|
||||
size_t npl_output_bytes = 0; // Max npl output bytes per round.
|
||||
size_t proc_cpu_seconds = 0; // Max CPU time the contract process can consume.
|
||||
size_t proc_mem_bytes = 0; // Max memory the contract process can allocate.
|
||||
size_t proc_ofd_count = 0; // Max no. of open file descriptors the contract process can allocate.
|
||||
};
|
||||
|
||||
struct contract_config
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
@@ -98,6 +98,13 @@ namespace sc
|
||||
|
||||
// Set up the process environment and overlay the contract binary program with execv().
|
||||
|
||||
// Set process resource limits.
|
||||
if (set_process_rlimits() == -1)
|
||||
{
|
||||
std::cerr << errno << ": Failed to set contract process resource limits." << (ctx.args.readonly ? " (rdonly)" : "") << "\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Close all fds unused by SC process.
|
||||
close_unused_fds(ctx, false);
|
||||
|
||||
@@ -153,6 +160,33 @@ namespace sc
|
||||
return ret;
|
||||
}
|
||||
|
||||
int set_process_rlimits()
|
||||
{
|
||||
rlimit lim;
|
||||
if (conf::cfg.contract.round_limits.proc_cpu_seconds > 0)
|
||||
{
|
||||
lim.rlim_cur = lim.rlim_max = conf::cfg.contract.round_limits.proc_cpu_seconds;
|
||||
if (setrlimit(RLIMIT_CPU, &lim) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conf::cfg.contract.round_limits.proc_mem_bytes > 0)
|
||||
{
|
||||
lim.rlim_cur = lim.rlim_max = conf::cfg.contract.round_limits.proc_mem_bytes;
|
||||
if (setrlimit(RLIMIT_DATA, &lim) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (conf::cfg.contract.round_limits.proc_ofd_count > 0)
|
||||
{
|
||||
lim.rlim_cur = lim.rlim_max = conf::cfg.contract.round_limits.proc_ofd_count;
|
||||
if (setrlimit(RLIMIT_NOFILE, &lim) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the contract process has exited.
|
||||
* @param ctx Contract execution context.
|
||||
@@ -185,7 +219,7 @@ namespace sc
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR << "Contract process" << (ctx.args.readonly ? " (rdonly)" : "") << " ended with code " << WEXITSTATUS(scstatus);
|
||||
LOG_ERROR << "Contract process" << (ctx.args.readonly ? " (rdonly)" : "") << " ended prematurely with code " << WEXITSTATUS(scstatus);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ namespace sc
|
||||
|
||||
//------Internal-use functions for this namespace.
|
||||
|
||||
int set_process_rlimits();
|
||||
|
||||
int check_contract_exited(execution_context &ctx, const bool block);
|
||||
|
||||
int start_hpfs_session(execution_context &ctx);
|
||||
|
||||
Reference in New Issue
Block a user