diff --git a/docs/developer_guide/contribution_guide.md b/docs/developer_guide/contribution_guide.md index d8f0d974d..8218dcc87 100644 --- a/docs/developer_guide/contribution_guide.md +++ b/docs/developer_guide/contribution_guide.md @@ -166,6 +166,7 @@ Users listed in [CI_PERMISSIONS.json](https://github.com/sgl-project/sglang/blob - If a single test file run longer than 500 seconds, split it into multiple smaller files (e.g., `test_eagle_infer_a.py`, `test_eagle_infer_b.py`). - If a single job in a github workflow runs longer than 30 mins, split it into smaller jobs/steps. - Reuse server launches in your unit tests to make tests run faster. +- Never use `pickle.loads()`, `pickle.load()`, or `recv_pyobj()` to deserialize untrusted or network-received data. Python's [pickle module is not secure](https://docs.python.org/3/library/pickle.html) — it can execute arbitrary code during deserialization. Use safe serialization formats such as [msgpack](https://github.com/jcrist/msgspec) or JSON instead. - When supporting new hardware or features, follow these guidelines: - Do not drastically change existing code. - Always prefer new files to introduce specific components for your new hardware (e.g., `allocator_ascend.py`). diff --git a/python/sglang/srt/utils/common.py b/python/sglang/srt/utils/common.py index 57d85eb2f..a325822ed 100644 --- a/python/sglang/srt/utils/common.py +++ b/python/sglang/srt/utils/common.py @@ -2144,6 +2144,11 @@ class SafeUnpickler(pickle.Unpickler): ) +def safe_pickle_load(fp): + """Drop-in replacement for pickle.load() that blocks unsafe class loading.""" + return SafeUnpickler(fp).load() + + def debug_timing(func): # todo: replace with a more organized instrumentation def wrapper(*args, **kwargs): diff --git a/scripts/playground/replay_request_dump.py b/scripts/playground/replay_request_dump.py index f99e5bbf5..5e42e80d6 100644 --- a/scripts/playground/replay_request_dump.py +++ b/scripts/playground/replay_request_dump.py @@ -10,7 +10,6 @@ python3 replay_request_dump.py --parallel 512 --input-file /data/sglang_crash_du import argparse import glob import json -import pickle import time from concurrent.futures import ThreadPoolExecutor from dataclasses import asdict @@ -19,6 +18,7 @@ from datetime import datetime import requests from sglang.benchmark.utils import set_ulimit +from sglang.srt.utils.common import safe_pickle_load from sglang.utils import get_exception_traceback @@ -54,7 +54,8 @@ def normalize_request_data(json_data): def read_records(files): records = [] for f in files: - tmp = pickle.load(open(f, "rb")) + with open(f, "rb") as fh: + tmp = safe_pickle_load(fh) if isinstance(tmp, dict) and "requests" in tmp: records.extend(tmp["requests"]) else: