66b7d7584f
no pickle shall pass thank you again, RyotaK
110 lines
3.8 KiB
Python
110 lines
3.8 KiB
Python
# this code is adapted from the script contributed by anon from /h/
|
|
|
|
import io
|
|
import pickle
|
|
import collections
|
|
import sys
|
|
import traceback
|
|
|
|
import torch
|
|
import numpy
|
|
import _codecs
|
|
import zipfile
|
|
import re
|
|
|
|
|
|
# PyTorch 1.13 and later have _TypedStorage renamed to TypedStorage
|
|
TypedStorage = torch.storage.TypedStorage if hasattr(torch.storage, 'TypedStorage') else torch.storage._TypedStorage
|
|
|
|
|
|
def encode(*args):
|
|
out = _codecs.encode(*args)
|
|
return out
|
|
|
|
|
|
class RestrictedUnpickler(pickle.Unpickler):
|
|
def persistent_load(self, saved_id):
|
|
assert saved_id[0] == 'storage'
|
|
return TypedStorage()
|
|
|
|
def find_class(self, module, name):
|
|
if module == 'collections' and name == 'OrderedDict':
|
|
return getattr(collections, name)
|
|
if module == 'torch._utils' and name in ['_rebuild_tensor_v2', '_rebuild_parameter']:
|
|
return getattr(torch._utils, name)
|
|
if module == 'torch' and name in ['FloatStorage', 'HalfStorage', 'IntStorage', 'LongStorage', 'DoubleStorage']:
|
|
return getattr(torch, name)
|
|
if module == 'torch.nn.modules.container' and name in ['ParameterDict']:
|
|
return getattr(torch.nn.modules.container, name)
|
|
if module == 'numpy.core.multiarray' and name == 'scalar':
|
|
return numpy.core.multiarray.scalar
|
|
if module == 'numpy' and name == 'dtype':
|
|
return numpy.dtype
|
|
if module == '_codecs' and name == 'encode':
|
|
return encode
|
|
if module == "pytorch_lightning.callbacks" and name == 'model_checkpoint':
|
|
import pytorch_lightning.callbacks
|
|
return pytorch_lightning.callbacks.model_checkpoint
|
|
if module == "pytorch_lightning.callbacks.model_checkpoint" and name == 'ModelCheckpoint':
|
|
import pytorch_lightning.callbacks.model_checkpoint
|
|
return pytorch_lightning.callbacks.model_checkpoint.ModelCheckpoint
|
|
if module == "__builtin__" and name == 'set':
|
|
return set
|
|
|
|
# Forbid everything else.
|
|
raise pickle.UnpicklingError(f"global '{module}/{name}' is forbidden")
|
|
|
|
|
|
allowed_zip_names = ["archive/data.pkl", "archive/version"]
|
|
allowed_zip_names_re = re.compile(r"^archive/data/\d+$")
|
|
|
|
|
|
def check_zip_filenames(filename, names):
|
|
for name in names:
|
|
if name in allowed_zip_names:
|
|
continue
|
|
if allowed_zip_names_re.match(name):
|
|
continue
|
|
|
|
raise Exception(f"bad file inside {filename}: {name}")
|
|
|
|
|
|
def check_pt(filename):
|
|
try:
|
|
|
|
# new pytorch format is a zip file
|
|
with zipfile.ZipFile(filename) as z:
|
|
check_zip_filenames(filename, z.namelist())
|
|
|
|
with z.open('archive/data.pkl') as file:
|
|
unpickler = RestrictedUnpickler(file)
|
|
unpickler.load()
|
|
|
|
except zipfile.BadZipfile:
|
|
|
|
# if it's not a zip file, it's an olf pytorch format, with five objects written to pickle
|
|
with open(filename, "rb") as file:
|
|
unpickler = RestrictedUnpickler(file)
|
|
for i in range(5):
|
|
unpickler.load()
|
|
|
|
|
|
def load(filename, *args, **kwargs):
|
|
from modules import shared
|
|
|
|
try:
|
|
if not shared.cmd_opts.disable_safe_unpickle:
|
|
check_pt(filename)
|
|
|
|
except Exception:
|
|
print(f"Error verifying pickled file from {filename}:", file=sys.stderr)
|
|
print(traceback.format_exc(), file=sys.stderr)
|
|
print(f"\nThe file may be malicious, so the program is not going to read it.", file=sys.stderr)
|
|
print(f"You can skip this check with --disable-safe-unpickle commandline argument.", file=sys.stderr)
|
|
return None
|
|
|
|
return unsafe_torch_load(filename, *args, **kwargs)
|
|
|
|
|
|
unsafe_torch_load = torch.load
|
|
torch.load = load
|