1## 2# Copyright (c) 2023 Apple Inc. All rights reserved. 3# 4# @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5# 6# This file contains Original Code and/or Modifications of Original Code 7# as defined in and that are subject to the Apple Public Source License 8# Version 2.0 (the 'License'). You may not use this file except in 9# compliance with the License. The rights granted to you under the License 10# may not be used to create, or enable the creation or redistribution of, 11# unlawful or unlicensed copies of an Apple operating system, or to 12# circumvent, violate, or enable the circumvention or violation of, any 13# terms of an Apple operating system software license agreement. 14# 15# Please obtain a copy of the License at 16# http://www.opensource.apple.com/apsl/ and read it before using this file. 17# 18# The Original Code and all software distributed under the License are 19# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23# Please see the License for the specific language governing rights and 24# limitations under the License. 25# 26# @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27## 28 29# pylint: disable=invalid-name 30 31""" Tests that ScriptedProcess mock behaves as expected. """ 32 33import unittest.mock 34import io 35 36import lldb 37from lldbtest.testcase import LLDBTestCase 38from lldbmock.memorymock import RawMock 39from lldbmock.utils import lookup_type 40 41 42class ScriptedProcessTest(LLDBTestCase): 43 """ Scripted process unit test. """ 44 45 def test_RawMock(self): 46 """ Install simple raw memory mock into a target. """ 47 48 RAWMOCK_ADDR = 0xffffffff00000000 49 50 mock = RawMock(100) 51 mock.setData(b"lldb-process-mock\x00") 52 self.add_mock(RAWMOCK_ADDR, mock) 53 54 # Test is using LLDB command intentionaly. 55 res = self.run_command(f'x/s {RAWMOCK_ADDR:#x}') 56 57 self.assertTrue(res.Succeeded()) 58 self.assertEqual( 59 res.GetOutput(), 60 f'{RAWMOCK_ADDR:#x}: "lldb-process-mock"\n' 61 ) 62 63 def test_RawMockIO(self): 64 """ Populate simple raw memory mock from provided IO. """ 65 66 RAWMOCK_ADDR = 0xffffffff50000000 67 68 mock = RawMock.fromBufferedIO(io.BytesIO(b"lldb-io-mock\x00")) 69 self.add_mock(RAWMOCK_ADDR, mock) 70 71 # Test is using LLDB command intentionaly. 72 res = self.run_command(f'x/s {RAWMOCK_ADDR:#x}') 73 74 self.assertTrue(res.Succeeded()) 75 self.assertEqual( 76 res.GetOutput(), 77 f'{RAWMOCK_ADDR:#x}: "lldb-io-mock"\n' 78 ) 79 80 def test_DuplicateMock(self): 81 """ Install same simple mock to two VA locations. """ 82 83 mock = RawMock(100) 84 mock.setData(b"shared-mock\x00") 85 self.add_mock(0xffffffff10000000, mock) 86 self.add_mock(0xffffffff20000000, mock) 87 88 # Test both locations 89 for addr in ('0xffffffff10000000', '0xffffffff20000000'): 90 res = self.run_command(f'x/s {addr}') 91 92 self.assertTrue(res.Succeeded()) 93 self.assertEqual( 94 res.GetOutput(), 95 f'{addr}: "shared-mock"\n' 96 ) 97 98 def test_MockConflict(self): 99 """ Check that we can't add overlapping mocks. """ 100 101 mock = RawMock(16) 102 self.add_mock(0x12345, mock) 103 with self.assertRaises(ValueError): 104 mock = RawMock(16) 105 self.add_mock(0x12346, mock) 106 107 def test_SimpleMock(self): 108 """ Mock instance of a simple type. """ 109 110 UINT_ADDR = 0xffffffff11223344 111 112 self.create_mock('uint32_t', UINT_ADDR).setData(0x1234) 113 114 res = self.run_command(f'p/x *((uint32_t *){UINT_ADDR:#x})') 115 116 self.assertTrue(res.Succeeded()) 117 self.assertEqual(res.GetOutput(), "(uint32_t) 0x00001234\n") 118 119 @unittest.skipIf(LLDBTestCase.kernel().startswith('mach.release'), 120 "Not available in RELEASE embedded") 121 def test_CompoundMock(self): 122 """ Mock instance of simple structure. """ 123 124 DOFHELPER_ADDR = 0xffffffff11220000 125 126 # Construct simple data structure mock. 127 self.create_mock('struct dof_helper', DOFHELPER_ADDR).fromDict({ 128 'dofhp_mod': b'mock-mod', 129 'dofhp_addr': 0x1234, 130 'dofhp_dof': 0x5678 131 }) 132 133 # Construct SBValue on top of the mock. 134 addr = self.target.ResolveLoadAddress(DOFHELPER_ADDR) 135 sbv = self.target.CreateValueFromAddress( 136 'test', addr, lookup_type('dof_helper_t')) 137 138 self.assertTrue(sbv.IsValid() and sbv.error.success) 139 140 # Check that LLDB SBAPI returns correct values from mock. 141 err = lldb.SBError() 142 self.assertEqual( 143 sbv.GetChildMemberWithName('dofhp_mod').GetData() 144 .GetString(err, 0), 145 "mock-mod" 146 ) 147 self.assertEqual( 148 sbv.GetChildMemberWithName('dofhp_addr').GetValueAsUnsigned(), 149 0x1234 150 ) 151 self.assertEqual( 152 sbv.GetChildMemberWithName('dofhp_dof').GetValueAsUnsigned(), 153 0x5678 154 ) 155 156 @unittest.skipIf(LLDBTestCase.kernel().startswith('mach.release'), 157 "Not available in RELEASE embedded") 158 def test_CompoundMock_UpdateProperty(self): 159 """ Test that mock can deserilize properties from update. """ 160 161 mock = self.create_mock('struct dof_helper', 0xffffffff55555555) 162 mock.setData( 163 b'hello-mock' + b'\x00'*54 + 164 0xfeedface.to_bytes(length=8, byteorder='little') + 165 0xdeadbeef.to_bytes(length=8, byteorder='little')) 166 167 # Test that mock has de-serialized correctly whole blob above. 168 self.assertEqual(mock.dofhp_mod[:10], b'hello-mock') 169 self.assertEqual(mock.dofhp_addr, 0xfeedface) 170 self.assertEqual(mock.dofhp_dof, 0xdeadbeef) 171 172 def test_UnionMock(self): 173 """ Test that simple union/bitfield propagates property updates. """ 174 175 mock = self.create_mock('kds_ptr', 0xffffffff30000000) 176 177 mock.buffer_index = 0b111111111111111111111 # 21-bits 178 self.assertEqual(mock.raw, 0x001fffff) 179 180 mock.buffer_index = 0 181 mock.offset = 0b11111111111 # 11-bits 182 self.assertEqual(mock.raw, 0xffe00000) 183 184 mock.raw = 0xffdffffe 185 self.assertEqual(mock.buffer_index, 0x001ffffe) 186 self.assertEqual(mock.offset, 0x7fe) 187 188 def test_MockArray(self): 189 """ Test simple mock of char array. """ 190 191 STR_ADDR = 0xffffffff33004400 192 PTR_ADDR = 0xffffffff44000000 193 194 # Construct an array in memory. 195 arrtype = lookup_type('char').GetArrayType(256) 196 marray = self.create_mock(arrtype, STR_ADDR) 197 marray.setData(b'Hello World\x00') 198 199 # Create a pointer to the array 200 ptrtype = lookup_type('char').GetPointerType() 201 mstr = self.create_mock(ptrtype, PTR_ADDR) 202 mstr.setData(STR_ADDR) 203 204 # Let LLDB print it. 205 addr = self.target.ResolveLoadAddress(PTR_ADDR) 206 sbv = self.target.CreateValueFromAddress('str', addr, ptrtype) 207 self.assertTrue(sbv.IsValid() and sbv.error.success) 208 209 err = lldb.SBError() 210 self.assertEqual(sbv.GetPointeeData(0, 256).GetString(err, 0), 211 'Hello World') 212 213 def test_MockTypedArray(self): 214 """ Test array of compound types. """ 215 216 ARRAY_ADDR = 0xffffffff44003300 217 218 arrtype = lookup_type('proc').GetArrayType(10) 219 self.create_mock(arrtype, ARRAY_ADDR).fromDict({ 220 '0': { 221 'p_comm': b'bar-foo\x00' 222 }, 223 '1': { 224 'p_comm': b'foo-bar\x00' 225 } 226 }) 227 228 res = self.run_command(f'p/x ((proc_t){ARRAY_ADDR:#x})[1].p_comm') 229 self.assertTrue(res.Succeeded()) 230 231 # Check that elements don't overlap somehow 232 # (use SBValue to exercise LLDB's internals) 233 addr = self.target.ResolveLoadAddress(ARRAY_ADDR) 234 sbv = self.target.CreateValueFromAddress('proc_arr', addr, arrtype) 235 self.assertTrue(sbv.IsValid() and sbv.error.success) 236 237 err = lldb.SBError() 238 self.assertEqual( 239 sbv.GetChildAtIndex(0).GetChildMemberWithName('p_comm') 240 .GetData().GetString(err, 0), 241 'bar-foo' 242 ) 243 self.assertEqual( 244 sbv.GetChildAtIndex(1).GetChildMemberWithName('p_comm') 245 .GetData().GetString(err, 0), 246 'foo-bar' 247 ) 248 249 def test_NoNewAttributes(self): 250 """ Test that mock instances are properly frozen after creation. """ 251 252 mock = self.create_mock(lookup_type('uint32_t')) 253 254 with self.assertRaises(TypeError): 255 mock.foo = 5 256 257 @unittest.skipIf(LLDBTestCase.kernel().startswith('mach.release'), 258 "Not available in RELEASE embedded") 259 def test_NestedStruct(self): 260 """ Test that nested mocks properly serialize. """ 261 262 PROVNAME_ADDR = 0xffffffff70707070 263 DTHELPER_ADDR = 0xffffffff80808080 264 265 # Setup mock with fake values. 266 arrtype = lookup_type('char').GetArrayType(256) 267 marray = self.create_mock(arrtype, PROVNAME_ADDR) 268 marray.setData(b'test-prov\x00') 269 270 sbtype = lookup_type('dtrace_helper_provdesc_t') 271 mock = self.create_mock(sbtype, DTHELPER_ADDR) 272 273 mock.dthpv_provname = PROVNAME_ADDR 274 mock.dthpv_pattr.dtpa_mod.dtat_name = 0x5 275 276 # Serializer should prevent overflowing a member's size. 277 with self.assertRaises(OverflowError): 278 mock.dthpv_pattr.dtpa_args.dtat_class = 0x7777 279 280 mock.dthpv_pattr.dtpa_args.dtat_class = 0x77 281 282 # Obtain SBValue and check modified members 283 addr = self.target.ResolveLoadAddress(DTHELPER_ADDR) 284 sbv = self.target.CreateValueFromAddress('test', addr, sbtype) 285 self.assertTrue(sbv.IsValid() and sbv.error.success) 286 287 err = lldb.SBError() 288 self.assertEqual( 289 sbv.GetChildMemberWithName('dthpv_provname') 290 .GetPointeeData(0, 256).GetString(err, 0), 291 'test-prov' 292 ) 293 self.assertEqual( 294 sbv.GetValueForExpressionPath('.dthpv_pattr.dtpa_mod.dtat_name') 295 .GetValueAsUnsigned(), 296 0x5 297 ) 298 self.assertEqual( 299 sbv.GetValueForExpressionPath('.dthpv_pattr.dtpa_args.dtat_class') 300 .GetValueAsUnsigned(), 301 0x77 302 ) 303 304 @unittest.mock.patch('xnu.kern.globals.proc_struct_size', 2048) 305 def test_ProxyMock(self): 306 """ Test anonymous members forwarding. """ 307 308 PROC_ADDR = 0xffffffff90909090 309 PROC_RO_ADDR = 0xffffff0040404040 310 311 mock = self.create_mock('proc', PROC_ADDR) 312 313 mock.p_list.le_next = 0x12345678 314 mock.p_smr_node.smrn_next = 0x12345678 315 mock.p_pid = 12345 316 mock.p_argc = 0x5 317 mock.p_textvp = 0xfeedface 318 mock.p_lflag = 0x00000002 319 320 mock.p_comm = b'foobar' # Use forwarding property 321 322 task = self.create_mock('task', PROC_ADDR + 2048) 323 324 task.effective_policy.tep_sup_active = 0 325 task.effective_policy.tep_darwinbg = 0 326 task.effective_policy.tep_lowpri_cpu = 1 327 task.t_flags = 0x00800000 328 329 self.create_mock('proc_ro', PROC_RO_ADDR) 330 331 mock.p_proc_ro = PROC_RO_ADDR 332 task.bsd_info_ro = PROC_RO_ADDR 333 334 # Populate and test mock. 335 res = self.run_command(f'p/x ((proc_t){PROC_ADDR:#x})->p_comm') 336 self.assertEqual(res.GetOutput(), '(command_t) "foobar"\n') 337 self.assertTrue(res.Succeeded()) 338 339 # Modify mock and test again. 340 mock.p_forkcopy.p_comm = b'barfoo' # Sub-mock prop wins 341 self.invalidate_cache() 342 343 res = self.run_command(f'p/x ((proc_t){PROC_ADDR:#x})->p_comm') 344 self.assertEqual(res.GetOutput(), '(command_t) "barfoo"\n') 345 self.assertTrue(res.Succeeded()) 346