Skip to content

Commit 3071ebf

Browse files
[LLDB][PythonFile] fix dangerous borrow semantics on python2
Summary: It is inherently unsafe to allow a python program to manipulate borrowed memory from a python object's destructor. It would be nice to flush a borrowed file when python is finished with it, but it's not safe to do on python 2. Python 3 does not suffer from this issue. Reviewers: labath, mgorny Reviewed By: labath Subscribers: lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D69532
1 parent fbe7f5e commit 3071ebf

File tree

2 files changed

+15
-17
lines changed

2 files changed

+15
-17
lines changed

lldb/packages/Python/lldbsuite/test/python_api/file_handle/TestFileHandle.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,6 @@ def i(sbf):
851851
yield sbf
852852
sbf.Write(str(i).encode('ascii') + b"\n")
853853
files = list(i(sbf))
854-
# delete them in reverse order, again because each is a borrow
855-
# of the previous.
856-
while files:
857-
files.pop()
858854
with open(self.out_filename, 'r') as f:
859855
self.assertEqual(list(range(10)), list(map(int, f.read().strip().split())))
860856

lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,21 +1500,23 @@ Expected<PythonFile> PythonFile::FromFile(File &file, const char *mode) {
15001500
PyObject *file_obj;
15011501
#if PY_MAJOR_VERSION >= 3
15021502
file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr,
1503-
"ignore", nullptr, 0);
1503+
"ignore", nullptr, /*closefd=*/0);
15041504
#else
1505-
// We pass ::flush instead of ::fclose here so we borrow the FILE* --
1506-
// the lldb_private::File still owns it. NetBSD does not allow
1507-
// input files to be flushed, so we have to check for that case too.
1508-
int (*closer)(FILE *);
1509-
auto opts = file.GetOptions();
1510-
if (!opts)
1511-
return opts.takeError();
1512-
if (opts.get() & File::eOpenOptionWrite)
1513-
closer = ::fflush;
1514-
else
1515-
closer = [](FILE *) { return 0; };
1505+
// I'd like to pass ::fflush here if the file is writable, so that
1506+
// when the python side destructs the file object it will be flushed.
1507+
// However, this would be dangerous. It can cause fflush to be called
1508+
// after fclose if the python program keeps a reference to the file after
1509+
// the original lldb_private::File has been destructed.
1510+
//
1511+
// It's all well and good to ask a python program not to use a closed file
1512+
// but asking a python program to make sure objects get released in a
1513+
// particular order is not safe.
1514+
//
1515+
// The tradeoff here is that if a python 2 program wants to make sure this
1516+
// file gets flushed, they'll have to do it explicitly or wait untill the
1517+
// original lldb File itself gets flushed.
15161518
file_obj = PyFile_FromFile(file.GetStream(), py2_const_cast(""),
1517-
py2_const_cast(mode), closer);
1519+
py2_const_cast(mode), [](FILE *) { return 0; });
15181520
#endif
15191521

15201522
if (!file_obj)

0 commit comments

Comments
 (0)