Repository navigation
Description
Describe the Bug:
BashToolPolicy says that max_memory_bytes, max_file_size_bytes and max_child_processes "will be enforced upon the spawned subprocess". A limit set to 0 is not enforced: _set_resource_limits (src/google/adk/tools/bash_tool.py) checks each one with if policy.max_file_size_bytes:, and 0 is falsy, so setrlimit is never called and the command runs with no limit at all.
So BashToolPolicy(max_file_size_bytes=0), meant to stop a command from writing to files, allows writes of any size, and max_child_processes=0 does not stop a command from starting child processes. A limit of 1 is enforced, which makes 0 the least restrictive value instead of the most restrictive.
Steps to Reproduce:
- Install
google-adkfrommain(reproduced @ f8ced6f). - Run the script under "Minimal Reproduction Code". It runs the same
ddcommand, which writes 4096 bytes, once withmax_file_size_bytes=1024and once withmax_file_size_bytes=0.
Expected Behavior:
With max_file_size_bytes=0, the write is blocked and the file stays empty, the way the write is cut off at 1024 bytes with max_file_size_bytes=1024.
Observed Behavior:
max_file_size_bytes=1024: returncode=-25, bytes written=1024
max_file_size_bytes=0: returncode=0, bytes written=4096
(-25 is SIGXFSZ, the signal sent when a process goes over its file size limit.)
Environment Details:
- ADK Library Version (pip show google-adk): main @ f8ced6f
- Desktop OS: macOS 26.6.2
- Python Version (python -V): 3.12.7
Model Information:
- Are you using LiteLLM: N/A (no model call needed to reproduce)
- Which model is being used: N/A
Minimal Reproduction Code:
"""max_file_size_bytes=0 is ignored by ExecuteBashTool (google/adk-python)."""
import asyncio
import pathlib
import tempfile
from unittest import mock
from google.adk.tools.bash_tool import BashToolPolicy, ExecuteBashTool
from google.adk.tools.tool_context import ToolContext
# A command that writes 4096 bytes to a file in the workspace.
COMMAND = "dd if=/dev/zero of=out.bin bs=1024 count=4"
async def run(limit: int) -> None:
with tempfile.TemporaryDirectory() as workspace:
tool = ExecuteBashTool(
workspace=pathlib.Path(workspace),
policy=BashToolPolicy(max_file_size_bytes=limit),
)
ctx = mock.create_autospec(ToolContext, instance=True)
ctx.tool_confirmation = mock.Mock(confirmed=True)
result = await tool.run_async(args={"command": COMMAND}, tool_context=ctx)
out = pathlib.Path(workspace, "out.bin")
size = out.stat().st_size if out.exists() else None
print(
f"max_file_size_bytes={limit}: returncode={result.get('returncode')},"
f" bytes written={size}"
)
asyncio.run(run(1024))
asyncio.run(run(0))Additional Context:
Checking is not None instead of truthiness fixes it, since None is the default that means "no limit". I'm happy to send a PR.
Activity
fix: enforce ExecuteBashTool resource limits that are set to 0