[lldb] Extend SWIG SBProcess interface with WriteMemoryAsCString method
authorMed Ismail Bennani <medismail.bennani@gmail.com>
Sat, 4 Mar 2023 02:52:15 +0000 (18:52 -0800)
committerMed Ismail Bennani <medismail.bennani@gmail.com>
Sat, 4 Mar 2023 03:33:01 +0000 (19:33 -0800)
This patch tries to address an interoperability issue when writing
python string into the process memory.

Since the python string is not null-terminated, it would still be
written to memory however, when trying to read it again with
`SBProcess::ReadCStringFromMemory`, the memory read would fail, since
the read string doens't contain a null-terminator, and therefore is not
a valid C string.

To address that, this patch extends the `SBProcess` SWIG interface to
expose a new `WriteMemoryAsCString` method that is only exposed to the
SWIG target language. That method checks that the buffer to write is
null-terminated and otherwise, it appends a null byte at the end of it.

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
lldb/bindings/interface/SBProcessExtensions.i
lldb/test/API/python_api/process/TestProcessAPI.py

index c6660d7..fe9e641 100644 (file)
@@ -2,6 +2,18 @@ STRING_EXTENSION_OUTSIDE(SBProcess)
 %extend lldb::SBProcess {
 #ifdef SWIGPYTHON
     %pythoncode %{
+        def WriteMemoryAsCString(self, addr, str, error):
+            '''
+              WriteMemoryAsCString(self, addr, str, error):
+                This functions the same as `WriteMemory` except a null-terminator is appended
+                to the end of the buffer if it is not there already.
+            '''
+            if not str or len(str) == 0:
+                return 0
+            if not str[-1] == '\0':
+                str += '\0'
+            return self.WriteMemory(addr, str, error)
+
         def __get_is_alive__(self):
             '''Returns "True" if the process is currently alive, "False" otherwise'''
             s = self.GetState()
index c56053d..edb5070 100644 (file)
@@ -186,6 +186,32 @@ class ProcessAPITestCase(TestBase):
             exe=False,
             startstr=b'a')
 
+        # Get the SBValue for the global variable 'my_cstring'.
+        val = frame.FindValue("my_cstring", lldb.eValueTypeVariableGlobal)
+        self.DebugSBValue(val)
+
+        addr = val.AddressOf().GetValueAsUnsigned()
+
+        # Write an empty string to memory
+        bytes_written = process.WriteMemoryAsCString(addr, "", error)
+        self.assertEqual(bytes_written, 0)
+        if not error.Success():
+            self.fail("SBProcess.WriteMemoryAsCString() failed")
+
+        message = "Hello!"
+        bytes_written = process.WriteMemoryAsCString(addr, message, error)
+        self.assertEqual(bytes_written, len(message) + 1)
+        if not error.Success():
+            self.fail("SBProcess.WriteMemoryAsCString() failed")
+
+        cstring = process.ReadCStringFromMemory(
+            val.AddressOf().GetValueAsUnsigned(), 256, error)
+        if not error.Success():
+            self.fail("SBProcess.ReadCStringFromMemory() failed")
+
+        self.assertEqual(cstring, message)
+
+
     def test_access_my_int(self):
         """Test access 'my_int' using Python SBProcess.GetByteOrder() and other APIs."""
         self.build()