fix(security): replace unsafe pickle.loads with SafeUnpickler for CVE-2026-3989 (#20904)

This commit is contained in:
zwang86
2026-03-27 00:43:41 -07:00
committed by GitHub
parent 8d4fca5908
commit 5fc5c18bed
3 changed files with 9 additions and 2 deletions
@@ -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 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. - 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. - 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: - When supporting new hardware or features, follow these guidelines:
- Do not drastically change existing code. - Do not drastically change existing code.
- Always prefer new files to introduce specific components for your new hardware (e.g., `allocator_ascend.py`). - Always prefer new files to introduce specific components for your new hardware (e.g., `allocator_ascend.py`).
+5
View File
@@ -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): def debug_timing(func):
# todo: replace with a more organized instrumentation # todo: replace with a more organized instrumentation
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
+3 -2
View File
@@ -10,7 +10,6 @@ python3 replay_request_dump.py --parallel 512 --input-file /data/sglang_crash_du
import argparse import argparse
import glob import glob
import json import json
import pickle
import time import time
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from dataclasses import asdict from dataclasses import asdict
@@ -19,6 +18,7 @@ from datetime import datetime
import requests import requests
from sglang.benchmark.utils import set_ulimit from sglang.benchmark.utils import set_ulimit
from sglang.srt.utils.common import safe_pickle_load
from sglang.utils import get_exception_traceback from sglang.utils import get_exception_traceback
@@ -54,7 +54,8 @@ def normalize_request_data(json_data):
def read_records(files): def read_records(files):
records = [] records = []
for f in files: 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: if isinstance(tmp, dict) and "requests" in tmp:
records.extend(tmp["requests"]) records.extend(tmp["requests"])
else: else: