#!/usr/bin/env python3 # libpulp - User-space Livepatching Library # # Copyright (C) 2021 SUSE Software Solutions GmbH # # This file is part of libpulp. # # libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('exception_handling') child.expect('caught in lib') child.expect('caught in main') child.close(force=True) exit(0)
# but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. # Check the comments of `libbuildid_livepatch1.ulp` Makefile rule in # Makefile.am on this folder. # # This test should fail because of buildid mismatch. import testsuite import subprocess errorcode = 1 child = testsuite.spawn('buildid') child.expect('Waiting for input.') child.sendline('') child.expect('1338') try: child.livepatch('libbuildid_livepatch1.ulp') except subprocess.CalledProcessError: errorcode = 0 child.close(force=True) exit(errorcode)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('contract') child.expect('Waiting for input.') errors = 0 child.sendline('') child.sendline('') child.expect('TYPE A data 128') child.sendline('') child.expect('TYPE B data 256.000000') # Send the test program into the library. child.sendline('') # Apply live patch while inside library child.livepatch('libcontract_livepatch1.ulp')
#!/usr/bin/env python3 # libpulp - User-space Livepatching Library # # Copyright (C) 2021 SUSE Software Solutions GmbH # # This file is part of libpulp. # # libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('cancel') child.expect('OK') child.close(force=True) exit(0)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('memory_protection') child.expect('Waiting for input.') # Every time a newline is sent, the test program touchs code memory child.sendline('') child.expect('pristine') child.livepatch('libaddress_livepatch1.ulp') # Try to touch code memory after live patching child.sendline('') try: child.expect('patched', reject='pristine') except EOFError: # Diagnose the error
# version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import signal import subprocess import testsuite child = testsuite.spawn('asunsafe_conversion') child.expect('Waiting for signals.') child.kill(signal.SIGUSR1) child.expect('hello') errors = 0 try: # Apply the live patch. child.livepatch('libblocked_livepatch1.ulp', retries=100) except subprocess.TimeoutExpired: print('Deadlock while live patching - AS-Unsafe conversion not tested') # The deadlock test (tests/deadlock) has a far greater chance of # detecting deadlocks during the application of live-patches, so # return 77 to report that this test case was unable to detect the
try: print('Applying/reverting live patch.') tool = subprocess.run(command, timeout=timeout) except subprocess.TimeoutExpired: print('Live patching timed out.') raise # The trigger tool returns 0 on success, so use check_returncode(), # which asserts that, and raises CalledProcessError otherwise. tool.check_returncode() print('Live patch applied/reverted successfully.') childs = [ testsuite.spawn('manyprocesses'), testsuite.spawn('manyprocesses'), testsuite.spawn('manyprocesses') ] for child in childs: child.expect('Waiting for input.') child.sendline('') child.expect('1-2-3-4-5-6-7-8') child.expect('1.0-2.0-3.0-4.0-5.0-6.0-7.0-8.0-9.0-10.0') childless_livepatch(wildcard='./libmanyprocesses*.ulp', verbose=True, revert_lib='libmanyprocesses.so.0')
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import subprocess import testsuite # Since the deadlock demonstrated by this test case does not occur # everytime it executes, run it in a loop. The amount of iterations # hardcoded is arbitrary errors = 0 child = testsuite.spawn('deadlock', log=None) child.expect('Waiting for input.') child.sendline('') child.expect('hello') # Applying a live patch to a process entails stopping all of its # threads, then stealing one of them to jack into the process and call # libpulp.so's routines that load and apply the live patch. These # routines are called from the context of a signal-handler, and, as # such, should not make calls to Asynchronous Signal Unsafe functions. # However, libpulp calls dlopen, which is AS-Unsafe. for attempt in range(200): print(' Attempt #' + str(attempt)) try:
# libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import os import subprocess import time import testsuite from testsuite import ulptool parent = testsuite.spawn('./terminal ./loop', script=False) # Parent signal readiness first parent.expect('Parent ready') # Read the pid of the child process, which wrote it to stdout parent.expect('Child ready') parent.expect('[0-9]+') pid = parent.after if not type(pid) == str: raise TypeError if not pid.isnumeric(): raise ValueError # If live patching SIGSTOPs the target process, the parent detects it # with waitpid, but only most of the time. If the trigger and check
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('redzone', timeout=50) child.expect('Waiting for input.') child.livepatch('libblocked_livepatch1.ulp') # Check live patch, which should not touch the redzone child.is_patch_applied('libblocked_livepatch1.ulp') # Read error output, if any child.readline() child.readline() ret = child.wait() if ret: exit(1)
# # libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('recursion', timeout=60) child.expect('Waiting for input.') child.sendline('45') child.expect('1134903170') child.livepatch('librecursion_livepatch1.ulp') child.sendline('45') child.expect('2537720636', reject='1134903170') child.close(force=True) exit(0)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('syscall_restart') child.expect('Waiting for input.') # After printing the greeting message, the target process makes a call # to fgets, which calls the read syscall. Applying a live patch will # interrupt the syscall. child.livepatch('libparameters_livepatch1.ulp') # Send a newline, which should be received by the read syscall if it has # been successfully restarted by libpulp. If the syscall has not been # restarted, the child program will exit without printing anything. child.sendline('') child.expect('8-7-6-5-4-3-2-1') child.expect('10.0-9.0-8.0-7.0-6.0-5.0-4.0-3.0-2.0-1.0')
# libpulp - User-space Livepatching Library # # Copyright (C) 2021 SUSE Software Solutions GmbH # # This file is part of libpulp. # # libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('constructor') # Simply check that the test program works correctly with malloc interposition. # See constructor.c for a lengthier explanation. child.expect('OK') child.close(force=True) exit(0)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('process', script=False) child.expect('Waiting for input.') child.sendline('') child.expect('1-2-3-4-5-6-7-8'); child.expect('1.0-2.0-3.0-4.0-5.0-6.0-7.0-8.0-9.0-10.0'); child.livepatch('libprocess_livepatch1.ulp') child.sendline('') child.expect('8-7-6-5-4-3-2-1', reject='1-2-3-4-5-6-7-8'); child.expect('10.0-9.0-8.0-7.0-6.0-5.0-4.0-3.0-2.0-1.0', reject='1.0-2.0-3.0-4.0-5.0-6.0-7.0-8.0-9.0-10.0'); child.livepatch('libprocess_livepatch1.rev')
# License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import signal import testsuite child = testsuite.spawn('blocked') # Wait for both threads to be ready (the order does not matter) child.expect('Waiting for signals.') child.expect('Waiting for signals.') # At program start, two threads are put into loops waiting for signals, # thread1 waits for SIGUSR1 and thread2 waits for SIGUSR2. child.kill(signal.SIGUSR1) child.expect('hello') child.kill(signal.SIGUSR2) child.expect('hello') # After the live patching, thread1, which is looping outside the # library, should produce a different output, whereas thread2, which # never leaves the library, should display the old behavior.
# License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import subprocess import testsuite livepatch_metadata = 'libparameters_livepatch2.ulp' livepatch_container = 'libparameters_livepatch2.so' child = testsuite.spawn('parameters') child.expect('Waiting for input.') errors = 1 try: child.livepatch(livepatch_metadata) except subprocess.CalledProcessError: if not child.is_so_loaded(livepatch_container): errors = 0 child.close(force=True) exit(errors)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn(testsuite.testname) child.expect('Original TLS banner') child.expect('Banner changed from thread_func: 0') child.expect('Banner changed from thread_func: 1') child.livepatch('libtls_livepatch1.ulp') child.sendline('') child.expect('String from live patch', reject=[ 'String from thread_func: 0', 'String from thread_func: 1', 'Live patch data references not initialized' ]) child.close(force=True)
# libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('numserv') child.expect('Waiting for input.') child.sendline('hundred') child.expect('100') child.livepatch('libhundreds_livepatch1.ulp') child.sendline('hundred') child.expect('200', reject='100') child.livepatch('libhundreds_livepatch2.ulp') child.sendline('hundred') child.expect('300', reject=['100', '200'])
# # libpulp is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # libpulp is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU General Public License # along with libpulp. If not, see <http://www.gnu.org/licenses/>. import testsuite child = testsuite.spawn('pagecross') child.expect('Waiting for input.') child.sendline('') child.expect('default') child.livepatch('libpagecross_livepatch1.ulp') child.sendline('') child.expect('patched', reject='default') child.close(force=True) exit(0)