From 7237c999918e44e2e76a6d2e2ae37ea709af08f6 Mon Sep 17 00:00:00 2001 From: physick Date: Tue, 28 Jul 2026 20:29:45 +0500 Subject: Added wren limiting fork support --- src/main.c | 4 ++ src/puzzles/puzzle.c | 22 ++++++---- src/puzzles/puzzle.h | 2 +- src/resources/config.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ src/resources/resources.h | 16 ++++++++ 5 files changed, 136 insertions(+), 8 deletions(-) create mode 100644 src/resources/config.c (limited to 'src') diff --git a/src/main.c b/src/main.c index 04e857c..e83454e 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,10 @@ int main(void) { + if (!ReadConfig(DEFAULT_CONFIG_LOCATION)) { + return 1; + } + if (!InitSceneStack()) { printf("Failed to create a scene stack\n"); return 1; diff --git a/src/puzzles/puzzle.c b/src/puzzles/puzzle.c index fad3f00..11328a8 100644 --- a/src/puzzles/puzzle.c +++ b/src/puzzles/puzzle.c @@ -1,5 +1,7 @@ +#include "../resources/resources.h" #include "puzzle.h" #include "../wren_inter/wren_inter.h" +#include #include #include #include @@ -116,7 +118,7 @@ bool CheckResult(PuzzleIO *io, WrenVM *vm) { } } -enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) { +enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op) { wrenEnsureSlots(vm, test->argc + 1); // finction handle + arguments wrenGetVariable(vm, USER_MODULE_NAME, test->RecieverName, 0); @@ -136,14 +138,15 @@ enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) { PutIOIntoSlot(i+1, &test->argv[i], vm); } - WrenInterpretResult call_result = wrenCall(vm, fn_handle); - + WrenInterpretResult call_result = wrenCallLim(vm, fn_handle, max_op); switch (call_result) { case WREN_RESULT_COMPILE_ERROR: return RESULT_COMPTIME_ERR; case WREN_RESULT_RUNTIME_ERROR: return RESULT_RUNTIME_ERR; + case WREN_RESULT_RUNTIME_LIMIT: + return RESULT_TIMEOUT; default: break; } @@ -160,8 +163,8 @@ enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm) { } // Returns the res ptr. Does not modify the address -SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *res) { - WrenInterpretResult result = wrenInterpret(vm, "main", script); +SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult *res, size_t max_op) { + WrenInterpretResult result = wrenInterpretLim(vm, "main", script, max_op); res->result = RESULT_OK; switch (result) { case WREN_RESULT_COMPILE_ERROR: @@ -170,6 +173,10 @@ SubmitionResult *TryEvaluateUserCode(WrenVM *vm, char *script, SubmitionResult * case WREN_RESULT_RUNTIME_ERROR: res->result = RESULT_RUNTIME_ERR; return res; + case WREN_RESULT_RUNTIME_LIMIT: + res->result = RESULT_TIMEOUT; + printf("Timeout with max_op %zu\n", max_op); + return res; default: break; } return res; @@ -187,6 +194,7 @@ SubmitionResult *PrepareResult(SubmitionResult *self, } SubmitionResult *SubmitSolution(Puzzle *self, char *script) { + Config *cfg = GetConfig(); if (self == NULL || script == NULL) return NULL; SubmitionResult *res = malloc(sizeof(SubmitionResult)); @@ -196,13 +204,13 @@ SubmitionResult *SubmitSolution(Puzzle *self, char *script) { return res; } - res = TryEvaluateUserCode(vm, script, res); + res = TryEvaluateUserCode(vm, script, res, cfg->MaxOperations); if (res->result != RESULT_OK) { return PrepareResult(res, vm, res->result); } for (size_t i = 0; i < self->TestC; i++) { - enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm); + enum PUZZLE_TEST_RESULT test_res = RunTest(&self->Tests[i], vm, cfg->MaxOperations); switch (test_res) { case WREN_RESULT_COMPILE_ERROR: return PrepareResult(res, vm, RESULT_COMPTIME_ERR); diff --git a/src/puzzles/puzzle.h b/src/puzzles/puzzle.h index 28ab6ca..4dfd0f8 100644 --- a/src/puzzles/puzzle.h +++ b/src/puzzles/puzzle.h @@ -29,7 +29,7 @@ typedef struct { /// The user submitted code should be evaluated by this point. /// The entry point should be registered and the absence of the /// Main method signatire will be concidered an error -enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm); +enum PUZZLE_TEST_RESULT RunTest(Test *test, WrenVM *vm, size_t max_op); typedef struct { char *Name; diff --git a/src/resources/config.c b/src/resources/config.c new file mode 100644 index 0000000..05b80cd --- /dev/null +++ b/src/resources/config.c @@ -0,0 +1,100 @@ +#include "resources.h" +#include +#include +#include +#include +#include + +Config config = { + .MaxOperations = DEFAULT_MAX_OPS, +}; + +bool startsWith(const char *str, const char *prefix) { + if (str == NULL || prefix == NULL) { + return false; + } + + size_t prefix_len = strlen(prefix); + + if (strlen(str) < prefix_len) { + return false; + } + + return strncmp(str, prefix, prefix_len) == 0; +} + +// Strips everything before '=' (including). Does not allocate. +char *stripName(char *line, size_t len) { + if (line == NULL) { + return NULL; + } + + for (size_t ptr = 0; ptr < len; ptr++) { + if (line[ptr] == '\0') { + return NULL; + } + if (line[ptr] == '=') { + return &line[ptr + 1]; + } + } + + return NULL; +} + +// Reads one line and assigns the param if found +void evaluateConfigLive(char *line) { + size_t len = strlen(line); + // Empty lines and comments + if (len <= 1 || line[0] == '#') return; + // Removing a new line + if (line[len-1] == '\n') { + line[len-1] = 0x0; + len -= 1; + } + + if (startsWith(line, "MaxOperations")) { + char *value = stripName(line, len); + + char *endptr; + unsigned long long parsed = strtoull(value, &endptr, 10); + if (value == endptr || *endptr != '\0') { + TraceLog(LOG_WARNING, "Failed to parse the value for MaxOperations. Provided value: '%s'", value); + return; + } + config.MaxOperations = parsed; + return; + } + +} + +bool ReadConfig(const char *location) { + FILE *file = fopen(location, "r"); + if (file == NULL) { + perror("Failed to open the config file"); + return false; + } + + fseek(file, 0, SEEK_END); + long size = ftell(file); + rewind(file); + + char* buffer = malloc(size + 1); + if (buffer == NULL) { + fclose(file); + perror("Failed to load the config file"); + return false; + } + + + while (fgets(buffer, size, file)) { + evaluateConfigLive(buffer); + } + + free(buffer); + fclose(file); + return true; +} + +Config *GetConfig() { + return &config; +} diff --git a/src/resources/resources.h b/src/resources/resources.h index d3ad840..8f6cd1d 100644 --- a/src/resources/resources.h +++ b/src/resources/resources.h @@ -1,8 +1,21 @@ +#include #include #ifndef RESOURCES_H #define RESOURCES_H +static const size_t DEFAULT_MAX_OPS = 2 * 1000 * 1000; +static const char *DEFAULT_CONFIG_LOCATION = "config.conf"; + +typedef struct { + // The maximum amount of operations performed by a VM on each check. + // + // Keep in mind that setting this parameter higher will make the infinite + // loops in your submission longer to detect. Lowering this parameter will make + // the limitation on the code runtime stricter. + size_t MaxOperations; +} Config; + void DrawBackground(); /// #670916 as RGBA @@ -18,6 +31,9 @@ static const Color COLORSCHEME_WHITE = { 233, 225, 185, 255 }; /// #48A56A as RGBA static const Color COLORSCHEME_GREEN = { 72, 165, 106, 255 }; +bool ReadConfig(const char *location); +Config *GetConfig(); + void LoadResources(); Font *GetMainFont(); Font *GetMonoFont(); -- cgit v1.3