Skip to content

Commit

Permalink
Fix REPRL executing of UTF-8 data (#443)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Samuel Groß committed Aug 30, 2024
1 parent e92af37 commit 7e20d53
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 17 deletions.
6 changes: 3 additions & 3 deletions Sources/Fuzzilli/Execution/REPRL.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
}
}

Expand Down
6 changes: 3 additions & 3 deletions Sources/REPRLRun/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/libreprl/include/libreprl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
9 changes: 5 additions & 4 deletions Sources/libreprl/libreprl-posix.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down Expand Up @@ -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;
Expand Down
9 changes: 5 additions & 4 deletions Sources/libreprl/libreprl-windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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);
Expand Down

0 comments on commit 7e20d53

Please sign in to comment.