├── .gitattributes ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.rst ├── Vagrantfile ├── dist ├── libcare.preset ├── libcare.service ├── libcare.socket ├── libcare.spec └── selinux │ ├── Makefile │ ├── libcare.fc │ ├── libcare.if │ └── libcare.te ├── docker └── kernelcare │ └── centos7 │ └── gcc-4.8.2-16.el7 │ └── Dockerfile ├── docs ├── Makefile ├── internals.rst ├── libcare-ctl.rst └── libcare-patch-make.rst ├── packages └── rhel7 │ └── glibc │ └── glibc-2.17-55.el7 │ ├── README.md │ ├── info │ ├── pkgfile.yaml │ ├── plist │ └── properties.yml ├── patches └── glibc │ └── 2.17 │ └── glibc-rh1183545.patch ├── samples ├── ghost │ ├── GHOST.c │ ├── Makefile │ └── README.rst └── server │ ├── Makefile │ ├── README.rst │ ├── hack.sh │ ├── server.c │ └── server.patch ├── scripts ├── de-offset-syms.awk ├── patch_list_apply ├── pkgbuild └── toil │ ├── README.md │ ├── build-patch.sh │ ├── pkgbuild.py │ ├── requirements.txt │ └── tests_pkgbuild.py ├── src ├── Makefile ├── deps │ └── keep ├── kpatch_cc.c ├── kpatch_common.c ├── kpatch_common.h ├── kpatch_coro.c ├── kpatch_coro.h ├── kpatch_dbgfilter.c ├── kpatch_dbgfilter.h ├── kpatch_elf.c ├── kpatch_elf.h ├── kpatch_elf_objinfo.c ├── kpatch_elf_objinfo.h ├── kpatch_file.h ├── kpatch_flags.h ├── kpatch_gensrc.c ├── kpatch_io.c ├── kpatch_io.h ├── kpatch_log.c ├── kpatch_log.h ├── kpatch_make.c ├── kpatch_parse.c ├── kpatch_parse.h ├── kpatch_patch.c ├── kpatch_patch.h ├── kpatch_process.c ├── kpatch_process.h ├── kpatch_ptrace.c ├── kpatch_ptrace.h ├── kpatch_storage.c ├── kpatch_storage.h ├── kpatch_str.h ├── kpatch_strip.c ├── kpatch_user.c ├── kpatch_user.h ├── libcare-client.c ├── libcare-patch-make ├── list.h ├── rbtree.c ├── rbtree.h └── util.h ├── tests ├── Makefile ├── README.rst ├── both │ ├── Makefile │ ├── both.c │ ├── both.diff │ ├── desc │ ├── libboth.c │ └── libboth.diff ├── execve │ ├── Makefile │ ├── README.rst │ └── execve.c ├── fail_busy_single │ ├── Makefile │ ├── desc │ ├── fail_busy_single.c │ └── fail_busy_single.diff ├── fail_busy_single_top │ ├── Makefile │ ├── desc │ ├── fail_busy_single_top.c │ └── fail_busy_single_top.diff ├── fail_busy_threads │ ├── Makefile │ ├── desc │ ├── fail_busy_threads.c │ └── fail_busy_threads.diff ├── fail_coro │ ├── Makefile │ ├── desc │ ├── fail_coro.c │ ├── fail_coro.diff │ └── fail_coro_common.c ├── fail_coro_listed │ ├── Makefile │ ├── desc │ ├── export.txt │ ├── fail_coro_listed.c │ └── fail_coro_listed.diff ├── fail_threading │ ├── Makefile │ ├── desc │ ├── fail_threading.c │ └── fail_threading.diff ├── fail_unpatch │ ├── Makefile │ ├── desc │ ├── fail_unpatch.c │ └── fail_unpatch.diff ├── fastsleep.c ├── frame_finish │ ├── Makefile │ ├── desc │ ├── frame_finish.c │ └── frame_finish.diff ├── ifunc │ ├── Makefile │ ├── desc │ ├── ifunc.c │ ├── ifunc.diff │ └── libifunc.c ├── makefile-lpmake.inc ├── makefile-patch-link.inc ├── makefile-patch.inc ├── makefile.inc ├── new_func │ ├── Makefile │ ├── desc │ ├── new_func.c │ └── new_func.diff ├── patchlevel │ ├── Makefile │ ├── desc_ │ ├── libpatchlevel.c │ ├── libpatchlevel.diff │ ├── libpatchlevel.diff2 │ ├── makefile.first │ ├── makefile.second │ ├── patchlevel.c │ ├── patchlevel.diff │ └── patchlevel.diff2 ├── ref_glibc │ ├── Makefile │ ├── desc │ ├── ref_glibc.c │ └── ref_glibc.diff ├── ref_glibc_data │ ├── Makefile │ ├── desc │ ├── ref_glibc_data.c │ └── ref_glibc_data.diff ├── ref_orig_single │ ├── Makefile │ ├── desc │ ├── ref_orig_single.c │ └── ref_orig_single.diff ├── ref_orig_threads │ ├── Makefile │ ├── desc │ ├── ref_orig_threads.c │ └── ref_orig_threads.diff ├── ref_rodata │ ├── Makefile │ ├── desc │ ├── ref_rodata.c │ └── ref_rodata.diff ├── run_tests.sh ├── shared │ ├── Makefile │ ├── desc │ ├── libshared.c │ ├── libshared.diff │ └── shared.c ├── shared_ref_glibc │ ├── Makefile │ ├── desc │ ├── libshared_ref_glibc.c │ ├── libshared_ref_glibc.diff │ └── shared_ref_glibc.c ├── simplest │ ├── Makefile │ ├── desc │ ├── simplest.c │ └── simplest.diff ├── tls_shared │ ├── Makefile │ ├── desc │ ├── libtls_shared.c │ ├── libtls_shared.diff │ └── tls_shared.c └── tls_simple │ ├── Makefile │ ├── desc │ ├── tls_simple.c │ └── tls_simple.diff └── vagrant_boxes /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pdf binary 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.d 3 | src/kpatch_gensrc 4 | src/kpatch_make 5 | src/kpatch_strip 6 | src/libcare-cc 7 | src/libcare-client 8 | src/libcare-ctl 9 | src/libcare-stresstest 10 | tags 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | script: make tests 2 | 3 | language: c 4 | 5 | dist: trusty 6 | sudo: required 7 | 8 | addons: 9 | apt: 10 | packages: 11 | - elfutils 12 | - libelf-dev 13 | - libunwind8-dev 14 | - realpath 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #dummy 2 | 3 | all: src 4 | 5 | src: FORCE 6 | make -C src 7 | 8 | tests: src FORCE 9 | make -C tests 10 | 11 | clean: 12 | make -C src clean 13 | make -C tests clean 14 | 15 | ALL_HOSTS ?= $(shell cat vagrant_boxes) 16 | LATEST_HOSTS ?= centos-6.8 centos-7.4 ubuntu-14.04 ubuntu-16.04 17 | KEEP_ENV ?= 18 | 19 | LOGDIR?=logs/ 20 | vagrant-ci-%: FORCE 21 | mkdir -p $(LOGDIR); \ 22 | host="$(subst vagrant-ci-,,$@)-test"; \ 23 | test -z "$(QUIET)" || \ 24 | exec 3<&1 4<&2 1>$(LOGDIR)$(LOGPREFIX)$$host.log 2>&1; \ 25 | vagrant up $$host && \ 26 | vagrant ssh $$host -- -tt 'ls -R ~/kernelcare'; \ 27 | vagrant ssh $$host -- -tt \ 28 | 'KPCC_DEBUG=1 \ 29 | make -C libcare clean tests'; \ 30 | rv=$$?; \ 31 | test "$(KEEP_ENV)" = "always" -o \ 32 | "$(KEEP_ENV)" = "failed" -a $$rv -ne 0 || \ 33 | vagrant destroy -f $$host; \ 34 | test $$rv -ne 0 -a -n "$(QUIET)" && \ 35 | { \ 36 | echo "LOG for $$host in $(LOGDIR)$(LOGPREFIX)$$host.log" >&4;\ 37 | cat $(LOGDIR)$(LOGPREFIX)$$host.log >&4; \ 38 | }; \ 39 | test $$rv -eq 0 40 | 41 | 42 | vagrant-ci: QUIET:=1 43 | vagrant-ci: $(addprefix vagrant-ci-,$(LATEST_HOSTS)) 44 | 45 | vagrant-ci-full: QUIET:=1 46 | vagrant-ci-full: $(addprefix vagrant-ci-,$(ALL_HOSTS)) 47 | 48 | vagrant-ci-clean: 49 | for host in $(addsuffix -test,$(HOSTS)); do \ 50 | vagrant destroy -f $$host; \ 51 | done 52 | 53 | FORCE: 54 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | LibCare -- Patch Userspace Code on Live Processes 2 | ================================================= 3 | 4 | .. image:: https://travis-ci.org/cloudlinux/libcare.svg?branch=master 5 | :target: https://travis-ci.org/cloudlinux/libcare 6 | 7 | Welcome to LibCare --- Live Patch Updates for Userspace Processes and Libraries. 8 | 9 | LibCare delivers live patches to any of your Linux executables or libraries at 10 | the runtime, without the need for restart of your applications. Most 11 | frequently it is used to perform critical security updates such as glibc's 12 | GHOST_ (aka CVE-2015-0235, see how we deal with it in `GHOST sample`_) and 13 | QEMU's `CVE-2017-2615`_, but can also be used for serious bug fixes on the fly 14 | to avoid interruption of service (see `server sample`_). 15 | 16 | See https://kernelcare.com for live Linux Kernel updates also. 17 | 18 | .. _GHOST: https://access.redhat.com/articles/1332213 19 | .. _`GHOST sample`: samples/ghost/README.rst 20 | .. _`CVE-2017-2615`: https://www.rapid7.com/db/vulnerabilities/centos_linux-cve-2017-2615 21 | .. _`server sample`: samples/server/README.rst 22 | 23 | FAQ 24 | ~~~ 25 | 26 | How the live patches are generated? 27 | ----------------------------------- 28 | 29 | We use the same code generating procedure we used in production for years in 30 | kernelcare.com: 31 | 32 | #. both original and patched source code are translated to assembler, 33 | #. corresponding assembler files are compared and new instrumented assembler 34 | code is generated, where patches are stored into special ELF sections, 35 | #. instrumented assembler code is compiled using target project's build system 36 | while patch ELF sections are collected in binaries', 37 | #. binary patch files are extracted from the ELF sections. 38 | 39 | The `libcare-patch-make`_ script is a handy script for the patch generation for a 40 | makeable project. Just do: 41 | 42 | .. code:: shell 43 | 44 | $ src/libcare-patch-make some_serious_bug.patch 45 | 46 | And find binary patches for all the deliverables of the project in the 47 | ``patchroot`` directory. See our `simple server `__ 48 | for usage sample. 49 | 50 | For more details follow to the `patch preparation 51 | `__ chapter of the `internals 52 | `__. 53 | 54 | .. _`libcare-patch-make`: docs/libcare-patch-make.rst 55 | 56 | How the live patches are applied? 57 | --------------------------------- 58 | 59 | It is a lot like loading a shared library into another process' memory: 60 | 61 | #. our binary `libcare-ctl`_ (the doctor) attaches to a patient via 62 | `ptrace(2)`_, 63 | #. patient's objects are examined by the doctor, 64 | #. doctor puppets the patient to allocate patch memory near the original 65 | object, 66 | #. references in the patch are resolved by the doctor, the patch 67 | code is ready to be executed now, 68 | #. doctor rewrites targeted original functions with unconditional jumps to the 69 | appropriate patched versions, ensuring that no thread of patient is 70 | executing them first. 71 | 72 | .. _`ptrace(2)`: http://man7.org/linux/man-pages/man2/ptrace.2.html 73 | .. _libcare-ctl: docs/libcare-ctl.rst 74 | 75 | Now the patient executes patched versions of the functions. 76 | 77 | For more details follow to the `Patching `__ 78 | chapter of the `internals `__. 79 | 80 | Will my patches re-apply if I restart the process? 81 | -------------------------------------------------- 82 | 83 | Not at the moment. We only track start of the new processes for the tests, see 84 | `here `__. 85 | 86 | Does live patching affect performance? 87 | -------------------------------------- 88 | 89 | Negligibly. Since code patches simply redirect execution from original code 90 | functions to the new ones the overhead is small and comparable to 91 | additional “jmp” instruction. 92 | 93 | Installation and dependencies 94 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 95 | 96 | .. _`installation`: 97 | .. _`installation guide`: 98 | 99 | All the Linux-distros with available ``libunwind``, ``elfutils`` and ``binutils`` 100 | packages are supported. 101 | 102 | However, the ``libcare`` is only tested on Ubuntu from 12.04 to 16.04 and on 103 | CentOS from 6.8 to 7.x. 104 | 105 | Dependencies 106 | ------------ 107 | 108 | To install the dependencies on RHEL/CentOS do the following: 109 | 110 | .. code:: console 111 | 112 | $ sudo yum install -y binutils elfutils elfutils-libelf-devel libunwind-devel 113 | 114 | To install the dependencies on Debian/Ubuntu do the following: 115 | 116 | .. code:: console 117 | 118 | $ sudo apt-get install -y binutils elfutils libelf-dev libunwind-dev 119 | 120 | Building ``libcare`` 121 | -------------------- 122 | 123 | To build ``libcare`` emit at project's root dir: 124 | 125 | .. code:: console 126 | 127 | $ make -C src 128 | ... 129 | 130 | This should build all the utilities required to produce a patch out of some 131 | project's source code. 132 | 133 | It is highly recommended to run the tests as well, enabling Doctor 134 | ``libcare-ctl`` to attach ``ptrace``\ cles to any of the processes first: 135 | 136 | .. code:: console 137 | 138 | $ sudo setcap cap_sys_ptrace+ep ./src/libcare-ctl 139 | $ make -C tests && echo OK 140 | ... 141 | OK 142 | 143 | Now all the required tools are built and we can build some patches. Skip to 144 | `server sample`_ for that. 145 | 146 | How does it work? 147 | ----------------- 148 | 149 | Internals are quite confusing and are described `here `__. 150 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | # See Documentation/vagrant.rst for more info 4 | 5 | Vagrant.configure("2") do |config| 6 | # The most common configuration options are documented and commented below. 7 | # For a complete reference, please see the online documentation at 8 | # https://docs.vagrantup.com. 9 | 10 | # Disable default shares: current dir as /vagrant and $HOME as ~/sync 11 | config.vm.synced_folder ENV["HOME"], "/home/vagrant/sync", disabled: true 12 | config.vm.synced_folder ".", "/vagrant", disabled: true 13 | 14 | # Share whole git repo via nfs 15 | config.vm.synced_folder ".", "/home/vagrant/libcare", type: "nfs", disabled: false, mount: false 16 | 17 | config.vm.provider "libvirt" do |v| 18 | # libvirt has a strict limitation on the length of domain names: 19 | # Call to virDomainCreateWithFlags failed: internal error: 20 | # Monitor path /var/lib/libvirt/qemu/domain-kernelcare_user_ubuntu-14.04-lts-utopic-test_1470670227_d2574f4934bc0e18fefc/monitor.sock 21 | # too big for destination 22 | # 23 | # domain names are constructed as: 24 | # default_prefix + box + timestamp + random_hostname 25 | # 26 | # so keep default_prefix short (empty) 27 | v.default_prefix = "" 28 | v.random_hostname = true 29 | end 30 | 31 | config.vm.provider "parallels" do |prl| 32 | prl.linked_clone = true 33 | end 34 | 35 | boxes = File.readlines("#{File.dirname(__FILE__)}/vagrant_boxes").map &:strip; 36 | 37 | boxes.each do |box| 38 | # Regular development VMs 39 | config.vm.define "#{box}" do |b| 40 | b.vm.box = "ucare/#{box}" 41 | b.vm.box_url = "https://kernelcare.s3.amazonaws.com/ucare/vagrant/ucare/#{box}/metadata.json" 42 | end 43 | 44 | # Test VMs 45 | config.vm.define "#{box}-test" do |b| 46 | b.vm.box = "ucare/#{box}" 47 | b.vm.box_url = "https://kernelcare.s3.amazonaws.com/ucare/vagrant/ucare/#{box}/metadata.json" 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /dist/libcare.preset: -------------------------------------------------------------------------------- 1 | enable libcare.socket 2 | -------------------------------------------------------------------------------- /dist/libcare.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=LibCare patch server 3 | 4 | [Service] 5 | 6 | ExecStart=/usr/bin/libcare-ctl server &3 7 | ExecStartPost=/usr/bin/libcare-client update 8 | 9 | ExecStop=/usr/bin/libcare-client stop 10 | -------------------------------------------------------------------------------- /dist/libcare.socket: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=LibCare patch server 3 | 4 | [Socket] 5 | ListenStream=/run/libcare/libcare.sock 6 | SocketGroup=libcare 7 | SocketMode=0660 8 | Symlinks=/run/libcare.sock 9 | 10 | [Install] 11 | WantedBy=libvirtd.service 12 | -------------------------------------------------------------------------------- /dist/libcare.spec: -------------------------------------------------------------------------------- 1 | 2 | %bcond_without selinux 3 | 4 | Version: 0.1.4 5 | Name: libcare 6 | Summary: LibCare tools 7 | Release: 1%{?dist} 8 | Group: Applications/System 9 | License: GPLv2 10 | Url: http://www.cloudlinux.com 11 | Source0: %{name}-%{version}.tar.bz2 12 | BuildRequires: elfutils-libelf-devel libunwind-devel 13 | 14 | %if 0%{with selinux} 15 | BuildRequires: checkpolicy 16 | BuildRequires: selinux-policy-devel 17 | BuildRequires: /usr/share/selinux/devel/policyhelp 18 | %endif 19 | 20 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 21 | 22 | %if 0%{with selinux} 23 | Requires: libcare-selinux = %{version}-%{release} 24 | %endif 25 | 26 | %description 27 | LibCare userland tools 28 | 29 | %if 0%{with selinux} 30 | 31 | %package selinux 32 | Summary: SELinux package for LibCare/QEMU integration 33 | Group: System Environment/Base 34 | Requires(post): selinux-policy-base, policycoreutils 35 | Requires(postun): policycoreutils 36 | %description selinux 37 | This package contains SELinux module required to allow for 38 | LibCare interoperability with the QEMU run by sVirt. 39 | 40 | %endif 41 | 42 | 43 | %package devel 44 | Summary: LibCare development package 45 | Group: System Environment/Development Tools 46 | %description devel 47 | LibCare devel files. 48 | 49 | 50 | %prep 51 | %setup -q 52 | 53 | %build 54 | 55 | make -C src 56 | %if 0%{with selinux} 57 | make -C dist/selinux 58 | %endif 59 | 60 | %install 61 | %{__rm} -rf %{buildroot} 62 | 63 | make -C src install \ 64 | DESTDIR=%{buildroot} \ 65 | bindir=%{_bindir} \ 66 | libexecdir=%{_libexecdir} 67 | 68 | %if 0%{with selinux} 69 | make -C dist/selinux install \ 70 | DESTDIR=%{buildroot} 71 | %endif 72 | 73 | 74 | install -m 0644 -D dist/libcare.service %{buildroot}%{_unitdir}/libcare.service 75 | install -m 0644 -D dist/libcare.socket %{buildroot}%{_unitdir}/libcare.socket 76 | install -m 0644 -D dist/libcare.preset %{buildroot}%{_presetdir}/90-libcare.preset 77 | 78 | %pre 79 | /usr/sbin/groupadd libcare -r 2>/dev/null || : 80 | /usr/sbin/usermod -a -G libcare qemu 2>/dev/null || : 81 | 82 | %post 83 | %systemd_post libcare.service 84 | %systemd_post libcare.socket 85 | 86 | if [ $1 -eq 1 ]; then 87 | # First install 88 | systemctl start libcare.socket 89 | fi 90 | if [ $1 -eq 2 ]; then 91 | # Upgrade. Just stop it, we will be reactivated 92 | # by a connect to /run/libcare.sock 93 | systemctl stop libcare.service 94 | fi 95 | 96 | %preun 97 | %systemd_preun libcare.service 98 | %systemd_preun libcare.socket 99 | 100 | %postun 101 | %systemd_postun libcare.service 102 | %systemd_postun libcare.socket 103 | 104 | %clean 105 | rm -rf $RPM_BUILD_ROOT 106 | 107 | %files 108 | %defattr(-,root,root) 109 | %{_bindir}/libcare-ctl 110 | %{_bindir}/libcare-client 111 | %{_unitdir}/libcare.service 112 | %{_unitdir}/libcare.socket 113 | %{_presetdir}/90-libcare.preset 114 | 115 | %files devel 116 | %defattr(-,root,root) 117 | %{_bindir}/libcare-cc 118 | %{_bindir}/libcare-patch-make 119 | %{_libexecdir}/libcare/kpatch_gensrc 120 | %{_libexecdir}/libcare/kpatch_strip 121 | %{_libexecdir}/libcare/kpatch_make 122 | 123 | %if 0%{with selinux} 124 | 125 | %files selinux 126 | %defattr(-,root,root,-) 127 | %attr(0600,root,root) %{_datadir}/selinux/packages/libcare.pp 128 | 129 | %post selinux 130 | . /etc/selinux/config 131 | FILE_CONTEXT=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts 132 | cp ${FILE_CONTEXT} ${FILE_CONTEXT}.pre 133 | 134 | /usr/sbin/semodule -i %{_datadir}/selinux/packages/libcare.pp 135 | 136 | # Load the policy if SELinux is enabled 137 | if ! /usr/sbin/selinuxenabled; then 138 | # Do not relabel if selinux is not enabled 139 | exit 0 140 | fi 141 | 142 | /usr/sbin/fixfiles -C ${FILE_CONTEXT}.pre restore 2> /dev/null 143 | 144 | rm -f ${FILE_CONTEXT}.pre 145 | 146 | exit 0 147 | 148 | %postun selinux 149 | if [ $1 -eq 0 ]; then 150 | . /etc/selinux/config 151 | FILE_CONTEXT=/etc/selinux/${SELINUXTYPE}/contexts/files/file_contexts 152 | cp ${FILE_CONTEXT} ${FILE_CONTEXT}.pre 153 | 154 | # Remove the module 155 | /usr/sbin/semodule -n -r libcare > /dev/null 2>&1 156 | 157 | /usr/sbin/fixfiles -C ${FILE_CONTEXT}.pre restore 2> /dev/null 158 | fi 159 | exit 0 160 | 161 | %endif 162 | 163 | %changelog 164 | * Tue Jan 02 2018 Pavel Boldin - 0.1.4-1 165 | - fix libcare service verbosity 166 | 167 | * Wed Dec 27 2017 Pavel Boldin - 0.1.3-1 168 | - use systemd's libcare.socket 169 | - use libcare-client default's path 170 | 171 | * Mon Dec 25 2017 Pavel Boldin - 0.1.2-1 172 | - add code executing after/before scripts 173 | - spec: exec systemctl's hooks 174 | - fix files in /run 175 | 176 | * Mon Dec 11 2017 Pavel Boldin - 0.1.1-1 177 | - add libcare-client 178 | - add systemd startup script 179 | - add selinux support so we can patch RHEL7's QEMU's 180 | 181 | * Mon Dec 11 2017 Pavel Boldin - 0.1-1 182 | - first version 183 | -------------------------------------------------------------------------------- /dist/selinux/Makefile: -------------------------------------------------------------------------------- 1 | 2 | datadir ?= /usr/share 3 | 4 | libcare.pp: libcare.te libcare.fc libcare.if 5 | $(MAKE) -j1 -f $(datadir)/selinux/devel/Makefile libcare.pp 6 | 7 | SELINUXROOT = $(DESTDIR)$(datadir)/selinux 8 | 9 | INSTALL?=install 10 | install: 11 | $(INSTALL) -d $(SELINUXROOT)/packages 12 | $(INSTALL) -m 644 libcare.pp $(SELINUXROOT)/packages 13 | -------------------------------------------------------------------------------- /dist/selinux/libcare.fc: -------------------------------------------------------------------------------- 1 | /usr/bin/libcare-ctl gen_context(system_u:object_r:libcare_exec_t,s0) 2 | /var/lib/libcare(/.*)? gen_context(system_u:object_r:libcare_file_t,s0) 3 | /var/log/libcare(/.*)? gen_context(system_u:object_r:libcare_log_t,s0) 4 | -------------------------------------------------------------------------------- /dist/selinux/libcare.if: -------------------------------------------------------------------------------- 1 | ## 2 | -------------------------------------------------------------------------------- /dist/selinux/libcare.te: -------------------------------------------------------------------------------- 1 | 2 | policy_module(libcare, 1.0) 3 | 4 | require { 5 | type svirt_t; 6 | type init_t; 7 | type var_run_t; 8 | type proc_t; 9 | type qemu_exec_t; 10 | attribute domain; 11 | } 12 | 13 | # systemd(init_t) executes /usr/bin/libcare-ctl(libcare_exec_t) 14 | # process becomes libcare_t 15 | systemd_domain_template(libcare) 16 | role system_r types libcare_t; 17 | 18 | # log files 19 | type libcare_log_t; 20 | logging_log_file(libcare_log_t) 21 | 22 | # Execute before/after scripts 23 | corecmd_exec_shell(libcare_t) 24 | 25 | # Execute setsebool from before/after scripts 26 | # TODO(pboldin): these should go into qemu-specific package 27 | seutil_domtrans_setsebool(libcare_t) 28 | #allow libcare_t setsebool_exec_t: file { read getattr execute open }; 29 | 30 | manage_dirs_pattern(libcare_t, libcare_log_t, libcare_log_t) 31 | manage_files_pattern(libcare_t, libcare_log_t, libcare_log_t) 32 | manage_lnk_files_pattern(libcare_t, libcare_log_t, libcare_log_t) 33 | 34 | manage_sock_files_pattern(libcare_t, var_run_t, var_run_t) 35 | 36 | # temporary files for libcare_t process (FIXME what for?) 37 | allow libcare_t libcare_tmp_t : file manage_file_perms; 38 | files_tmp_filetrans(libcare_t,libcare_tmp_t,file) 39 | 40 | type libcare_tmp_t; 41 | files_tmp_file(libcare_tmp_t) 42 | 43 | # libcare data files /var/lib/libcare/* 44 | type libcare_file_t; 45 | files_type(libcare_file_t) 46 | 47 | allow libcare_t libcare_file_t: file read_file_perms; 48 | allow libcare_t libcare_file_t: file exec_file_perms; 49 | allow libcare_t libcare_file_t: dir list_dir_perms; 50 | allow libcare_t libcare_file_t: lnk_file read_lnk_file_perms; 51 | 52 | # to read patient's /proc entries and be able to attach to it 53 | allow libcare_t self: capability { dac_override dac_read_search sys_ptrace }; 54 | 55 | allow libcare_t svirt_t : process ptrace; 56 | allow libcare_t svirt_t : dir list_dir_perms; 57 | allow libcare_t svirt_t : file rw_file_perms; 58 | allow libcare_t svirt_t : lnk_file read_lnk_file_perms; 59 | 60 | # libunwind needs this 61 | allow libcare_t qemu_exec_t : file read_file_perms; 62 | 63 | # to allow patient to send us 'startup' message 64 | allow svirt_t libcare_t : unix_stream_socket connectto; 65 | 66 | # to allow patient to send us SIGCHLD (required for ptrace) 67 | allow svirt_t libcare_t : process sigchld; 68 | 69 | # silence all the audits about /proc/pid/exe 70 | dontaudit libcare_t domain : dir search; 71 | 72 | # DEBUG 73 | ## Run runcon system_u:system_r:libcare_t:s0 /usr/bin/libcare-ctl -v patch -p $(pidof qemu-kvm) /var/lib/libcare 74 | #require { 75 | # type bin_t; 76 | # type user_devpts_t; 77 | #} 78 | #allow libcare_t bin_t : file { open read execute entrypoint }; 79 | #allow libcare_t user_devpts_t : chr_file { read write append getattr ioctl }; 80 | # END DEBUG 81 | -------------------------------------------------------------------------------- /docker/kernelcare/centos7/gcc-4.8.2-16.el7/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos:centos7 2 | RUN yum install -y --enablerepo=C7.0.1406-base gcc rpm-build xmlto "perl(ExtUtils::Embed)" patchutils redhat-rpm-config asciidoc elfutils-devel zlib-devel binutils-devel newt-devel python-devel audit-libs-devel perl bison flex hmaccalc tar gzip bzip2 vim python-setuptools ncurses-devel make net-tools bc openssl pesign numactl-devel pciutils-devel gettext kmod hostname libunwind-devel 3 | RUN yum downgrade -y --enablerepo=C7.0.1406-base gcc-4.8.2-16.el7 cpp-4.8.2-16.el7 kernel-headers-3.10.0-123.el7 libgomp-4.8.2-16.el7 cpp-4.8.2-16.el7 binutils-2.23.52.0.1-16.el7 binutils-devel-2.23.52.0.1-16.el7 4 | RUN easy_install pyelftools 5 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DOCS := internals.pdf libcare-patch-make.pdf libcare-src.pdf 3 | 4 | all: $(DOCS) 5 | 6 | %.pdf: %.rst 7 | rst2pdf $< -o $@ 8 | -------------------------------------------------------------------------------- /docs/libcare-ctl.rst: -------------------------------------------------------------------------------- 1 | The Doctor: ``libcare-ctl`` 2 | ------------------------------ 3 | 4 | Detailed description is in the `internals `__. 5 | 6 | All the job is done by the ``libcare-ctl``. It is called ``doctor`` hereafter 7 | and the targets of operations are thus called ``patients``. 8 | 9 | The doctor accepts a few arguments that are common for all types of operations: 10 | 11 | -v enable verbose output 12 | -h show commands list 13 | 14 | Applying patches via ``patch`` 15 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 16 | 17 | 18 | The ``patch`` mode patches a process with ID given as an argument to ``-p`` option 19 | or all of them except self and ``init`` when the argument is ``all``. The patch 20 | (or directory with patches) to be applied should be specified as the only 21 | positional argument: 22 | 23 | .. code:: console 24 | 25 | $ libcare-ctl patch -p some_patch_file.kpatch 26 | 27 | The patches are basically ELF files of relocatable type ``REL`` with binary 28 | meta-information such as BuildID and name of the patch target prepended. 29 | Loading patches is thus a lot like loading a shared object (library) 30 | into a process. Except we are puppeting it by strings going through a 31 | keyhole in other process' memory. 32 | 33 | First, the memory near the original object is allocated, then all the 34 | relocations and symbols are resolved in a local copy of patch content. This 35 | pre-baked patch is copied to the patient's memory and, finally, original 36 | functions are overwritten with the unconditional jumps to the patched version. 37 | 38 | For more details look at the `Patching`_. 39 | 40 | .. _Patching: internals.rst#Patching 41 | 42 | Cancelling patches via ``unpatch`` 43 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 44 | 45 | The ``unpatch`` mode makes doctor remove patches listed by target BuildID from 46 | the patients' memory. It simply restores the original code of the patched 47 | functions from a stash allocated along with the patch and puppets patients to 48 | ``munmap`` the memory areas used by patches. 49 | 50 | Showing info via ``info`` 51 | ~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | 53 | The last entry to the ``libcare-ctl`` is the ``info`` command that lists all 54 | the objects and their BuildIDs for the set of the processes requested. Its 55 | primary use is as the utility for the book-keeping software. 56 | 57 | Patchlevel support 58 | ~~~~~~~~~~~~~~~~~~ 59 | 60 | .. _patchlevel: 61 | 62 | Since patches to the objects such as libraries can be updated, there is a way to 63 | distinguish them, called ``patchlevel``. This information is parsed 64 | from the layout of the directory where the patches are stored. If on 65 | patching stage a patch with a bigger ``patchlevel`` is found, the old one is 66 | removed and the new one is applied. 67 | -------------------------------------------------------------------------------- /docs/libcare-patch-make.rst: -------------------------------------------------------------------------------- 1 | Using ``libcare-patch-make`` 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | The ``libcare-patch-make`` script can be used to build patches for a project built locally 5 | via ``./configure && make && make install``. 6 | 7 | The usage is simple, just call ``libcare-patch-make`` with a list of source patches as 8 | arguments and ``libcare-patch-make`` will build the binary patches and store them to 9 | ``patchroot`` directory. 10 | 11 | ``libcare-patch-make`` requires the following simple criteria to be met on the build system: 12 | 13 | 1. The default target SHOULD be the one that builds all the files in 14 | the project. This is by default the ``all`` target in most of the projects. 15 | 16 | 2. The ``install`` target MUST install the project deliverables 17 | into the directory specified as ``DESTDIR`` environment variable. 18 | This is default for most projects. Other projects are either 19 | patched by distributions to include that target or have it under a 20 | different environment variable. 21 | 22 | 3. The ``clean`` target SHOULD be the one that cleans the project. 23 | 24 | The typical usage is the following for the ``configur``\ able project: 25 | 26 | .. code:: console 27 | 28 | $ cd project_dir 29 | $ KPATCH_STAGE=configure CC=libcare-cc ./configure 30 | $ libcare-patch-make first.patch second.patch 31 | BUILDING ORIGINAL CODE 32 | ... 33 | INSTALLING ORIGINAL OBJECTS INTO libcare-patch-make 34 | ... 35 | applying patch ~/first.patch 36 | ... 37 | applying patch ~/second.patch 38 | ... 39 | BUILDING PATCHED CODE 40 | ... 41 | INSTALLING PATCHED CODE 42 | ... 43 | MAKING PATCHES 44 | patch for foobar is in patchroot/${buildid}.patch 45 | ... 46 | 47 | Available options are: 48 | 49 | --help, -h display a short help, 50 | 51 | --update just update the ``kpatches``. Useful when working on the kpatch tools, 52 | 53 | --clean invoke ``make clean`` before building, 54 | 55 | --srcdir DIR change to the ``DIR`` before applying patches. 56 | 57 | Note that ``libcare-patch-make`` uses ``libcare-cc`` under the hood. Read about it 58 | `libcare-cc`_. 59 | 60 | .. _libcare-cc: libcare.rst#building-originals 61 | -------------------------------------------------------------------------------- /packages/rhel7/glibc/glibc-2.17-55.el7/README.md: -------------------------------------------------------------------------------- 1 | Use toil-based build script to build patches for the `glibc`. For that simple 2 | run:: 3 | 4 | ```shell 5 | $ LIBCARE_DIR=~/libcare-opensource 6 | $ pip install -r $LIBCARE_DIR/scripts/toil/requirements.txt 7 | $ python $LIBCARE_DIR/scripts/toil/pkgbuild.py workdir pkgfile.yaml 8 | ... 9 | ``` 10 | 11 | This should build the following files: 12 | ```shell 13 | $ ls /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz /tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz 14 | /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz 15 | /tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz 16 | ``` 17 | -------------------------------------------------------------------------------- /packages/rhel7/glibc/glibc-2.17-55.el7/info: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | KP_PROJECT=glibc 4 | KP_PROJECT_FORMAT=rpm 5 | KP_PROJECT_BUILD_ROOT=/root/rpmbuild 6 | 7 | KP_PROJECT_SPEC=glibc.spec 8 | 9 | KP_PROJECT_DIR=$KP_PROJECT_BUILD_ROOT/BUILD/glibc-2.17-c758a686 10 | KP_PROJECT_BUILD_DIR=$KP_PROJECT_DIR/build-x86_64-redhat-linux 11 | 12 | KP_PROJECT_SOURCE_URL=http://vault.centos.org/7.0.1406/os/Source/SPackages/glibc-2.17-55.el7.src.rpm 13 | KP_PROJECT_SOURCE=glibc-2.17-55.el7.src.rpm 14 | KP_PROJECT_BINARY=glibc-2.17-55.el7.$ARCH.rpm 15 | 16 | KP_PROJECT_PREBUILT=build.orig-$KP_PROJECT_BINARY.tgz 17 | KP_PROJECT_PATCH=kpatch-${KP_PROJECT_BINARY%.*}.tgz 18 | KP_RPMBUILD_FLAGS="'--define=dist .el7'" 19 | KP_RPM_REPOS="--enablerepo=C7.0.1406-base" 20 | 21 | KP_ORIG_RPMS=" 22 | nscd-2.17-55.el7.x86_64 23 | glibc-2.17-55.el7.x86_64 24 | glibc-debuginfo-2.17-55.el7.x86_64 25 | glibc-debuginfo-common-2.17-55.el7.x86_64" 26 | 27 | # TODO(pboldin): this list is incomplete, enhance it as needed (e.g. tests 28 | # failing) 29 | KP_INSTALL_FILES=" 30 | /nscd/nscd /usr/sbin/nscd 31 | /linkobj/libc.so IGNORE 32 | /libc.so /lib64/libc-2.17.so 33 | /rt/librt.so /lib64/librt-2.17.so 34 | /nptl/libpthread.so /lib64/libpthread-2.17.so 35 | /math/libm.so /lib64/libm-2.17.so 36 | /rtkaio/librtkaio.so /lib64/rtkaio/librtkaio-2.17.so 37 | " 38 | 39 | KPATCH_ASM_DIR=$KP_PROJECT_BUILD_ROOT/asmdir 40 | export KPATCH_ASM_DIR 41 | 42 | KPCC_PATCH_ARGS="--force-gotpcrel;--os=rhel6;--ignore-changes=banner,compilation" 43 | export KPCC_PATCH_ARGS 44 | 45 | KPCC_DBGFILTER_ARGS="--dbg-filter;--dbg-filter-eh-frame;--dbg-filter-gcc-except-table;--os=rhel6" 46 | export KPCC_DBGFILTER_ARGS 47 | 48 | kp_prebuild_hook() { 49 | sed -i 's/run_glibc_tests 1/run_glibc_tests 0/' \ 50 | $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC 51 | } 52 | 53 | kp_build_hook() { 54 | sed -i '/\(mkdir\|rm -rf\) \$builddir/ s/^/#/' \ 55 | $KP_PROJECT_BUILD_ROOT/SPECS/$KP_PROJECT_SPEC 56 | } 57 | 58 | 59 | _run_tests() { 60 | if test -f $LIBCARE_DIR/execve/execve.so; then 61 | LD_PRELOAD=$LIBCARE_DIR/execve/execve.so 62 | elif test -f $LIBCARE_DIR/tests/execve/execve.so; then 63 | LD_PRELOAD=$LIBCARE_DIR/tests/execve/execve.so 64 | else 65 | echo "Can't find execve.so required to run tests" 66 | exit 1 67 | fi 68 | 69 | export LD_PRELOAD 70 | 71 | local ld_linux="$KP_PROJECT_BUILD_DIR/elf/ld*" 72 | 73 | export KP_EXECVE_PATTERN="+($ld_linux)" 74 | #export KP_EXECVE_DEBUG=1 75 | 76 | LD_LIBRARY_PATH= 77 | export LD_LIBRARY_PATH 78 | 79 | export KCPATH=$PATH 80 | export PATH=$OLDPATH 81 | 82 | export TIMEOUTFACTOR=16 83 | parent=$$ 84 | ( make $PARALLEL -k check 2>&1 85 | sleep 10s 86 | teepid="`ps -eo ppid,pid,command | awk '($1 == '${parent}' && $3 ~ /^tee/) { print $2 }'`" 87 | [ -n "$teepid" ] && kill $teepid 88 | ) | tee check.log || : 89 | 90 | echo ====================TESTING DETAILS================= 91 | for i in `sed -n 's|^.*\*\*\* \[\([^]]*\.out\)\].*$|\1|p' check.log`; do 92 | echo =====$i===== 93 | cat $i || : 94 | echo ============ 95 | done 96 | echo ====================TESTING END===================== 97 | 98 | export PATH=$KCPATH 99 | } 100 | 101 | kp_prepare_test_binaries() { 102 | # Replace patch build results with original libraries for testing 103 | kp_install_files /root/root.original \ 104 | $KP_PROJECT_BUILD_DIR \ 105 | "to_prebuild" \ 106 | "$KP_INSTALL_FILES" 107 | } 108 | 109 | kp_patch_test() { 110 | rm -f /var/run/libcare.sock 111 | 112 | PATCH_ROOT=$KP_PROJECT_BUILD_ROOT/storage 113 | $KPATCH_PATH/libcare-ctl -v server /var/run/libcare.sock $PATCH_ROOT \ 114 | >/data/libcare-ctl.log 2>&1 & : 115 | LISTENER_PID=$! 116 | 117 | sleep 1 118 | kill -0 $LISTENER_PID 119 | 120 | pushd $KP_PROJECT_BUILD_DIR 121 | 122 | (trap 'kill -9 '$LISTENER_PID'' 0; 123 | _run_tests) 124 | 125 | local executed=$(awk '/^(env.*)?\/[^ ]*\/ld/ { n++ } END { print n }' check.log) 126 | 127 | popd 128 | 129 | local patched=$(awk '/kpatch_ctl targeting/ { n++ } END { print n }' /data/libcare-ctl.log) 130 | 131 | test $patched -ge $executed 132 | 133 | grep -vq 'No patch(es) applicable to' /data/libcare-ctl.log 134 | grep 'patch hunk(s) have been successfully applied' /data/libcare-ctl.log \ 135 | | wc -l 136 | } 137 | -------------------------------------------------------------------------------- /packages/rhel7/glibc/glibc-2.17-55.el7/pkgfile.yaml: -------------------------------------------------------------------------------- 1 | image: kernelcare/centos7:gcc-4.8.2-16.el7 2 | prebuild: /tmp/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz 3 | patch: /tmp/kpatch-glibc-2.17-55.el7.x86_64.tgz 4 | input: 5 | - package: . 6 | - patches: ../../../../patches/ 7 | -------------------------------------------------------------------------------- /packages/rhel7/glibc/glibc-2.17-55.el7/plist: -------------------------------------------------------------------------------- 1 | glibc/2.17/glibc-rh1183545.patch 2 | -------------------------------------------------------------------------------- /packages/rhel7/glibc/glibc-2.17-55.el7/properties.yml: -------------------------------------------------------------------------------- 1 | docker: 2 | image: kernelcare/centos7 3 | tag: gcc-4.8.2-16.el7 4 | urls: 5 | src: ftp://bo.mirror.garr.it/2/scientific/7x/SRPMS/vendor/glibc-2.17-55.el7.src.rpm 6 | x86_64: 7 | none: 8 | bin: ftp://ftp.icm.edu.pl/vol/rzm5/linux-scientificlinux/7.0/x86_64/os/Packages/glibc-2.17-55.el7.x86_64.rpm 9 | prebuilt: https://s3.amazonaws.com/kpatch/kernels/rhel7/glibc-2.17-55.el7/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz 10 | environment: 11 | x86_64: 12 | none: 13 | PREBUILT_PATH: /kcdata/build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz 14 | -------------------------------------------------------------------------------- /patches/glibc/2.17/glibc-rh1183545.patch: -------------------------------------------------------------------------------- 1 | commit d5dd6189d506068ed11c8bfa1e1e9bffde04decd 2 | Author: Andreas Schwab 3 | Date: Mon Jan 21 17:41:28 2013 +0100 4 | 5 | Fix parsing of numeric hosts in gethostbyname_r 6 | 7 | diff --git a/nss/digits_dots.c b/nss/digits_dots.c 8 | index 2b86295..e007ef4 100644 9 | --- a/nss/digits_dots.c 10 | +++ b/nss/digits_dots.c 11 | @@ -46,7 +46,10 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 12 | { 13 | if (h_errnop) 14 | *h_errnop = NETDB_INTERNAL; 15 | - *result = NULL; 16 | + if (buffer_size == NULL) 17 | + *status = NSS_STATUS_TRYAGAIN; 18 | + else 19 | + *result = NULL; 20 | return -1; 21 | } 22 | 23 | @@ -83,14 +86,16 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 24 | } 25 | 26 | size_needed = (sizeof (*host_addr) 27 | - + sizeof (*h_addr_ptrs) + strlen (name) + 1); 28 | + + sizeof (*h_addr_ptrs) 29 | + + sizeof (*h_alias_ptr) + strlen (name) + 1); 30 | 31 | if (buffer_size == NULL) 32 | { 33 | if (buflen < size_needed) 34 | { 35 | + *status = NSS_STATUS_TRYAGAIN; 36 | if (h_errnop != NULL) 37 | - *h_errnop = TRY_AGAIN; 38 | + *h_errnop = NETDB_INTERNAL; 39 | __set_errno (ERANGE); 40 | goto done; 41 | } 42 | @@ -109,7 +114,7 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 43 | *buffer_size = 0; 44 | __set_errno (save); 45 | if (h_errnop != NULL) 46 | - *h_errnop = TRY_AGAIN; 47 | + *h_errnop = NETDB_INTERNAL; 48 | *result = NULL; 49 | goto done; 50 | } 51 | @@ -149,7 +154,9 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 52 | if (! ok) 53 | { 54 | *h_errnop = HOST_NOT_FOUND; 55 | - if (buffer_size) 56 | + if (buffer_size == NULL) 57 | + *status = NSS_STATUS_NOTFOUND; 58 | + else 59 | *result = NULL; 60 | goto done; 61 | } 62 | @@ -190,7 +197,7 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 63 | if (buffer_size == NULL) 64 | *status = NSS_STATUS_SUCCESS; 65 | else 66 | - *result = resbuf; 67 | + *result = resbuf; 68 | goto done; 69 | } 70 | 71 | @@ -201,15 +208,6 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 72 | 73 | if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':') 74 | { 75 | - const char *cp; 76 | - char *hostname; 77 | - typedef unsigned char host_addr_t[16]; 78 | - host_addr_t *host_addr; 79 | - typedef char *host_addr_list_t[2]; 80 | - host_addr_list_t *h_addr_ptrs; 81 | - size_t size_needed; 82 | - int addr_size; 83 | - 84 | switch (af) 85 | { 86 | default: 87 | @@ -225,7 +223,10 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 88 | /* This is not possible. We cannot represent an IPv6 address 89 | in an `struct in_addr' variable. */ 90 | *h_errnop = HOST_NOT_FOUND; 91 | - *result = NULL; 92 | + if (buffer_size == NULL) 93 | + *status = NSS_STATUS_NOTFOUND; 94 | + else 95 | + *result = NULL; 96 | goto done; 97 | 98 | case AF_INET6: 99 | @@ -233,42 +234,6 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 100 | break; 101 | } 102 | 103 | - size_needed = (sizeof (*host_addr) 104 | - + sizeof (*h_addr_ptrs) + strlen (name) + 1); 105 | - 106 | - if (buffer_size == NULL && buflen < size_needed) 107 | - { 108 | - if (h_errnop != NULL) 109 | - *h_errnop = TRY_AGAIN; 110 | - __set_errno (ERANGE); 111 | - goto done; 112 | - } 113 | - else if (buffer_size != NULL && *buffer_size < size_needed) 114 | - { 115 | - char *new_buf; 116 | - *buffer_size = size_needed; 117 | - new_buf = realloc (*buffer, *buffer_size); 118 | - 119 | - if (new_buf == NULL) 120 | - { 121 | - save = errno; 122 | - free (*buffer); 123 | - __set_errno (save); 124 | - *buffer = NULL; 125 | - *buffer_size = 0; 126 | - *result = NULL; 127 | - goto done; 128 | - } 129 | - *buffer = new_buf; 130 | - } 131 | - 132 | - memset (*buffer, '\0', size_needed); 133 | - 134 | - host_addr = (host_addr_t *) *buffer; 135 | - h_addr_ptrs = (host_addr_list_t *) 136 | - ((char *) host_addr + sizeof (*host_addr)); 137 | - hostname = (char *) h_addr_ptrs + sizeof (*h_addr_ptrs); 138 | - 139 | for (cp = name;; ++cp) 140 | { 141 | if (!*cp) 142 | @@ -281,7 +246,9 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf, 143 | if (inet_pton (AF_INET6, name, host_addr) <= 0) 144 | { 145 | *h_errnop = HOST_NOT_FOUND; 146 | - if (buffer_size) 147 | + if (buffer_size == NULL) 148 | + *status = NSS_STATUS_NOTFOUND; 149 | + else 150 | *result = NULL; 151 | goto done; 152 | } 153 | diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c 154 | index 1067744..44d00f4 100644 155 | --- a/nss/getXXbyYY_r.c 156 | +++ b/nss/getXXbyYY_r.c 157 | @@ -179,6 +179,9 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer, 158 | case -1: 159 | return errno; 160 | case 1: 161 | +#ifdef NEED_H_ERRNO 162 | + any_service = true; 163 | +#endif 164 | goto done; 165 | } 166 | #endif 167 | @@ -288,7 +291,7 @@ done: 168 | /* This happens when we weren't able to use a service for reasons other 169 | than the module not being found. In such a case, we'd want to tell the 170 | caller that errno has the real reason for failure. */ 171 | - *h_errnop = NETDB_INTERNAL; 172 | + *h_errnop = NETDB_INTERNAL; 173 | else if (status != NSS_STATUS_SUCCESS && !any_service) 174 | /* We were not able to use any service. */ 175 | *h_errnop = NO_RECOVERY; 176 | diff --git a/nss/test-digits-dots.c b/nss/test-digits-dots.c 177 | new file mode 100644 178 | index 0000000..1efa344 179 | --- /dev/null 180 | +++ b/nss/test-digits-dots.c 181 | @@ -0,0 +1,38 @@ 182 | +/* Copyright (C) 2013 Free Software Foundation, Inc. 183 | + This file is part of the GNU C Library. 184 | + 185 | + The GNU C Library is free software; you can redistribute it and/or 186 | + modify it under the terms of the GNU Lesser General Public 187 | + License as published by the Free Software Foundation; either 188 | + version 2.1 of the License, or (at your option) any later version. 189 | + 190 | + The GNU C Library is distributed in the hope that it will be useful, 191 | + but WITHOUT ANY WARRANTY; without even the implied warranty of 192 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 193 | + Lesser General Public License for more details. 194 | + 195 | + You should have received a copy of the GNU Lesser General Public 196 | + License along with the GNU C Library; if not, see 197 | + . */ 198 | + 199 | +/* Testcase for BZ #15014 */ 200 | + 201 | +#include 202 | +#include 203 | +#include 204 | + 205 | +static int 206 | +do_test (void) 207 | +{ 208 | + char buf[32]; 209 | + struct hostent *result = NULL; 210 | + struct hostent ret; 211 | + int h_err = 0; 212 | + int err; 213 | + 214 | + err = gethostbyname_r ("1.2.3.4", &ret, buf, sizeof (buf), &result, &h_err); 215 | + return err == ERANGE && h_err == NETDB_INTERNAL ? EXIT_SUCCESS : EXIT_FAILURE; 216 | +} 217 | + 218 | +#define TEST_FUNCTION do_test () 219 | +#include "../test-skeleton.c" 220 | --- ./nss/Makefile.orig 2016-08-08 00:47:48.562921339 +0300 221 | +++ ./nss/Makefile 2016-08-08 00:48:33.912193342 +0300 222 | @@ -38,7 +38,7 @@ 223 | makedb-modules = xmalloc hash-string 224 | extra-objs += $(makedb-modules:=.o) 225 | 226 | -tests = test-netdb tst-nss-test1 227 | +tests = test-netdb tst-nss-test1 test-digits-dots 228 | xtests = bug-erange 229 | 230 | include ../Makeconfig 231 | -------------------------------------------------------------------------------- /samples/ghost/GHOST.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #define CANARY "in_the_coal_mine" 12 | 13 | struct canary { 14 | char buffer[1024]; 15 | char canary[sizeof(CANARY)]; 16 | }; 17 | 18 | const struct canary orig = { "buffer", CANARY }; 19 | 20 | struct canary temp; 21 | 22 | void *thread(void *data) 23 | { 24 | struct hostent resbuf; 25 | struct hostent *result; 26 | int herrno; 27 | int retval; 28 | 29 | while (1) { 30 | /*** strlen (name) = size_needed - sizeof (*host_addr) - sizeof (*h_addr_ptrs) - 1; ***/ 31 | size_t len = sizeof(temp.buffer) - 16*sizeof(unsigned char) - 2*sizeof(char *) - 1; 32 | char name[sizeof(temp.buffer)]; 33 | memset(name, '0', len); 34 | name[len] = '\0'; 35 | 36 | memcpy(&temp, &orig, sizeof(temp)); 37 | 38 | retval = gethostbyname_r(name, &resbuf, temp.buffer, sizeof(temp.buffer), &result, &herrno); 39 | 40 | if (strcmp(temp.canary, CANARY) != 0) { 41 | putc('.', stderr); 42 | } 43 | if (retval == ERANGE) { 44 | //puts("not vulnerable"); 45 | return NULL; 46 | } 47 | sleep(1); 48 | } 49 | } 50 | 51 | #define NTHREADS 16 52 | 53 | int main(void) 54 | { 55 | pthread_t threads[NTHREADS]; 56 | int i; 57 | 58 | for (i = 0; i < NTHREADS; i++){ 59 | pthread_create(&threads[i], NULL, thread, NULL); 60 | } 61 | 62 | for (i = 0; i < NTHREADS; i++){ 63 | pthread_join(threads[i], NULL); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /samples/ghost/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDLIBS += -lpthread 3 | 4 | all: GHOST 5 | -------------------------------------------------------------------------------- /samples/ghost/README.rst: -------------------------------------------------------------------------------- 1 | RHEL7 ``glibc`` sample 2 | ---------------------- 3 | 4 | Most of the binaries in the system are coming from distribution packages so 5 | building patches for them is different from the above. Here is how to do it. 6 | 7 | This example builds ``glibc`` patch for an old fashioned CVE-2015-0235 GHOST_ 8 | vulnerability for RHEL7. The build is done using `scripts/pkgbuild`_ and 9 | package files are stored in ``packages/rhel7/glibc/glibc-2.17-55.el7``. 10 | 11 | Preparing environment 12 | ~~~~~~~~~~~~~~~~~~~~~ 13 | 14 | First, we need the exact versions of tools and libs. Let's build a 15 | Docker_ image and a container for it: 16 | 17 | .. code:: console 18 | 19 | $ docker build -t kernelcare/centos7:gcc-4.8.2-16.el7 \ 20 | docker/kernelcare/centos7/gcc-4.8.2-16.el7 21 | ... 22 | $ docker run -v $PWD:/libcare --cap-add SYS_PTRACE -it \ 23 | kernelcare/centos7:gcc-4.8.2-16.el7 /bin/bash 24 | [root@... /]# 25 | 26 | Now, from inside the container let's install vulnerable version of glibc: 27 | 28 | .. code:: console 29 | 30 | [root@... /]# yum downgrade -y --enablerepo=C7.0.1406-base \ 31 | glibc-2.17-55.el7 glibc-devel-2.17-55.el7 \ 32 | glibc-headers-2.17-55.el7 glibc-common-2.17-55.el7 33 | ... 34 | 35 | Also we have to downgrade elfutils since newer versions of ``eu-unstrip`` 36 | fail to work with glibc utilities: 37 | 38 | .. code:: console 39 | 40 | [root@... /]# yum downgrade -y --enablerepo=C7.0.1406-base \ 41 | elfutils-devel-0.158-3.el7.x86_64 elfutils-0.158-3.el7.x86_64 \ 42 | elfutils-libs-0.158-3.el7.x86_64 elfutils-libelf-0.158-3.el7.x86_64 \ 43 | elfutils-libelf-devel-0.158-3.el7.x86_64 44 | ... 45 | 46 | Build the ``libcare`` tools: 47 | 48 | .. code:: console 49 | 50 | [root@... /]# make -C /libcare/src clean all && make -C /libcare/tests/execve 51 | ... 52 | 53 | Now build and run the sample GHOST app that runs 16 threads to constantly check 54 | whether the ``glibc`` is vulnerable to GHOST_ and prints a dot every time it 55 | detects a buffer overflow in the ``gethostbyname_r`` function. 56 | The downgraded ``glibc`` is vulnerable: 57 | 58 | .. code:: console 59 | 60 | [root@... /]# cd /libcare/samples/ghost 61 | [root@... ghost]# make 62 | ... 63 | [root@... ghost]# ./GHOST 64 | ............^C 65 | 66 | Press Ctrl+C to get your console back and let's start building the patch for 67 | ``glibc``. 68 | 69 | Building and applying the patch 70 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71 | 72 | The build is done in two stages. 73 | 74 | First, the original package build is repeated with all the `intermediate 75 | assembly files`_ stored and saved for later. This greatly helps to speed up 76 | builds against the same base code. Run the following from inside our docker 77 | container to pre-build ``glibc`` package: 78 | 79 | .. code:: console 80 | 81 | [root@... /]# cd /libcare/ 82 | [root@... /libcare]# ./scripts/pkgbuild -p packages/rhel7/glibc/glibc-2.17-55.el7 83 | ... 84 | 85 | This should download the package, do a regular RPM build with ``kpatch_cc`` 86 | wrapper substituted for GCC and store the pre-built data into the archive under 87 | ``/kcdata`` directory: 88 | 89 | .. code:: console 90 | 91 | [root@... /libcare]# ls /kcdata 92 | build.orig-glibc-2.17-55.el7.x86_64.rpm.tgz glibc-2.17-55.el7.src.rpm 93 | 94 | Now let's build the patch, the output will be verbose since it contains tests run 95 | by the ``kp_patch_test`` defined in ``packages/rhel7/glibc/glibc-2.17-55.el7/info``: 96 | 97 | .. code:: console 98 | 99 | [root@... /libcare]# ./scripts/pkgbuild packages/rhel7/glibc/glibc-2.17-55.el7 100 | ... 101 | [root@... /libcare]# ls /kcdata/kpatch* 102 | /kcdata/kpatch-glibc-2.17-55.el7.x86_64.tgz 103 | 104 | Unwrap patches and run the GHOST_ sample: 105 | 106 | .. code:: console 107 | 108 | [root@... /libcare]# cd /kcdata 109 | [root@... /kcdata]# tar xf kpatch* 110 | [root@... /kcdata]# /libcare/samples/ghost/GHOST 2>/dev/null & 111 | [root@... /kcdata]# patient_pid=$! 112 | 113 | And, finally, patch it. All the threads of the sample must stop when the GHOST 114 | vulnerability is patched: 115 | 116 | .. code:: console 117 | 118 | [root@... /kcdata]# /libcare/src/libcare-ctl -v patch -p $patient_pid \ 119 | root/kpatch-glibc-2.17-55.el7.x86_64 120 | ... 121 | 1 patch hunk(s) have been successfully applied to PID '...' 122 | (Press Enter again) 123 | [1]+ Done /libcare/samples/ghost/GHOST 2> /dev/null 124 | 125 | You can patch any running application this way: 126 | 127 | .. code:: console 128 | 129 | [root@... /kcdata]# sleep 100 & 130 | [root@... /kcdata]# patient_pid=$! 131 | [root@... /kcdata]# /libcare/src/libcare-ctl -v patch -p $patient_pid \ 132 | root/kpatch-glibc-2.17-55.el7.x86_64 133 | ... 134 | 1 patch hunk(s) have been successfully applied to PID '...' 135 | 136 | Congratulations on finishing this rather confusing sample! 137 | 138 | .. _GHOST: https://access.redhat.com/articles/1332213 139 | .. _docker: https://www.docker.com/ 140 | -------------------------------------------------------------------------------- /samples/server/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: server 3 | 4 | 5 | server: server.c 6 | $(CC) -o $@ $< -fno-stack-protector -fomit-frame-pointer 7 | 8 | clean: 9 | rm -f server 10 | 11 | install: server 12 | mkdir $$DESTDIR || : 13 | cp server $$DESTDIR 14 | -------------------------------------------------------------------------------- /samples/server/README.rst: -------------------------------------------------------------------------------- 1 | Sample ``samples/server`` 2 | ------------------------- 3 | 4 | For instance, your backend developer made a typo during server development. 5 | This typo introduced a stack overflow vulnerability exploitable from the client 6 | side. Common automatic checks were disabled for the sake of performance and now 7 | your server is vulnerable to anyone who can find the vulnerability. 8 | 9 | The sample code is in ``samples/server/server.c`` where function 10 | ``handle_connection`` supplies wrong buffer size to the ``recv(2)`` at line 24: 11 | 12 | .. code:: c 13 | 14 | void handle_connection(int sock) 15 | { 16 | char buf[16]; 17 | 18 | (void) recv(sock, buf, 128, 0); // bug is here 19 | fprintf(stdout, "Got %s\n", buf); 20 | close(sock); 21 | } 22 | 23 | 1. Build the original server and run it: 24 | 25 | .. code:: console 26 | 27 | $ cd samples/server 28 | $ make install DESTDIR=vuln 29 | cc -o server server.c -fno-stack-protector -fomit-frame-pointer 30 | $ ./vuln/server 31 | 32 | 2. Now let's install dependencies and build utils. Refer to `installation`_ for 33 | more details on the installation procedure and supported OSes. 34 | 35 | For RHEL-based distros do: 36 | 37 | .. code:: console 38 | 39 | $ sudo yum install -y binutils elfutils elfutils-libelf-devel nc libunwind-devel 40 | ... 41 | $ make -C ../../src 42 | ... 43 | 44 | For Debian-based distros do: 45 | 46 | .. code:: console 47 | 48 | $ sudo apt-get install -y binutils elfutils libelf-dev netcat-openbsd libunwind-dev 49 | ... 50 | $ make -C ../../src 51 | ... 52 | 53 | .. _installation: ../../README.rst#installation 54 | 55 | 3. Try to connect to the server using freshly installed `netcat`_: 56 | 57 | .. code:: console 58 | 59 | $ echo 'Hi!' | nc localhost 3345 60 | 61 | The server should print on its console: 62 | 63 | .. code:: console 64 | 65 | $ ./vuln/server 66 | Got Hi! 67 | 68 | .. _`netcat`: https://www.freebsd.org/cgi/man.cgi?query=nc&sektion=1 69 | 70 | 4. Now exploit the server via the ``hack.sh`` script. The script analyzes binary 71 | and builds a string that causes server's buffer to overflow. The string 72 | rewrites return address stored on the stack with the address of 73 | ``you_hacked_me`` function, which prints "You hacked me!" as a server. 74 | 75 | Open another console and run ``./hack.sh`` there: 76 | 77 | .. code:: console 78 | 79 | $ ./hack.sh 80 | 81 | Server console should print: 82 | 83 | .. code:: console 84 | 85 | Got 0123456789ABCDEF01234567@ 86 | You hacked me! 87 | 88 | This sample emulates a packaged binary network server vulnerable to 89 | `return-to-libc attack`_. 90 | 91 | .. _`return-to-libc attack`: https://en.wikipedia.org/wiki/Return-to-libc_attack 92 | 93 | 5. Now build the patch for this code via `lcmake`_: 94 | 95 | .. code:: console 96 | 97 | $ ../../src/libcare-patch-make --clean server.patch 98 | ... 99 | patch for $HOME/libcare/samples/server/lcmake/server is in ... 100 | 101 | Please note that this overwrites ``./server`` binary file with a 102 | patch-containing file, storing the original vulnerable server into 103 | ``./lcmake/server``. 104 | 105 | 6. Examine ``patchroot`` directory and find patches there: 106 | 107 | .. code:: console 108 | 109 | $ ls patchroot 110 | 2d0e03e41bd82ec8b840a973077932cb2856a5ec.kpatch 111 | 112 | 7. Apply patch to the running application via `libcare-ctl`_: 113 | 114 | .. code:: console 115 | 116 | $ ../../src/libcare-ctl -v patch -p $(pidof server) patchroot 117 | ... 118 | 1 patch hunk(s) have been successfully applied to PID '31209' 119 | 120 | 8. And check the hack again, ``You hacked me!`` string should go away: 121 | 122 | .. code:: console 123 | 124 | (console2) $ ./hack.sh 125 | (console1) $ # with running ./vuln/server 126 | Got 0123456789ABCDEF@ 127 | 128 | 129 | Congratulations on going through this sample! Go on and learn how the magic of 130 | `libcare-patch-make`_ script works, read how the patch is `built under the hood`_ and how 131 | it is applied by the `libcare-ctl`_. Or even jump to our `hacking guide`_! 132 | -------------------------------------------------------------------------------- /samples/server/hack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -x 2 | 3 | serverpath=server 4 | if test -f kpmake/server; then 5 | serverpath=kpmake/server 6 | fi 7 | objdump -d $serverpath > server.disas 8 | 9 | LC_ALL=C \ 10 | awk ' 11 | function reverse(a) { 12 | s = ""; 13 | for (i = 0; i < 8; i++) { 14 | s = s "" sprintf("%c", and(a, 0xFF)); 15 | a = rshift(a, 8); 16 | } 17 | 18 | return s; 19 | } 20 | 21 | /you_hacked_me/ { 22 | hack_addr = reverse(strtonum("0x"$1)); 23 | } 24 | 25 | /callq.*handle_connection/ { 26 | getline 27 | ret_addr = reverse(strtonum("0x"$1)); 28 | } 29 | 30 | END { 31 | printf "0123456789ABCDEF01234567" hack_addr "" ret_addr 32 | } 33 | ' server.disas | \ 34 | nc localhost 3345 35 | 36 | rm -f server.disas 37 | -------------------------------------------------------------------------------- /samples/server/server.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | void die(const char *msg) 10 | { 11 | perror(msg); 12 | exit(EXIT_FAILURE); 13 | } 14 | 15 | void you_hacked_me() 16 | { 17 | fprintf(stdout, "You hacked me!\n"); 18 | } 19 | 20 | void handle_connection(int sock) 21 | { 22 | char buf[16]; 23 | 24 | (void) recv(sock, buf, 128, 0); /* bug is here */ 25 | fprintf(stdout, "Got %s\n", buf); 26 | close(sock); 27 | } 28 | 29 | int main() 30 | { 31 | static int sock; 32 | int rv, len; 33 | struct sockaddr_in addr; 34 | 35 | sock = socket(AF_INET, SOCK_STREAM, 0); 36 | if (sock == -1) 37 | die("socket"); 38 | 39 | rv = 1; 40 | rv = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &rv, sizeof(rv)); 41 | if (rv == -1) 42 | die("setsockopt"); 43 | 44 | addr.sin_family = AF_INET; 45 | addr.sin_port = htons(3345); 46 | addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 47 | rv = bind(sock, (const struct sockaddr *)&addr, sizeof(addr)); 48 | if (rv == -1) 49 | die("bind"); 50 | 51 | rv = listen(sock, 10); 52 | if (rv == -1) 53 | die("listen"); 54 | 55 | while (1) { 56 | len = sizeof(addr); 57 | rv = accept(sock, (struct sockaddr *)&addr, &len); 58 | if (rv == -1) 59 | die("accept"); 60 | 61 | handle_connection(rv); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /samples/server/server.patch: -------------------------------------------------------------------------------- 1 | --- a/server.c 2017-05-26 12:54:44.456787454 +0300 2 | +++ b/server.c 2017-05-26 12:55:03.912872313 +0300 3 | @@ -21,8 +21,8 @@ 4 | { 5 | char buf[16]; 6 | 7 | - (void) recv(sock, buf, 128, 0); /* bug is here */ 8 | + (void) recv(sock, buf, sizeof(buf), 0); /* bug was here */ 9 | fprintf(stdout, "Got %s\n", buf); 10 | close(sock); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /scripts/de-offset-syms.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | 3 | /Section Headers/,/Key to Flags/ { 4 | gsub("\\[|\\]", ""); 5 | shnum = strtonum($1) 6 | if (shnum != 0) { 7 | name = $2; 8 | sectionsname[shnum] = name; 9 | if (name ~ /kpatch/) { 10 | sections[shnum] = -1; 11 | } else { 12 | addr = $4; 13 | sections[shnum] = strtonum("0x"addr); 14 | } 15 | } 16 | } 17 | 18 | function skip_symbol(name) 19 | { 20 | return (name ~ /^__/) || (name ~ /^[[:alnum:]_]+\.[[:digit:]]+$/) || 21 | (name ~ /^_L_(robust_)?(cond_)?(timed)?(un)?lock/) || 22 | (name ~ /^\.L/); 23 | } 24 | 25 | /.symtab/,!/./ { 26 | value = strtonum("0x"$2); 27 | size = strtonum($3); 28 | type = $4; 29 | shnum = $7; 30 | name = $8; 31 | if (value != 0 && (type == "FUNC" || type == "OBJECT") && 32 | !skip_symbol(name) && sections[shnum] != -1) { 33 | printf "%016x %-16s %016x %s\n", value - sections[shnum], sectionsname[shnum], size, name; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /scripts/patch_list_apply: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Applies list of patches to the specified directory 3 | # It takes three arguments: 4 | # SRC_DIR - directory with sources to be patched 5 | # PLIST - path to the file which contains list of patches in order they should be applied 6 | # PDIR - path to the directory which contains all patches from the PLIST 7 | 8 | get_abs_filename() { 9 | echo "$(cd "$(dirname "$1")" && pwd)/$(basename "$1")" 10 | } 11 | 12 | SRC_DIR=$(get_abs_filename $1) 13 | PLIST=$(get_abs_filename $2) 14 | PDIR=$(get_abs_filename $3) 15 | TEMP_PLIST=/tmp/build.kpatch/tmpplist 16 | 17 | if [ ! -f $PLIST ]; then 18 | echo "File $PLIST not found" 19 | exit 1; 20 | fi 21 | 22 | echo "patching $PWD with patches from $PLIST" 23 | 24 | pushd $SRC_DIR # go to the directory with sources to be patched 25 | 26 | #in case we don't have a newline in plist 27 | cat $PLIST > $TEMP_PLIST 28 | echo -e "\n" >> $TEMP_PLIST 29 | 30 | # iterate through patches in PLIST 31 | while read NAME 32 | do 33 | COMMENT=`echo $NAME | cut -c1` 34 | if [ "$COMMENT" == "#" ]; then 35 | continue; 36 | fi 37 | 38 | if [ -z "${NAME}" ]; then 39 | continue; 40 | fi 41 | 42 | echo "Applying patch $NAME" 43 | patch -p1 -u --fuzz=0 --batch < $PDIR/$NAME 44 | if [ $? != 0 ]; 45 | then 46 | echo "Failed applying patch $NAME"; popd; rm $TEMP_PLIST; exit 1 47 | else 48 | echo "Successfully applied patch $NAME" 49 | fi 50 | done < $TEMP_PLIST 51 | rm $TEMP_PLIST 52 | 53 | popd 54 | -------------------------------------------------------------------------------- /scripts/toil/README.md: -------------------------------------------------------------------------------- 1 | This is where [toil](https://toil.readthedocs.io/)-based scripts do live. 2 | -------------------------------------------------------------------------------- /scripts/toil/build-patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | exec 1>&2 4 | 5 | ln -fs /data /kcdata 6 | 7 | ls -lR /data 8 | 9 | yum install -y rpm-build 10 | 11 | LIBCARE_DIR="/data" 12 | KPATCH_PATH="/data/src" 13 | export LIBCARE_DIR KPATCH_PATH 14 | make -C $KPATCH_PATH clean all 15 | make -C /data/execve clean all 16 | 17 | /kcdata/scripts/pkgbuild $@ /kcdata/package 18 | -------------------------------------------------------------------------------- /scripts/toil/requirements.txt: -------------------------------------------------------------------------------- 1 | toil==3.10.1 2 | boto3 3 | requests 4 | pyyaml 5 | -------------------------------------------------------------------------------- /scripts/toil/tests_pkgbuild.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import contextlib 4 | import functools 5 | import os 6 | import shutil 7 | from StringIO import StringIO 8 | import tempfile 9 | 10 | from toil.common import Toil 11 | 12 | import pkgbuild 13 | 14 | def test_storage_update(): 15 | Storage = pkgbuild.Storage 16 | 17 | storage = Storage(**{'test.sh': 'nothing'}) 18 | newStorage = Storage(**{'test2.sh': 'nothing'}) 19 | 20 | storage.update('promised_value2') 21 | newStorage.update('promised_value') 22 | 23 | newStorage.update(storage) 24 | 25 | assert newStorage == {'test.sh': 'nothing', 'test2.sh': 'nothing'} 26 | assert newStorage.promised_updates == ['promised_value', 'promised_value2'] 27 | assert Storage(newStorage) == newStorage 28 | 29 | def test_storage_pickle(): 30 | import cPickle 31 | Storage = pkgbuild.Storage 32 | 33 | storage = Storage() 34 | storage['abc'] = 'test' 35 | storage.update('promised_value') 36 | 37 | storage = cPickle.loads(cPickle.dumps(storage, protocol=-1)) 38 | assert storage['abc'] == 'test' 39 | assert storage.promised_updates == ['promised_value'] 40 | 41 | def test_parseFileInfo(): 42 | parseFileInfo = pkgbuild.parseFileInfo 43 | 44 | info = parseFileInfo('http://kernel.org/index.html') 45 | assert info == pkgbuild.FileInfo( 46 | fileName='index.html', 47 | url='http://kernel.org/index.html', 48 | required=True) 49 | 50 | info = parseFileInfo('*http://kernel.org/index.html') 51 | assert info == pkgbuild.FileInfo( 52 | fileName='index.html', 53 | url='http://kernel.org/index.html', 54 | required=False) 55 | 56 | info = parseFileInfo({'foobar.html': 'http://kernel.org/index.html'}) 57 | assert info == pkgbuild.FileInfo( 58 | fileName='foobar.html', 59 | url='http://kernel.org/index.html', 60 | required=True) 61 | 62 | info = parseFileInfo({'foobar.html': '*http://kernel.org/index.html'}) 63 | assert info == pkgbuild.FileInfo( 64 | fileName='foobar.html', 65 | url='http://kernel.org/index.html', 66 | required=False) 67 | 68 | try: 69 | info = parseFileInfo({ 70 | 'foobar.html': '*http://kernel.org/index.html', 71 | 'other': 'things'}) 72 | except ValueError: 73 | pass 74 | else: 75 | assert False 76 | 77 | class FakeJobStorage(object): 78 | def __init__(self, imported): 79 | self.imported = imported 80 | 81 | @contextlib.contextmanager 82 | def readSharedFileStream(self, fileId): 83 | buf = fileId 84 | if self.imported and fileId in self.imported: 85 | buf = self.imported[fileId] 86 | yield StringIO(buf) 87 | 88 | class FakeFileStorage(object): 89 | def __init__(self, imported=None): 90 | self.localTempDir = tempfile.mkdtemp() 91 | self.jobStore = FakeJobStorage(imported) 92 | 93 | def getLocalTempDir(self): 94 | return self.localTempDir 95 | 96 | def readGlobalFile(self, fileId): 97 | fileName = tempfile.mktemp() 98 | with open(fileName, "w") as fh: 99 | print(fileId) 100 | fh.write(fileId) 101 | return fileName 102 | 103 | def writeGlobalFile(self, path): 104 | with open(path) as fh: 105 | return fh.read() 106 | 107 | @contextlib.contextmanager 108 | def writeGlobalFileStream(self): 109 | fh_and_fileId = StringIO() 110 | yield fh_and_fileId, fh_and_fileId 111 | 112 | class FakeWorkflow(object): 113 | def __init__(self): 114 | self.imported = {} 115 | 116 | def importFile(self, path, sharedFileName): 117 | self.imported[sharedFileName] = slurp(path.replace('file://', '')) 118 | 119 | def slurp(fileName): 120 | return open(fileName).read() 121 | 122 | def test_storage(): 123 | storage = pkgbuild.Storage( 124 | **{ 125 | 'file.txt': pkgbuild.File('file.txt', 'some content'), 126 | 'imported.txt': pkgbuild.ImportedFile('imported.txt') 127 | } 128 | ) 129 | storage.fileStore = FakeFileStorage() 130 | 131 | with storage.expose() as dirname: 132 | 133 | assert set(os.listdir(dirname)) == set(['file.txt', 'imported.txt']) 134 | 135 | assert slurp(os.path.join(dirname, 'file.txt')) == 'some content' 136 | assert slurp(os.path.join(dirname, 'imported.txt')) == 'imported.txt' 137 | 138 | with open(os.path.join(dirname, 'newfile.txt'), 'w') as fh: 139 | fh.write('and some content here') 140 | 141 | 142 | assert storage['newfile.txt'].fileId == 'and some content here' 143 | 144 | with storage.expose() as dirname: 145 | 146 | 147 | newdir = os.path.join(dirname, 'testdir') 148 | os.mkdir(newdir) 149 | with open(os.path.join(newdir, 'subfile'), "w") as fh: 150 | fh.write('foobar') 151 | 152 | inputs = os.listdir(dirname) + ['http://somefile.com/index.html'] 153 | workflow = FakeWorkflow() 154 | newStorage = pkgbuild.Storage.fromLocalFiles( 155 | workflow, inputs, dirname) 156 | assert inputs == ['http://somefile.com/index.html'] 157 | 158 | 159 | assert isinstance(newStorage['testdir'], pkgbuild.ImportedDirectory) 160 | 161 | newStorage.fileStore = FakeFileStorage(workflow.imported) 162 | with newStorage.expose() as dirname: 163 | 164 | assert slurp(os.path.join(dirname, 'file.txt')) == 'some content' 165 | assert slurp(os.path.join(dirname, 'imported.txt')) == 'imported.txt' 166 | assert slurp(os.path.join(dirname, 'newfile.txt')) == 'and some content here' 167 | assert slurp(os.path.join(dirname, 'testdir', 'subfile')) == 'foobar' 168 | 169 | 170 | class DummyStorageUpdate(pkgbuild.Job): 171 | def __init__(self, filename): 172 | super(DummyStorageUpdate, self).__init__(unitName=filename) 173 | self.filename = filename 174 | 175 | def run(self, fileStore): 176 | self.storage[self.filename] = 'something' 177 | 178 | class DummyStorageAsChild(pkgbuild.Job): 179 | def run(self, fileStore): 180 | a = DummyStorageUpdate('bar') 181 | self.addChild(a) 182 | 183 | class RootJob(pkgbuild.Job): 184 | def __init__(self, storage): 185 | super(RootJob, self).__init__() 186 | self._storage = storage 187 | 188 | def run(self, fileStore): 189 | 190 | tail = self 191 | 192 | a = DummyStorageUpdate('foo') 193 | self.addChild(a) 194 | tail = a 195 | 196 | b = DummyStorageAsChild() 197 | tail.addFollowOn(b) 198 | tail = b 199 | 200 | return tail.resultStorage 201 | 202 | def test_job_storage(): 203 | parser = pkgbuild.Job.Runner.getDefaultArgumentParser() 204 | options = parser.parse_args(['test_job_storage']) 205 | 206 | options.workDir = tempfile.mkdtemp() 207 | options.jobStore = os.path.join(options.workDir, 'workdir') 208 | 209 | storage = pkgbuild.Storage(foobar='here i go') 210 | root = RootJob(storage=storage) 211 | 212 | try: 213 | with Toil(options) as toil: 214 | storage = toil.start(root) 215 | finally: 216 | shutil.rmtree(options.workDir) 217 | 218 | assert storage == {'foo': 'something', 'bar': 'something', 'foobar': 'here i go'} 219 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | TARGETS = kpatch_gensrc \ 2 | kpatch_make \ 3 | kpatch_strip \ 4 | libcare-cc \ 5 | libcare-client \ 6 | libcare-ctl \ 7 | libcare-stresstest 8 | DEBUG = yes # comment out this line if not debug 9 | 10 | CC = gcc 11 | CFLAGS_MISC = -Wall -g -O2 -D_GNU_SOURCE 12 | 13 | cc-option = $(shell if $(CC) $(CFLAGS_MISC) $(1) -S -o /dev/null -xc /dev/null \ 14 | > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;) 15 | 16 | CFLAGS_WARN = -Wuninitialized -Wreturn-type 17 | CFLAGS_WARN += $(call cc-option,-Wno-builtin-macro-redefined) 18 | CFLAGS_WARN += $(call cc-option,-Wno-deprecated-declarations) 19 | CFLAGS = $(CFLAGS_MISC) $(CFLAGS_WARN) 20 | 21 | ifdef DEBUG 22 | CFLAGS += -DDEBUG -O0 -g 23 | endif 24 | 25 | ifeq ($(STATIC),y) 26 | STATIC_OPTS += -static 27 | endif 28 | 29 | all: $(TARGETS) 30 | 31 | .SUFFIXES: 32 | 33 | kpatch_gensrc: kpatch_gensrc.o kpatch_dbgfilter.o kpatch_parse.o kpatch_io.o rbtree.o kpatch_log.o 34 | kpatch_make: kpatch_make.o 35 | 36 | LIBUNWIND_LIBS := $(shell pkg-config --libs libunwind libunwind-ptrace) 37 | 38 | 39 | libcare-ctl: kpatch_user.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o 40 | libcare-ctl: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o 41 | libcare-ctl: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) 42 | 43 | libcare-stresstest: kpatch_user-stresstest.o kpatch_storage.o kpatch_patch.c kpatch_elf.o kpatch_ptrace.o kpatch_coro.o 44 | libcare-stresstest: kpatch_process.o kpatch_common.o rbtree.o kpatch_log.o 45 | libcare-stresstest: LDLIBS += -lelf -lrt $(LIBUNWIND_LIBS) 46 | 47 | libcare-client: libcare-client.o 48 | 49 | kpatch_strip: kpatch_strip.o kpatch_elf_objinfo.o kpatch_log.o 50 | kpatch_strip: LDLIBS = -lelf 51 | 52 | libcare-cc: kpatch_cc.o 53 | 54 | $(TARGETS): %: 55 | $(CC) -o $(@) $(^) $(STATIC_OPTS) $(LDLIBS) 56 | 57 | deps/%.d: %.c 58 | $(CC) $(CFLAGS) $(CFLAGS_$(*)) -MM -MF "$@" -MG -MP -MT"$@" -MT"$(<:.c=.o)" $(<) 59 | 60 | ifneq ($(wildcard deps/*.d),) 61 | -include $(wildcard deps/*.d) 62 | endif 63 | 64 | .PRECIOUS: deps/%.d 65 | 66 | %.o: %.c deps/%.d 67 | $(CC) $(CFLAGS) $(CFLAGS_$(*)) -o $(@) -c $(<) 68 | 69 | %-stresstest.o: %.c 70 | $(CC) -DSTRESS_TEST=1 $(CFLAGS) $(CFLAGS_$(*)) -o $(@) -c $(<) 71 | 72 | clean: 73 | rm -rf *.o core.* deps/*.d $(TARGETS) 74 | for f in tests/gensrc/*.s; do \ 75 | rm -f $$f.test; \ 76 | done 77 | 78 | distclean: clean 79 | 80 | test: tests 81 | tests: tests-gensrc tests-kpcc tests-strip 82 | tests-gensrc: kpatch_gensrc 83 | for f in tests/gensrc/*.s; do \ 84 | [ -f $$f.cmdline ] && opts="`cat $$f.cmdline`" || opts="" ; \ 85 | if [ -f $$f.negative ]; then \ 86 | ./kpatch_gensrc --os=rhel6 -i $$f.s1 -i $$f.s2 -o /dev/null -d 1 $$opts && echo FAIL: $$f || echo PASS: $$f; \ 87 | else \ 88 | ./kpatch_gensrc --os=rhel6 -i $$f.s1 -i $$f.s2 -o $$f.test -d 1 $$opts ; \ 89 | diff $$f $$f.test > /dev/null && echo PASS: $$f || echo FAIL: $$f and $$f.test differ ; \ 90 | fi \ 91 | done; 92 | 93 | tests-kpcc: kpatch_cc 94 | ./tests/kpatch_cc/test_kpcc.sh 95 | 96 | tests-strip: kpatch_strip 97 | KPATCH_STRIP=$(CURDIR)/kpatch_strip ./tests/strip/test_simple.sh 98 | 99 | 100 | bindir ?= /usr/local/bin 101 | libexecdir ?= /usr/local/libexec 102 | INSTALL ?= install 103 | install: all 104 | $(INSTALL) -d $(DESTDIR)/$(bindir) 105 | $(INSTALL) -m 0755 libcare-ctl $(DESTDIR)/$(bindir)/libcare-ctl 106 | $(INSTALL) -m 0755 libcare-client $(DESTDIR)/$(bindir)/libcare-client 107 | $(INSTALL) -m 0755 libcare-cc $(DESTDIR)/$(bindir)/libcare-cc 108 | $(INSTALL) -m 0755 libcare-patch-make $(DESTDIR)/$(bindir)/libcare-patch-make 109 | 110 | $(INSTALL) -d $(DESTDIR)/$(libexecdir)/libcare 111 | $(INSTALL) -m 0755 kpatch_gensrc $(DESTDIR)/$(libexecdir)/libcare/kpatch_gensrc 112 | $(INSTALL) -m 0755 kpatch_make $(DESTDIR)/$(libexecdir)/libcare/kpatch_make 113 | $(INSTALL) -m 0755 kpatch_strip $(DESTDIR)/$(libexecdir)/libcare/kpatch_strip 114 | 115 | .PHONY: all clean test tests tests-gensrc tests-strip 116 | -------------------------------------------------------------------------------- /src/deps/keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudlinux/libcare/40f12435a0d0f1b76aa19a93aad830b1f6e1e36e/src/deps/keep -------------------------------------------------------------------------------- /src/kpatch_common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "kpatch_file.h" 10 | #include "kpatch_common.h" 11 | #include "kpatch_log.h" 12 | 13 | int kpatch_openat_file(int atfd, const char *fname, struct kp_file *kpatch) 14 | { 15 | int fd; 16 | 17 | if (!kpatch) 18 | return -1; 19 | 20 | if (atfd == AT_FDCWD) 21 | kpdebug("Opening patch file '%s'...", fname); 22 | else 23 | kpdebug("Opening patch file '%s' at dirfd %d...", fname, atfd); 24 | fd = openat(atfd, fname, O_RDONLY); 25 | if (fd == -1) { 26 | kpdebug("FAIL: %s\n", strerror(errno)); 27 | return -1; 28 | } 29 | if (kpatch_open_fd(fd, kpatch) < 0) 30 | goto error; 31 | kpdebug("OK\n"); 32 | close(fd); 33 | return 0; 34 | error: 35 | close(fd); 36 | return -1; 37 | } 38 | 39 | int kpatch_open_fd(int fd, struct kp_file *kpatch) 40 | { 41 | struct stat st; 42 | 43 | kpdebug("OK\nQuerying file size..."); 44 | if (fstat(fd, &st) == -1) { 45 | kpdebug("FAIL: %s\n", strerror(errno)); 46 | return -1; 47 | } 48 | kpdebug("OK\nMapping patch file..."); 49 | kpatch->size = st.st_size; 50 | kpatch->patch = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); 51 | if (kpatch->patch == MAP_FAILED) { 52 | kpdebug("FAIL: %s\n", strerror(errno)); 53 | return -1; 54 | } 55 | 56 | return 0; 57 | } 58 | 59 | int kpatch_open_file(const char *fname, struct kp_file *kpatch) 60 | { 61 | return kpatch_openat_file(AT_FDCWD, fname, kpatch); 62 | } 63 | 64 | int kpatch_close_file(struct kp_file *kpatch) 65 | { 66 | return munmap(kpatch->patch, kpatch->size); 67 | } 68 | -------------------------------------------------------------------------------- /src/kpatch_common.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_COMMON__ 2 | #define __KPATCH_COMMON__ 3 | 4 | struct kp_file { 5 | struct kpatch_file *patch; 6 | ssize_t size; 7 | }; 8 | 9 | #define INIT_KP_FILE() { .patch = NULL, .size = -1 } 10 | static inline void 11 | init_kp_file(struct kp_file *kpf) 12 | { 13 | kpf->patch = NULL; 14 | kpf->size = -1; 15 | } 16 | 17 | #include /* GNU has TLS errno */ 18 | 19 | #define ROUND_DOWN(x, m) ((x) & ~((m) - 1)) 20 | #define ROUND_UP(x, m) (((x) + (m) - 1) & ~((m) - 1)) 21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) 22 | 23 | #define proc2pctx(proc) list_first_entry(&(proc)->ptrace.pctxs, \ 24 | struct kpatch_ptrace_ctx, list) 25 | #define ks2pctx(ks) proc2pctx(&((ks)->proc)) 26 | 27 | int kpatch_open_fd(int fd, struct kp_file *kpatch); 28 | int kpatch_openat_file(int atfd, const char *fname, struct kp_file *kpatch); 29 | int kpatch_open_file(const char *fname, struct kp_file *kpatch); 30 | int kpatch_close_file(struct kp_file *kpatch); 31 | 32 | #ifndef R_X86_64_REX_GOTPCRELX 33 | /* 34 | * Ubuntu 1604 specific relocation that is GOTPCREL with code rewrite, more 35 | * info here https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI 36 | */ 37 | # define R_X86_64_REX_GOTPCRELX 0x2A 38 | #endif 39 | 40 | #ifndef R_X86_64_GOTPCRELX 41 | # define R_X86_64_GOTPCRELX 0x29 42 | #endif 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/kpatch_coro.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_CORO__ 2 | #define __KPATCH_CORO__ 3 | 4 | #include 5 | 6 | #include "list.h" 7 | 8 | struct kpatch_process; 9 | 10 | struct kpatch_coro_ops { 11 | int (*find_coroutines)(struct kpatch_process *proc); 12 | }; 13 | 14 | struct kpatch_coro { 15 | struct list_head list; 16 | sigjmp_buf env; 17 | }; 18 | 19 | void *_UCORO_create(struct kpatch_coro *coro, pid_t pid); 20 | void _UCORO_destroy(void *arg); 21 | 22 | int kpatch_coroutines_init(struct kpatch_process *proc); 23 | int kpatch_coroutines_find(struct kpatch_process *proc); 24 | void kpatch_coroutines_free(struct kpatch_process *proc); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/kpatch_dbgfilter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "kpatch_parse.h" 5 | #include "kpatch_str.h" 6 | #include "kpatch_dbgfilter.h" 7 | 8 | static int is_cold_hot(char *s) 9 | { 10 | kpstr_t t; 11 | get_token(&s, &t); 12 | return !kpstrncmpz(&t, ".LCOLD") || !kpstrncmpz(&t, ".LHOT"); 13 | } 14 | 15 | /* matches comments starting from '#' */ 16 | static int is_comment(char *s) 17 | { 18 | kpstr_t t; 19 | get_token(&s, &t); 20 | return !kpstrncmpz(&t, "#"); 21 | } 22 | 23 | /* matches '.loc' */ 24 | static int is_loc_cmd(char *s) 25 | { 26 | kpstr_t t; 27 | get_token(&s, &t); 28 | return !kpstrcmpz(&t, ".loc"); 29 | } 30 | 31 | /* matches '.file n' */ 32 | static int is_file_cmd(char *s) 33 | { 34 | kpstr_t t; 35 | get_token(&s, &t); 36 | if (kpstrcmpz(&t, ".file")) 37 | return 0; 38 | 39 | if (!s || !isdigit(*s)) 40 | return 0; 41 | 42 | return 1; 43 | } 44 | 45 | /* matches '.cfi*' */ 46 | static int is_cfi_cmd(char *s) 47 | { 48 | kpstr_t t; 49 | get_token(&s, &t); 50 | if (kpstrncmpz(&t, ".cfi")) 51 | return 0; 52 | 53 | return 1; 54 | } 55 | 56 | /* matches some gcc specific debug labels */ 57 | static int is_debug_lbl(char *s) 58 | { 59 | kpstr_t t; 60 | get_token(&s, &t); 61 | return !kpstrncmpz(&t, ".LBB") || !kpstrncmpz(&t, ".LBE") || !kpstrncmpz(&t, ".LFB") || 62 | !kpstrncmpz(&t, ".LCFI") || !kpstrncmpz(&t, ".LFE") || !kpstrncmpz(&t, ".LVL"); 63 | } 64 | 65 | /* gcc generates the following sequence with debug info: 66 | * .text (optional) 67 | * .Ltext0: 68 | * and the same with .Letext0 label. Match it. 69 | */ 70 | static int skip_ltext_lbl(struct kp_file *f, int l, char *s) 71 | { 72 | kpstr_t t; 73 | int n = 0; 74 | 75 | get_token(&s, &t); 76 | if (!kpstrcmpz(&t, ".text")) 77 | {l++; n++;} 78 | 79 | s = cline(f, l + n); 80 | get_token(&s, &t); 81 | if (kpstrncmpz(&t, ".Ltext") && kpstrncmpz(&t, ".Letext")) 82 | return 0; 83 | 84 | return n + 1; 85 | } 86 | 87 | static int is_section_start(char *s, char *prefix) 88 | { 89 | kpstr_t t; 90 | int type; 91 | 92 | if (!s) 93 | return 0; 94 | 95 | type = parse_ctype(s, false); 96 | if (type == DIRECTIVE_SECTION) { 97 | get_token(&s, &t); 98 | get_token(&s, &t); 99 | if (!kpstrncmpz(&t, prefix)) 100 | return 1; 101 | } 102 | return 0; 103 | } 104 | 105 | static int is_section_end(char *s, int *pl) 106 | { 107 | int type; 108 | 109 | if (!s) 110 | return 1; 111 | 112 | /* skip all aligns, labels and data defs in section - we will filter it out */ 113 | type = parse_ctype(s, false); 114 | if (type == DIRECTIVE_PREVIOUS) { 115 | (*pl)++; 116 | return 1; 117 | } 118 | 119 | if (type != DIRECTIVE_ALIGN && 120 | type != DIRECTIVE_LABEL && 121 | type != DIRECTIVE_LOCAL_LABEL && 122 | !is_data_def(s, type)) 123 | return 1; 124 | return 0; 125 | } 126 | 127 | static int skip_section(struct kp_file *f, int l0, char *prefix) 128 | { 129 | int l = l0; 130 | while (is_section_start(cline(f, l), prefix)) { 131 | l++; 132 | while (!is_section_end(cline(f, l), &l)) 133 | l++; 134 | } 135 | return l - l0; 136 | } 137 | 138 | static inline int skip_debug_section(struct kp_file *f, int l0) 139 | { 140 | return skip_section(f, l0, ".debug_"); 141 | } 142 | 143 | static inline int skip_eh_frame_section(struct kp_file *f, int l0) 144 | { 145 | return skip_section(f, l0, ".eh_frame"); 146 | } 147 | 148 | static inline int skip_gcc_except_table(struct kp_file *f, int l0) 149 | { 150 | return skip_section(f, l0, ".gcc_except_table"); 151 | } 152 | 153 | /* function returns a number of lines to be removed from the tail of the file */ 154 | static int debug_filter_skip(struct kp_file *f, int l, int options) 155 | { 156 | char *s = cline(f, l); 157 | int n; 158 | 159 | if (is_cold_hot(s)) 160 | return 1; 161 | if (is_comment(s)) 162 | return 1; 163 | if (is_loc_cmd(s)) 164 | return 1; 165 | if (is_file_cmd(s)) 166 | return 1; 167 | if (is_debug_lbl(s)) 168 | return 1; 169 | if ((options & DFO_SKIP_CFI) && is_cfi_cmd(s)) 170 | return 1; 171 | if ((n = skip_ltext_lbl(f, l, s))) 172 | return n; 173 | if ((n = skip_debug_section(f, l))) 174 | return n; 175 | if (options & DFO_SKIP_EH_FRAME) 176 | if ((n = skip_eh_frame_section(f, l))) 177 | return n; 178 | if (options & DFO_SKIP_GCC_EXCEPT_TABLE) 179 | if ((n = skip_gcc_except_table(f, l))) 180 | return n; 181 | return 0; 182 | } 183 | 184 | void debug_filter(struct kp_file *fin, struct kp_file *fout, int options) 185 | { 186 | int i, n; 187 | 188 | for (i = 1; i < fin->nr_lines;) { 189 | n = debug_filter_skip(fin, i, options); 190 | if (n == 0) { 191 | const char *s = cline(fin, i); 192 | fprintf(fout->f, "%s", s); 193 | 194 | if (clinenum(fin, i) != clinenum(fin, i + 1)) 195 | fprintf(fout->f, "\n"); 196 | else if (s[strlen(s) - 1] != ':') 197 | fprintf(fout->f, "; "); 198 | } 199 | i += n ?: 1; 200 | 201 | if (options & DFO_EMIT_NEWLINES) { 202 | if (n > 16) 203 | n = 1; 204 | while (n-- > 0) 205 | fprintf(fout->f, "\n"); 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/kpatch_dbgfilter.h: -------------------------------------------------------------------------------- 1 | #ifndef __DBGFILTER_H__ 2 | #define __DBGFILTER_H__ 3 | 4 | #include "kpatch_io.h" 5 | 6 | #define DFO_SKIP_EH_FRAME 1 << 0 7 | #define DFO_SKIP_GCC_EXCEPT_TABLE 1 << 1 8 | #define DFO_SKIP_CFI 1 << 2 9 | #define DFO_EMIT_NEWLINES 1 << 3 10 | 11 | void debug_filter(struct kp_file *fin, struct kp_file *fout, int options); 12 | 13 | #endif /* __DBGFILTER_H__ */ 14 | -------------------------------------------------------------------------------- /src/kpatch_elf.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_ELF__ 2 | #define __KPATCH_ELF__ 3 | 4 | #include "kpatch_process.h" 5 | 6 | const char *kpatch_get_buildid(struct object_file *o); 7 | 8 | /* 9 | * Set ELF header (and program headers if they fit) 10 | * from the already read `buf` of size `bufsize`. 11 | */ 12 | int 13 | kpatch_elf_object_set_ehdr(struct object_file *o, 14 | const unsigned char *buf, 15 | size_t bufsize); 16 | 17 | int kpatch_elf_object_is_shared_lib(struct object_file *o); 18 | int kpatch_elf_parse_program_header(struct object_file *o); 19 | int kpatch_elf_load_kpatch_info(struct object_file *o); 20 | 21 | int kpatch_resolve(struct object_file *o); 22 | int kpatch_relocate(struct object_file *o); 23 | 24 | struct kpatch_jmp_table *kpatch_new_jmp_table(int entries); 25 | int kpatch_count_undefined(struct object_file *o); 26 | 27 | int kpatch_resolve_undefined_single_dynamic(struct object_file *o, 28 | const char *sname, 29 | unsigned long *addr); 30 | 31 | unsigned long vaddr2addr(struct object_file *o, unsigned long vaddr); 32 | 33 | struct kpatch_jmp_table_entry { 34 | unsigned long jmp; 35 | unsigned long addr; 36 | }; 37 | 38 | struct kpatch_jmp_table { 39 | unsigned int size; 40 | unsigned int cur_entry; 41 | unsigned int max_entry; 42 | 43 | struct kpatch_jmp_table_entry entries[0]; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /src/kpatch_elf_objinfo.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "kpatch_common.h" 8 | #include "kpatch_elf_objinfo.h" 9 | #include "kpatch_log.h" 10 | 11 | const char *kpatch_objinfo_strptr(kpatch_objinfo *oi, int type, size_t nameidx) 12 | { 13 | size_t strsecidx; 14 | 15 | switch (type) { 16 | case SECTION_NAME: 17 | strsecidx = oi->shdrstridx; 18 | break; 19 | case SYMBOL_NAME: 20 | strsecidx = oi->symstridx; 21 | break; 22 | case DYNAMIC_NAME: 23 | strsecidx = oi->dynsymstridx; 24 | break; 25 | default: 26 | return NULL; 27 | } 28 | 29 | return elf_strptr(oi->elf, strsecidx, nameidx); 30 | } 31 | 32 | int kpatch_objinfo_load(kpatch_objinfo *oi) 33 | { 34 | size_t i, n; 35 | 36 | if (oi->loaded) 37 | return 0; 38 | 39 | if (_elf_getshdrstrndx(oi->elf, &oi->shdrstridx)) 40 | return -1; 41 | if (!gelf_getehdr(oi->elf, &oi->ehdr)) 42 | return -1; 43 | if (elf_getshnum(oi->elf, &oi->shnum) < 0) 44 | return -1; 45 | 46 | /* symtab is usually placed at the end */ 47 | for (n = 0, i = 1; i < oi->shnum; i++) { 48 | const char *secname; 49 | GElf_Shdr shdr; 50 | Elf_Scn *scn = NULL; 51 | 52 | scn = kpatch_objinfo_getshdr(oi, i, &shdr); 53 | if (scn == NULL) 54 | return -1; 55 | 56 | secname = kpatch_objinfo_strptr(oi, SECTION_NAME, 57 | shdr.sh_name); 58 | if (!strncmp(secname, ".kpatch", 7)) { 59 | if (n == ARRAY_SIZE(oi->_kpatch_sections)) 60 | return -1; 61 | oi->_kpatch_sections[n++] = i; 62 | } 63 | 64 | switch (shdr.sh_type) { 65 | case SHT_SYMTAB: 66 | oi->symtab = elf_getdata(scn, NULL); 67 | oi->nsym = shdr.sh_size / shdr.sh_entsize; 68 | oi->symstridx = shdr.sh_link; 69 | oi->symidx = i; 70 | break; 71 | case SHT_DYNSYM: 72 | oi->dynsymtab = elf_getdata(scn, NULL); 73 | oi->ndynsym = shdr.sh_size / shdr.sh_entsize; 74 | oi->dynsymstridx = shdr.sh_link; 75 | oi->dynsymidx = i; 76 | break; 77 | } 78 | } 79 | oi->loaded = 1; 80 | 81 | return 0; 82 | } 83 | 84 | extern int elf_getshdrstrndx(Elf *elf, size_t *ndx) __attribute__ ((weak)); 85 | 86 | int _elf_getshdrstrndx(Elf *elf, size_t *ndx) 87 | { 88 | if (elf_getshdrstrndx) 89 | return elf_getshdrstrndx(elf, ndx); 90 | else 91 | return elf_getshstrndx(elf, ndx); 92 | } 93 | 94 | Elf_Scn *kpatch_objinfo_getshdr(kpatch_objinfo *oi, int secnum, GElf_Shdr *shdr) 95 | { 96 | Elf_Scn *scn; 97 | 98 | scn = elf_getscn(oi->elf, secnum); 99 | if (scn == NULL) { 100 | kplogerror("elf_getscn(%d)", secnum); 101 | return NULL; 102 | } 103 | 104 | if (shdr != NULL && !gelf_getshdr(scn, shdr)) { 105 | kplogerror("gelf_getshdr"); 106 | return NULL; 107 | } 108 | 109 | return scn; 110 | } 111 | 112 | Elf_Scn * 113 | kpatch_objinfo_find_scn_by_name(kpatch_objinfo *oi, 114 | const char *name, 115 | GElf_Shdr *pshdr) 116 | { 117 | size_t i; 118 | Elf_Scn *scn; 119 | GElf_Shdr shdr; 120 | const char *tmp; 121 | 122 | for (i = 1; i < oi->shnum; i++) { 123 | scn = kpatch_objinfo_getshdr(oi, i, &shdr); 124 | if (scn == NULL) 125 | return NULL; 126 | 127 | tmp = kpatch_objinfo_strptr(oi, SECTION_NAME, shdr.sh_name); 128 | if (tmp != NULL && !strcmp(name, tmp)) { 129 | if (pshdr) 130 | *pshdr = shdr; 131 | return scn; 132 | } 133 | } 134 | 135 | return NULL; 136 | } 137 | 138 | int kpatch_objinfo_is_our_section(kpatch_objinfo *oi, int secnum) 139 | { 140 | int i = 0, n = ARRAY_SIZE(oi->_kpatch_sections); 141 | unsigned short *s = oi->_kpatch_sections; 142 | 143 | for (; s[i] && i < n; i++) { 144 | if (s[i] == secnum) 145 | return 1; 146 | } 147 | return 0; 148 | } 149 | 150 | int 151 | kpatch_objinfo_load_tls_reladyn(kpatch_objinfo *oi) 152 | { 153 | Elf64_Rela *rela; 154 | Elf_Scn *scn_rela_dyn; 155 | Elf_Data *data_rela_dyn; 156 | size_t nrela, i, nlast; 157 | 158 | if (oi->tlsreladyn != NULL) 159 | return 0; 160 | 161 | scn_rela_dyn = kpatch_objinfo_find_scn_by_name(oi, ".rela.dyn", NULL); 162 | if (scn_rela_dyn == NULL) { 163 | kpfatalerror("unable to find .rela.dyn"); 164 | return -1; 165 | } 166 | 167 | data_rela_dyn = elf_getdata(scn_rela_dyn, NULL); 168 | if (data_rela_dyn == NULL || data_rela_dyn->d_buf == NULL) { 169 | kpfatalerror("no data for .rela.dyn"); 170 | return -1; 171 | } 172 | 173 | rela = data_rela_dyn->d_buf; 174 | nrela = data_rela_dyn->d_size / sizeof(*rela); 175 | 176 | /* Skip RELATIVE and others at the start */ 177 | for (; nrela != 0; rela++, nrela--) { 178 | if (kpatch_is_tls_rela(rela)) 179 | break; 180 | } 181 | 182 | oi->tlsreladyn = rela; 183 | 184 | /* Chop at last TLS reloc */ 185 | for (i = 0; i < nrela; rela++, i++) { 186 | if (kpatch_is_tls_rela(rela)) 187 | nlast = i; 188 | } 189 | 190 | oi->ntlsreladyn = nlast + 1; 191 | 192 | return 0; 193 | } 194 | -------------------------------------------------------------------------------- /src/kpatch_elf_objinfo.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef __KPATCH_ELF_OBJINFO_INCLUDED__ 3 | #define __KPATCH_ELF_OBJINFO_INCLUDED__ 4 | 5 | typedef struct { 6 | Elf *elf; 7 | 8 | GElf_Ehdr ehdr; 9 | 10 | Elf_Data *symtab; 11 | size_t nsym; 12 | size_t symidx; 13 | 14 | 15 | Elf_Data *dynsymtab; 16 | size_t ndynsym; 17 | size_t dynsymidx; 18 | size_t dynsymstridx; 19 | 20 | size_t shdrstridx; 21 | size_t symstridx; 22 | 23 | size_t shnum; 24 | 25 | unsigned short _kpatch_sections[16]; 26 | 27 | int loaded; 28 | 29 | Elf64_Rela *tlsreladyn; 30 | size_t ntlsreladyn; 31 | 32 | } kpatch_objinfo; 33 | 34 | static inline 35 | void init_kpatch_object_info(kpatch_objinfo *oi, Elf *elf) 36 | { 37 | oi->elf = elf; 38 | oi->symtab = NULL; 39 | oi->dynsymtab = NULL; 40 | oi->loaded = 0; 41 | oi->tlsreladyn = NULL; 42 | oi->ntlsreladyn = 0; 43 | } 44 | 45 | #define OBJINFO_INIT(elf_) { .elf = elf_, .symtab = NULL, .loaded = 0 } 46 | 47 | int kpatch_objinfo_load(kpatch_objinfo *oi); 48 | 49 | Elf_Scn *kpatch_objinfo_getshdr(kpatch_objinfo *oi, int secnum, GElf_Shdr *shdr); 50 | 51 | int kpatch_objinfo_is_our_section(kpatch_objinfo *oi, int secnum); 52 | 53 | /* TODO(pboldin): SYMBOL_NAME -> SYMBOL_NAMEIDX and make SYMBOL_NAME resolve 54 | * name from the symbol number, not the .strtab offset */ 55 | #define SECTION_NAME 0x0 56 | #define SYMBOL_NAME 0x1 57 | #define DYNAMIC_NAME 0x2 58 | const char *kpatch_objinfo_strptr(kpatch_objinfo *oi, int type, 59 | size_t nameidx); 60 | 61 | int _elf_getshdrstrndx(Elf *elf, size_t *ndx); 62 | 63 | Elf_Scn *kpatch_objinfo_find_scn_by_name(kpatch_objinfo *oi, 64 | const char *name, GElf_Shdr *shdr); 65 | 66 | static inline int 67 | kpatch_is_tls_rela(Elf64_Rela *rela) 68 | { 69 | return (ELF64_R_TYPE(rela->r_info) == R_X86_64_TPOFF64 || 70 | ELF64_R_TYPE(rela->r_info) == R_X86_64_DTPOFF64 || 71 | ELF64_R_TYPE(rela->r_info) == R_X86_64_DTPMOD64); 72 | } 73 | 74 | int 75 | kpatch_objinfo_load_tls_reladyn(kpatch_objinfo *oi); 76 | 77 | #endif /* __KPATCH_ELF_OBJINFO_INCLUDED__ */ 78 | -------------------------------------------------------------------------------- /src/kpatch_file.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_FILE_H__ 2 | #define __KPATCH_FILE_H__ 3 | 4 | #ifndef __ASSEMBLY__ 5 | 6 | #ifndef __KERNEL__ 7 | #include 8 | #else 9 | #include 10 | #include 11 | #endif 12 | #ifndef __MACH__ 13 | #include 14 | #endif 15 | 16 | #ifdef __KERNEL__ 17 | 18 | #include /* for hlist_head */ 19 | #include /* for rcu_head */ 20 | 21 | struct kpatch_binding_type { 22 | void (*dtor)(void *); 23 | unsigned int size; 24 | unsigned int offset; 25 | const char *name; 26 | unsigned long objsize; 27 | }; 28 | 29 | struct kpatch_binding { 30 | struct kpatch_binding_type *type; 31 | struct kmem_cache *cache; 32 | struct hlist_head hash[0]; 33 | }; 34 | 35 | struct kpatch_binding_node { 36 | struct kpatch_binding *binding; 37 | void *ptr; 38 | struct hlist_node hlist; 39 | struct rcu_head rcu; 40 | }; 41 | 42 | /* functions to be used by init/exit calls */ 43 | struct kpatch_binding * kpatch_binding_create( struct kpatch_binding_type *); 44 | void kpatch_binding_destroy(struct kpatch_binding *); 45 | /* kpatch_binding API */ 46 | void * kpatch_binding_node_alloc(struct kpatch_binding *, gfp_t); 47 | void kpatch_binding_node_free(struct kpatch_binding_node *); 48 | void kpatch_binding_node_bind(struct kpatch_binding_node *, void *); 49 | void kpatch_binding_node_unbind(struct kpatch_binding_node *); 50 | void * kpatch_binding_lookup_entry(struct kpatch_binding *, void *); 51 | struct kpatch_binding_node * kpatch_binding_lookup_node(struct kpatch_binding *, void *); 52 | 53 | #ifndef __used 54 | #define __used __attribute__((__used__)) 55 | #endif 56 | 57 | #define kpatch_init_pre(fn) static initcall_t __kpatch_init_pre_##fn __used \ 58 | __attribute__((__section__(".kpatch.init.pre"))) = fn 59 | #define kpatch_init(fn) static initcall_t __kpatch_init_##fn __used \ 60 | __attribute__((__section__(".kpatch.init"))) = fn 61 | #define kpatch_init_post(fn) static initcall_t __kpatch_init_post_##fn __used \ 62 | __attribute__((__section__(".kpatch.init.post"))) = fn 63 | 64 | #define kpatch_exit_pre(fn) static exitcall_t __kpatch_exit_pre_##fn __used \ 65 | __attribute__((__section__(".kpatch.exit.pre"))) = fn 66 | #define kpatch_exit(fn) static exitcall_t __kpatch_exit_##fn __used \ 67 | __attribute__((__section__(".kpatch.exit"))) = fn 68 | #define kpatch_exit_post(fn) static exitcall_t __kpatch_exit_post_##fn __used \ 69 | __attribute__((__section__(".kpatch.exit.post"))) = fn 70 | #endif 71 | 72 | typedef uint32_t kpatch_offset_t; 73 | typedef uint32_t kpatch_reloc_t; 74 | 75 | /* Load patch into memory, verifies it (checksum, etc...) and applies it */ 76 | #define KPATCH_APPLY _IOW('k', 0, struct kpatch_payload *) 77 | /* Undo the patch */ 78 | #define KPATCH_UNDO _IO('k', 1) 79 | /* Query info about patches */ 80 | #define KPATCH_INFO _IOR('k', 2, struct kpatch_query *) 81 | 82 | struct kpatch_file; 83 | struct kpatch_data; 84 | 85 | typedef int (*kpatch_entry)(struct kpatch_file *, unsigned long size, void **); 86 | typedef void *(*kpatch_alloc)(unsigned long); 87 | typedef int (*kpatch_undo_patch)(struct kpatch_data *); 88 | 89 | #define SET_BIT(r,b) ((r) |= (1 << (b))) 90 | #define CLR_BIT(r,b) ((r) &= ~((1) << (b))) 91 | #define TEST_BIT(r,b) ((r) & (1<<(b))) 92 | 93 | #define KPATCH_FILE_MAGIC1 "KPATCH1" 94 | #define KPATCH_MAX_NR_ENTRIES 16 95 | #define KPATCH_UNAME_LEN 256 96 | 97 | #define KPATCH_DEBUG_FLAG 0 98 | #define KPATCH_NOFREEZE_FLAG 1 /* this flag is ignored, use safety method insted */ 99 | 100 | enum { 101 | KPATCH_SAFETY_METHOD_DEFAULT = 0, 102 | KPATCH_SAFETY_METHOD_FREEZE_ALL, 103 | KPATCH_SAFETY_METHOD_FREEZE_NONE, 104 | KPATCH_SAFETY_METHOD_FREEZE_CONFLICT, 105 | KPATCH_SAFETY_METHOD_MAX, 106 | }; 107 | 108 | struct kpatch_payload { 109 | size_t size; 110 | char *patch; 111 | char *description; // could be NULL 112 | }; 113 | 114 | struct kpatch_file { 115 | char magic[8]; /* magic string */ 116 | unsigned char flags; 117 | unsigned char safety_method; 118 | char pad[6]; 119 | char modulename[64]; /* "vmlinux" or module name */ 120 | char uname[KPATCH_UNAME_LEN]; /* /proc/version of the kernel */ 121 | 122 | uint64_t build_time; /* build time */ 123 | uint32_t csum; /* checksum of the whole kpatch */ 124 | uint32_t nr_reloc; /* number of relocations */ 125 | 126 | kpatch_offset_t kpatch_offset; /* content offset */ 127 | kpatch_offset_t rel_offset; /* relocations offset (vmlinux) */ 128 | kpatch_offset_t total_size; /* total size = header + relocations + content */ 129 | kpatch_offset_t jmp_offset; /* jump table offset for user-space patches */ 130 | 131 | /* array of entry offsets in the patch content */ 132 | union { 133 | kpatch_offset_t entries[KPATCH_MAX_NR_ENTRIES]; 134 | struct { 135 | kpatch_offset_t kpatch_entry; 136 | kpatch_offset_t kpatch_module_alloc; 137 | kpatch_offset_t kpatch_unpatch; 138 | kpatch_offset_t kpatch_delta; 139 | }; 140 | struct { 141 | kpatch_offset_t elf_hdr; /* saved offset to ELF hdr */ 142 | kpatch_offset_t extbl_start; /* exception table start for modules */ 143 | kpatch_offset_t extbl_end; /* exception table end for modules */ 144 | kpatch_offset_t bugtbl_start; /* bug table start for modules */ 145 | kpatch_offset_t bugtbl_end; /* bug table end for modules */ 146 | }; 147 | struct { 148 | kpatch_offset_t user_undo; /* undo information for userspace */ 149 | kpatch_offset_t user_info; /* patch information */ 150 | kpatch_offset_t user_level; /* FIXME(pboldin) */ 151 | }; 152 | }; 153 | 154 | char srcversion[25]; /* srcversion of module or zeros */ 155 | char pad2[231]; 156 | 157 | /* relocations */ 158 | /* content */ 159 | }; 160 | 161 | struct kpatch_reloc { 162 | kpatch_reloc_t offset; /* offset in file */ 163 | char type; /* relocation type */ 164 | /* possible pcrel values according to area offset points to: 165 | * == 0 - offset points to kpatch area, 166 | * == 1 - offset points to vmlinux area, 167 | * == -1 - offset points to per_cpu variable */ 168 | #define KPATCH_PCREL_EXT_KPATCH (0) 169 | #define KPATCH_PCREL_EXT_VMLINUX (1) 170 | #define KPATCH_PCREL_EXT_PER_CPU (-1) 171 | char pcrel; /* pcrel = 0 => code reloation +x bytes should adjust relocation value by +x, otherwise -x */ 172 | char pad[2]; 173 | char pad2[4]; 174 | kpatch_reloc_t delta; /* not used for patching, only during make stage */ 175 | }; 176 | 177 | #define KPATCH_INFO_PTR64(name) union { unsigned long name; uint64_t name ## 64; } 178 | struct kpatch_info { 179 | KPATCH_INFO_PTR64(daddr); 180 | KPATCH_INFO_PTR64(saddr); 181 | uint32_t dlen; 182 | uint32_t slen; 183 | KPATCH_INFO_PTR64(symstr); 184 | KPATCH_INFO_PTR64(vaddr); 185 | uint32_t flags; 186 | char pad[4]; 187 | }; 188 | 189 | struct kpatch_undo_entry { 190 | #define UNDO_ENTRY_ALLOCATED (1 << 0) 191 | #define UNDO_ENTRY_PATCHED (1 << 1) 192 | #define UNDO_ENTRY_EXITED (1 << 2) 193 | long flags; 194 | /* module name or vmlinux */ 195 | char modulename[64]; 196 | /* pointer original code (all hunks stored sequentially) */ 197 | void *orig_code; 198 | /* pointer to original patch (for exitcalls) */ 199 | struct kpatch_file *kpatch; 200 | }; 201 | 202 | /* data stored by kpatch-loader to support unpatching */ 203 | struct kpatch_undo { 204 | /* number of entries */ 205 | uint32_t nr_entries; 206 | /* size of the area */ 207 | size_t size; 208 | /* entries */ 209 | struct kpatch_undo_entry entries[]; 210 | }; 211 | 212 | struct kpatch_query_info { 213 | /* Patch state */ 214 | #define KPATCH_STATE_NONE 0 /* No patch applied */ 215 | #define KPATCH_STATE_APPLIED 1 /* Patch is applied */ 216 | uint32_t state; 217 | 218 | char uname[KPATCH_UNAME_LEN]; 219 | char description[512]; 220 | 221 | uint64_t build_time; 222 | /* Add info about patch (name?/modules?, etc.) */ 223 | char pad[1024]; 224 | }; 225 | 226 | #ifndef __kpatch_text 227 | # define __kpatch_text 228 | #endif /* ifndef __kpatch_text */ 229 | 230 | static inline int 231 | __kpatch_text 232 | is_end_info(struct kpatch_info *info) 233 | { 234 | return (info->daddr == 0) && (info->dlen == 0) && 235 | (info->saddr == 0) && (info->slen == 0); 236 | } 237 | 238 | static inline int 239 | __kpatch_text 240 | is_new_func(struct kpatch_info *info) 241 | { 242 | return (info->daddr == 0) && (info->dlen == 0); 243 | } 244 | 245 | #endif /* __ASSEMBLY__ */ 246 | 247 | #ifdef __KPATCH_ASSEMBLY__ 248 | 249 | #define KPATCH_INFO_DEFINE(fname, flags) KPATCH_INFO_DEFINE fname, flags 250 | /* we use can CPP to define macro, but if we do it macro 251 | * will expand in one line, which is nearly impossible to 252 | * read in preprocessed file 253 | */ 254 | .macro KPATCH_INFO_DEFINE fname, flags 255 | .pushsection .kpatch.strtab,"a",@progbits 256 | \fname\().kpatch_strtab: 257 | .string "\fname\().kpatch" 258 | .popsection 259 | .pushsection .kpatch.info,"a",@progbits 260 | \fname\().Lpi: 261 | .ifdef \fname 262 | .quad \fname 263 | .else 264 | .quad 0 265 | .endif 266 | .quad \fname\().kpatch 267 | .ifdef \fname 268 | .long \fname\().Lfe - \fname 269 | .else 270 | .long 0 271 | .endif 272 | .long \fname\().kpatch_end - \fname\().kpatch 273 | .quad \fname\().kpatch_strtab 274 | .quad 0 275 | .long \flags 276 | .byte 0, 0, 0, 0 277 | .popsection 278 | .endm 279 | 280 | #endif /* __KPATCH_ASSEMBLY__ */ 281 | #endif 282 | -------------------------------------------------------------------------------- /src/kpatch_flags.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_FLAGS_H__ 2 | #define __KPATCH_FLAGS_H__ 3 | 4 | #define pp_make_str(x) #x 5 | 6 | #define KPINFO_DEFINE_FLAGS(fname, flags) \ 7 | asm(".equ " pp_make_str(fname) ".kpatch.flags, " pp_make_str(flags)) 8 | 9 | /* Mark function as adapted, see --must-adapt kpatch_gensrc option for details */ 10 | #define KPGENSRC_ADAPTED (1 << 0) 11 | 12 | #define KPGENSRC_DEFINE_FLAGS(flags) \ 13 | asm(".kpgensrc_flags " # flags) 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /src/kpatch_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "kpatch_log.h" 8 | #include "kpatch_io.h" 9 | #include "kpatch_str.h" 10 | 11 | void *kp_realloc(void *p, int oldsz, int newsz) 12 | { 13 | void *p2; 14 | 15 | p2 = malloc(newsz); 16 | if (p2 == NULL) 17 | kpfatal("failed to allocate %d bytes", newsz); 18 | 19 | if (p) { 20 | int sz = (oldsz > newsz) ? newsz : oldsz; 21 | memcpy(p2, p, sz); 22 | free(p); 23 | } 24 | 25 | return p2; 26 | } 27 | 28 | int read_file(struct kp_file *file, char *fname) 29 | { 30 | int sz = 64; 31 | char buf[BUFSIZE]; 32 | 33 | memset(file, 0, sizeof(*file)); 34 | if (!strcmp(fname, "-")) 35 | file->f = stdin; 36 | else 37 | file->f = fopen(fname, "rt"); 38 | 39 | file->rpath = realpath(fname, NULL); 40 | if (!file->rpath) 41 | file->rpath = ""; 42 | file->dirname = strdup(file->rpath); 43 | file->dirname = dirname(file->dirname); 44 | file->basename = strdup(file->rpath); 45 | file->basename = basename(file->basename); 46 | 47 | if (!file->f) 48 | return errno; 49 | 50 | file->nr_lines = 1; 51 | while (1) { 52 | if (file->nr_lines >= sz || !file->lines) { 53 | sz *= 2; 54 | file->lines = kp_realloc(file->lines, (sz/2) * sizeof(char *), sz * sizeof(char *)); 55 | } 56 | 57 | if (!fgets(buf, BUFSIZE, file->f)) 58 | break; 59 | 60 | trim_crlf(buf); 61 | file->lines[file->nr_lines++] = strdup(buf); 62 | } 63 | file->lines[0] = ""; /* make line with index 0 to be empty, so that our line numbers would match and editor for easier debugging, i.e. we start from index=1 */ 64 | fclose(file->f); 65 | return 0; 66 | } 67 | 68 | int create_file(struct kp_file *file, char *fname) 69 | { 70 | if (!strcmp(fname, "-")) { 71 | file->f = stdout; 72 | return 0; 73 | } 74 | 75 | file->f = fopen(fname, "wt"); 76 | if (!file->f) 77 | return errno; 78 | return 0; 79 | } 80 | 81 | void close_file(struct kp_file *file) 82 | { 83 | fclose(file->f); 84 | } 85 | -------------------------------------------------------------------------------- /src/kpatch_io.h: -------------------------------------------------------------------------------- 1 | #ifndef __KP_IO_H__ 2 | #define __KP_IO_H__ 3 | 4 | #include "kpatch_log.h" 5 | #include "rbtree.h" 6 | 7 | #define BUFSIZE 65536 8 | 9 | struct kp_file { 10 | int id; 11 | 12 | FILE *f; 13 | char *rpath; 14 | char *dirname; 15 | char *basename; 16 | int nr_lines; 17 | char **lines; 18 | int *lines_num; /* original line numbers */ 19 | 20 | int *ctype; 21 | void **section; 22 | 23 | struct rb_root cblocks_by_name; 24 | struct rb_root cblocks_by_human_name; 25 | struct rb_root cblocks_by_start; 26 | struct rb_root renames; 27 | }; 28 | 29 | int read_file(struct kp_file *f, char *fname); 30 | int create_file(struct kp_file *f, char *fname); 31 | void close_file(struct kp_file *f); 32 | void *kp_realloc(void *p, int oldsz, int newsz); 33 | 34 | #endif /* __KP_IO_H__ */ 35 | -------------------------------------------------------------------------------- /src/kpatch_log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "kpatch_log.h" 8 | 9 | int log_level = LOG_INFO; 10 | int log_indent; 11 | 12 | static void __valog(int level, const char *prefix, const char *fmt, va_list va) 13 | { 14 | FILE *f = level <= LOG_WARN ? stderr : stdout; 15 | if (prefix) 16 | fprintf(f, "%s", prefix); 17 | 18 | vfprintf(f, fmt, va); 19 | } 20 | 21 | void kplog(int level, const char *fmt, ...) 22 | { 23 | int errno_sv; 24 | va_list va; 25 | char indent[8]; 26 | 27 | if (level > log_level) 28 | return; 29 | 30 | errno_sv = errno; 31 | 32 | va_start(va, fmt); 33 | memset(indent, ' ', sizeof(indent)); 34 | indent[log_indent] = 0; 35 | __valog(level, indent, fmt, va); 36 | #if 0 37 | if (fmt[0] == '+') 38 | log_indent++; 39 | else if (fmt[0] == '-') 40 | log_indent--; 41 | #endif 42 | va_end(va); 43 | 44 | errno = errno_sv; 45 | } 46 | 47 | void kpfatal(const char *fmt, ...) 48 | { 49 | va_list va; 50 | 51 | va_start(va, fmt); 52 | __valog(LOG_ERR, "FATAL! ", fmt, va); 53 | va_end(va); 54 | 55 | exit(1); 56 | } 57 | 58 | extern int elf_errno(void) __attribute__((weak)); 59 | extern const char *elf_errmsg(int err) __attribute((weak)); 60 | 61 | void __valogerror(const char *file, int line, const char *fmt, va_list va) 62 | { 63 | int errno_sv = errno; 64 | 65 | fprintf(stderr, "%s(%d): ", file, line); 66 | __valog(LOG_ERR, NULL, fmt, va); 67 | if (errno_sv) { 68 | fprintf(stderr, "errno = %d, msg = '%s'\n", 69 | errno, strerror(errno)); 70 | } 71 | 72 | #ifndef __MACH__ 73 | if (elf_errno && elf_errmsg) { 74 | int errno_elf = elf_errno(); 75 | if (errno_elf) { 76 | fprintf(stderr, "errno_elf = %d, msg = '%s'\n", 77 | errno_elf, elf_errmsg(errno_elf)); 78 | } 79 | } 80 | #endif 81 | errno = errno_sv; 82 | } 83 | 84 | void _kplogerror(const char *file, int line, const char *fmt, ...) 85 | { 86 | va_list va; 87 | 88 | va_start(va, fmt); 89 | __valogerror(file, line, fmt, va); 90 | va_end(va); 91 | } 92 | 93 | void _kpfatalerror(const char *file, int line, const char *fmt, ...) 94 | { 95 | va_list va; 96 | 97 | va_start(va, fmt); 98 | __valogerror(file, line, fmt, va); 99 | va_end(va); 100 | 101 | exit(EXIT_FAILURE); 102 | } 103 | -------------------------------------------------------------------------------- /src/kpatch_log.h: -------------------------------------------------------------------------------- 1 | #ifndef __KP_LOG_H__ 2 | #define __KP_LOG_H__ 3 | 4 | #include 5 | 6 | extern int log_level, log_indent; 7 | 8 | void kplog(int level, const char *fmt, ...) __attribute__((format(printf, 2, 3))); 9 | void kpfatal(const char *fmt, ...) __attribute__((noreturn,format(printf,1,2))); 10 | 11 | #define kpdebug(fmt...) kplog(LOG_DEBUG, fmt) 12 | #define kpwarn(fmt...) kplog(LOG_WARN, fmt) 13 | #define kpinfo(fmt...) kplog(LOG_INFO, fmt) 14 | 15 | void _kpfatalerror(const char *filename, int line, const char *fmt, ...) 16 | __attribute__((noreturn,format(printf,3,4))); 17 | void _kplogerror(const char *filename, int line, const char *fmt, ...) 18 | __attribute__((format(printf,3,4))); 19 | 20 | #define kpfatalerror(fmt...) _kpfatalerror(__FILE__, __LINE__, fmt) 21 | #define kplogerror(fmt...) _kplogerror(__FILE__, __LINE__, fmt) 22 | 23 | #define kperr(fmt...) do { \ 24 | int errsv = errno; \ 25 | errno = 0; \ 26 | _kplogerror(__FILE__, __LINE__, fmt); \ 27 | errno = errsv; \ 28 | } while (0) 29 | 30 | #define LOG_ERR 0 31 | #define LOG_WARN 1 32 | #define LOG_INFO 2 33 | #define LOG_DEBUG 3 34 | #define LOG_TRACE 5 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/kpatch_make.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "kpatch_file.h" 13 | 14 | #define ALIGN(x, align) ((x + align - 1) & (~(align - 1))) 15 | 16 | static int verbose; 17 | 18 | static void xerror(const char *fmt, ...) 19 | { 20 | va_list va; 21 | 22 | va_start(va, fmt); 23 | vfprintf(stderr, fmt, va); 24 | va_end(va); 25 | 26 | exit(1); 27 | } 28 | 29 | int make_file(int fdo, void *buf1, off_t size, const char *buildid) 30 | { 31 | int res; 32 | struct kpatch_file khdr; 33 | 34 | memset(&khdr, 0, sizeof(khdr)); 35 | 36 | memcpy(khdr.magic, KPATCH_FILE_MAGIC1, sizeof(khdr.magic)); 37 | strncpy(khdr.uname, buildid, sizeof(khdr.uname)); 38 | khdr.build_time = (uint64_t)time(NULL); 39 | khdr.csum = 0; /* FIXME */ 40 | khdr.nr_reloc = 0; 41 | 42 | khdr.rel_offset = sizeof(khdr); 43 | khdr.kpatch_offset = khdr.rel_offset; 44 | size = ALIGN(size, 16); 45 | khdr.total_size = khdr.kpatch_offset + size; 46 | 47 | res = write(fdo, &khdr, sizeof(khdr)); 48 | res += write(fdo, buf1, size); 49 | 50 | if (res != sizeof(khdr) + size) 51 | xerror("write error"); 52 | 53 | return 0; 54 | } 55 | 56 | static void usage(void) 57 | { 58 | printf("Usage: kpatch_make [-d] -n [-v ] -e [-o ] [input2]\n"); 59 | printf(" -b buildid = target buildid for patch\n"); 60 | printf(" -d debug (verbose)\n"); 61 | printf("\n"); 62 | printf(" result is printed to output and is the following:\n"); 63 | printf(" header - struct kpatch_file\n"); 64 | printf(" .kpatch.* - sections with binary patch text/data and info\n"); 65 | exit(EXIT_FAILURE); 66 | } 67 | 68 | int main(int argc, char **argv) 69 | { 70 | int opt; 71 | int fd1, fdo; 72 | void *buf; 73 | struct stat st; 74 | char *buildid = NULL, *outputname = NULL; 75 | 76 | while ((opt = getopt(argc, argv, "db:o:v:s:")) != -1) { 77 | switch (opt) { 78 | case 'd': 79 | verbose = 1; 80 | break; 81 | case 'b': 82 | buildid = strdup(optarg); 83 | break; 84 | case 'o': 85 | outputname = strdup(optarg); 86 | break; 87 | default: /* '?' */ 88 | usage(); 89 | } 90 | } 91 | 92 | if (buildid == NULL) 93 | usage(); 94 | 95 | fd1 = open(argv[optind], O_RDONLY); 96 | if (fd1 == -1) 97 | xerror("Can't open 1st input file '%s'", argv[optind]); 98 | if (fstat(fd1, &st) == -1) 99 | xerror("Can't stat file1"); 100 | buf = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd1, 0); 101 | if (buf == MAP_FAILED) 102 | xerror("mmap error %d", errno); 103 | close(fd1); 104 | 105 | fdo = 1; 106 | if (outputname) { 107 | fdo = open(outputname, O_CREAT | O_TRUNC | O_WRONLY, 0660); 108 | if (fdo == -1) 109 | xerror("Can't open output file '%s'", outputname); 110 | } 111 | 112 | return make_file(fdo, buf, st.st_size, buildid); 113 | } 114 | -------------------------------------------------------------------------------- /src/kpatch_parse.h: -------------------------------------------------------------------------------- 1 | #ifndef __PARSE_H__ 2 | #define __PARSE_H__ 3 | 4 | #include 5 | 6 | #include "kpatch_log.h" 7 | #include "kpatch_io.h" 8 | #include "kpatch_str.h" 9 | #include "rbtree.h" 10 | 11 | /* fetch code line */ 12 | char *cline(struct kp_file *f, int l); 13 | int clinenum(struct kp_file *f, int l); 14 | 15 | /* ------------------------------------------- as directives ---------------------------------------- */ 16 | 17 | #define DIRECTIVE_ALIGN 1 18 | #define DIRECTIVE_TYPE 2 19 | #define DIRECTIVE_COMM 3 20 | #define DIRECTIVE_WEAK 4 21 | #define DIRECTIVE_SIZE 5 22 | #define DIRECTIVE_LABEL 6 23 | #define DIRECTIVE_LOCAL_LABEL 7 24 | 25 | #define DIRECTIVE_GLOBL 10 26 | #define DIRECTIVE_LOCAL 11 27 | #define DIRECTIVE_HIDDEN 12 28 | #define DIRECTIVE_PROTECTED 13 29 | #define DIRECTIVE_INTERNAL 14 30 | 31 | #define DIRECTIVE_TEXT 20 32 | #define DIRECTIVE_DATA 21 33 | #define DIRECTIVE_BSS 22 34 | 35 | #define DIRECTIVE_SECTION 30 36 | #define DIRECTIVE_PUSHSECTION 31 37 | #define DIRECTIVE_POPSECTION 32 38 | #define DIRECTIVE_SUBSECTION 33 39 | #define DIRECTIVE_PREVIOUS 34 40 | 41 | #define DIRECTIVE_COMMENT 40 42 | #define DIRECTIVE_SET 41 43 | 44 | #define DIRECTIVE_OTHER 100 45 | 46 | #define DIRECTIVE_KPFLAGS 500 47 | 48 | void init_multilines(struct kp_file *f); 49 | 50 | void init_ctypes(struct kp_file *f); 51 | int ctype(struct kp_file *f, int l); 52 | int is_sect_cmd(struct kp_file *f, int l); 53 | 54 | int parse_ctype(char *s, bool with_checks); 55 | 56 | /* ----------------------------------------- sections ----------------------------------------- */ 57 | 58 | /* we keep a separate structure for each code block whichs allows to track previous section easily. 59 | * yet we have to track all possible sections ever indexed by name for correct attributes / type handling */ 60 | struct section_desc { 61 | char *name; 62 | char *outname; /* name how to put this in result output */ 63 | #define SECTION_EXECUTABLE 0x10000000 64 | int type; /* SECTION_XXX */ 65 | struct rb_node rbnm; /* sorted by name list */ 66 | struct section_desc *prev; /* previous section for .popsection / .previous */ 67 | }; 68 | 69 | struct section_desc *find_section(char *name); 70 | struct section_desc *csect(struct kp_file *f, int l); 71 | void init_sections(struct kp_file *f); 72 | 73 | int is_data_sect(struct section_desc *sect); 74 | int is_code_sect(struct section_desc *sect); 75 | 76 | /* --------------------------------------- code blocks ----------------------------------------- */ 77 | /* 78 | * echo code block describes some object in asm file: function or data variable. 79 | * it has a name, line range [start; end) and a link to matched cblock in another file (if any). 80 | */ 81 | struct cblock { 82 | struct kp_file *f; /* file */ 83 | int start, end; /* line numbers [start;end) */ 84 | 85 | kpstr_t name; 86 | kpstr_t human_name; 87 | char auto_name; /* auto names = (name != human_name), e.g. fn.isra.2 */ 88 | 89 | #define CBLOCK_FUNC 1 90 | #define CBLOCK_VAR 2 91 | #define CBLOCK_ATTR 3 92 | #define CBLOCK_OTHER 4 93 | char type; /* function, variable or smth else */ 94 | char globl; /* whether symbol is global */ 95 | char handled; /* whether this block was handled and output already */ 96 | char ignore; /* ignore changes in this symbol */ 97 | char unlink; /* unlink this symbol and do not patch */ 98 | char adapted; /* this block marked with KPATCH_ADAPTED at source code level */ 99 | 100 | struct cblock *pair; /* matched cblock in another file */ 101 | struct rb_node rbnm, rb_hnm, rbs; 102 | }; 103 | 104 | void get_token(char **str, kpstr_t *x); 105 | void __get_token(char **str, kpstr_t *x, const char *delim); 106 | 107 | int is_function_start(struct kp_file *f, int l, kpstr_t *nm); 108 | int is_function_end(struct kp_file *f, int l, kpstr_t *nm); 109 | 110 | int is_variable_start(struct kp_file *f, int l, int *e, int *globl, kpstr_t *nm); 111 | int is_data_def(char *s, int type); 112 | 113 | struct cblock *cblock_find_by_name(struct kp_file *f, kpstr_t *nm); 114 | struct cblock *cblock_find_by_human_name(struct kp_file *f, kpstr_t *nm); 115 | void cblocks_init(struct kp_file *f); 116 | void cblock_print2(struct cblock *b0, struct cblock *b1); 117 | struct cblock *cblock_first(struct kp_file *f); 118 | struct cblock *cblock_next(struct cblock *blk); 119 | struct cblock *cblock_skip(struct cblock *blk, int type); 120 | void cblock_split(struct cblock *b, int len); 121 | 122 | #endif /* __PARSE_H__ */ 123 | -------------------------------------------------------------------------------- /src/kpatch_patch.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_PATCH__ 2 | #define __KPATCH_PATCH__ 3 | 4 | #include "kpatch_common.h" 5 | #include "kpatch_storage.h" 6 | #include "kpatch_file.h" 7 | #include "rbtree.h" 8 | 9 | enum { 10 | ACTION_APPLY_PATCH, 11 | ACTION_UNAPPLY_PATCH 12 | }; 13 | 14 | struct patch_data { 15 | kpatch_storage_t *storage; 16 | int is_just_started; 17 | int send_fd; 18 | }; 19 | 20 | struct unpatch_data { 21 | char **buildids; 22 | int nbuildids; 23 | }; 24 | 25 | int process_patch(int pid, void *_data); 26 | int process_unpatch(int pid, void *_data); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/kpatch_process.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_PROCESS__ 2 | #define __KPATCH_PROCESS__ 3 | 4 | #include 5 | 6 | #include 7 | #include "kpatch_common.h" 8 | #include "kpatch_coro.h" 9 | #include "kpatch_file.h" 10 | #include "list.h" 11 | 12 | struct kpatch_process; 13 | typedef struct kpatch_process kpatch_process_t; 14 | 15 | struct vm_area { 16 | unsigned long start; 17 | unsigned long end; 18 | unsigned long offset; 19 | unsigned int prot; 20 | }; 21 | 22 | struct vm_hole { 23 | unsigned long start; 24 | unsigned long end; 25 | struct list_head list; 26 | }; 27 | 28 | struct obj_vm_area { 29 | struct vm_area inmem; 30 | struct vm_area inelf; 31 | struct vm_area ondisk; 32 | struct list_head list; 33 | }; 34 | 35 | struct object_file { 36 | struct list_head list; 37 | kpatch_process_t *proc; 38 | 39 | /** 40 | * This is a pointer to storage's kpfile, readonly. 41 | */ 42 | const struct kp_file *skpfile; 43 | 44 | /** 45 | * This is filled with kpatch information if is_patch = 1 46 | * and used as a storage for copy of a patch from storage. 47 | */ 48 | struct kp_file kpfile; 49 | 50 | /* Pointer to jump table for DSO relocations */ 51 | struct kpatch_jmp_table *jmp_table; 52 | 53 | /* Address of the patch in target's process address space */ 54 | unsigned long kpta; 55 | 56 | /* Device the object resides on */ 57 | dev_t dev; 58 | ino_t inode; 59 | 60 | /* Object name (as seen in /proc//maps) */ 61 | char *name; 62 | 63 | /* List of object's VM areas */ 64 | struct list_head vma; 65 | 66 | /* Object's Build-ID */ 67 | char buildid[41]; 68 | 69 | /* Patch information */ 70 | struct kpatch_info *info; 71 | size_t ninfo; 72 | 73 | /* Address of the first allocated virtual memory area */ 74 | unsigned long vma_start; 75 | 76 | /* 77 | * Load offset. Add this values to symbol addresses to get 78 | * correct addresses in the loaded binary. Zero for EXEC, 79 | * equals to `vma_start` for DYN (libs and PIEs) 80 | */ 81 | unsigned long load_offset; 82 | 83 | /* ELF header for the object file */ 84 | Elf64_Ehdr ehdr; 85 | 86 | /* Program header */ 87 | Elf64_Phdr *phdr; 88 | 89 | /* Dynamic symbols exported by the object if it is a library */ 90 | Elf64_Sym *dynsyms; 91 | size_t ndynsyms; 92 | 93 | char **dynsymnames; 94 | 95 | /* Pointer to the previous hole in the patient's mapping */ 96 | struct vm_hole *previous_hole; 97 | 98 | /* Pointer to the applied patch, if any */ 99 | struct object_file *applied_patch; 100 | 101 | /* Do we have patch for the object? */ 102 | unsigned int has_patch:1; 103 | 104 | /* Is that a patch for some object? */ 105 | unsigned int is_patch:1; 106 | 107 | /* Is it a shared library? */ 108 | unsigned int is_shared_lib:1; 109 | 110 | /* Is it an ELF or a mmap'ed regular file? */ 111 | unsigned int is_elf:1; 112 | }; 113 | 114 | struct kpatch_process { 115 | /* Pid of target process */ 116 | int pid; 117 | 118 | /* memory fd of /proc//mem */ 119 | int memfd; 120 | 121 | /* /proc//maps FD, also works as lock */ 122 | int fdmaps; 123 | 124 | /* Process name */ 125 | char comm[16]; 126 | 127 | /* List of process objects */ 128 | struct list_head objs; 129 | int num_objs; 130 | 131 | /* List ptrace contexts (one per each thread) */ 132 | struct { 133 | struct list_head pctxs; 134 | unw_addr_space_t unwd; 135 | } ptrace; 136 | 137 | /* List of coroutines + ops to manipulate */ 138 | struct { 139 | struct list_head coros; 140 | unw_addr_space_t unwd; 141 | } coro; 142 | 143 | /* List of free VMA areas */ 144 | struct list_head vmaholes; 145 | 146 | /* libc's base address to use as a worksheet */ 147 | unsigned long libc_base; 148 | 149 | /* 150 | * Is client have been stopped right before the `execve` 151 | * and awaiting our response via this fd? 152 | */ 153 | int send_fd; 154 | 155 | /* Just started process? */ 156 | unsigned int is_just_started:1; 157 | 158 | /* Is it an ld-linux trampoline? */ 159 | unsigned int is_ld_linux:1; 160 | }; 161 | 162 | void 163 | kpatch_object_dump(struct object_file *o); 164 | 165 | int 166 | kpatch_object_allocate_patch(struct object_file *obj, 167 | size_t sz); 168 | 169 | int 170 | kpatch_process_associate_patches(kpatch_process_t *proc); 171 | int 172 | kpatch_process_parse_proc_maps(kpatch_process_t *proc); 173 | int 174 | kpatch_process_map_object_files(kpatch_process_t *proc); 175 | int 176 | kpatch_process_attach(kpatch_process_t *proc); 177 | 178 | enum { 179 | MEM_READ, 180 | MEM_WRITE, 181 | }; 182 | int 183 | kpatch_process_mem_open(kpatch_process_t *proc, int mode); 184 | int 185 | kpatch_process_load_libraries(kpatch_process_t *proc); 186 | int 187 | kpatch_process_kick_send_fd(kpatch_process_t *proc); 188 | 189 | void 190 | kpatch_process_print_short(kpatch_process_t *proc); 191 | 192 | int 193 | kpatch_process_init(kpatch_process_t *proc, 194 | int pid, 195 | int is_just_started, 196 | int send_fd); 197 | void 198 | kpatch_process_free(kpatch_process_t *proc); 199 | 200 | 201 | struct object_file * 202 | kpatch_process_get_obj_by_regex(kpatch_process_t *proc, const char *regex); 203 | 204 | static inline int 205 | is_kernel_object_name(char *name) 206 | { 207 | if ((name[0] == '[') && (name[strlen(name) - 1] == ']')) 208 | return 1; 209 | if (strncmp(name, "anon_inode", 10) == 0) 210 | return 1; 211 | return 0; 212 | } 213 | 214 | #endif /* ifndef __KPATCH_PROCESS__ */ 215 | -------------------------------------------------------------------------------- /src/kpatch_ptrace.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_PTRACE_H__ 2 | #define __KPATCH_PTRACE_H__ 3 | 4 | #include 5 | 6 | #include "list.h" 7 | 8 | struct kpatch_ptrace_ctx { 9 | int pid; 10 | int running; 11 | unsigned long execute_until; 12 | kpatch_process_t *proc; 13 | struct list_head list; 14 | }; 15 | 16 | struct process_mem_iter { 17 | kpatch_process_t *proc; 18 | unsigned long base; 19 | size_t buflen; 20 | size_t buffer_size; 21 | char buffer[]; 22 | }; 23 | 24 | struct process_mem_iter * 25 | kpatch_process_mem_iter_init(kpatch_process_t *proc); 26 | void kpatch_process_mem_iter_free(struct process_mem_iter *iter); 27 | int kpatch_process_mem_iter_peek_ulong(struct process_mem_iter *iter, 28 | unsigned long *dst, 29 | unsigned long remote_addr); 30 | int kpatch_process_mem_iter_peek(struct process_mem_iter *iter, 31 | void *dst, size_t size, 32 | unsigned long remote_addr); 33 | 34 | #define REMOTE_PEEK(iter, dst, remote_addr) \ 35 | kpatch_process_mem_iter_peek((iter), &(dst), sizeof(dst), \ 36 | (unsigned long)(remote_addr)) 37 | 38 | #define PEEK_ULONG(p) ({ \ 39 | unsigned long l; \ 40 | if (kpatch_process_mem_iter_peek_ulong(iter, &l, \ 41 | (unsigned long)(p)) < 0) {\ 42 | kpdebug("FAIL. Failed to peek at 0x%lx - %s\n", \ 43 | (unsigned long)(p), strerror(errno)); \ 44 | return -1; \ 45 | } \ 46 | l; \ 47 | }) 48 | 49 | 50 | void kpatch_ptrace_ctx_destroy(struct kpatch_ptrace_ctx *pctx); 51 | 52 | int kpatch_ptrace_attach_thread(kpatch_process_t *proc, int tid); 53 | int kpatch_ptrace_detach(struct kpatch_ptrace_ctx *pctx); 54 | 55 | int kpatch_ptrace_handle_ld_linux(kpatch_process_t *proc, 56 | unsigned long *pentry_point); 57 | 58 | int kpatch_ptrace_kickstart_execve_wrapper(kpatch_process_t *proc); 59 | int kpatch_ptrace_get_entry_point(struct kpatch_ptrace_ctx *pctx, 60 | unsigned long *pentry_point); 61 | 62 | #define EXECUTE_ALL_THREADS (1 << 0) /* execute all threads not just these 63 | having non-zero execute_until */ 64 | int kpatch_ptrace_execute_until(kpatch_process_t *proc, 65 | int timeout_msec, 66 | int flags); 67 | 68 | int kpatch_execute_remote(struct kpatch_ptrace_ctx *pctx, 69 | const unsigned char *code, 70 | size_t codelen, 71 | struct user_regs_struct *pregs); 72 | 73 | int kpatch_ptrace_resolve_ifunc(struct kpatch_ptrace_ctx *pctx, 74 | unsigned long *addr); 75 | unsigned long 76 | kpatch_mmap_remote(struct kpatch_ptrace_ctx *pctx, 77 | unsigned long addr, 78 | size_t length, 79 | int prot, 80 | int flags, 81 | int fd, 82 | off_t offset); 83 | int 84 | kpatch_munmap_remote(struct kpatch_ptrace_ctx *pctx, 85 | unsigned long addr, 86 | size_t length); 87 | int kpatch_arch_prctl_remote(struct kpatch_ptrace_ctx *pctx, int code, unsigned long *addr); 88 | 89 | int 90 | kpatch_process_mem_read(kpatch_process_t *proc, 91 | unsigned long src, 92 | void *dst, 93 | size_t size); 94 | int 95 | kpatch_process_mem_write(kpatch_process_t *proc, 96 | void *src, 97 | unsigned long dst, 98 | size_t size); 99 | 100 | int 101 | kpatch_process_memcpy(kpatch_process_t *proc, 102 | unsigned long dst, 103 | unsigned long src, 104 | size_t size); 105 | #endif 106 | -------------------------------------------------------------------------------- /src/kpatch_storage.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_STORAGE__ 2 | #define __KPATCH_STORAGE__ 3 | 4 | #include "kpatch_common.h" 5 | #include "kpatch_file.h" 6 | #include "kpatch_process.h" 7 | #include "rbtree.h" 8 | 9 | struct kpatch_storage_patch { 10 | /* Pointer to the object's patch (if any) */ 11 | struct kp_file kpfile; 12 | 13 | /* Build id kept here for negative caching */ 14 | char buildid[41]; 15 | 16 | /* Patch level */ 17 | int patchlevel; 18 | 19 | /* Description cache */ 20 | char *desc; 21 | 22 | /* Node for rb_root */ 23 | struct rb_node node; 24 | }; 25 | 26 | struct kpatch_storage { 27 | /* Patch storage path */ 28 | char *path; 29 | 30 | /* Patch file (or directory) descriptor */ 31 | int patch_fd; 32 | 33 | /* Is patch_fd a directory or a file? */ 34 | char is_patch_dir; 35 | 36 | union { 37 | /* Tree with BuildID keyed `kp_file's, 38 | * is_patch_dir = 1 */ 39 | struct rb_root tree; 40 | 41 | /* A single file, is_patch_dir = 0 */ 42 | struct kpatch_storage_patch patch; 43 | }; 44 | }; 45 | 46 | typedef struct kpatch_storage kpatch_storage_t; 47 | 48 | int storage_init(kpatch_storage_t *storage, 49 | const char *fname); 50 | void storage_free(kpatch_storage_t *storage); 51 | 52 | enum { 53 | PATCH_OPEN_ERROR = -1, 54 | PATCH_NOT_FOUND = 0, 55 | PATCH_FOUND = 1, 56 | }; 57 | int storage_lookup_patches(kpatch_storage_t *storage, kpatch_process_t *proc); 58 | int storage_have_patch(kpatch_storage_t *storage, const char *buildid, 59 | struct kpatch_storage_patch **ppatch); 60 | int storage_patch_found(struct kpatch_storage_patch *patch); 61 | int storage_execute_before_script(kpatch_storage_t *storage, kpatch_process_t *proc); 62 | int storage_execute_after_script(kpatch_storage_t *storage, kpatch_process_t *proc); 63 | char *storage_get_description(kpatch_storage_t *storage, 64 | struct kpatch_storage_patch *patch); 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/kpatch_str.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPSTR_H__ 2 | #define __KPSTR_H__ 3 | 4 | #include 5 | #include 6 | 7 | #include "kpatch_log.h" 8 | 9 | /* --------------------------------------- kp strings -------------------------------------- */ 10 | 11 | typedef struct { 12 | char *s; 13 | int l; 14 | } kpstr_t; 15 | 16 | static inline void kpstrset(kpstr_t *x, char *s, int len) 17 | { 18 | x->s = s; 19 | x->l = len; 20 | } 21 | 22 | static inline void kpstrskip(kpstr_t *x, int len) 23 | { 24 | if (x->l < len) 25 | kpfatal("kpstrskip: skipping too much"); 26 | x->s += len; 27 | x->l -= len; 28 | } 29 | 30 | static inline int kpstrncmp(kpstr_t *s1, kpstr_t *s2, int maxlen) 31 | { 32 | int len = (s1->l < s2->l) ? s1->l : s2->l; 33 | int res; 34 | 35 | len = (len < maxlen) ? len : maxlen; 36 | res = memcmp(s1->s, s2->s, len); 37 | if (res) 38 | return res; 39 | 40 | if (s1->l > s2->l && s2->l < maxlen) 41 | return 1; 42 | else if (s1->l < s2->l && s1->l < maxlen) 43 | return -1; 44 | else 45 | return 0; 46 | } 47 | 48 | static inline int kpstrcmp(kpstr_t *s1, kpstr_t *s2) 49 | { 50 | return kpstrncmp(s1, s2, (s1->l > s2->l) ? s1->l : s2->l); 51 | } 52 | 53 | /* compares kpstr and asciiz for exact match */ 54 | static inline int kpstrcmpz(kpstr_t *s1, char *s) 55 | { 56 | kpstr_t s2; 57 | kpstrset(&s2, s, strlen(s)); 58 | return kpstrcmp(s1, &s2); 59 | } 60 | 61 | /* compares that kpstr *starts* with asciiz string s */ 62 | static inline int kpstrncmpz(kpstr_t *s1, char *s) 63 | { 64 | kpstr_t s2; 65 | kpstrset(&s2, s, strlen(s)); 66 | return kpstrncmp(s1, &s2, s2.l); 67 | } 68 | 69 | /* ----------------------------------- helpers ------------------------------------ */ 70 | 71 | static inline char *skip_blanks(char *s) 72 | { 73 | while (isblank(*s)) 74 | s++; 75 | return s; 76 | } 77 | 78 | static inline void trim_crlf(char *s) 79 | { 80 | char *se; 81 | 82 | /* remove trailing \n */ 83 | se = s + strlen(s) - 1; 84 | while (se >= s) { 85 | if (*se != '\n' && *se != '\r') 86 | break; 87 | se--; 88 | } 89 | *(se + 1) = 0; 90 | } 91 | 92 | #endif /* __KPSTR_H__ */ 93 | -------------------------------------------------------------------------------- /src/kpatch_user.h: -------------------------------------------------------------------------------- 1 | #ifndef __KPATCH_USER__ 2 | #define __KPATCH_USER__ 3 | 4 | #include "kpatch_common.h" 5 | #include "kpatch_file.h" 6 | #include "rbtree.h" 7 | 8 | int cmd_patch_user(int argc, char *argv[]); 9 | int cmd_unpatch_user(int argc, char *argv[]); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/libcare-client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define handle_error(errstr) do { perror(errstr); exit(EXIT_FAILURE); } while (0) 9 | 10 | #define DEFAULT_SOCKET "/var/run/libcare.sock" 11 | 12 | int main(int argc, char **argv) 13 | { 14 | int sock, rv, buflen, i; 15 | struct sockaddr_un sockaddr; 16 | char *buffer = NULL, *p, *sockpath = DEFAULT_SOCKET; 17 | 18 | if (argc < 2 || (argv[1][0] == '/' && argc < 3)) { 19 | printf("%s: [/SOCKET] ARG0 [ARG1] [ARG2]\n", argv[0]); 20 | exit(EXIT_FAILURE); 21 | } 22 | 23 | argv++; 24 | argc--; 25 | 26 | sock = socket(AF_UNIX, SOCK_STREAM, 0); 27 | if (sock == -1) 28 | handle_error("socket(AF_UNIX)"); 29 | 30 | sockaddr.sun_family = AF_UNIX; 31 | 32 | if (argv[0][0] == '/') { 33 | sockpath = argv[0]; 34 | argv++; 35 | argc--; 36 | } 37 | strncpy(sockaddr.sun_path, sockpath, sizeof(sockaddr.sun_path)); 38 | 39 | rv = connect(sock, (const struct sockaddr *)&sockaddr, sizeof(sockaddr)); 40 | if (rv == -1) 41 | handle_error("connect"); 42 | 43 | buflen = 0; 44 | for (i = 0; i < argc; i++) { 45 | buflen += strlen(argv[i]) + 1; 46 | } 47 | buflen++; 48 | 49 | p = buffer = malloc(buflen); 50 | if (buffer == NULL) 51 | handle_error("malloc"); 52 | for (i = 0; i < argc; i++) { 53 | p = stpcpy(p, argv[i]); 54 | p++; 55 | } 56 | *p++ = '\0'; 57 | 58 | rv = send(sock, (void *)buffer, buflen, 0); 59 | if (rv == -1) 60 | handle_error("send"); 61 | 62 | if (buflen < 4096) { 63 | free(buffer); 64 | buflen = 4096; 65 | buffer = malloc(buflen); 66 | } 67 | 68 | while (1) { 69 | rv = recv(sock, buffer, buflen, 0); 70 | if (rv == 0) 71 | break; 72 | if (rv < 0) 73 | handle_error("recv"); 74 | write(1, buffer, rv); 75 | } 76 | 77 | close(sock); 78 | free(buffer); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /src/libcare-patch-make: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | usage() { 4 | cat<<'EOF' 5 | Makes `kpatch'es for the makesystem in the current directory. 6 | 7 | Usage: libcare-patch-make [-h|--help] [-u|--update || -c|--clean] 8 | [-s|--srcdir=SRCDIR] \ 9 | [-d|--destdir=DESTDIRVAR] \ 10 | PATCH1 PATCH2 ... 11 | 12 | Run from inside the directory with `make'ble software. Makesystem must support 13 | install with specified DESTDIR. 14 | 15 | -c --clean do a clean build, execute `make clean` first 16 | -u --update only update existing patches without rebuild. useful when 17 | working on patch utils. 18 | -d --destdir specify variable makefile system uses to specify destination 19 | directory for the installation 20 | EOF 21 | exit ${1-0} 22 | } 23 | 24 | 25 | prepare_env() { 26 | KPATCH_PATH=$(dirname $0) 27 | 28 | if test ! -x "$KPATCH_PATH/kpatch_gensrc"; then 29 | echo "kpatch tools are missing" >&2 30 | exit 1 31 | fi 32 | 33 | export IS_LIBCARE_CC=y 34 | export CC=$KPATCH_PATH/libcare-cc 35 | export CXX=$CC 36 | 37 | MAKE_OUTPUT=/dev/stdout 38 | 39 | LPMAKEFILE="" 40 | test -f lpmakefile && LPMAKEFILE="-f lpmakefile" 41 | 42 | LPMAKE_ORIGINAL_DIR="${LPMAKE_ORIGINAL_DIR-$PWD/lpmake}" 43 | LPMAKE_PATCHED_DIR="${LPMAKE_PATCHED_DIR-$PWD/.lpmaketmp/patched}" 44 | LPMAKE_PATCHROOT="${LPMAKE_PATCHROOT-$PWD/patchroot}" 45 | 46 | export LPMAKE_ORIGINAL_DIR LPMAKE_PATCHED_DIR LPMAKE_PATCHROOT 47 | mkdir -p "$LPMAKE_ORIGINAL_DIR" "$LPMAKE_PATCHED_DIR" "$LPMAKE_PATCHROOT" 48 | 49 | unset MAKELEVEL 50 | unset MAKEFLAGS 51 | 52 | red=$(tput setaf 1) 53 | green=$(tput setaf 2) 54 | reset=$(tput sgr0) 55 | } 56 | 57 | restore_origs() { 58 | find $srcdir -regex '.+\.[0-9]+\.lpmakeorig' | awk ' 59 | { 60 | origfname = $0; 61 | gsub("\.[0-9]+\.lpmakeorig$", ""); 62 | fname = $0; 63 | if (!vers[fname] || vers[fname] > origfname) 64 | { vers[fname] = origfname; } 65 | } 66 | END { for (f in vers) system("mv " vers[f] " " f); } 67 | ' 68 | } 69 | 70 | trap "restore_origs" 0 71 | 72 | build_objects() { 73 | restore_origs 74 | 75 | if test -n "$do_clean"; then 76 | make $LPMAKEFILE clean >$MAKE_OUTPUT 2>&1 77 | rm -rf "$LPMAKE_ORIGINAL_DIR" "$LPMAKE_PATCHED_DIR" 78 | fi 79 | 80 | export KPATCH_STAGE=original 81 | export KPCC_DBGFILTER_ARGS="" 82 | 83 | echo "${green}BUILDING ORIGINAL CODE${reset}" 84 | make $LPMAKEFILE >$MAKE_OUTPUT 2>&1 85 | 86 | echo "${green}INSTALLING ORIGINAL OBJECTS INTO $LPMAKE_ORIGINAL_DIR${reset}" 87 | make $LPMAKEFILE install \ 88 | "$destdir=$LPMAKE_ORIGINAL_DIR" \ 89 | >$MAKE_OUTPUT 2>&1 90 | 91 | local oldpwd="$(pwd)" 92 | if test -n "$srcdir"; then 93 | cd "$srcdir" 94 | fi 95 | 96 | i=0 97 | for patch; do 98 | echo "${red}applying $patch...${reset}" 99 | patch -b -z .${i}.lpmakeorig -p1 < $patch 100 | done 101 | 102 | if test -n "$srcdir"; then 103 | cd "$oldpwd" 104 | fi 105 | 106 | export KPATCH_STAGE=patched 107 | export KPCC_APPEND_ARGS="-Wl,-q" 108 | 109 | echo "${green}BUILDING PATCHED CODE${reset}" 110 | make $LPMAKEFILE >$MAKE_OUTPUT 2>&1 111 | 112 | echo "${green}INSTALLING PATCHED OBJECTS INTO $LPMAKE_PATCHED_DIR${reset}" 113 | make $LPMAKEFILE install \ 114 | "$destdir=$LPMAKE_PATCHED_DIR" \ 115 | >$MAKE_OUTPUT 2>&1 116 | } 117 | 118 | build_kpatches() { 119 | mkdir -p "${LPMAKE_PATCHROOT}" 120 | 121 | echo "${green}MAKING PATCHES${reset}" 122 | 123 | for execfile in $(find "$LPMAKE_ORIGINAL_DIR" -perm /0111 -type f); do 124 | origexec="$execfile" 125 | filename="${origexec##*$LPMAKE_ORIGINAL_DIR/}" 126 | patchedexec="$LPMAKE_PATCHED_DIR/$filename" 127 | 128 | buildid=$(eu-readelf -n "$origexec" | sed -n '/Build ID:/ { s/.* //; p }') 129 | if ! eu-readelf -S "$patchedexec" | grep -q '.kpatch'; then 130 | continue 131 | fi 132 | 133 | test -n "$buildid" || continue 134 | 135 | chmod u+w "${origexec}" "${patchedexec}" 136 | $KPATCH_PATH/kpatch_strip --strip "${patchedexec}" \ 137 | "${patchedexec}.stripped" >/dev/null 138 | $KPATCH_PATH/kpatch_strip --rel-fixup "$origexec" \ 139 | "${patchedexec}.stripped" || continue 140 | /usr/bin/strip --strip-unneeded "${patchedexec}.stripped" 141 | $KPATCH_PATH/kpatch_strip --undo-link "$origexec" "${patchedexec}.stripped" 142 | $KPATCH_PATH/kpatch_make -b "$buildid" \ 143 | "${patchedexec}.stripped" -o "${patchedexec}.kpatch" 144 | cp "${patchedexec}.kpatch" "${LPMAKE_PATCHROOT}"/${buildid}.kpatch 145 | echo "patch for ${origexec} is in ${LPMAKE_PATCHROOT}/${buildid}.kpatch" 146 | done 147 | } 148 | 149 | main() { 150 | PROG_NAME=$(basename $0) 151 | 152 | TEMP=$(getopt -o s:ucd --long srcdir:,update,clean,destdir: -n ${PROG_NAME} -- "$@" || usage 1) 153 | eval set -- "$TEMP" 154 | 155 | destdir="DESTDIR" 156 | while true; do 157 | case $1 in 158 | -s|--srcdir) 159 | shift 160 | srcdir="$1" 161 | shift 162 | ;; 163 | -u|--update) 164 | shift 165 | only_update=1 166 | ;; 167 | -c|--clean) 168 | shift 169 | do_clean=1 170 | ;; 171 | -d|--destdir) 172 | shift 173 | destdir=$1 174 | shift 175 | ;; 176 | --) 177 | shift; break; 178 | ;; 179 | esac 180 | done 181 | 182 | prepare_env 183 | 184 | if test -z "$only_update"; then 185 | build_objects "$@" 186 | fi 187 | build_kpatches 188 | } 189 | 190 | main "$@" 191 | -------------------------------------------------------------------------------- /src/rbtree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Red Black Trees 3 | (C) 1999 Andrea Arcangeli 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 | 19 | linux/include/linux/rbtree.h 20 | 21 | To use rbtrees you'll have to implement your own insert and search cores. 22 | This will avoid us to use callbacks and to drop drammatically performances. 23 | I know it's not the cleaner way, but in C (not in C++) to get 24 | performances and genericity... 25 | 26 | See Documentation/rbtree.txt for documentation and samples. 27 | */ 28 | 29 | #ifndef _LINUX_RBTREE_H 30 | #define _LINUX_RBTREE_H 31 | 32 | #include 33 | 34 | #include "util.h" 35 | 36 | #define WRITE_ONCE(a, b) (a) = (b) 37 | 38 | struct rb_node { 39 | unsigned long __rb_parent_color; 40 | struct rb_node *rb_right; 41 | struct rb_node *rb_left; 42 | } __attribute__((aligned(sizeof(long)))); 43 | /* The alignment might seem pointless, but allegedly CRIS needs it */ 44 | 45 | struct rb_root { 46 | struct rb_node *rb_node; 47 | }; 48 | 49 | 50 | #define rb_parent(r) ((struct rb_node *)((r)->__rb_parent_color & ~3)) 51 | 52 | #define RB_ROOT (struct rb_root) { NULL, } 53 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) 54 | 55 | #define RB_EMPTY_ROOT(root) (READ_ONCE((root)->rb_node) == NULL) 56 | 57 | /* 'empty' nodes are nodes that are known not to be inserted in an rbtree */ 58 | #define RB_EMPTY_NODE(node) \ 59 | ((node)->__rb_parent_color == (unsigned long)(node)) 60 | #define RB_CLEAR_NODE(node) \ 61 | ((node)->__rb_parent_color = (unsigned long)(node)) 62 | 63 | 64 | extern void rb_insert_color(struct rb_node *, struct rb_root *); 65 | extern void rb_erase(struct rb_node *, struct rb_root *); 66 | 67 | 68 | /* Find logical next and previous nodes in a tree */ 69 | extern struct rb_node *rb_next(const struct rb_node *); 70 | extern struct rb_node *rb_prev(const struct rb_node *); 71 | extern struct rb_node *rb_first(const struct rb_root *); 72 | extern struct rb_node *rb_last(const struct rb_root *); 73 | 74 | /* Postorder iteration - always visit the parent after its children */ 75 | extern struct rb_node *rb_first_postorder(const struct rb_root *); 76 | extern struct rb_node *rb_next_postorder(const struct rb_node *); 77 | 78 | /* Fast replacement of a single node without remove/rebalance/add/rebalance */ 79 | extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 80 | struct rb_root *root); 81 | extern void rb_replace_node_rcu(struct rb_node *victim, struct rb_node *new, 82 | struct rb_root *root); 83 | 84 | static inline void rb_link_node(struct rb_node *node, struct rb_node *parent, 85 | struct rb_node **rb_link) 86 | { 87 | node->__rb_parent_color = (unsigned long)parent; 88 | node->rb_left = node->rb_right = NULL; 89 | 90 | *rb_link = node; 91 | } 92 | 93 | #define rb_entry_safe(ptr, type, member) \ 94 | ({ typeof(ptr) ____ptr = (ptr); \ 95 | ____ptr ? rb_entry(____ptr, type, member) : NULL; \ 96 | }) 97 | 98 | /** 99 | * rbtree_postorder_for_each_entry_safe - iterate in post-order over rb_root of 100 | * given type allowing the backing memory of @pos to be invalidated 101 | * 102 | * @pos: the 'type *' to use as a loop cursor. 103 | * @n: another 'type *' to use as temporary storage 104 | * @root: 'rb_root *' of the rbtree. 105 | * @field: the name of the rb_node field within 'type'. 106 | * 107 | * rbtree_postorder_for_each_entry_safe() provides a similar guarantee as 108 | * list_for_each_entry_safe() and allows the iteration to continue independent 109 | * of changes to @pos by the body of the loop. 110 | * 111 | * Note, however, that it cannot handle other modifications that re-order the 112 | * rbtree it is iterating over. This includes calling rb_erase() on @pos, as 113 | * rb_erase() may rebalance the tree, causing us to miss some nodes. 114 | */ 115 | #define rbtree_postorder_for_each_entry_safe(pos, n, root, field) \ 116 | for (pos = rb_entry_safe(rb_first_postorder(root), typeof(*pos), field); \ 117 | pos && ({ n = rb_entry_safe(rb_next_postorder(&pos->field), \ 118 | typeof(*pos), field); 1; }); \ 119 | pos = n) 120 | 121 | /* include/linux/rbtree_augmented.h */ 122 | 123 | #define RB_RED 0 124 | #define RB_BLACK 1 125 | 126 | #define __rb_parent(pc) ((struct rb_node *)(pc & ~3)) 127 | 128 | #define __rb_color(pc) ((pc) & 1) 129 | #define __rb_is_black(pc) __rb_color(pc) 130 | #define __rb_is_red(pc) (!__rb_color(pc)) 131 | #define rb_color(rb) __rb_color((rb)->__rb_parent_color) 132 | #define rb_is_red(rb) __rb_is_red((rb)->__rb_parent_color) 133 | #define rb_is_black(rb) __rb_is_black((rb)->__rb_parent_color) 134 | 135 | static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) 136 | { 137 | rb->__rb_parent_color = rb_color(rb) | (unsigned long)p; 138 | } 139 | 140 | static inline void rb_set_parent_color(struct rb_node *rb, 141 | struct rb_node *p, int color) 142 | { 143 | rb->__rb_parent_color = (unsigned long)p | color; 144 | } 145 | 146 | static inline void 147 | __rb_change_child(struct rb_node *old, struct rb_node *new, 148 | struct rb_node *parent, struct rb_root *root) 149 | { 150 | if (parent) { 151 | if (parent->rb_left == old) 152 | WRITE_ONCE(parent->rb_left, new); 153 | else 154 | WRITE_ONCE(parent->rb_right, new); 155 | } else 156 | WRITE_ONCE(root->rb_node, new); 157 | } 158 | 159 | extern void __rb_erase_color(struct rb_node *parent, struct rb_root *root); 160 | 161 | static __always_inline struct rb_node * 162 | __rb_erase(struct rb_node *node, struct rb_root *root) 163 | { 164 | struct rb_node *child = node->rb_right; 165 | struct rb_node *tmp = node->rb_left; 166 | struct rb_node *parent, *rebalance; 167 | unsigned long pc; 168 | 169 | if (!tmp) { 170 | /* 171 | * Case 1: node to erase has no more than 1 child (easy!) 172 | * 173 | * Note that if there is one child it must be red due to 5) 174 | * and node must be black due to 4). We adjust colors locally 175 | * so as to bypass __rb_erase_color() later on. 176 | */ 177 | pc = node->__rb_parent_color; 178 | parent = __rb_parent(pc); 179 | __rb_change_child(node, child, parent, root); 180 | if (child) { 181 | child->__rb_parent_color = pc; 182 | rebalance = NULL; 183 | } else 184 | rebalance = __rb_is_black(pc) ? parent : NULL; 185 | tmp = parent; 186 | } else if (!child) { 187 | /* Still case 1, but this time the child is node->rb_left */ 188 | tmp->__rb_parent_color = pc = node->__rb_parent_color; 189 | parent = __rb_parent(pc); 190 | __rb_change_child(node, tmp, parent, root); 191 | rebalance = NULL; 192 | tmp = parent; 193 | } else { 194 | struct rb_node *successor = child, *child2; 195 | 196 | tmp = child->rb_left; 197 | if (!tmp) { 198 | /* 199 | * Case 2: node's successor is its right child 200 | * 201 | * (n) (s) 202 | * / \ / \ 203 | * (x) (s) -> (x) (c) 204 | * \ 205 | * (c) 206 | */ 207 | parent = successor; 208 | child2 = successor->rb_right; 209 | } else { 210 | /* 211 | * Case 3: node's successor is leftmost under 212 | * node's right child subtree 213 | * 214 | * (n) (s) 215 | * / \ / \ 216 | * (x) (y) -> (x) (y) 217 | * / / 218 | * (p) (p) 219 | * / / 220 | * (s) (c) 221 | * \ 222 | * (c) 223 | */ 224 | do { 225 | parent = successor; 226 | successor = tmp; 227 | tmp = tmp->rb_left; 228 | } while (tmp); 229 | child2 = successor->rb_right; 230 | WRITE_ONCE(parent->rb_left, child2); 231 | WRITE_ONCE(successor->rb_right, child); 232 | rb_set_parent(child, successor); 233 | } 234 | 235 | tmp = node->rb_left; 236 | WRITE_ONCE(successor->rb_left, tmp); 237 | rb_set_parent(tmp, successor); 238 | 239 | pc = node->__rb_parent_color; 240 | tmp = __rb_parent(pc); 241 | __rb_change_child(node, successor, tmp, root); 242 | 243 | if (child2) { 244 | successor->__rb_parent_color = pc; 245 | rb_set_parent_color(child2, parent, RB_BLACK); 246 | rebalance = NULL; 247 | } else { 248 | unsigned long pc2 = successor->__rb_parent_color; 249 | successor->__rb_parent_color = pc; 250 | rebalance = __rb_is_black(pc2) ? parent : NULL; 251 | } 252 | tmp = successor; 253 | } 254 | 255 | return rebalance; 256 | } 257 | 258 | 259 | /* LibCare API */ 260 | 261 | /* comparator function for node element: should return {<0, 0, >0} if 262 | * key { key, ==node->key, >node->key } 263 | * the tree will be ordered in increasing order 264 | */ 265 | typedef int (*rb_cmp_fn_t)(struct rb_node *node, unsigned long key); 266 | 267 | static inline void rb_init(struct rb_root *root) 268 | { 269 | root->rb_node = NULL; 270 | } 271 | 272 | #define rb_empty(root) ((root)->rb_node == NULL) 273 | 274 | 275 | static inline 276 | struct rb_node *rb_search_node(struct rb_root *root, 277 | rb_cmp_fn_t cmp, 278 | unsigned long key) 279 | { 280 | struct rb_node *node = root->rb_node; 281 | 282 | while (node) { 283 | int result; 284 | 285 | result = cmp(node, key); 286 | 287 | if (result < 0) 288 | node = node->rb_left; 289 | else if (result > 0) 290 | node = node->rb_right; 291 | else 292 | return node; 293 | } 294 | return NULL; 295 | } 296 | 297 | /* if node with given key exists, returns it. Otherwise inserts new node */ 298 | static inline 299 | struct rb_node *rb_insert_node(struct rb_root *root, 300 | struct rb_node *new_node, 301 | rb_cmp_fn_t cmp_fn, 302 | unsigned long key) 303 | { 304 | int cmp_res; 305 | struct rb_node **node = &root->rb_node; 306 | struct rb_node *parent = NULL; 307 | 308 | while (*node) { 309 | parent = *node; 310 | cmp_res = cmp_fn(*node, key); 311 | if (cmp_res < 0) 312 | node = &(*node)->rb_left; 313 | else if (cmp_res > 0) 314 | node = &(*node)->rb_right; 315 | else 316 | return *node; 317 | } 318 | 319 | /* insert & rebalance rb-tree */ 320 | rb_link_node(new_node, parent, node); 321 | rb_insert_color(new_node, root); 322 | 323 | return NULL; 324 | } 325 | 326 | static inline 327 | void rb_destroy(struct rb_root *root, void(*free_node_cb)(struct rb_node *)) 328 | { 329 | struct rb_node *node = rb_first(root), *right; 330 | struct rb_node *parent; 331 | 332 | while (node) { 333 | right = node->rb_right; 334 | parent = rb_parent(node); 335 | 336 | free_node_cb(node); 337 | 338 | if (right) { 339 | node = right; 340 | right->__rb_parent_color = (unsigned long)parent; 341 | /* go left as far as we can */ 342 | while (node->rb_left) 343 | node = node->rb_left; 344 | } else { 345 | while (parent && node == parent->rb_right) { 346 | node = parent; 347 | parent = rb_parent(node); 348 | } 349 | node = parent; 350 | } 351 | } 352 | } 353 | 354 | #endif /* _LINUX_RBTREE_H */ 355 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _UTIL_H 3 | #define _UTIL_H 4 | 5 | #ifndef offsetof 6 | #define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE*)0)->MEMBER) 7 | #endif 8 | 9 | #define container_of(ptr, type, member) \ 10 | ((type *)(((char *)(ptr)) - offsetof(type,member))) 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | MAKEFLAGS += --no-builtin-rules 2 | 3 | SUBDIRS := $(patsubst %/desc,%,$(wildcard */desc)) 4 | 5 | KPATCH_PATH:=$(CURDIR)/../src 6 | export KPATCH_PATH 7 | 8 | all: run 9 | 10 | list: 11 | @echo TESTS: $(SUBDIRS) 12 | 13 | fastsleep.so: CFLAGS += -fPIC 14 | fastsleep.so: fastsleep.c 15 | $(LINK.c) $^ -o $@ -shared -ldl 16 | 17 | clean: $(addprefix clean-,$(SUBDIRS)) 18 | rm -fr $(CURDIR)/build-patchroot \ 19 | $(CURDIR)/lpmake-patchroot \ 20 | $(CURDIR)/lpmakelevel-patchroot 21 | rm -f fastsleep.so 22 | 23 | clean-%: FORCE 24 | make -C $* clean 25 | 26 | build: $(addprefix build-,$(SUBDIRS)) 27 | 28 | build-%: FORCE 29 | make -C $* clean all install DESTDIR=build 30 | 31 | build-patchroot: build 32 | mkdir -p $(CURDIR)/build-patchroot 33 | find -type l -iname \*.kpatch \ 34 | -exec cp -L \{} $(CURDIR)/build-patchroot \; 35 | 36 | LPMAKE_TGTS := $(addprefix lpmake-,$(SUBDIRS)) 37 | lpmake: $(LPMAKE_TGTS) 38 | lpmakelevel: $(LPMAKE_TGTS) 39 | 40 | lpmake-%: export LPMAKE_PATCHROOT := lpmake 41 | lpmake-%: FORCE 42 | cd $*; $(CURDIR)/../src/libcare-patch-make --clean *.diff 43 | 44 | lpmake-patchroot: lpmake 45 | mkdir -p $(CURDIR)/lpmake-patchroot 46 | find -path '*/lpmake/*.kpatch' \ 47 | -exec cp \{} $(CURDIR)/lpmake-patchroot \; 48 | 49 | lpmakelevel-patchroot: lpmake 50 | mkdir -p $(CURDIR)/lpmakelevel-patchroot 51 | for f in $$(find -path '*/lpmake/*.kpatch'); do \ 52 | buildid=$${f%.kpatch}; \ 53 | buildid=$${buildid##*/}; \ 54 | dir=$(CURDIR)/lpmakelevel-patchroot/$$buildid/; \ 55 | mkdir -p $$dir/1/; \ 56 | cp $$f $$dir/1/kpatch.bin; \ 57 | ln -fs 1 $$dir/latest; \ 58 | done 59 | 60 | RUN_TESTS = ./run_tests.sh $(RUNTESTSFLAGS) 61 | 62 | run-file-%: % 63 | $(RUN_TESTS) 64 | 65 | run-unpatch: 66 | $(RUN_TESTS) -f test_unpatch_files 67 | 68 | run-dir-%: % 69 | $(RUN_TESTS) -f test_patch_dir 70 | 71 | run-startup-%: % %-patchroot 72 | $(RUN_TESTS) -f test_patch_startup 73 | 74 | run-startup-ld-linux-%: % %-patchroot 75 | $(RUN_TESTS) -f test_patch_startup_ld_linux 76 | 77 | run-patchlevel: fastsleep.so 78 | run-patchlevel: build-patchlevel 79 | $(RUN_TESTS) -f test_patch_patchlevel 80 | 81 | run-build: fastsleep.so 82 | run-build: run-file-build run-dir-build run-startup-build run-unpatch 83 | run-build: run-startup-ld-linux-build 84 | 85 | run-lpmake: RUNTESTSFLAGS := -d lpmake 86 | run-lpmake: fastsleep.so 87 | run-lpmake: run-dir-lpmake run-startup-lpmake 88 | run-lpmake: run-startup-ld-linux-lpmake 89 | 90 | run-lpmakelevel: RUNTESTSFLAGS := -d lpmake -p $(CURDIR)/lpmakelevel-patchroot 91 | run-lpmakelevel: fastsleep.so 92 | run-lpmakelevel: run-startup-lpmakelevel 93 | 94 | run: run-build run-patchlevel run-lpmake run-lpmakelevel 95 | 96 | FORCE: 97 | -------------------------------------------------------------------------------- /tests/README.rst: -------------------------------------------------------------------------------- 1 | Test infrastructure ``./tests`` 2 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 3 | 4 | .. _tests: 5 | 6 | This directory contains the tests and the infrastructure to run them. To keep 7 | the ``tests`` directory clean, each test is placed in its own directory. 8 | 9 | Each directory containing a file `desc` is considered to be a directory test 10 | and is build by the makesystem and run by the `run_tests.sh` script. 11 | 12 | To run the tests emit: 13 | 14 | :: 15 | 16 | $ make 17 | 18 | this will build and run all the tests discovered for all types of build 19 | and all flavors of the ``libcare-ctl`` usage. 20 | 21 | There are two types of test builds. 22 | 23 | The first one is the regular build done 24 | by manually emitting assembler files for both original and patched 25 | source files, and then applying ``kpatch_gensrc`` to them and compiling 26 | the result into a kpatch-containing object where from it was extracted from by the 27 | utils, as described in `Manual Patch Creation`_ section. 28 | 29 | The second one is the build done by the ``libcare-patch-make`` tool which uses ``libcare-cc`` 30 | compiler wrapper, as described in `libcare-patch-make`_ section. The build results for 31 | each build type are placed in their own subfolder ina test directory. 32 | 33 | A test can be built with the particular build type using either ``make 34 | build-$test`` or ``make libcare-patch-make-$test`` commands. 35 | 36 | Sometimes it is necessary to debug a particular test so all changes MUST 37 | retain the ability to run the tests manually. The manual run is done by 38 | executing an appropriate binary (with the ``LD_LIBRARY_PATH`` set as 39 | needed) and target ``libcare-ctl patch`` at its process. 40 | 41 | However, it is recommended to run tests by the ``./run_tests.sh`` script, 42 | available in the ``tests`` directory. 43 | 44 | The ``run_tests.sh`` script accepts the following options: 45 | 46 | -f FLAVOR 47 | execute ``FLAVOR`` of tests from those listed in `test flavors`_. 48 | 49 | 50 | -d DESTDIR 51 | assume that test binaries are located in ``DESTDIR`` subdirectory of a 52 | test. The ``build`` subdirectory is a default one. Use ``libcare-patch-make`` to run 53 | the tests build with the libcare-patch-make with binaries stored in the subdirectory 54 | with the same name. 55 | 56 | -v 57 | be verbose 58 | 59 | The only argument it accepts is a string with space separated names of 60 | tests to execute. The default is to execute all the tests discovered. 61 | 62 | Test flavors 63 | ^^^^^^^^^^^^ 64 | 65 | There are the following test flavors. Most of the tests are executed in all 66 | flavors, it depends on what ``should_skip`` function of ``run_tests.sh`` 67 | returns. Some of the tests have different success criteria between different 68 | flavors: e.g. ``fail_*`` tests check that binary is succesfully patched upon 69 | execution with ``test_patch_startup`` flavor. 70 | 71 | The flavors are: 72 | 73 | ``test_patch_files`` 74 | (default) that simply executes a test process and points ``kpatch_ctl 75 | patch`` to it, doing so for present patches for both binary and 76 | shared libraries. 77 | 78 | ``test_patch_dir`` 79 | that executes a test and patches it with a per-test patch-containing 80 | directory fed to ``kpatch_ctl patch``. 81 | 82 | ``test_patch_startup`` 83 | that starts a ``kcare_genl_sink`` helper that listens to notifications 84 | about a start of a listed binary and executes ``kpatch_ctl patch`` 85 | with the directory containing patches for all the tests discovered. 86 | 87 | ``test_patch_patchlevel`` 88 | that checks that patchlevel_ code works as expected. This applies two 89 | patches with different patch levels to the ``patchlevel`` test and checks 90 | that the patching is done to the latest one. 91 | 92 | Adding or fixing a test 93 | ^^^^^^^^^^^^^^^^^^^^^^^ 94 | 95 | Each test has its own directory that MUST have the file named ``desc`` 96 | which contains a one-line description of the test. The ``desc`` files are 97 | used to discover the tests. 98 | 99 | The makefile inside the test directory MUST compile the code into a 100 | binary. The binary name MUST coincide with the directory and test name, the 101 | library name (if present) must be equal to ``lib$test.so``. The source 102 | code is typically called ``$test.c`` for the binary and ``lib$test.c`` 103 | for the library. Patch files are ``$test.diff`` and ``lib$test.diff``. 104 | 105 | When the above rules are followed the test can simply include 106 | ``../makefile.inc`` file that will provide build system for all of the 107 | build types described above. 108 | 109 | The ``tests/makefile.inc`` file itself includes either 110 | ``makefile-libcare-patch-make.inc`` file when the ``CC`` variable equals 111 | ``libcare-cc`` or ``makefile-patch.inc`` otherwise. The former provides a set 112 | of rules that meet ``libcare-patch-make``\ s criteria described in 113 | `libcare-patch-make`_. The later provides a set of rules described in `Manual 114 | Patch Creation`_, except for the libraries output that is broken with them and 115 | requires including of a makefile ``makefile-patch-link.inc`` that links the 116 | shared library to extract proper names of the sections for the kpatch. For the 117 | usage example take a look at the test ``both`` that tests patching of both 118 | binary and a library it loads. 119 | 120 | ``fastsleep.so`` 121 | ^^^^^^^^^^^^^^^^ 122 | 123 | To speed up test execution while allowing them to be run manually we had to 124 | adjust tests with a ``LD_PRELOAD``\ ed library that redefines ``sleep`` and 125 | ``nanosleep`` to change their arguments so the code sleeps faster. The code is 126 | in the file ``fastsleep.c``. 127 | -------------------------------------------------------------------------------- /tests/both/Makefile: -------------------------------------------------------------------------------- 1 | 2 | HAS_LIBRARY := 1 3 | 4 | include ../makefile.inc 5 | 6 | ifneq ($(IS_LIBCARE_CC),y) 7 | TGT := libboth.so 8 | TGT_LDFLAGS := -shared 9 | include ../makefile-patch-link.inc 10 | endif 11 | -------------------------------------------------------------------------------- /tests/both/both.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void print_greetings(void); 5 | 6 | void local_print_greetings(void) 7 | { 8 | print_greetings(); 9 | } 10 | 11 | int main() 12 | { 13 | while(1) { 14 | local_print_greetings(); 15 | sleep(1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/both/both.diff: -------------------------------------------------------------------------------- 1 | --- ./both.c 2016-05-11 03:49:22.378062900 +0300 2 | +++ ./both.c 2016-05-11 03:49:36.362130679 +0300 3 | @@ -6,6 +6,7 @@ 4 | void local_print_greetings(void) 5 | { 6 | print_greetings(); 7 | + printf("Welcome from PATCHED binary!\n"); 8 | } 9 | 10 | int main() 11 | -------------------------------------------------------------------------------- /tests/both/desc: -------------------------------------------------------------------------------- 1 | patch both binary and library 2 | -------------------------------------------------------------------------------- /tests/both/libboth.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const char *msg = "Hello from %s shared library\n"; 4 | void print_greetings(void) 5 | { 6 | printf(msg, "UNPATCHED"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/both/libboth.diff: -------------------------------------------------------------------------------- 1 | --- ./libboth.c 2016-02-07 21:05:13.564346849 +0300 2 | +++ ./libboth.c 2016-02-07 21:05:21.748640167 +0300 3 | @@ -3,5 +3,5 @@ 4 | static const char *msg = "Hello from %s shared library\n"; 5 | void print_greetings(void) 6 | { 7 | - printf(msg, "UNPATCHED"); 8 | + printf(msg, "PATCHED"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/execve/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: execve.so 3 | 4 | clean: 5 | rm -f execve.so 6 | 7 | execve.so: execve.c 8 | $(LINK.c) -o $@ $< -shared -fPIC -ldl 9 | -------------------------------------------------------------------------------- /tests/execve/README.rst: -------------------------------------------------------------------------------- 1 | ``execve(2)`` wrapper 2 | --------------------- 3 | 4 | The code in ``execve.c`` file is the wrapper for the family of 5 | `execve(2) `__ 6 | library calls. It is compiled into a shared object ``execve.so`` and 7 | is ``LD_PRELOAD``\ ed, so the dynamic linker takes implementation of 8 | the ``execve(2)``-like library calls from this object. 9 | 10 | The wrapper first checks if the target executable path matches a 11 | `fnmatch(3) `__ pattern specified by the environment 12 | variable ``KP_EXECVE_PATTERN``. If it is, the wrapper sends current process pid to a 13 | TCP socket at address 127.0.0.1 and port 4233, waits for response and calls for 14 | interrupt #3, which is a software breakpoint at the x86-64 arch. 15 | 16 | Then the appropriate library call is done and, if successful, code of the new 17 | binary takes control over the process. 18 | 19 | 20 | .. _`libcare-ctl`: ../../docs/libcare-ctl.rst 21 | 22 | 23 | ``libcare-ctl`` part 24 | ----------------------- 25 | 26 | ``libcare-ctl`` is instructed with the ``-r`` option that it should expect 27 | the process that is currently executing the ``execve`` wrapper code. 28 | 29 | The doctor attaches to the patient as usual. 30 | It then sends 4-byte to the file descriptor specified as an argument to the 31 | ``-r`` option. The patient receives that and continues the wrapper code up to 32 | the software breakpoint ``int $3``. When the patient hits breakpoint the doctor 33 | receives a ``SIGTRAP`` signal and checks if the code causing it was indeed a 34 | ``int $3`` (``0xcc``). 35 | 36 | The corresponding code is in the file ``src/kpatch_process.c`` function 37 | ``kpatch_process_load_libraries``. 38 | -------------------------------------------------------------------------------- /tests/execve/execve.c: -------------------------------------------------------------------------------- 1 | /* 2 | * execve(2) wrapper notifying us about the test application 3 | * about to be executed. Used instead of `binfmt` handler in 4 | * Docker tests. 5 | */ 6 | 7 | #define _GNU_SOURCE 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | static const char *pattern; 26 | static int exact_match; 27 | static int debug; 28 | static int verbose; 29 | static int (*real_execve)(const char *filename, 30 | char *const argv[], 31 | char *const envp[]); 32 | static int (*real_execvpe)(const char *filename, 33 | char *const argv[], 34 | char *const envp[]); 35 | 36 | __attribute__((constructor)) 37 | void init_execve(void) 38 | { 39 | real_execve = dlsym(RTLD_NEXT, "execve"); 40 | real_execvpe = dlsym(RTLD_NEXT, "execvpe"); 41 | 42 | pattern = getenv("KP_EXECVE_PATTERN"); 43 | exact_match = getenv("KP_EXECVE_PATTERN_PATHNAME") != NULL; 44 | debug = getenv("KP_EXECVE_DEBUG") != NULL; 45 | verbose = getenv("KP_EXECVE_VERBOSE") != NULL; 46 | } 47 | 48 | #define dprintf(fmt...) do { \ 49 | if (debug) { \ 50 | int errsv = errno; \ 51 | fprintf(stderr, fmt); \ 52 | errno = errsv; \ 53 | } \ 54 | } while (0) 55 | 56 | static int 57 | is_listed_binary(const char *filename) 58 | { 59 | int rv; 60 | 61 | if (pattern == NULL) 62 | return 0; 63 | 64 | rv = fnmatch(pattern, filename, 65 | (exact_match ? FNM_PATHNAME : 0) | 66 | FNM_EXTMATCH); 67 | dprintf("Match pattern '%s' against '%s', result is %d\n", 68 | pattern, filename, rv); 69 | 70 | return rv == 0; 71 | } 72 | 73 | static void 74 | notify_listener(void) 75 | { 76 | int sock, rv; 77 | struct sockaddr_un sockaddr; 78 | const char *unix_path = "/var/run/libcare.sock"; 79 | char buf[128], *p; 80 | 81 | sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); 82 | if (sock == -1) { 83 | dprintf("socket() error: %s(%d)\n", strerror(errno), errno); 84 | abort(); 85 | } 86 | dprintf("socket()\n"); 87 | 88 | p = getenv("LIBCARE_CTL_UNIX"); 89 | if (p) 90 | unix_path = p; 91 | 92 | sockaddr.sun_family = AF_UNIX; 93 | strncpy(sockaddr.sun_path, unix_path, sizeof(sockaddr.sun_path)); 94 | 95 | rv = connect(sock, (const struct sockaddr *)&sockaddr, sizeof(sockaddr)); 96 | if (rv == -1) { 97 | fprintf(stderr, "libcare-execve: connect() error: %s(%d)\n", strerror(errno), errno); 98 | (void) close(sock); 99 | abort(); 100 | } 101 | dprintf("connect()\n"); 102 | 103 | p = stpcpy(buf, "execve") + 1; 104 | sprintf(p, "%d", (int) syscall(SYS_gettid)); 105 | p += strlen(p) + 1; 106 | *p = '\0'; 107 | p++; 108 | 109 | do { 110 | rv = send(sock, buf, p - buf, 0); 111 | } while (rv == -1 && errno == EINTR); 112 | 113 | if (rv == -1) { 114 | fprintf(stderr, "send() error: %s(%d)\n", strerror(errno), errno); 115 | (void) close(sock); 116 | abort(); 117 | } 118 | dprintf("send()\n"); 119 | 120 | do { 121 | rv = recv(sock, buf, sizeof(int), 0); 122 | } while (rv == -1 && errno == EINTR); 123 | 124 | if (rv == -1) { 125 | fprintf(stderr, "recv() error: %s(%d)\n", strerror(errno), errno); 126 | abort(); 127 | } 128 | dprintf("recv()\n"); 129 | 130 | (void) close(sock); 131 | 132 | asm (".align 8;\n" 133 | " int $3;\n" 134 | ".align 8;\n"); 135 | } 136 | 137 | #define PRELOAD_ENV_STR "LD_PRELOAD=" 138 | #define PRELOAD_ENV_LEN (sizeof(PRELOAD_ENV_STR) - 1) 139 | #define EXECVE_SO_STR "/execve.so" 140 | #define EXECVE_SO_LEN (sizeof(EXECVE_SO_STR) - 1) 141 | 142 | static char** 143 | filter_environ(char *const *envp) 144 | { 145 | int i = 0, j = 0; 146 | char **newenvp = NULL; 147 | 148 | while (envp[i++] != NULL); 149 | 150 | newenvp = malloc(sizeof(char *) * i); 151 | if (newenvp == NULL) { 152 | dprintf("ERROR: no place for newenvp: %d\n", errno); 153 | return NULL; 154 | } 155 | 156 | for (i = 0, j = 0; envp[i] != NULL; i++) { 157 | if (!strncmp(envp[i], PRELOAD_ENV_STR, PRELOAD_ENV_LEN)) { 158 | char *val = envp[i] + PRELOAD_ENV_LEN; 159 | char *sep = strchr(val, ':'); 160 | char *newval; 161 | char *has_this_lib = strstr(val, EXECVE_SO_STR); 162 | 163 | if (has_this_lib == NULL || sep == NULL) { 164 | newenvp[j++] = envp[i]; 165 | continue; 166 | } 167 | 168 | newval = malloc(strlen(val)); 169 | if (newval == NULL) { 170 | dprintf("ERROR: no place for newval: %d\n", 171 | errno); 172 | return NULL; 173 | } 174 | 175 | sep = has_this_lib; 176 | 177 | while (*sep != ':' && sep > val) 178 | sep--; 179 | 180 | strcpy(newval, PRELOAD_ENV_STR); 181 | strncat(newval, val, sep - val); 182 | strcat(newval, has_this_lib + EXECVE_SO_LEN + 1); 183 | printf("%s\n", newval); 184 | 185 | newenvp[j++] = newval; 186 | } else 187 | newenvp[j++] = envp[i]; 188 | } 189 | newenvp[j] = NULL; 190 | 191 | return newenvp; 192 | } 193 | 194 | int execve(const char *filename, 195 | char *const argv[], 196 | char *const envp[]) 197 | { 198 | int to_be_patched = 0, rv, errnosv; 199 | dprintf("%s\n", __func__); 200 | 201 | if (*filename != '/' && strchr(filename, '/') != NULL) { 202 | char path[PATH_MAX]; 203 | realpath(filename, path); 204 | dprintf("realpath('%s', '%s')\n", filename, path); 205 | to_be_patched = is_listed_binary(path); 206 | } 207 | else { 208 | to_be_patched = is_listed_binary(filename); 209 | } 210 | 211 | if (to_be_patched) { 212 | int i; 213 | 214 | envp = filter_environ(envp); 215 | if (envp == NULL) 216 | return -1; 217 | notify_listener(); 218 | 219 | if (verbose) { 220 | fprintf(stderr, "KPEXECVE %d '%s'", 221 | getpid(), filename); 222 | for (i = 0; argv[i] != NULL; i++) 223 | fprintf(stderr, " '%s'", argv[i]); 224 | fprintf(stderr, "\n"); 225 | } 226 | 227 | } 228 | 229 | dprintf("real_execve('%s', ...)\n", filename); 230 | rv = real_execve(filename, argv, envp); 231 | 232 | if (to_be_patched) { 233 | errnosv = errno; 234 | free((void *)envp); 235 | errno = errnosv; 236 | } 237 | 238 | return rv; 239 | } 240 | 241 | int execv(const char *filename, 242 | char *const argv[]) 243 | { 244 | dprintf("%s\n", __func__); 245 | return execve(filename, argv, __environ); 246 | } 247 | 248 | int execvpe(const char *filename, 249 | char *const argv[], 250 | char *const envp[]) 251 | { 252 | dprintf("%s\n", __func__); 253 | if (strchr(filename, '/') != NULL) { 254 | return execve(filename, argv, envp); 255 | } 256 | dprintf("real_execvpe('%s', ...)\n", filename); 257 | return real_execvpe(filename, argv, envp); 258 | } 259 | 260 | int execvp(const char *filename, 261 | char *const argv[]) 262 | { 263 | dprintf("%s\n", __func__); 264 | return execvpe(filename, argv, __environ); 265 | } 266 | 267 | int vexecle(const char *filename, 268 | const char *arg, 269 | va_list args, 270 | int has_envp, 271 | int search_path) 272 | { 273 | #define INITIAL_ARGV_SIZE 1024 274 | const char *initial_argv[INITIAL_ARGV_SIZE]; 275 | const char **argv = initial_argv; 276 | char *const *envp = __environ; 277 | size_t i = 0; 278 | int ret; 279 | 280 | argv[0] = arg; 281 | 282 | while (argv[i++] != NULL) { 283 | if (i == INITIAL_ARGV_SIZE) { 284 | dprintf("Not implemented execl argc>=%d\n", 285 | INITIAL_ARGV_SIZE); 286 | errno = ENOSYS; 287 | return -1; 288 | } 289 | 290 | argv[i] = va_arg(args, const char *); 291 | } 292 | 293 | if (has_envp) 294 | envp = va_arg(args, char *const *); 295 | 296 | va_end(args); 297 | 298 | if (search_path == 0) 299 | return execve(filename, (char *const *)argv, envp); 300 | else 301 | return execvpe(filename, (char *const *)argv, envp); 302 | } 303 | 304 | int execl(const char *filename, const char *arg, ...) 305 | { 306 | va_list args; 307 | 308 | va_start(args, arg); 309 | 310 | dprintf("%s\n", __func__); 311 | return vexecle(filename, arg, args, 0, 0); 312 | } 313 | 314 | int execlp(const char *filename, const char *arg, ...) 315 | { 316 | va_list args; 317 | 318 | va_start(args, arg); 319 | 320 | dprintf("%s\n", __func__); 321 | return vexecle(filename, arg, args, 0, 1); 322 | } 323 | 324 | int execle(const char *filename, const char *arg, ...) 325 | { 326 | va_list args; 327 | 328 | va_start(args, arg); 329 | 330 | dprintf("%s\n", __func__); 331 | return vexecle(filename, arg, args, 1, 0); 332 | } 333 | -------------------------------------------------------------------------------- /tests/fail_busy_single/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/fail_busy_single/desc: -------------------------------------------------------------------------------- 1 | always has patched symbol on a stack. failed unless startup 2 | -------------------------------------------------------------------------------- /tests/fail_busy_single/fail_busy_single.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print_greetings_patched(void) 5 | { 6 | printf("Hello. This a PATCHED version!\n"); 7 | } 8 | 9 | void print_greetings(void) 10 | { 11 | printf("Hello. This is an UNPATCHED version!\n"); 12 | } 13 | 14 | void do_work2() { 15 | while (1) { 16 | print_greetings(); 17 | sleep(1); 18 | } 19 | } 20 | 21 | void do_work() { 22 | do_work2(); 23 | } 24 | 25 | int main() 26 | { 27 | do_work(); 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /tests/fail_busy_single/fail_busy_single.diff: -------------------------------------------------------------------------------- 1 | --- ./fail_busy_single.c 2016-02-03 22:06:15.402608283 +0300 2 | +++ ./fail_busy_single.c 2016-02-03 22:06:33.738579308 +0300 3 | @@ -19,7 +19,7 @@ void do_work2() { 4 | } 5 | 6 | void do_work() { 7 | - do_work2(); 8 | + print_greetings_patched(); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/fail_busy_single_top/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/fail_busy_single_top/desc: -------------------------------------------------------------------------------- 1 | always has patched symbol on a stack (top frame). failed unless startup 2 | -------------------------------------------------------------------------------- /tests/fail_busy_single_top/fail_busy_single_top.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print_greetings_patched(void) 6 | { 7 | printf("Hello. This a PATCHED version!\n"); 8 | } 9 | 10 | void print_greetings(void) 11 | { 12 | printf("Hello. This is an UNPATCHED version!\n"); 13 | } 14 | 15 | void do_work2() { 16 | volatile int i = 0; 17 | while (1) { 18 | print_greetings(); 19 | for (i = 0; i < INT_MAX / 50; i++) 20 | asm ("pause"); 21 | } 22 | } 23 | 24 | void do_work() { 25 | do_work2(); 26 | } 27 | 28 | int main() 29 | { 30 | do_work(); 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/fail_busy_single_top/fail_busy_single_top.diff: -------------------------------------------------------------------------------- 1 | --- ./fail_busy_single_top.c 2016-07-21 16:39:21.860750661 +0300 2 | +++ ./fail_busy_single_top.c 2016-07-21 16:39:07.238092509 +0300 3 | @@ -15,7 +15,7 @@ void print_greetings(void) 4 | void do_work2() { 5 | volatile int i = 0; 6 | while (1) { 7 | - print_greetings(); 8 | + print_greetings_patched(); 9 | for (i = 0; i < INT_MAX / 50; i++) 10 | asm ("pause"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/fail_busy_threads/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDLIBS = -lpthread 3 | 4 | include ../makefile.inc 5 | -------------------------------------------------------------------------------- /tests/fail_busy_threads/desc: -------------------------------------------------------------------------------- 1 | one thread always has patched symbol on a stack. failed unless startup 2 | -------------------------------------------------------------------------------- /tests/fail_busy_threads/fail_busy_threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print_patch(void) 6 | { 7 | printf("Hello from thread2 (PATCHED)\n"); 8 | } 9 | 10 | void print_greetings1(void) 11 | { 12 | printf("Hello from thread1 (UNPATCHED)\n"); 13 | } 14 | 15 | void print_greetings2(void) 16 | { 17 | printf("Hello from thread2 (UNPATCHED)\n"); 18 | } 19 | 20 | void *thread1_func(void *unused) 21 | { 22 | while (1) { 23 | print_greetings1(); 24 | sleep(1); 25 | } 26 | } 27 | 28 | void do_thread2_work() 29 | { 30 | while (1) { 31 | print_greetings2(); 32 | sleep(1); 33 | } 34 | } 35 | 36 | void *thread2_func(void *unused) 37 | { 38 | do_thread2_work(); 39 | } 40 | 41 | int main() 42 | { 43 | pthread_t thrs[2]; 44 | pthread_create(&thrs[0], NULL, thread1_func, NULL); 45 | pthread_create(&thrs[1], NULL, thread2_func, NULL); 46 | 47 | pthread_join(thrs[0], NULL); 48 | pthread_join(thrs[1], NULL); 49 | 50 | return 0; 51 | } 52 | -------------------------------------------------------------------------------- /tests/fail_busy_threads/fail_busy_threads.diff: -------------------------------------------------------------------------------- 1 | --- ./fail_busy_threads.c 2016-02-03 22:22:51.633655690 +0300 2 | +++ ./fail_busy_threads.c 2016-02-03 22:23:05.002532693 +0300 3 | @@ -35,7 +35,7 @@ void do_thread2_work() 4 | 5 | void *thread2_func(void *unused) 6 | { 7 | - do_thread2_work(); 8 | + print_patch(); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/fail_coro/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/fail_coro/desc: -------------------------------------------------------------------------------- 1 | simply failing test for finding coro stacks 2 | -------------------------------------------------------------------------------- /tests/fail_coro/fail_coro.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | /* This test mimics QEMU usage of coroutine-ucontext.c: 9 | * 1. Use `makecontext` to initialize context with a separate stack. 10 | * 2. Use `siglongjmp`/`sigsetjmp` pair to use that context. 11 | */ 12 | 13 | typedef struct { 14 | void (*entry)(void *); 15 | void *entry_arg; 16 | 17 | /* align structure properly */ 18 | void *a1; 19 | void *a2; 20 | void *a3; 21 | void *stack_orig; 22 | 23 | void *stack; 24 | sigjmp_buf env; 25 | 26 | sigjmp_buf caller; 27 | } CoroutineUContext; 28 | 29 | #include "fail_coro_common.c" 30 | 31 | static void 32 | func(void *arg) 33 | { 34 | const char *str = arg; 35 | 36 | while (1) { 37 | printf("%s\n", str); 38 | coroutine_yield(); 39 | } 40 | } 41 | 42 | int 43 | main(void) 44 | { 45 | CoroutineUContext *co; 46 | 47 | co = coroutine_new(func, "Hello from UNPATCHED"); 48 | 49 | while (1) { 50 | coroutine_exec(co); 51 | sleep(1); 52 | } 53 | 54 | coroutine_free(co); 55 | 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /tests/fail_coro/fail_coro.diff: -------------------------------------------------------------------------------- 1 | --- a/fail_coro.c 2016-12-11 20:36:48.788000000 +0000 2 | +++ b/fail_coro.c 2016-12-11 20:36:58.368000000 +0000 3 | @@ -81,8 +81,8 @@ 4 | const char *str = arg; 5 | 6 | while (1) { 7 | - printf("%s\n", str); 8 | - coroutine_yield(); 9 | + printf("Hello from PATCHED\n"); 10 | + sleep(1); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/fail_coro/fail_coro_common.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | union cc_arg { 6 | void *p; 7 | int i[2]; 8 | }; 9 | 10 | /* 11 | * Reproduce the exact stack layout expected by the coroutine-detection 12 | * algorithm. Since different versions of GCC produce different machine code 13 | * we had to place the asm code there. 14 | * 15 | * The following is the C code: 16 | */ 17 | 18 | #if 0 19 | static void coroutine_trampoline(int i0, int i1) 20 | { 21 | union cc_arg arg; 22 | CoroutineUContext *self; 23 | Coroutine *co; 24 | 25 | arg.i[0] = i0; 26 | arg.i[1] = i1; 27 | self = arg.p; 28 | co = &self->base; 29 | 30 | /* Initialize longjmp environment and switch back the caller */ 31 | if (!sigsetjmp(self->env, 0)) { 32 | siglongjmp(*(sigjmp_buf *)co->entry_arg, 1); 33 | } 34 | 35 | while (true) { 36 | co->entry(co->entry_arg); 37 | /* qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); */ 38 | } 39 | } 40 | #endif 41 | 42 | asm ( 43 | ".text\n" 44 | ".type coroutine_trampoline, @function\n" 45 | "coroutine_trampoline:\n" 46 | "push %rbx\n" 47 | "mov %edi,%edi\n" 48 | "sub $0x20,%rsp\n" 49 | "mov %fs:0x28,%rax\n" 50 | "mov %rax,0x18(%rsp)\n" 51 | "xor %eax,%eax\n" 52 | "mov %rsi,%rax\n" 53 | "xor %esi,%esi\n" 54 | "shl $0x20,%rax\n" 55 | "or %rax,%rdi\n" 56 | "mov %rdi,0x8(%rsp)\n" 57 | "mov %rdi,(%rsp)\n" 58 | "add $0x38,%rdi\n" 59 | "callq __sigsetjmp\n" 60 | "test %eax,%eax\n" 61 | "je 2f\n" 62 | "nopl 0x0(%rax)\n" 63 | "1:" 64 | "mov (%rsp),%rbx\n" 65 | "mov 0x8(%rbx),%rdi\n" 66 | "callq *(%rbx)\n" 67 | "mov 0x10(%rbx),%rsi\n" 68 | "mov $0x2,%edx\n" 69 | "mov %rbx,%rdi\n" 70 | "jmp 1b\n" 71 | "2:" 72 | "mov 0x8(%rsp),%rax\n" 73 | "mov $0x1,%esi\n" 74 | "mov 0x8(%rax),%rdi\n" 75 | "callq siglongjmp\n" 76 | ".size coroutine_trampoline, .-coroutine_trampoline\n" 77 | ); 78 | 79 | extern void coroutine_trampoline(void); 80 | 81 | static CoroutineUContext *running_co; 82 | 83 | static void coroutine_yield(void) 84 | { 85 | if (!sigsetjmp(running_co->env, 0)) 86 | siglongjmp(running_co->caller, 1); 87 | } 88 | 89 | #define PAGE_SIZE 4096 90 | #define PAGE_MASK (PAGE_SIZE - 1) 91 | 92 | static CoroutineUContext *coroutine_new(void (*entry)(void *), 93 | void *entry_arg) 94 | { 95 | CoroutineUContext *co; 96 | const size_t stack_size = 1 << 14; 97 | ucontext_t uc, old_uc; 98 | sigjmp_buf old_env; 99 | union cc_arg arg = { 0 }; 100 | void *stack; 101 | 102 | if (getcontext(&uc) == -1) 103 | return NULL; 104 | 105 | co = calloc(1, sizeof(*co)); 106 | if (co == NULL) 107 | return NULL; 108 | 109 | stack = malloc(stack_size); 110 | if (stack == NULL) 111 | return NULL; 112 | 113 | co->stack_orig = stack; 114 | co->stack = (void *)((unsigned long)(stack + PAGE_SIZE - 1) & ~PAGE_MASK); 115 | 116 | co->entry_arg = &old_env; 117 | 118 | uc.uc_link = &old_uc; 119 | uc.uc_stack.ss_sp = co->stack; 120 | uc.uc_stack.ss_size = stack_size - (co->stack - stack) & ~PAGE_MASK; 121 | uc.uc_stack.ss_flags = 0; 122 | 123 | printf("entry=%p co=%p old_uc=%p uc=%p stack=%p ssize=%lx\n", 124 | entry, co, &old_uc, &uc, co->stack, uc.uc_stack.ss_size); 125 | 126 | arg.p = co; 127 | 128 | makecontext(&uc, (void (*)(void))coroutine_trampoline, 129 | 2, arg.i[0], arg.i[1]); 130 | 131 | if (!sigsetjmp(old_env, 0)) { 132 | swapcontext(&old_uc, &uc); 133 | } 134 | 135 | co->entry = entry; 136 | co->entry_arg = entry_arg; 137 | 138 | return co; 139 | } 140 | 141 | static void coroutine_free(CoroutineUContext *co) 142 | { 143 | free(co->stack_orig); 144 | free(co); 145 | } 146 | 147 | static void coroutine_exec(CoroutineUContext *co) 148 | { 149 | running_co = co; 150 | 151 | if (!sigsetjmp(co->caller, 0)) { 152 | siglongjmp(co->env, 1); 153 | } 154 | 155 | running_co = NULL; 156 | } 157 | -------------------------------------------------------------------------------- /tests/fail_coro_listed/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDFLAGS += -Wl,--dynamic-list -Wl,export.txt 3 | include ../makefile.inc 4 | -------------------------------------------------------------------------------- /tests/fail_coro_listed/desc: -------------------------------------------------------------------------------- 1 | simply failing test for finding coro stacks from the listed 2 | -------------------------------------------------------------------------------- /tests/fail_coro_listed/export.txt: -------------------------------------------------------------------------------- 1 | { 2 | coroutines_list; 3 | coroutines_list_offset; 4 | coroutine_env_offset; 5 | }; 6 | -------------------------------------------------------------------------------- /tests/fail_coro_listed/fail_coro_listed.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | /* This test mimics QEMU usage of coroutine-ucontext.c: 10 | * 1. Use `makecontext` to initialize context with a separate stack. 11 | * 2. Use `siglongjmp`/`sigsetjmp` pair to use that context. 12 | */ 13 | 14 | typedef struct CoroutineUContext CoroutineUContext; 15 | 16 | struct CoroutineUContext { 17 | void (*entry)(void *); 18 | void *entry_arg; 19 | 20 | /* align structure properly */ 21 | void *a1; 22 | void *a2; 23 | CoroutineUContext *list_next; 24 | void *stack_orig; 25 | 26 | void *stack; 27 | sigjmp_buf env; 28 | 29 | sigjmp_buf caller; 30 | }; 31 | 32 | 33 | CoroutineUContext *coroutines_list = NULL; 34 | unsigned int coroutines_list_offset = offsetof(CoroutineUContext, list_next); 35 | unsigned int coroutine_env_offset = offsetof(CoroutineUContext, env); 36 | 37 | #include "../fail_coro/fail_coro_common.c" 38 | 39 | static void 40 | func(void *arg) 41 | { 42 | const char *str = arg; 43 | 44 | while (1) { 45 | printf("%s\n", str); 46 | coroutine_yield(); 47 | } 48 | } 49 | 50 | void listed_coroutine_exec(CoroutineUContext *co) 51 | { 52 | CoroutineUContext *p = coroutines_list; 53 | 54 | while (p && p != co) 55 | p = p->list_next; 56 | 57 | if (p == NULL) { 58 | co->list_next = coroutines_list; 59 | coroutines_list = co; 60 | } 61 | 62 | coroutine_exec(co); 63 | } 64 | 65 | void listed_coroutine_free(CoroutineUContext *co) 66 | { 67 | CoroutineUContext **prev = &coroutines_list; 68 | 69 | while (*prev) { 70 | if (*prev == co) { 71 | *prev = co->list_next; 72 | break; 73 | } 74 | 75 | prev = &(*prev)->list_next; 76 | } 77 | 78 | coroutine_free(co); 79 | } 80 | 81 | int 82 | main(void) 83 | { 84 | CoroutineUContext *co1, *co2; 85 | 86 | co1 = coroutine_new(func, "Hello from UNPATCHED"); 87 | co2 = coroutine_new(func, "From UNPATCHED Hello 2"); 88 | 89 | while (1) { 90 | listed_coroutine_exec(co1); 91 | listed_coroutine_exec(co2); 92 | sleep(1); 93 | } 94 | 95 | listed_coroutine_free(co1); 96 | listed_coroutine_free(co2); 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /tests/fail_coro_listed/fail_coro_listed.diff: -------------------------------------------------------------------------------- 1 | --- a/fail_coro_listed.c 2016-12-11 20:36:48.788000000 +0000 2 | +++ b/fail_coro_listed.c 2016-12-11 20:36:58.368000000 +0000 3 | @@ -81,8 +81,8 @@ 4 | const char *str = arg; 5 | 6 | while (1) { 7 | - printf("%s\n", str); 8 | - coroutine_yield(); 9 | + printf("Hello from PATCHED %s\n", str); 10 | + coroutine_yield(); 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /tests/fail_threading/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDLIBS:=-lpthread 3 | 4 | include ../makefile.inc 5 | -------------------------------------------------------------------------------- /tests/fail_threading/desc: -------------------------------------------------------------------------------- 1 | crazy threading tests. spawn more threads! 2 | -------------------------------------------------------------------------------- /tests/fail_threading/fail_threading.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #define MAX_STACK 4 8 | 9 | static pthread_t mainthr; 10 | static const struct timespec short_sleep = { 11 | .tv_nsec = 200000000, 12 | }, long_sleep = { 13 | .tv_nsec = 800000000, 14 | }; 15 | 16 | static void *func(void *); 17 | 18 | static int payload(pthread_t *thread) 19 | { 20 | int rv; 21 | 22 | nanosleep(&short_sleep, NULL); 23 | rv = pthread_create(thread, NULL, func, (void *)1UL); 24 | printf("HELLO FROM UNPATCHED\n"); 25 | if (rv == -1) 26 | abort(); 27 | nanosleep(&long_sleep, NULL); 28 | 29 | return 1; 30 | } 31 | 32 | static void *func(void *data) 33 | { 34 | int rv; 35 | pthread_t thread; 36 | 37 | rv = payload(&thread); 38 | 39 | if (rv == 1) 40 | pthread_join(thread, NULL); 41 | 42 | return NULL; 43 | } 44 | 45 | static void init() __attribute__((constructor)); 46 | 47 | static void init() 48 | { 49 | pthread_create(&mainthr, NULL, func, NULL); 50 | } 51 | 52 | int main() 53 | { 54 | pthread_join(mainthr, NULL); 55 | } 56 | -------------------------------------------------------------------------------- /tests/fail_threading/fail_threading.diff: -------------------------------------------------------------------------------- 1 | --- a/fail_threading.c 2016-01-28 14:02:33.225472198 +0000 2 | +++ b/fail_threading.c 2016-01-28 14:02:49.645610472 +0000 3 | @@ -17,16 +17,8 @@ static void *func(void *); 4 | 5 | static int payload(pthread_t *thread) 6 | { 7 | - int rv; 8 | - 9 | - nanosleep(&short_sleep, NULL); 10 | - rv = pthread_create(thread, NULL, func, (void *)1UL); 11 | - printf("HELLO FROM UNPATCHED\n"); 12 | - if (rv == -1) 13 | - abort(); 14 | - nanosleep(&long_sleep, NULL); 15 | - 16 | - return 1; 17 | + printf("HELLO FROM PATCHED\n"); 18 | + return 0; 19 | } 20 | 21 | static void *func(void *data) 22 | -------------------------------------------------------------------------------- /tests/fail_unpatch/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDLIBS:=-lpthread 3 | 4 | include ../makefile.inc 5 | -------------------------------------------------------------------------------- /tests/fail_unpatch/desc: -------------------------------------------------------------------------------- 1 | fails to unpatch the code 2 | -------------------------------------------------------------------------------- /tests/fail_unpatch/fail_unpatch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print_greetings_patched(void) 5 | { 6 | while (1) { 7 | printf("Hello. This a PATCHED version!\n"); 8 | sleep(1); 9 | } 10 | } 11 | 12 | void print_greetings(void) 13 | { 14 | printf("Hello. This is an UNPATCHED version!\n"); 15 | } 16 | 17 | void do_work() { 18 | while (1) { 19 | print_greetings(); 20 | sleep(1); 21 | } 22 | } 23 | 24 | int main() 25 | { 26 | do_work(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /tests/fail_unpatch/fail_unpatch.diff: -------------------------------------------------------------------------------- 1 | --- ./fail_unpatch.c 2018-02-07 18:39:27.145493215 +0200 2 | +++ ./fail_unpatch.c 2018-02-07 18:39:43.349482218 +0200 3 | @@ -11,7 +11,7 @@ 4 | 5 | void print_greetings(void) 6 | { 7 | - printf("Hello. This is an UNPATCHED version!\n"); 8 | + print_greetings_patched(); 9 | } 10 | 11 | void do_work() { 12 | -------------------------------------------------------------------------------- /tests/fastsleep.c: -------------------------------------------------------------------------------- 1 | 2 | #define _GNU_SOURCE 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void init_sleeper(void) __attribute__((constructor)); 11 | 12 | #define NSEC_TO_SEC 1000000000 13 | #define DENOMINATOR NSEC_TO_SEC 14 | 15 | static uint64_t mult = NSEC_TO_SEC / 10; 16 | int (*real_nanosleep)(const struct timespec *req, struct timespec *rem); 17 | 18 | void init_sleeper(void) 19 | { 20 | const char *mult_str = getenv("SLEEP_MULT"); 21 | if (mult_str) { 22 | uint64_t newmult; 23 | newmult = atol(mult_str); 24 | mult = newmult < (DENOMINATOR / 1000) ? mult : newmult; 25 | } 26 | 27 | real_nanosleep = dlsym(RTLD_NEXT, "nanosleep"); 28 | } 29 | 30 | unsigned int sleep(unsigned int t) 31 | { 32 | struct timespec ts = { 33 | .tv_sec = t * mult / DENOMINATOR, 34 | .tv_nsec = t * mult % DENOMINATOR 35 | }; 36 | 37 | real_nanosleep(&ts, NULL); 38 | return 0; 39 | } 40 | 41 | int nanosleep(const struct timespec *req, struct timespec *rem) 42 | { 43 | uint64_t tv_sec = req->tv_sec * mult / DENOMINATOR + 44 | req->tv_nsec * mult / NSEC_TO_SEC / DENOMINATOR; 45 | uint64_t tv_nsec = req->tv_sec * NSEC_TO_SEC / DENOMINATOR * mult + 46 | req->tv_nsec * mult / DENOMINATOR; 47 | const struct timespec nreq = { 48 | .tv_sec = tv_sec, 49 | .tv_nsec = tv_nsec % NSEC_TO_SEC, 50 | }; 51 | 52 | return real_nanosleep(&nreq, rem); 53 | } 54 | -------------------------------------------------------------------------------- /tests/frame_finish/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/frame_finish/desc: -------------------------------------------------------------------------------- 1 | test waiting for thread to leave to-be-patched function 2 | -------------------------------------------------------------------------------- /tests/frame_finish/frame_finish.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | void local_print_greetings(void) 7 | { 8 | struct timespec req = { 9 | .tv_sec = 1, 10 | .tv_nsec = 0, 11 | }; 12 | printf("Hello from UNPATCHED\n"); 13 | nanosleep(&req, NULL); 14 | } 15 | 16 | int main() 17 | { 18 | while(1) { 19 | local_print_greetings(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/frame_finish/frame_finish.diff: -------------------------------------------------------------------------------- 1 | --- ./frame_finish.c 2016-06-21 01:36:05.635930305 +0200 2 | +++ ./frame_finish.c 2016-06-21 01:36:21.985803907 +0200 3 | @@ -9,7 +9,7 @@ 4 | .tv_sec = 1, 5 | .tv_nsec = 0, 6 | }; 7 | - printf("Hello from UNPATCHED\n"); 8 | + printf("Hello from PATCHED\n"); 9 | nanosleep(&req, NULL); 10 | } 11 | 12 | -------------------------------------------------------------------------------- /tests/ifunc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ifeq ($(shell grep 'release 6' /etc/redhat-release 2>/dev/null),) 3 | 4 | HAS_LIBRARY := 1 5 | LIBRARY_PATCH := 6 | 7 | include ../makefile.inc 8 | 9 | else 10 | 11 | install all clean: 12 | 13 | endif 14 | -------------------------------------------------------------------------------- /tests/ifunc/desc: -------------------------------------------------------------------------------- 1 | test STT_GNU_IFUNC symbol resolving 2 | -------------------------------------------------------------------------------- /tests/ifunc/ifunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void print_greetings_unpatched(); 5 | extern void print_greetings_patched(); 6 | 7 | void local_print_greetings(void) 8 | { 9 | print_greetings_unpatched(); 10 | } 11 | 12 | int main() 13 | { 14 | while(1) { 15 | local_print_greetings(); 16 | sleep(1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/ifunc/ifunc.diff: -------------------------------------------------------------------------------- 1 | --- ./ifunc.c 2016-06-22 10:51:05.056000000 -0400 2 | +++ ./ifunc.c 2016-06-22 10:51:09.641000000 -0400 3 | @@ -5,7 +5,7 @@ 4 | 5 | void local_print_greetings(void) 6 | { 7 | - print_greetings_unpatched(); 8 | + print_greetings_patched(); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/ifunc/libifunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void print_greetings_unpatched() 4 | { 5 | printf("Resolved IFUNC to UNPATCHED\n"); 6 | } 7 | 8 | void _print_greetings_patched() 9 | { 10 | printf("Resolved IFUNC to PATCHED\n"); 11 | } 12 | 13 | static void (*resolve_print_greetings (void))(void) 14 | { 15 | return (void *)_print_greetings_patched; 16 | } 17 | 18 | void print_greetings_patched(int) 19 | __attribute__ ((ifunc ("resolve_print_greetings"))); 20 | -------------------------------------------------------------------------------- /tests/makefile-lpmake.inc: -------------------------------------------------------------------------------- 1 | # vim: set filetype=make: 2 | # 3 | ifneq ($(HAS_LIBRARY),) 4 | 5 | ifeq ($(KPATCH_STAGE),original) 6 | $(BINARY): LDLIBS += -l$(TESTNAME) -L$(OBJDIR) 7 | else 8 | $(BINARY): LDLIBS += -l$(TESTNAME) -L$(LPMAKE_ORIGINAL_DIR) 9 | endif 10 | 11 | endif 12 | 13 | %.so: LDFLAGS += -shared 14 | %.so: CFLAGS += -fPIC 15 | %.so: %.o 16 | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ 17 | 18 | %: %.o 19 | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ 20 | -------------------------------------------------------------------------------- /tests/makefile-patch-link.inc: -------------------------------------------------------------------------------- 1 | 2 | OBJ := $(subst .so,,$(TGT)) 3 | $(TGT).kpatch: $(OBJ).o $(TGT) 4 | $(CC) $(TGT_LDFLAGS) -Wl,-q $< $(LIBS) -o $(TGT).patched 5 | $(KPATCH_STRIP) --strip $(TGT).patched $(TGT).stripped 6 | $(KPATCH_STRIP) --rel-fixup $(TGT) $(TGT).stripped 7 | /usr/bin/strip --strip-unneeded $(TGT).stripped 8 | $(KPATCH_STRIP) --undo-link $(TGT) $(TGT).stripped 9 | 10 | # build kpatch 11 | buildid=$(call get_buildid,$(TGT)); \ 12 | $(KPATCH_MAKE) $(TGT).stripped -o $@ \ 13 | -b $${buildid}; \ 14 | cp -fs $@ $(OBJDIR)/$${buildid}.kpatch 15 | 16 | clean:: 17 | rm -f $(TGT) $(TGT).patched $(TGT).stripped $(OBJ).o 18 | -------------------------------------------------------------------------------- /tests/makefile-patch.inc: -------------------------------------------------------------------------------- 1 | # vim: set filetype=make: 2 | 3 | %.stripped: %.patched 4 | $(KPATCH_STRIP) --strip $< $*.stripped 5 | /usr/bin/strip --strip-unneeded $*.stripped 6 | 7 | %.so.stripped: %.patched 8 | $(KPATCH_STRIP) --strip $< $*.so.stripped 9 | /usr/bin/strip --strip-unneeded $*.so.stripped 10 | 11 | %.so.kpatch: CFLAGS += -fPIC 12 | 13 | %.undo-link: %.stripped % 14 | cp $< $@ 15 | readelf -a $(word 2,$^) 16 | $(KPATCH_STRIP) --rel-fixup $(word 2,$^) $@ 17 | $(KPATCH_STRIP) --undo-link $(word 2,$^) $@ 18 | 19 | %.kpatch: %.undo-link % 20 | buildid=$(call get_buildid,$(word 2,$^)) && \ 21 | $(KPATCH_MAKE) -b $${buildid} $< -o $@ && \ 22 | cp -fs $@ $(OBJDIR)/$${buildid}.kpatch 23 | 24 | $(OBJDIR)/%.o: $(OBJDIR)/%.s 25 | $(AS) $< -o $@ 26 | 27 | $(OBJDIR)/%.orig.s: %.c 28 | $(CC) $< -S -o - $(CFLAGS) | \ 29 | $(KPATCH_GENSRC) --dbg-filter --os=rhel6 -i - -o $@ 30 | 31 | DIFFEXT ?= diff 32 | 33 | $(OBJDIR)/%.patched.s: %.c %.$(DIFFEXT) 34 | patch -b -p1 < $(word 2,$^) 35 | $(CC) $< -S -o - $(CFLAGS) | \ 36 | $(KPATCH_GENSRC) --dbg-filter --os=rhel6 -i - -o $@ 37 | mv $<.orig $< 38 | 39 | $(OBJDIR)/%.s: $(OBJDIR)/%.orig.s $(OBJDIR)/%.patched.s 40 | $(KPATCH_GENSRC) --os=rhel6 -i $< -i $(word 2,$^) --force-global \ 41 | --force-gotpcrel -o $@ 42 | 43 | lib%.patched: LDFLAGS += -shared 44 | lib%.patched: LIBRARY := 45 | lib%.patched: CFLAGS += -fPIC 46 | 47 | %.patched: %.o $(LIBRARY) 48 | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ -Wl,-q 49 | 50 | %: %.orig.o $(LIBRARY) 51 | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ 52 | 53 | %.so: CFLAGS += -fPIC 54 | %.so: LDFLAGS += -shared 55 | %.so: %.orig.o 56 | $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ 57 | 58 | .SUFFIXES: 59 | .PRECIOUS = %.s %.orig.s %.patched.s 60 | 61 | BINARY_PATCH ?= $(BINARY).kpatch 62 | 63 | ifneq ($(HAS_LIBRARY),) 64 | LIBRARY_PATCH ?= $(LIBRARY).kpatch 65 | $(BINARY): LDLIBS += -l$(TESTNAME) -L$(OBJDIR) 66 | endif 67 | -------------------------------------------------------------------------------- /tests/makefile.inc: -------------------------------------------------------------------------------- 1 | # vim: set filetype=make: 2 | 3 | 4 | DESTDIR ?= . 5 | OBJDIR ?= . 6 | 7 | KPTOOLS:=$(CURDIR)/../../src 8 | KPATCH_GENSRC:=$(KPTOOLS)/kpatch_gensrc 9 | KPATCH_MAKE:=$(KPTOOLS)/kpatch_make 10 | KPATCH_STRIP:=$(KPTOOLS)/kpatch_strip 11 | 12 | get_buildid = $(shell eu-readelf -n $(1) 2>/dev/null | awk '/Build ID:/{print $$3 "'$(2)'"}') 13 | 14 | TESTNAME ?= $(notdir $(CURDIR)) 15 | BINARY ?= $(OBJDIR)/$(TESTNAME) 16 | 17 | ifneq ($(HAS_LIBRARY),) 18 | LIBRARY ?= $(OBJDIR)/lib$(TESTNAME).so 19 | LIBOBJ ?= $(subst .so,,$(LIBRARY)) 20 | endif 21 | 22 | ifneq ($(IS_LIBCARE_CC),y) 23 | include ../makefile-patch.inc 24 | else 25 | include ../makefile-lpmake.inc 26 | endif 27 | 28 | all: $(LIBRARY) $(LIBRARY_PATCH) $(BINARY) $(BINARY_PATCH) 29 | 30 | clean:: 31 | -rm -f $(call get_buildid,$(BINARY),.kpatch) 32 | -rm -f $(BINARY) $(BINARY).stripped $(BINARY).undo-link $(BINARY_PATCH) 33 | ifneq ($(LIBRARY_PATCH),) 34 | test -z "$(LIBRARY_PATCH)" || rm -f $(call get_buildid,$(LIBRARY),.kpatch) 35 | endif 36 | -rm -f $(BINARY).o $(BINARY).orig.o $(BINARY).patched.o 37 | -rm -f $(BINARY).s $(BINARY).orig.s $(BINARY).patched.s 38 | ifneq ($(LIBRARY),) 39 | -rm -f $(LIBOBJ).o $(LIBOBJ).orig.o $(LIBOBJ).patched.o 40 | -rm -f $(LIBOBJ).s $(LIBOBJ).orig.s $(LIBOBJ).patched.s 41 | -rm -f $(LIBRARY) $(LIBRARY).stripped $(LIBRARY).undo-link $(LIBRARY_PATCH) 42 | endif 43 | -rm -rf .kpatch*.s .lpmaketmp lpmake build 44 | 45 | install: all 46 | mkdir -p $(DESTDIR) || : 47 | cp $(BINARY) $(BINARY_PATCH) $(LIBRARY) $(LIBRARY_PATCH) $(DESTDIR) 48 | if test -n "$(BINARY_PATCH)"; then \ 49 | cp $(call get_buildid,$(BINARY),.kpatch) $(DESTDIR); \ 50 | fi 51 | if test -n "$(LIBRARY_PATCH)"; then \ 52 | cp -L $(call get_buildid,$(LIBRARY),.kpatch) $(DESTDIR);\ 53 | fi 54 | -------------------------------------------------------------------------------- /tests/new_func/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/new_func/desc: -------------------------------------------------------------------------------- 1 | patch adds a function 2 | -------------------------------------------------------------------------------- /tests/new_func/new_func.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print_greetings_patched(void) 5 | { 6 | printf("Hello. This is a PATCHED version\n"); 7 | } 8 | 9 | void print_greetings(void) 10 | { 11 | printf("Hello. This is an UNPATCHED version\n"); 12 | } 13 | 14 | int main() 15 | { 16 | while (1) { 17 | print_greetings(); 18 | sleep(1); 19 | } 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /tests/new_func/new_func.diff: -------------------------------------------------------------------------------- 1 | --- ./new_func.c 2016-02-03 22:17:10.665376232 +0300 2 | +++ ./new_func.c 2016-02-03 22:17:48.688490849 +0300 3 | @@ -6,9 +6,14 @@ char *print_greetings_patched(void) 4 | printf("Hello. This is a PATCHED version\n"); 5 | } 6 | 7 | +void newly_added_function(void) 8 | +{ 9 | + print_greetings_patched(); 10 | +} 11 | + 12 | void print_greetings(void) 13 | { 14 | - printf("Hello. This is an UNPATCHED version\n"); 15 | + newly_added_function(); 16 | } 17 | 18 | int main() 19 | -------------------------------------------------------------------------------- /tests/patchlevel/Makefile: -------------------------------------------------------------------------------- 1 | 2 | 3 | all: first second 4 | 5 | build_patchlevel = \ 6 | for f in $$(find -type l -name '*.kpatch'); do \ 7 | buildid=$${f%.kpatch}; \ 8 | buildid="$${buildid\#\#*/}"; \ 9 | mkdir -p patchlevel-root/$${buildid}/$(1) || :; \ 10 | cp $$f patchlevel-root/$${buildid}/$(1)/kpatch.bin; \ 11 | done 12 | 13 | first: FORCE 14 | make -f makefile.first clean all 15 | $(call build_patchlevel,1) 16 | 17 | second: FORCE 18 | make -f makefile.second clean all 19 | $(call build_patchlevel,2) 20 | 21 | clean: 22 | make -f makefile.first clean 23 | rm -fr patchlevel-root 24 | 25 | install: 26 | make -f makefile.second install 27 | 28 | FORCE: 29 | -------------------------------------------------------------------------------- /tests/patchlevel/desc_: -------------------------------------------------------------------------------- 1 | check how patchlevel handling is working 2 | -------------------------------------------------------------------------------- /tests/patchlevel/libpatchlevel.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static const char *msg = "Hello from %s shared library\n"; 4 | void print_greetings(void) 5 | { 6 | printf(msg, "UNPATCHED"); 7 | } 8 | -------------------------------------------------------------------------------- /tests/patchlevel/libpatchlevel.diff: -------------------------------------------------------------------------------- 1 | --- ./libpatchlevel.c 2016-02-07 21:05:13.564346849 +0300 2 | +++ ./libpatchlevel.c 2016-02-07 21:05:21.748640167 +0300 3 | @@ -3,5 +3,5 @@ 4 | static const char *msg = "Hello from %s shared library\n"; 5 | void print_greetings(void) 6 | { 7 | - printf(msg, "UNPATCHED"); 8 | + printf(msg, "SEMIPATCHED"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/patchlevel/libpatchlevel.diff2: -------------------------------------------------------------------------------- 1 | --- ./libpatchlevel.c 2016-02-07 21:05:13.564346849 +0300 2 | +++ ./libpatchlevel.c 2016-02-07 21:05:21.748640167 +0300 3 | @@ -3,5 +3,5 @@ 4 | static const char *msg = "Hello from %s shared library\n"; 5 | void print_greetings(void) 6 | { 7 | - printf(msg, "UNPATCHED"); 8 | + printf(msg, "PATCHED"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/patchlevel/makefile.first: -------------------------------------------------------------------------------- 1 | 2 | HAS_LIBRARY := 1 3 | DIFFEXT := diff 4 | 5 | include ../makefile.inc 6 | -------------------------------------------------------------------------------- /tests/patchlevel/makefile.second: -------------------------------------------------------------------------------- 1 | 2 | HAS_LIBRARY := 1 3 | DIFFEXT := diff2 4 | 5 | include ../makefile.inc 6 | -------------------------------------------------------------------------------- /tests/patchlevel/patchlevel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern void print_greetings(void); 5 | 6 | void local_print_greetings(void) 7 | { 8 | print_greetings(); 9 | } 10 | 11 | int main() 12 | { 13 | while(1) { 14 | local_print_greetings(); 15 | sleep(1); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/patchlevel/patchlevel.diff: -------------------------------------------------------------------------------- 1 | --- ./patchlevel.c 2016-05-11 03:49:22.378062900 +0300 2 | +++ ./patchlevel.c 2016-05-11 03:49:36.362130679 +0300 3 | @@ -6,6 +6,7 @@ 4 | void local_print_greetings(void) 5 | { 6 | print_greetings(); 7 | + printf("Welcome from SEMIPATCHED binary!\n"); 8 | } 9 | 10 | int main() 11 | -------------------------------------------------------------------------------- /tests/patchlevel/patchlevel.diff2: -------------------------------------------------------------------------------- 1 | --- ./patchlevel.c 2016-05-11 03:49:22.378062900 +0300 2 | +++ ./patchlevel.c 2016-05-11 03:49:36.362130679 +0300 3 | @@ -6,6 +6,7 @@ 4 | void local_print_greetings(void) 5 | { 6 | print_greetings(); 7 | + printf("Welcome from PATCHED binary!\n"); 8 | } 9 | 10 | int main() 11 | -------------------------------------------------------------------------------- /tests/ref_glibc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/ref_glibc/desc: -------------------------------------------------------------------------------- 1 | reference to a glibc 2 | -------------------------------------------------------------------------------- /tests/ref_glibc/ref_glibc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print_greetings(void) 5 | { 6 | printf("Hello. This is an UNPATCHED version!\n"); 7 | } 8 | 9 | int main() 10 | { 11 | while (1) { 12 | print_greetings(); 13 | sleep(1); 14 | } 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/ref_glibc/ref_glibc.diff: -------------------------------------------------------------------------------- 1 | --- ./ref_glibc.c 2016-02-07 18:19:06.154834106 +0300 2 | +++ ./ref_glibc.c 2016-02-07 18:19:16.709477075 +0300 3 | @@ -3,7 +3,7 @@ 4 | 5 | void print_greetings(void) 6 | { 7 | - printf("Hello. This is an UNPATCHED version!\n"); 8 | + puts("Hello. This is a PATCHED version!\n"); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/ref_glibc_data/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/ref_glibc_data/desc: -------------------------------------------------------------------------------- 1 | reference data symbol from glibc (stderr) 2 | -------------------------------------------------------------------------------- /tests/ref_glibc_data/ref_glibc_data.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void local_print_greetings(void) 5 | { 6 | printf("Hello from UNPATCHED\n"); 7 | } 8 | 9 | int main() 10 | { 11 | while(1) { 12 | local_print_greetings(); 13 | sleep(1); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/ref_glibc_data/ref_glibc_data.diff: -------------------------------------------------------------------------------- 1 | --- ./ref_glibc_data.c 2016-05-31 19:21:26.701317475 +0300 2 | +++ ./ref_glibc_data.c 2016-05-31 19:21:41.817395022 +0300 3 | @@ -3,7 +3,7 @@ 4 | 5 | void local_print_greetings(void) 6 | { 7 | - printf("Hello from UNPATCHED\n"); 8 | + fprintf(stderr, "Hello from PATCHED\n"); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/ref_orig_single/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/ref_orig_single/desc: -------------------------------------------------------------------------------- 1 | references original code 2 | -------------------------------------------------------------------------------- /tests/ref_orig_single/ref_orig_single.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void print_second_greetings(void) 5 | { 6 | printf("Hello from UNPATCHED binary\n"); 7 | } 8 | 9 | void print_third_greetings(void) 10 | { 11 | printf("Hello from PATCHED binary!\n"); 12 | } 13 | 14 | void print_greetings(void) 15 | { 16 | print_second_greetings(); 17 | } 18 | 19 | int main() 20 | { 21 | while (1) { 22 | print_greetings(); 23 | sleep(1); 24 | } 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /tests/ref_orig_single/ref_orig_single.diff: -------------------------------------------------------------------------------- 1 | --- ./ref_orig_single.c 2016-02-02 16:57:36.541619206 +0300 2 | +++ ./ref_orig_single.c 2016-02-02 16:57:54.144432487 +0300 3 | @@ -13,7 +13,7 @@ void print_third_greetings(void) 4 | 5 | void print_greetings(void) 6 | { 7 | - print_second_greetings(); 8 | + print_third_greetings(); 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/ref_orig_threads/Makefile: -------------------------------------------------------------------------------- 1 | 2 | LDLIBS = -lpthread 3 | 4 | include ../makefile.inc 5 | -------------------------------------------------------------------------------- /tests/ref_orig_threads/desc: -------------------------------------------------------------------------------- 1 | references original code, threaded 2 | -------------------------------------------------------------------------------- /tests/ref_orig_threads/ref_orig_threads.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void print_patch(void) 6 | { 7 | printf("Hello from thread2 (PATCHED)\n"); 8 | } 9 | 10 | void print_greetings1(void) 11 | { 12 | printf("Hello from thread1 (UNPATCHED)\n"); 13 | } 14 | 15 | void print_greetings2(void) 16 | { 17 | printf("Hello from thread2 (UNPATCHED)\n"); 18 | } 19 | 20 | void *thread1_func(void *unused) 21 | { 22 | while (1) { 23 | print_greetings1(); 24 | sleep(1); 25 | } 26 | } 27 | 28 | void *thread2_func(void *unused) 29 | { 30 | while (1) { 31 | print_greetings2(); 32 | sleep(1); 33 | } 34 | } 35 | 36 | int main() 37 | { 38 | pthread_t thrs[3]; 39 | pthread_create(&thrs[0], NULL, thread1_func, NULL); 40 | pthread_create(&thrs[1], NULL, thread2_func, NULL); 41 | pthread_create(&thrs[2], NULL, thread2_func, NULL); 42 | 43 | pthread_join(thrs[0], NULL); 44 | pthread_join(thrs[1], NULL); 45 | pthread_join(thrs[2], NULL); 46 | 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /tests/ref_orig_threads/ref_orig_threads.diff: -------------------------------------------------------------------------------- 1 | --- ./ref_orig_threads.c 2016-02-02 23:22:54.560688975 +0300 2 | +++ ./ref_orig_threads.c 2016-02-02 23:23:05.112124251 +0300 3 | @@ -14,7 +14,7 @@ void print_greetings1(void) 4 | 5 | void print_greetings2(void) 6 | { 7 | - printf("Hello from thread2 (UNPATCHED)\n"); 8 | + print_patch(); 9 | } 10 | 11 | void *thread1_func(void *unused) 12 | -------------------------------------------------------------------------------- /tests/ref_rodata/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/ref_rodata/desc: -------------------------------------------------------------------------------- 1 | references .rodata 2 | -------------------------------------------------------------------------------- /tests/ref_rodata/ref_rodata.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static char *msg = "UNPATCHED binary!!!"; 5 | 6 | char *get_msg(void) 7 | { 8 | return msg; 9 | } 10 | 11 | void print_greetings(void) 12 | { 13 | printf("Hello. This is a '%s'\n", get_msg()); 14 | } 15 | 16 | int main() 17 | { 18 | while (1) { 19 | print_greetings(); 20 | sleep(1); 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/ref_rodata/ref_rodata.diff: -------------------------------------------------------------------------------- 1 | --- a/ref_rodata.c 2016-02-02 18:05:48.880037651 +0300 2 | +++ b/ref_rodata.c 2016-02-02 18:10:00.303019957 +0300 3 | @@ -5,7 +5,7 @@ 4 | 5 | char *get_msg(void) 6 | { 7 | - return msg; 8 | + return msg + 2; 9 | } 10 | 11 | void print_greetings(void) 12 | -------------------------------------------------------------------------------- /tests/shared/Makefile: -------------------------------------------------------------------------------- 1 | 2 | BINARY_PATCH := 3 | HAS_LIBRARY := 1 4 | 5 | include ../makefile.inc 6 | -------------------------------------------------------------------------------- /tests/shared/desc: -------------------------------------------------------------------------------- 1 | shared library patch 2 | -------------------------------------------------------------------------------- /tests/shared/libshared.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void print_second_greetings(void) 4 | { 5 | printf("Hello from UNPATCHED shared library\n"); 6 | } 7 | 8 | void print_third_greetings(void) 9 | { 10 | printf("Hello from PATCHED shared library!\n"); 11 | } 12 | 13 | void print_greetings(void) 14 | { 15 | print_second_greetings(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/shared/libshared.diff: -------------------------------------------------------------------------------- 1 | --- ./libshared.c 2016-02-05 21:36:39.967343528 +0300 2 | +++ ./libshared.c 2016-02-05 22:06:58.113719493 +0300 3 | @@ -12,5 +12,5 @@ void print_third_greetings(void) 4 | 5 | void print_greetings(void) 6 | { 7 | - print_second_greetings(); 8 | + print_third_greetings(); 9 | } 10 | -------------------------------------------------------------------------------- /tests/shared/shared.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void print_greetings(void); 4 | 5 | int main() 6 | { 7 | while(1) { 8 | print_greetings(); 9 | sleep(1); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/shared_ref_glibc/Makefile: -------------------------------------------------------------------------------- 1 | 2 | BINARY_PATCH := 3 | HAS_LIBRARY := 1 4 | 5 | include ../makefile.inc 6 | -------------------------------------------------------------------------------- /tests/shared_ref_glibc/desc: -------------------------------------------------------------------------------- 1 | shared object reference to a glibc 2 | -------------------------------------------------------------------------------- /tests/shared_ref_glibc/libshared_ref_glibc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void print_greetings(void) 4 | { 5 | printf("Hello from UNPATCHED shared library\n"); 6 | } 7 | -------------------------------------------------------------------------------- /tests/shared_ref_glibc/libshared_ref_glibc.diff: -------------------------------------------------------------------------------- 1 | --- ./libshared_ref_glibc.c 2016-02-07 21:05:13.564346849 +0300 2 | +++ ./libshared_ref_glibc.c 2016-02-07 21:05:21.748640167 +0300 3 | @@ -2,5 +2,5 @@ 4 | 5 | void print_greetings(void) 6 | { 7 | - printf("Hello from UNPATCHED shared library\n"); 8 | + printf("Hello from PATCHED shared library\n"); 9 | } 10 | -------------------------------------------------------------------------------- /tests/shared_ref_glibc/shared_ref_glibc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void print_greetings(void); 4 | 5 | int main() 6 | { 7 | while(1) { 8 | print_greetings(); 9 | sleep(1); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/simplest/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | -------------------------------------------------------------------------------- /tests/simplest/desc: -------------------------------------------------------------------------------- 1 | simple function patch 2 | -------------------------------------------------------------------------------- /tests/simplest/simplest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | void print_second_greetings(void) 6 | { 7 | printf("Hello from another function\n"); 8 | } 9 | 10 | void print_greetings(void) 11 | { 12 | printf("Hello. This is an UNPATCHED version!\n"); 13 | } 14 | 15 | int main() 16 | { 17 | while (1) { 18 | print_greetings(); 19 | sleep(1); 20 | print_second_greetings(); 21 | sleep(1); 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/simplest/simplest.diff: -------------------------------------------------------------------------------- 1 | --- ./simplest.c 2016-01-28 14:02:33.225472198 +0000 2 | +++ ./simplest.c 2016-01-28 14:02:49.645610472 +0000 3 | @@ -8,7 +8,7 @@ void print_second_greetings(void) 4 | 5 | void print_greetings(void) 6 | { 7 | - printf("Hello. This is an UNPATCHED version!\n"); 8 | + volatile int i = 0; 9 | } 10 | 11 | int main() 12 | -------------------------------------------------------------------------------- /tests/tls_shared/Makefile: -------------------------------------------------------------------------------- 1 | 2 | BINARY_PATCH := 3 | HAS_LIBRARY := 1 4 | CFLAGS += -ftls-model=initial-exec 5 | 6 | include ../makefile.inc 7 | -------------------------------------------------------------------------------- /tests/tls_shared/desc: -------------------------------------------------------------------------------- 1 | patch shared lib with TLS entry 2 | -------------------------------------------------------------------------------- /tests/tls_shared/libtls_shared.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static __thread int tls_abc = 10; 4 | 5 | void print_second_greetings(void) 6 | { 7 | tls_abc = 10; 8 | printf("Hello from UNPATCHED shared library\n"); 9 | } 10 | 11 | void print_third_greetings(void) 12 | { 13 | printf("Hello from PATCHED shared library!\n"); 14 | } 15 | 16 | void print_greetings(void) 17 | { 18 | print_second_greetings(); 19 | } 20 | -------------------------------------------------------------------------------- /tests/tls_shared/libtls_shared.diff: -------------------------------------------------------------------------------- 1 | --- ./libtls_shared.c 2016-02-05 21:36:39.967343528 +0300 2 | +++ ./libtls_shared.c 2016-02-05 22:06:58.113719493 +0300 3 | @@ -12,5 +12,7 @@ void print_third_greetings(void) 4 | 5 | void print_greetings(void) 6 | { 7 | - print_second_greetings(); 8 | + if (tls_abc == 10) { 9 | + print_third_greetings(); 10 | + } 11 | } 12 | -------------------------------------------------------------------------------- /tests/tls_shared/tls_shared.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern void print_greetings(void); 4 | 5 | int main() 6 | { 7 | while(1) { 8 | print_greetings(); 9 | sleep(1); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/tls_simple/Makefile: -------------------------------------------------------------------------------- 1 | 2 | include ../makefile.inc 3 | 4 | ifneq ($(IS_LIBCARE_CC),y) 5 | TGT := tls_simple 6 | TGT_LDFLAGS := 7 | include ../makefile-patch-link.inc 8 | endif 9 | -------------------------------------------------------------------------------- /tests/tls_simple/desc: -------------------------------------------------------------------------------- 1 | test simple tls reference 2 | -------------------------------------------------------------------------------- /tests/tls_simple/tls_simple.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | int *p; 7 | int __thread v; 8 | 9 | void print_greetings(void) 10 | { 11 | printf("TLS UNPATCHED\n"); 12 | } 13 | 14 | int main() 15 | { 16 | v = 0xDEADBEAF; 17 | p = &v; 18 | 19 | while (1) { 20 | print_greetings(); 21 | sleep(1); 22 | } 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/tls_simple/tls_simple.diff: -------------------------------------------------------------------------------- 1 | --- ./tls_simple.c 2016-07-15 14:13:57.734443175 +0200 2 | +++ ./tls_simple.c 2016-07-15 14:14:09.523350690 +0200 3 | @@ -7,7 +7,12 @@ 4 | 5 | void print_greetings(void) 6 | { 7 | - printf("TLS UNPATCHED\n"); 8 | + if (p == &v && v == 0xDEADBEAF) { 9 | + printf("TLS PATCHED\n"); 10 | + } else { 11 | + /* Provoke segfault w/o messing with %rax */ 12 | + asm ("movl $0, %ebx; movl $0, (%rbx)"); 13 | + } 14 | } 15 | 16 | int main() 17 | -------------------------------------------------------------------------------- /vagrant_boxes: -------------------------------------------------------------------------------- 1 | centos-5.11 2 | centos-6.3 3 | centos-6.4 4 | centos-6.5 5 | centos-6.6 6 | centos-6.7 7 | centos-6.8 8 | centos-7.0 9 | centos-7.1 10 | centos-7.2 11 | centos-7.3 12 | centos-7.4 13 | ubuntu-14.04 14 | ubuntu-14.04-lts-utopic 15 | ubuntu-14.04-lts-vivid 16 | ubuntu-14.04-lts-wily 17 | ubuntu-14.04-lts-xenial 18 | ubuntu-16.04 19 | debian-7.11.0 20 | debian-8.9.0 21 | debian-9.1.0 22 | --------------------------------------------------------------------------------