Skip to content

Commit da9d3a4

Browse files
committed
Fix Use-After-Free in NumPy/df
For AoS and SoA `.to_numpy()` and dependent logic like `.to_df()`, the lifetime of the temporary copied variable is only handled by NumPy if we first create a named variable. The `to_host()` returned object is a temporary, and `np.array` using the `__array_interface__` protocol does not keep it alive automatically unless it is stored in an actual variable (eg., `tmp`).
1 parent e73fc60 commit da9d3a4

File tree

2 files changed

+16
-8
lines changed

2 files changed

+16
-8
lines changed

src/amrex/extensions/ArrayOfStructs.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ def aos_to_numpy(self, copy=False):
3232
if copy:
3333
# This supports a device-to-host copy.
3434
#
35-
# todo: validate of the to_host() returned object
36-
# lifetime is always managed correctly by
37-
# Python's GC - otherwise copy twice via copy=True
38-
return np.array(self.to_host(), copy=False)
35+
# The to_host() returned object is a temporary, and
36+
# np.array using the __array_interface__ protocol does
37+
# not keep it alive automatically unless it is stored
38+
# in an actual variable (tmp).
39+
tmp = self.to_host()
40+
ret = np.array(tmp, copy=False)
41+
assert ret.base is tmp
42+
return ret
3943
else:
4044
return np.array(self, copy=False)
4145

src/amrex/extensions/PODVector.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ def podvector_to_numpy(self, copy=False):
2929
if copy:
3030
# This supports a device-to-host copy.
3131
#
32-
# todo: validate of the to_host() returned object
33-
# lifetime is always managed correctly by
34-
# Python's GC - otherwise copy twice via copy=True
35-
return np.array(self.to_host(), copy=False)
32+
# The to_host() returned object is a temporary, and
33+
# np.array using the __array_interface__ protocol does
34+
# not keep it alive automatically unless it is stored
35+
# in an actual variable (tmp).
36+
tmp = self.to_host()
37+
ret = np.array(tmp, copy=False)
38+
assert ret.base is tmp
39+
return ret
3640
else:
3741
return np.array(self, copy=False)
3842
else:

0 commit comments

Comments
 (0)