Skip to content
This repository has been archived by the owner on Nov 1, 2023. It is now read-only.

Commit

Permalink
Make min_available_memory_mb configurable (#3577)
Browse files Browse the repository at this point in the history
* Add min_available_memory_mb to cli and service configs to be passed to agent

* Add min_available_memory_mb to task create and template parameters

* Add logging to indicate the configured min_available_memory

* Add missing parameter

* Remove unnecessary import

* Update webhook_events.py

* test change

* original version of docs.

* Update webhook_events.py again

* Don't start memory checking task when min_available_memory_mb is zero

* cargo fmt

* Remove comments

* Add min_available_memory_mb to libfuzzer's _create_tasks()

* Add a megabyte converted value for min available memory logs

* Use float instead of int for division

---------

Co-authored-by: Noah Harper <[email protected]>
  • Loading branch information
kananb and nharper285 authored Oct 18, 2023
1 parent 679309a commit d9ac2a7
Show file tree
Hide file tree
Showing 12 changed files with 107 additions and 21 deletions.
40 changes: 40 additions & 0 deletions docs/webhook_events.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -2332,6 +2337,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -3054,6 +3064,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -3572,6 +3587,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -4056,6 +4076,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -4514,6 +4539,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -4999,6 +5029,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down Expand Up @@ -6743,6 +6778,11 @@ If webhook is set to have Event Grid message format then the payload will look a
"title": "Generator Options",
"type": "array"
},
"min_available_memory_mb": {
"minimum": 0,
"title": "Min Available Memory Mb",
"type": "integer"
},
"minimized_stack_depth": {
"title": "Minimized Stack Depth",
"type": "integer"
Expand Down
2 changes: 2 additions & 0 deletions src/ApiService/ApiService/OneFuzzTypes/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ public record TaskDetails(
List<string>? ReportList = null,
long? MinimizedStackDepth = null,
Dictionary<string, string>? TaskEnv = null,
ulong? MinAvailableMemoryMb = null,

// Deprecated. Retained for processing old table data.
string? CoverageFilter = null,
Expand Down Expand Up @@ -1164,6 +1165,7 @@ Dictionary<string, string> Tags
public IContainerDef? RegressionReports { get; set; }
public IContainerDef? ExtraSetup { get; set; }
public IContainerDef? ExtraOutput { get; set; }
public ulong? MinAvailableMemoryMb { get; set; }
}

public record NodeCommandEnvelope(
Expand Down
5 changes: 4 additions & 1 deletion src/ApiService/ApiService/onefuzzlib/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ private static BlobContainerSasPermissions ConvertPermissions(ContainerPermissio
HeartbeatQueue: await _queue.GetQueueSas("task-heartbeat", StorageType.Config, QueueSasPermissions.Add) ?? throw new Exception("unable to get heartbeat queue sas"),
JobResultQueue: await _queue.GetQueueSas("job-result", StorageType.Config, QueueSasPermissions.Add) ?? throw new Exception("unable to get heartbeat queue sas"),
Tags: task.Config.Tags ?? new Dictionary<string, string>()
);
) {
// It's okay if this is null because the agent will use a default value if so.
MinAvailableMemoryMb = task.Config.Task.MinAvailableMemoryMb,
};

if (definition.MonitorQueue != null) {
config.inputQueue = await _queue.GetQueueSas(task.TaskId.ToString(), StorageType.Corpus, QueueSasPermissions.All);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ public static async Async.Task<TemplateValidationResponse> ValidateScribanTempla
targetOptions,
1,
new Dictionary<string, string>(),
null,
"coverage filter",
"module allow list",
"source allow list",
Expand Down
44 changes: 25 additions & 19 deletions src/agent/onefuzz-task/src/managed/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.
use std::path::PathBuf;

use anyhow::{bail, Result};
use anyhow::Result;
use clap::{value_parser, Arg, Command};
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};

Expand Down Expand Up @@ -96,21 +96,29 @@ pub async fn run(args: &clap::ArgMatches) -> Result<()> {

let min_available_memory_bytes = 1_000_000 * config.common().min_available_memory_mb;

// If the memory limit is 0, this will resolve immediately with an error.
let check_oom = out_of_memory(min_available_memory_bytes);

let result = tokio::select! {
result = config.run() => result,

// Ignore this task if it returns due to a querying error.
Ok(oom) = check_oom => {
// Convert the OOM notification to an error, so we can log it below.
let err = anyhow::format_err!("out of memory: {} bytes available, {} required", oom.available_bytes, oom.min_bytes);
Err(err)
},

_shutdown = shutdown_listener => {
Ok(())
let result = match min_available_memory_bytes {
0 => {
log::info!("memory watchdog is disabled: this task may fail suddenly if it runs out of memory.");
config.run().await
}
_ => {
// If the memory limit is 0, this will never return.
let check_oom = out_of_memory(min_available_memory_bytes);

tokio::select! {
result = config.run() => result,

// Ignore this task if it returns due to a querying error.
Ok(oom) = check_oom => {
// Convert the OOM notification to an error, so we can log it below.
let err = anyhow::format_err!("out of memory: {} bytes available, {} required", oom.available_bytes, oom.min_bytes);
Err(err)
},

_shutdown = shutdown_listener => {
Ok(())
}
}
}
};

Expand All @@ -131,9 +139,7 @@ const MAX_OOM_QUERY_ERRORS: usize = 5;
//
// Parameterized to enable future configuration by VMSS.
async fn out_of_memory(min_bytes: u64) -> Result<OutOfMemory> {
if min_bytes == 0 {
bail!("available memory minimum is unreachable");
}
log::info!("memory watchdog is enabled: this task will fail informatively if there are {} bytes ({:.2}MB) or fewer of usable memory left.", min_bytes, min_bytes as f64 / 1_000_000f64);

let mut consecutive_query_errors = 0;

Expand Down
2 changes: 2 additions & 0 deletions src/cli/onefuzz/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,7 @@ def create(
module_allowlist: Optional[str] = None,
source_allowlist: Optional[str] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> models.Task:
"""
Create a task
Expand Down Expand Up @@ -1138,6 +1139,7 @@ def create(
module_allowlist=module_allowlist,
source_allowlist=source_allowlist,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
),
)

Expand Down
2 changes: 2 additions & 0 deletions src/cli/onefuzz/templates/afl.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def basic(
debug: Optional[List[TaskDebugFlag]] = None,
ensemble_sync_delay: Optional[int] = None,
extra_setup_container: Optional[Container] = None,
min_available_memory_mb: Optional[int] = None,
) -> Optional[Job]:
"""
Basic AFL job
Expand Down Expand Up @@ -166,6 +167,7 @@ def basic(
target_env=target_env,
debug=debug,
ensemble_sync_delay=ensemble_sync_delay,
min_available_memory_mb=min_available_memory_mb,
)

report_containers = [
Expand Down
17 changes: 17 additions & 0 deletions src/cli/onefuzz/templates/libfuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ def _create_tasks(
analyzer_env: Optional[Dict[str, str]] = None,
tools: Optional[Container] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> None:
target_options = target_options or []
regression_containers: List[Tuple[ContainerType, Container]] = [
Expand Down Expand Up @@ -127,6 +128,7 @@ def _create_tasks(
colocate=colocate_all_tasks or colocate_secondary_tasks,
minimized_stack_depth=minimized_stack_depth,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

fuzzer_containers: List[Tuple[ContainerType, Container]] = [
Expand Down Expand Up @@ -180,6 +182,7 @@ def _create_tasks(
check_fuzzer_help=check_fuzzer_help,
expect_crash_on_failure=expect_crash_on_failure,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

prereq_tasks = [fuzzer_task.task_id, regression_task.task_id]
Expand Down Expand Up @@ -243,6 +246,7 @@ def _create_tasks(
module_allowlist=module_allowlist,
source_allowlist=source_allowlist,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

report_containers: List[Tuple[ContainerType, Container]] = [
Expand Down Expand Up @@ -280,6 +284,7 @@ def _create_tasks(
colocate=colocate_all_tasks or colocate_secondary_tasks,
minimized_stack_depth=minimized_stack_depth,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

if analyzer_exe is not None:
Expand Down Expand Up @@ -320,6 +325,7 @@ def _create_tasks(
debug=debug,
target_timeout=target_timeout,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

def basic(
Expand Down Expand Up @@ -369,6 +375,7 @@ def basic(
extra_output_container: Optional[Container] = None,
crashes: Optional[Container] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> Optional[Job]:
"""
Basic libfuzzer job
Expand Down Expand Up @@ -507,6 +514,7 @@ def basic(
tools=tools,
task_env=task_env,
target_timeout=target_timeout,
min_available_memory_mb=min_available_memory_mb,
)

self.logger.info("done creating tasks")
Expand Down Expand Up @@ -543,6 +551,7 @@ def merge(
no_check_fuzzer_help: bool = False,
extra_setup_container: Optional[Container] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> Optional[Job]:
"""
libfuzzer merge task
Expand Down Expand Up @@ -657,6 +666,7 @@ def merge(
preserve_existing_outputs=preserve_existing_outputs,
check_fuzzer_help=check_fuzzer_help,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

self.logger.info("done creating tasks")
Expand Down Expand Up @@ -698,6 +708,7 @@ def dotnet(
extra_setup_container: Optional[Container] = None,
crashes: Optional[Container] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> Optional[Job]:
pool = self.onefuzz.pools.get(pool_name)

Expand Down Expand Up @@ -820,6 +831,7 @@ def dotnet(
expect_crash_on_failure=expect_crash_on_failure,
check_fuzzer_help=False,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

# Ensure the fuzzing task starts before we schedule the coverage and
Expand Down Expand Up @@ -874,6 +886,7 @@ def dotnet(
debug=debug,
colocate=colocate_all_tasks or colocate_secondary_tasks,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

report_containers = [
Expand Down Expand Up @@ -915,6 +928,7 @@ def dotnet(
debug=debug,
colocate=colocate_all_tasks or colocate_secondary_tasks,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

self.logger.info("done creating tasks")
Expand Down Expand Up @@ -956,6 +970,7 @@ def qemu_user(
crashes: Optional[Container] = None,
readonly_inputs: Optional[Container] = None,
task_env: Optional[Dict[str, str]] = None,
min_available_memory_mb: Optional[int] = None,
) -> Optional[Job]:
"""
libfuzzer tasks, wrapped via qemu-user (PREVIEW FEATURE)
Expand Down Expand Up @@ -1125,6 +1140,7 @@ def qemu_user(
expect_crash_on_failure=False,
check_fuzzer_help=check_fuzzer_help,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

report_containers = [
Expand Down Expand Up @@ -1167,6 +1183,7 @@ def qemu_user(
expect_crash_on_failure=False,
check_fuzzer_help=check_fuzzer_help,
task_env=task_env,
min_available_memory_mb=min_available_memory_mb,
)

self.logger.info("done creating tasks")
Expand Down
2 changes: 2 additions & 0 deletions src/cli/onefuzz/templates/ossfuzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ def libfuzzer(
debug: Optional[List[TaskDebugFlag]] = None,
ensemble_sync_delay: Optional[int] = None,
extra_setup_container: Optional[Container] = None,
min_available_memory_mb: Optional[int] = None,
) -> None:
"""
OssFuzz style libfuzzer jobs
Expand Down Expand Up @@ -257,6 +258,7 @@ def libfuzzer(
tags=helper.tags,
debug=debug,
ensemble_sync_delay=ensemble_sync_delay,
min_available_memory_mb=min_available_memory_mb,
)
helpers.append(helper)
base_helper.wait()
Loading

0 comments on commit d9ac2a7

Please sign in to comment.