fix: restrict SafeUnpickler to explicit globals (#40259)

Co-authored-by: yhzhuang <yhzhuang@fb.com>
Co-authored-by: Jihui Yang <16509088+jihuiyang@users.noreply.github.com>
This commit is contained in:
Yonghao Zhuang
2026-09-18 22:38:11 -07:00
committed by GitHub
co-authored by yhzhuang Jihui Yang
parent 5e9342d16f
commit 5b42d10edf
+196 -32
View File
@@ -2966,6 +2966,15 @@ def normalize_serialized_named_tensor_payloads(
return [normalize_serialized_named_tensor_payload(data) for data in payloads] return [normalize_serialized_named_tensor_payload(data) for data in payloads]
def _safe_load_torch_storage(data: bytes):
storage = torch.load(io.BytesIO(data), weights_only=True)
if not isinstance(storage, (torch.storage.TypedStorage, torch.UntypedStorage)):
raise pickle.UnpicklingError(
f"Expected a Torch storage, got {type(storage).__name__}"
)
return storage
class SafeUnpickler(pickle.Unpickler): class SafeUnpickler(pickle.Unpickler):
# Standard-library modules expose powerful callables alongside harmless data # Standard-library modules expose powerful callables alongside harmless data
# types. Keep these globals exact so a newly added callable is denied by # types. Keep these globals exact so a newly added callable is denied by
@@ -2989,6 +2998,9 @@ class SafeUnpickler(pickle.Unpickler):
("collections", "OrderedDict"), ("collections", "OrderedDict"),
("collections", "defaultdict"), ("collections", "defaultdict"),
("collections", "deque"), ("collections", "deque"),
("collections", "Counter"),
("copyreg", "__newobj__"),
("copyreg", "__newobj_ex__"),
("functools", "partial"), ("functools", "partial"),
("itertools", "chain"), ("itertools", "chain"),
("itertools", "repeat"), ("itertools", "repeat"),
@@ -2996,41 +3008,193 @@ class SafeUnpickler(pickle.Unpickler):
("multiprocessing.reduction", "_rebuild_socket"), ("multiprocessing.reduction", "_rebuild_socket"),
("multiprocessing.resource_sharer", "DupFd"), ("multiprocessing.resource_sharer", "DupFd"),
("types", "SimpleNamespace"), ("types", "SimpleNamespace"),
} ("_codecs", "encode"),
# --- PyTorch data containers & rebuild functions ---
ALLOWED_MODULE_PREFIXES = { # Code-module prefixes (torch.*, sglang.srt.*) are NOT allowed: they
# --- PyTorch types --- # contain gadgets like sglang.srt.utils.common.dynamic_import
"torch.", ("torch", "Tensor"),
"torch._tensor.", ("torch", "BFloat16Tensor"),
"torch.storage.", ("torch", "BoolTensor"),
"torch.nn.parameter.", ("torch", "ByteTensor"),
"torch.autograd.function.", ("torch", "CharTensor"),
# --- torch distributed --- ("torch", "DoubleTensor"),
"torch.distributed.", ("torch", "FloatTensor"),
"torch.distributed._shard.", ("torch", "HalfTensor"),
"torch.distributed._composable.", ("torch", "IntTensor"),
"torch._C._distributed_c10d.", ("torch", "LongTensor"),
"torch._C._distributed_fsdp.", ("torch", "ShortTensor"),
"torch.distributed.optim.", ("torch.cuda", "BFloat16Tensor"),
# --- PEFT / LoRA --- ("torch.cuda", "BoolTensor"),
"peft.", ("torch.cuda", "ByteTensor"),
"transformers.", ("torch.cuda", "CharTensor"),
"huggingface_hub.", ("torch.cuda", "DoubleTensor"),
# --- SGLang & Unitest --- ("torch.cuda", "FloatTensor"),
"sglang.srt.weight_sync.tensor_bucket.", ("torch.cuda", "HalfTensor"),
"sglang.srt.model_executor.model_runner.", ("torch.cuda", "IntTensor"),
"sglang.srt.model_executor.model_runner_components.weight_updater.", ("torch.cuda", "LongTensor"),
"sglang.srt.layers.", ("torch.cuda", "ShortTensor"),
"sglang.srt.utils.", ("torch.cuda.sparse", "BFloat16Tensor"),
"sglang.srt.disaggregation.", ("torch.cuda.sparse", "ByteTensor"),
"sglang.srt.managers.", ("torch.cuda.sparse", "CharTensor"),
"torch_npu.", ("torch.cuda.sparse", "DoubleTensor"),
("torch.cuda.sparse", "FloatTensor"),
("torch.cuda.sparse", "HalfTensor"),
("torch.cuda.sparse", "IntTensor"),
("torch.cuda.sparse", "LongTensor"),
("torch.cuda.sparse", "ShortTensor"),
("torch.sparse", "BFloat16Tensor"),
("torch.sparse", "ByteTensor"),
("torch.sparse", "CharTensor"),
("torch.sparse", "DoubleTensor"),
("torch.sparse", "FloatTensor"),
("torch.sparse", "HalfTensor"),
("torch.sparse", "IntTensor"),
("torch.sparse", "LongTensor"),
("torch.sparse", "ShortTensor"),
("torch", "Size"),
("torch", "device"),
("torch", "dtype"),
("torch", "bfloat16"),
("torch", "bit"),
("torch", "bits16"),
("torch", "bits1x8"),
("torch", "bits2x4"),
("torch", "bits4x2"),
("torch", "bits8"),
("torch", "bool"),
("torch", "cdouble"),
("torch", "cfloat"),
("torch", "chalf"),
("torch", "complex128"),
("torch", "complex32"),
("torch", "complex64"),
("torch", "double"),
("torch", "float"),
("torch", "float16"),
("torch", "float32"),
("torch", "float4_e2m1fn_x2"),
("torch", "float64"),
("torch", "float8_e4m3fn"),
("torch", "float8_e4m3fnuz"),
("torch", "float8_e5m2"),
("torch", "float8_e5m2fnuz"),
("torch", "float8_e8m0fnu"),
("torch", "half"),
("torch", "int"),
("torch", "int1"),
("torch", "int16"),
("torch", "int2"),
("torch", "int3"),
("torch", "int32"),
("torch", "int4"),
("torch", "int5"),
("torch", "int6"),
("torch", "int64"),
("torch", "int7"),
("torch", "int8"),
("torch", "long"),
("torch", "qint32"),
("torch", "qint8"),
("torch", "quint2x4"),
("torch", "quint4x2"),
("torch", "quint8"),
("torch", "short"),
("torch", "uint1"),
("torch", "uint16"),
("torch", "uint2"),
("torch", "uint3"),
("torch", "uint32"),
("torch", "uint4"),
("torch", "uint5"),
("torch", "uint6"),
("torch", "uint64"),
("torch", "uint7"),
("torch", "uint8"),
("torch.nn.parameter", "Parameter"),
("torch.serialization", "_get_layout"),
("torch._utils", "_rebuild_tensor"),
("torch._utils", "_rebuild_tensor_v2"),
("torch._utils", "_rebuild_tensor_v3"),
("torch._utils", "_rebuild_parameter"),
("torch._utils", "_rebuild_parameter_with_state"),
("torch._utils", "_rebuild_qtensor"),
("torch._utils", "_rebuild_sparse_tensor"),
("torch._utils", "_rebuild_meta_tensor_no_storage"),
("torch._utils", "_rebuild_wrapper_subclass"),
("torch._utils", "_rebuild_device_tensor_from_numpy"),
("torch._utils", "_rebuild_device_tensor_from_cpu_tensor"),
("torch._tensor", "_rebuild_from_type_v2"),
("torch.storage", "UntypedStorage"),
("torch.storage", "_UntypedStorage"),
("torch.storage", "TypedStorage"),
("torch", "UntypedStorage"),
("torch", "BFloat16Storage"),
("torch", "BoolStorage"),
("torch", "ByteStorage"),
("torch", "CharStorage"),
("torch", "ComplexDoubleStorage"),
("torch", "ComplexFloatStorage"),
("torch", "DoubleStorage"),
("torch", "FloatStorage"),
("torch", "HalfStorage"),
("torch", "IntStorage"),
("torch", "LongStorage"),
("torch", "QInt32Storage"),
("torch", "QInt8Storage"),
("torch", "QUInt2x4Storage"),
("torch", "QUInt4x2Storage"),
("torch", "QUInt8Storage"),
("torch", "ShortStorage"),
("torch.cuda", "BFloat16Storage"),
("torch.cuda", "BoolStorage"),
("torch.cuda", "ByteStorage"),
("torch.cuda", "CharStorage"),
("torch.cuda", "ComplexDoubleStorage"),
("torch.cuda", "ComplexFloatStorage"),
("torch.cuda", "DoubleStorage"),
("torch.cuda", "FloatStorage"),
("torch.cuda", "HalfStorage"),
("torch.cuda", "IntStorage"),
("torch.cuda", "LongStorage"),
("torch.cuda", "ShortStorage"),
("torch.multiprocessing.reductions", "rebuild_tensor"),
("torch.multiprocessing.reductions", "rebuild_meta_tensor"),
("torch.multiprocessing.reductions", "rebuild_cuda_tensor"),
("sglang.srt.utils.patch_torch", "_rebuild_cuda_tensor_modified"),
("torch_npu.multiprocessing.reductions", "rebuild_npu_tensor"),
("sglang.srt.utils.patch_torch", "_rebuild_npu_tensor_modified"),
("torch.multiprocessing.reductions", "rebuild_nested_tensor"),
("torch.multiprocessing.reductions", "rebuild_sparse_coo_tensor"),
("torch.multiprocessing.reductions", "rebuild_sparse_compressed_tensor"),
("torch.multiprocessing.reductions", "rebuild_storage_fd"),
("torch.multiprocessing.reductions", "rebuild_storage_filename"),
("torch.multiprocessing.reductions", "rebuild_storage_empty"),
("torch.multiprocessing.reductions", "rebuild_typed_storage"),
("torch.multiprocessing.reductions", "rebuild_typed_storage_child"),
("torch", "per_tensor_affine"),
("torch", "per_tensor_symmetric"),
("torch", "per_channel_affine"),
("torch", "per_channel_symmetric"),
("torch", "per_channel_affine_float_qparams"),
# --- SGLang data containers only (no code modules) ---
("sglang.srt.managers.io_struct", "GenerateReqInput"),
("sglang.srt.managers.io_struct", "EmbeddingReqInput"),
("sglang.srt.disaggregation.encoder.receiver", "EmbeddingData"),
("sglang.srt.managers.schedule_batch", "Modality"),
("sglang.srt.weight_sync.tensor_bucket", "FlattenedTensorMetadata"),
("sglang.srt.weight_sync.tensor_bucket", "FlattenedTensorBucket"),
(
"sglang.srt.model_executor.model_runner_components.weight_updater",
"LocalSerializedTensor",
),
("sglang.srt.model_executor.model_runner", "LocalSerializedTensor"),
} }
def find_class(self, module, name): def find_class(self, module, name):
if (module, name) in self.ALLOWED_GLOBALS or any( if (module, name) == ("torch.storage", "_load_from_bytes"):
(module + ".").startswith(prefix) for prefix in self.ALLOWED_MODULE_PREFIXES # Torch's helper calls an unrestricted nested torch.load.
): return _safe_load_torch_storage
if (module, name) in self.ALLOWED_GLOBALS:
return super().find_class(module, name) return super().find_class(module, name)
raise RuntimeError( raise RuntimeError(