fix(security): replace unsafe pickle.loads with SafeUnpickler for CVE-2026-3989 (#20904)
This commit is contained in:
@@ -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`).
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user