Skip to content

Commit d64670a

Browse files
Merge PR "[AUTO-CHERRYPICK] [AutoPR- Security] Patch keras for CVE-2026-1669 [HIGH] - branch 3.0-dev" #16780
Co-authored-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
1 parent fe39a9f commit d64670a

2 files changed

Lines changed: 179 additions & 1 deletion

File tree

SPECS/keras/CVE-2026-1669.patch

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
From e24a3c620061070910de069ad5eed298241bbc9d Mon Sep 17 00:00:00 2001
2+
From: AllSpark <allspark@microsoft.com>
3+
Date: Tue, 14 Apr 2026 10:18:35 +0000
4+
Subject: [PATCH] Do not allow external links in HDF5 files; verify
5+
group/dataset; remove unverified items/values; adjust failed_saveables
6+
handling
7+
8+
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
9+
Upstream-reference: AI Backport of https://github.com/keras-team/keras/commit/8a37f9dadd8e23fa4ee3f537eeb6413e75d12553.patch
10+
---
11+
keras/src/saving/saving_lib.py | 72 ++++++++++++++++++----------------
12+
1 file changed, 39 insertions(+), 33 deletions(-)
13+
14+
diff --git a/keras/src/saving/saving_lib.py b/keras/src/saving/saving_lib.py
15+
index 0bcce01..fe5eb10 100644
16+
--- a/keras/src/saving/saving_lib.py
17+
+++ b/keras/src/saving/saving_lib.py
18+
@@ -413,7 +413,8 @@ def _load_state(
19+
try:
20+
saveable.load_own_variables(weights_store.get(inner_path))
21+
except Exception as e:
22+
- failed_saveables.add(id(saveable))
23+
+ if failed_saveables is not None:
24+
+ failed_saveables.add(id(saveable))
25+
error_msgs[id(saveable)] = saveable, e
26+
failure = True
27+
else:
28+
@@ -424,7 +425,8 @@ def _load_state(
29+
try:
30+
saveable.load_assets(assets_store.get(inner_path))
31+
except Exception as e:
32+
- failed_saveables.add(id(saveable))
33+
+ if failed_saveables is not None:
34+
+ failed_saveables.add(id(saveable))
35+
error_msgs[id(saveable)] = saveable, e
36+
failure = True
37+
else:
38+
@@ -472,7 +474,7 @@ def _load_state(
39+
if not failure:
40+
if visited_saveables is not None and newly_failed <= 0:
41+
visited_saveables.add(id(saveable))
42+
- if id(saveable) in failed_saveables:
43+
+ if failed_saveables is not None and id(saveable) in failed_saveables:
44+
failed_saveables.remove(id(saveable))
45+
error_msgs.pop(id(saveable))
46+
47+
@@ -657,10 +659,12 @@ class H5Entry:
48+
else:
49+
found = False
50+
if not path:
51+
- self.group = self.h5_file["vars"]
52+
+ self.group = self._verify_group(self.h5_file["vars"])
53+
found = True
54+
elif path in self.h5_file and "vars" in self.h5_file[path]:
55+
- self.group = self.h5_file[path]["vars"]
56+
+ self.group = self._verify_group(
57+
+ self._verify_group(self.h5_file[path])["vars"]
58+
+ )
59+
found = True
60+
else:
61+
# No hit.
62+
@@ -671,22 +675,43 @@ class H5Entry:
63+
)
64+
self.path = path
65+
if path in self.h5_file and "vars" in self.h5_file[path]:
66+
- self.group = self.h5_file[path]["vars"]
67+
+ self.group = self._verify_group(
68+
+ self._verify_group(self.h5_file[path])["vars"]
69+
+ )
70+
found = True
71+
if not found:
72+
self.group = {}
73+
74+
+ def _verify_group(self, group):
75+
+ if not isinstance(group, h5py.Group):
76+
+ raise ValueError(
77+
+ f"Invalid H5 file, expected Group but received {type(group)}"
78+
+ )
79+
+ return group
80+
+
81+
+ def _verify_dataset(self, dataset):
82+
+ if not isinstance(dataset, h5py.Dataset):
83+
+ raise ValueError(
84+
+ f"Invalid H5 file, expected Dataset, received {type(dataset)}"
85+
+ )
86+
+ # Disallow external links
87+
+ try:
88+
+ external = dataset.external
89+
+ except Exception:
90+
+ external = False
91+
+ if external:
92+
+ raise ValueError(
93+
+ "Not allowed: H5 file Dataset with external links: "
94+
+ f"{dataset.external}"
95+
+ )
96+
+ return dataset
97+
+
98+
def __len__(self):
99+
return self.group.__len__()
100+
101+
def keys(self):
102+
return self.group.keys()
103+
104+
- def items(self):
105+
- return self.group.items()
106+
-
107+
- def values(self):
108+
- return self.group.values()
109+
110+
def __setitem__(self, key, value):
111+
if self.mode != "w":
112+
@@ -700,56 +725,37 @@ class H5Entry:
113+
114+
def __getitem__(self, name):
115+
value = self.group[name]
116+
-
117+
- # ------------------------------------------------------
118+
- # CASE 2 — HDF5 DATASET → SAFE LOADING
119+
- # ------------------------------------------------------
120+
-
121+
- # Skip any objects that are not proper datasets
122+
+ # Not a dataset: try to read scalar content if possible
123+
if not hasattr(value, "shape") or not hasattr(value, "dtype"):
124+
- # Fallback: attempt read if possible, else return as-is
125+
try:
126+
return value[()]
127+
except Exception:
128+
return value
129+
-
130+
+ # Verify dataset and disallow external links
131+
+ value = self._verify_dataset(value)
132+
shape = value.shape
133+
dtype = value.dtype
134+
-
135+
- # ------------------------------------------------------
136+
- # Validate SHAPE (avoid malformed / malicious metadata)
137+
- # ------------------------------------------------------
138+
-
139+
# No negative dimensions
140+
if any(dim < 0 for dim in shape):
141+
raise ValueError(
142+
"Malformed HDF5 dataset shape encountered in .keras file; "
143+
"negative dimension detected."
144+
)
145+
-
146+
# Prevent absurdly high-rank tensors
147+
if len(shape) > 64:
148+
raise ValueError(
149+
"Malformed HDF5 dataset shape encountered in .keras file; "
150+
"tensor rank exceeds safety limit."
151+
)
152+
-
153+
# Safe product computation (Python int is unbounded)
154+
num_elems = int(np.prod(shape))
155+
-
156+
- # ------------------------------------------------------
157+
# Validate TOTAL memory size
158+
- # ------------------------------------------------------
159+
size_bytes = num_elems * dtype.itemsize
160+
if size_bytes > MAX_BYTES:
161+
raise ValueError(
162+
f"HDF5 dataset too large to load safely "
163+
f"({size_bytes} bytes; limit is {MAX_BYTES})."
164+
)
165+
-
166+
- # ------------------------------------------------------
167+
- # SAFE — load dataset (guaranteed ≤ 4 GiB)
168+
- # ------------------------------------------------------
169+
arr = value[()]
170+
if "dtype" in value.attrs and value.attrs["dtype"] == "bfloat16":
171+
arr = np.array(arr, dtype=ml_dtypes.bfloat16)
172+
--
173+
2.45.4
174+

SPECS/keras/keras.spec

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
Summary: Keras is a high-level neural networks API.
44
Name: keras
55
Version: 3.3.3
6-
Release: 6%{?dist}
6+
Release: 7%{?dist}
77
License: ASL 2.0
88
Vendor: Microsoft Corporation
99
Distribution: Azure Linux
@@ -17,6 +17,7 @@ Patch02: CVE-2025-8747.patch
1717
Patch03: CVE-2025-9905.patch
1818
Patch4: CVE-2025-12060.patch
1919
Patch5: CVE-2026-0897.patch
20+
Patch6: CVE-2026-1669.patch
2021

2122
# Fix for CVE-2025-9906 included as part of CVE-2025-8747 and kept here as nopatch
2223
# and commented out, because from patch command perspective, these files
@@ -81,6 +82,9 @@ python3 pip_build.py --install
8182

8283

8384
%changelog
85+
* Tue Apr 14 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.3.3-7
86+
- Patch for CVE-2026-1669
87+
8488
* Fri Jan 16 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 3.3.3-6
8589
- Patch for CVE-2026-0897
8690

0 commit comments

Comments
 (0)