From 7e20d53e3f0514cdbf5c2934c7c37193f1d54d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20Gro=C3=9F?= Date: Fri, 30 Aug 2024 16:29:34 +0200 Subject: [PATCH] Fix REPRL executing of UTF-8 data (#443) If the script to execute contains multibyte UTF-8 sequences, we were previously passing the wrong size to reprl_execute: we were giving it the number of characters, but not the number of bytes. --- Sources/Fuzzilli/Execution/REPRL.swift | 6 +++--- Sources/REPRLRun/main.swift | 6 +++--- Sources/libreprl/include/libreprl.h | 6 +++--- Sources/libreprl/libreprl-posix.c | 9 +++++---- Sources/libreprl/libreprl-windows.c | 9 +++++---- 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/Sources/Fuzzilli/Execution/REPRL.swift b/Sources/Fuzzilli/Execution/REPRL.swift index 185d1f8c2..a2d31956d 100644 --- a/Sources/Fuzzilli/Execution/REPRL.swift +++ b/Sources/Fuzzilli/Execution/REPRL.swift @@ -108,8 +108,8 @@ public class REPRL: ComponentBase, ScriptRunner { var execTime: UInt64 = 0 // In microseconds let timeout = UInt64(timeout) * 1000 // In microseconds var status: Int32 = 0 - script.withCString { - status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, freshInstance) + script.withCString { ptr in + status = reprl_execute(reprlContext, ptr, UInt64(script.utf8.count), UInt64(timeout), &execTime, freshInstance) // If we fail, we retry after a short timeout and with a fresh instance. If we still fail, we give up trying // to execute this program. If we repeatedly fail to execute any program, we abort. if status < 0 { @@ -118,7 +118,7 @@ public class REPRL: ComponentBase, ScriptRunner { fuzzer.dispatchEvent(fuzzer.events.DiagnosticsEvent, data: (name: "REPRLFail", content: scriptBuffer)) } Thread.sleep(forTimeInterval: 1) - status = reprl_execute(reprlContext, $0, UInt64(script.count), UInt64(timeout), &execTime, 1) + status = reprl_execute(reprlContext, ptr, UInt64(script.utf8.count), UInt64(timeout), &execTime, 1) } } diff --git a/Sources/REPRLRun/main.swift b/Sources/REPRLRun/main.swift index eb0b036fd..41fa352f6 100644 --- a/Sources/REPRLRun/main.swift +++ b/Sources/REPRLRun/main.swift @@ -42,11 +42,11 @@ if reprl_initialize_context(ctx, argv, envp, /* capture_stdout: */ 1, /* capture print("Failed to initialize REPRL context: \(String(cString: reprl_get_last_error(ctx)))") } -func execute(_ code: String) -> (status: Int32, exec_time: UInt64) { +func execute(_ script: String) -> (status: Int32, exec_time: UInt64) { var exec_time: UInt64 = 0 var status: Int32 = 0 - code.withCString { - status = reprl_execute(ctx, $0, UInt64(code.count), 1_000_000, &exec_time, 0) + script.withCString { ptr in + status = reprl_execute(ctx, ptr, UInt64(script.utf8.count), 1_000_000, &exec_time, 0) } return (status, exec_time) } diff --git a/Sources/libreprl/include/libreprl.h b/Sources/libreprl/include/libreprl.h index 1bfd6930f..ed7268bca 100644 --- a/Sources/libreprl/include/libreprl.h +++ b/Sources/libreprl/include/libreprl.h @@ -48,13 +48,13 @@ void reprl_destroy_context(struct reprl_context* ctx); /// If necessary, or if fresh_instance is true, this will automatically spawn a new instance of the target process. /// /// @param ctx The REPRL context -/// @param script The script to execute -/// @param script_length The size of the script in bytes +/// @param script The script to execute as utf-8 encoded data +/// @param script_size Size of the script as number of bytes /// @param timeout The maximum allowed execution time in microseconds /// @param execution_time A pointer to which, if execution succeeds, the execution time in microseconds is written to /// @param fresh_instance if true, forces the creation of a new instance of the target /// @return A REPRL exit status (see below) or a negative number in case of an error -int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance); +int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_size, uint64_t timeout, uint64_t* execution_time, int fresh_instance); /// Returns true if the execution terminated due to a signal. /// diff --git a/Sources/libreprl/libreprl-posix.c b/Sources/libreprl/libreprl-posix.c index 7f1491b49..d9ba927db 100644 --- a/Sources/libreprl/libreprl-posix.c +++ b/Sources/libreprl/libreprl-posix.c @@ -452,12 +452,13 @@ void reprl_destroy_context(struct reprl_context* ctx) free(ctx); } -int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance) +int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_size, uint64_t timeout, uint64_t* execution_time, int fresh_instance) { if (!ctx->initialized) { return reprl_error(ctx, "REPRL context is not initialized"); } - if (script_length > REPRL_MAX_DATA_SIZE) { + + if (script_size > REPRL_MAX_DATA_SIZE) { return reprl_error(ctx, "Script too large"); } @@ -488,11 +489,11 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script } // Copy the script to the data channel. - memcpy(ctx->data_out->mapping, script, script_length); + memcpy(ctx->data_out->mapping, script, script_size); // Tell child to execute the script. if (write(ctx->ctrl_out, "exec", 4) != 4 || - write(ctx->ctrl_out, &script_length, 8) != 8) { + write(ctx->ctrl_out, &script_size, 8) != 8) { // These can fail if the child unexpectedly terminated between executions. // Check for that here to be able to provide a better error message. int status; diff --git a/Sources/libreprl/libreprl-windows.c b/Sources/libreprl/libreprl-windows.c index f9a2a699d..f222440f4 100644 --- a/Sources/libreprl/libreprl-windows.c +++ b/Sources/libreprl/libreprl-windows.c @@ -309,11 +309,12 @@ void reprl_destroy_context(struct reprl_context* ctx) free(ctx); } -int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_length, uint64_t timeout, uint64_t* execution_time, int fresh_instance) +int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script_size, uint64_t timeout, uint64_t* execution_time, int fresh_instance) { if (!ctx->initialized) return reprl_error(ctx, "REPRL context is not initialized"); - if (script_length > REPRL_MAX_DATA_SIZE) + + if (script_size > REPRL_MAX_DATA_SIZE) return reprl_error(ctx, "Script too large"); if (fresh_instance && ctx->hChild != INVALID_HANDLE_VALUE) @@ -333,7 +334,7 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script } // Copy the script to the data channel. - memcpy(ctx->data_out->mapping, script, script_length); + memcpy(ctx->data_out->mapping, script, script_size); // Tell child to execute the script. DWORD dwBytesWritten; @@ -345,7 +346,7 @@ int reprl_execute(struct reprl_context* ctx, const char* script, uint64_t script } return reprl_error(ctx, "Failed to send command to child process: %d", GetLastError()); } - if (!WriteFile(ctx->hControlWrite, &script_length, sizeof(script_length), &dwBytesWritten, NULL) || dwBytesWritten != sizeof(script_length)) { + if (!WriteFile(ctx->hControlWrite, &script_size, sizeof(script_size), &dwBytesWritten, NULL) || dwBytesWritten != sizeof(script_size)) { DWORD dwExitCode; if (GetExitCodeProcess(ctx->hChild, &dwExitCode) != STILL_ACTIVE) { reprl_child_terminated(ctx);