Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store coroutine_env_offset in patch header #39

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions scripts/pkgbuild
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,13 @@ kp_build() {
kp_gen_kpatch() {
echo " generating kpatches"

pushd $HOME/root.patched
if test -n $KP_PATCHED_BINARIES; then
targets=$KP_PATCHED_BINARIES
else
pushd $HOME/root.patched
targets=$(find . -perm /0111 -type f)
popd
popd
fi

rm -rf $HOME/${KP_PROJECT_PATCH%.*}
mkdir $HOME/${KP_PROJECT_PATCH%.*}
Expand All @@ -365,14 +369,20 @@ kp_gen_kpatch() {

eu-unstrip "$HOME/root.original/$t" "$debug"

local coroutine_arg=""
if test -n "$COROUTINE_ENV_OFFSET"; then
coroutine_arg="-c $COROUTINE_ENV_OFFSET"
fi

$KPATCH_PATH/kpatch_strip --strip $patched $patched.kpstripped
cp $patched.kpstripped $patched.relfixup
$KPATCH_PATH/kpatch_strip --rel-fixup $debug $patched.relfixup
cp $patched.relfixup $patched.stripped
/usr/bin/strip --strip-unneeded $patched.stripped
cp $patched.stripped $patched.undolink
$KPATCH_PATH/kpatch_strip --undo-link $debug $patched.undolink
$KPATCH_PATH/kpatch_make -b "$buildid" $patched.undolink -o $patched.kpatch
$KPATCH_PATH/kpatch_make -b "$buildid" $coroutine_arg \
$patched.undolink -o $patched.kpatch
cp $patched.kpatch $HOME/${KP_PROJECT_PATCH%.*}/$buildid.kpatch
no_patches=0
done
Expand Down Expand Up @@ -447,6 +457,10 @@ kp_patch_test() {
exit 1
}

kp_calc_coroutine_env_offset() {
unset COROUTINE_ENV_OFFSET
}

main() {
echo "Starting at "`date +%T`"..."

Expand All @@ -469,6 +483,7 @@ main() {
kp_build_hook
kp_build
kp_sanity_check
kp_calc_coroutine_env_offset
kp_gen_kpatch
kp_pack_patch
elif test "$ACTION" == "test"; then
Expand Down
123 changes: 80 additions & 43 deletions src/kpatch_coro.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <libunwind-ptrace.h>

#include <sys/utsname.h>
#include <sys/mman.h>

#include <asm/prctl.h>

Expand Down Expand Up @@ -115,8 +116,6 @@ kpatch_coro_free(struct kpatch_coro *c)
#define STACK_OFFSET_COROUTINE_UCONTEXT (7 * sizeof(long))
#define STACK_OFFSET_COROUTINE (8 * sizeof(long))

#define UCONTEXT_OFFSET_JMPBUF 0x38

#define UCONTEXT_OFFSET_UC_STACK_SS_SP offsetof(ucontext_t, uc_stack.ss_sp)
#define UCONTEXT_OFFSET_UC_STACK_SS_SIZE offsetof(ucontext_t, uc_stack.ss_size)

Expand Down Expand Up @@ -235,60 +234,40 @@ int is_centos7_qemu(struct kpatch_process *proc)
return 1;
};


static int qemu_centos7_find_coroutines(struct kpatch_process *proc)
static int get_patch_coroutine_env_offset(struct kpatch_process *proc)
{
struct object_file *oheap, *tcmalloc;
struct process_mem_iter *iter;
struct kpatch_coro *coro;
struct vm_area heap;
unsigned long __start_context, ptr_guard, cur;
int ret;

if (!is_test_target(proc, "fail_coro") && !is_centos7_qemu(proc))
return CORO_SEARCH_NEXT;

kpdebug("Looking for coroutines in QEMU %d...\n", proc->pid);
oheap = kpatch_process_get_obj_by_regex(proc, "^\\[heap\\]$");
tcmalloc = kpatch_process_get_obj_by_regex(proc, "^libtcmalloc.*\\.so\\.4.*");
if (!oheap) {
kpdebug("FAIL. Can't find [heap](%p)\n", oheap);
return -1;
}
struct object_file *o;
list_for_each_entry(o, &proc->objs, list) {
if (!o->name || strcmp(proc->comm, o->name))
continue;

/* NOTE(pboldin) We accurately craft stack for test so we
* don't need tcmalloc installed and used
*/
if (!tcmalloc && !is_test_target(proc, "fail_coro")) {
kpdebug("FAIL. Can't find tcmalloc lib. Full [heap] scan is not "
"implemented yet");
return -1;
}
heap = list_first_entry(&oheap->vma, struct obj_vm_area, list)->inmem;
if (o->kpfile.patch)
return o->kpfile.patch->coroutine_env_offset;

ret = locate_start_context_symbol(proc, &__start_context);
if (ret < 0) {
kpdebug("FAIL. Can't locate_start_context_symbol\n");
return CORO_SEARCH_NEXT;
if (o->skpfile && o->skpfile->patch)
return o->skpfile->patch->coroutine_env_offset;
}
return 0;
}

ret = get_ptr_guard(proc, &ptr_guard);
if (ret < 0) {
kpdebug("FAIL. Can't get_ptr_guard\n");
return -1;
}
static int qemu_centos7_find_coroutines_vma(struct kpatch_process *proc, struct vm_area vma,
unsigned long __start_context, unsigned long ptr_guard, int patch_coroutine_env_offset)
{
int ret;
struct process_mem_iter *iter;
unsigned long cur;
struct kpatch_coro *coro;

iter = kpatch_process_mem_iter_init(proc);
if (iter == NULL) {
kpdebug("FAIL. Can't allocate process memory iterator.\n");
return -1;
}

for (cur = heap.start; cur < heap.end; cur += PAGE_SIZE) {
for (cur = vma.start; cur < vma.end; cur += PAGE_SIZE) {
unsigned long val, val2;
unsigned long ptr = cur + PAGE_SIZE;
unsigned long *regs;

val = PEEK_ULONG(ptr - STACK_OFFSET_START_CONTEXT);
if (val != __start_context)
continue;
Expand All @@ -308,7 +287,7 @@ static int qemu_centos7_find_coroutines(struct kpatch_process *proc)
break;
}
ret = kpatch_process_mem_read(proc,
val + UCONTEXT_OFFSET_JMPBUF,
val + patch_coroutine_env_offset,
&coro->env,
sizeof(coro->env));
if (ret < 0) {
Expand All @@ -321,8 +300,66 @@ static int qemu_centos7_find_coroutines(struct kpatch_process *proc)
regs[JB_RSP] = PTR_DEMANGLE(regs[JB_RSP], ptr_guard);
regs[JB_RIP] = PTR_DEMANGLE(regs[JB_RIP], ptr_guard);
}

kpatch_process_mem_iter_free(iter);
return ret;
}

static int qemu_centos7_find_coroutines(struct kpatch_process *proc)
{
struct object_file *oanonymous, *oheap, *tcmalloc;
struct obj_vm_area *ovma;
struct vm_area heap;
unsigned long __start_context, ptr_guard;
int patch_coroutine_env_offset ;
int ret;

if (!is_test_target(proc, "fail_coro") && !is_centos7_qemu(proc))
return CORO_SEARCH_NEXT;

kpdebug("Looking for coroutines in QEMU %d...\n", proc->pid);
tcmalloc = kpatch_process_get_obj_by_regex(proc, "^libtcmalloc.*\\.so\\.4.*");

/* NOTE(pboldin) We accurately craft stack for test so we
* don't need tcmalloc installed and used
*/
if (!tcmalloc && !is_test_target(proc, "fail_coro")) {
kpdebug("FAIL. Can't find tcmalloc lib. Full [heap] scan is not "
"implemented yet");
return -1;
}

ret = get_ptr_guard(proc, &ptr_guard);
if (ret < 0) {
kpdebug("FAIL. Can't get_ptr_guard\n");
return -1;
}

ret = locate_start_context_symbol(proc, &__start_context);
if (ret < 0) {
kpdebug("FAIL. Can't locate_start_context_symbol\n");
return CORO_SEARCH_NEXT;
}

patch_coroutine_env_offset = get_patch_coroutine_env_offset(proc);
if (patch_coroutine_env_offset <= 0) {
kpdebug("FAIL. No patch_coroutine_env_offset\n");
return patch_coroutine_env_offset;
}

oheap = kpatch_process_get_obj_by_regex(proc, "^\\[heap\\]$");
if (!oheap) {
kpdebug("FAIL. Can't find [heap](%p)\n", oheap);
return -1;
}
heap = list_first_entry(&oheap->vma, struct obj_vm_area, list)->inmem;
qemu_centos7_find_coroutines_vma(proc, heap, __start_context, ptr_guard, patch_coroutine_env_offset);

oanonymous = kpatch_process_get_obj_by_regex(proc, "\\[anonymous\\]$");
list_for_each_entry(ovma, &oanonymous->vma, list) {
if (!(ovma->inmem.prot & (PROT_READ|PROT_WRITE)))
continue;
qemu_centos7_find_coroutines_vma(proc, ovma->inmem, __start_context, ptr_guard, patch_coroutine_env_offset);
}

return ret;
}
Expand Down
3 changes: 2 additions & 1 deletion src/kpatch_file.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ struct kpatch_file {
};

char srcversion[25]; /* srcversion of module or zeros */
char pad2[231];
uint32_t coroutine_env_offset;
char pad2[227];

/* relocations */
/* content */
Expand Down
16 changes: 13 additions & 3 deletions src/kpatch_make.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static void xerror(const char *fmt, ...)
exit(1);
}

int make_file(int fdo, void *buf1, off_t size, const char *buildid)
int make_file(int fdo, void *buf1, off_t size, const char *buildid, int coroutine_env_offset)
{
int res;
struct kpatch_file khdr;
Expand All @@ -44,6 +44,8 @@ int make_file(int fdo, void *buf1, off_t size, const char *buildid)
size = ALIGN(size, 16);
khdr.total_size = khdr.kpatch_offset + size;

khdr.coroutine_env_offset = coroutine_env_offset;

res = write(fdo, &khdr, sizeof(khdr));
res += write(fdo, buf1, size);

Expand All @@ -57,6 +59,7 @@ static void usage(void)
{
printf("Usage: kpatch_make [-d] -n <modulename> [-v <version>] -e <entryaddr> [-o <output>] <input1> [input2]\n");
printf(" -b buildid = target buildid for patch\n");
printf(" -c offset = env offset in coroutine\n");
printf(" -d debug (verbose)\n");
printf("\n");
printf(" result is printed to output and is the following:\n");
Expand All @@ -72,8 +75,9 @@ int main(int argc, char **argv)
void *buf;
struct stat st;
char *buildid = NULL, *outputname = NULL;
int coroutine_env_offset = -1;

while ((opt = getopt(argc, argv, "db:o:v:s:")) != -1) {
while ((opt = getopt(argc, argv, "db:o:v:s:c:")) != -1) {
switch (opt) {
case 'd':
verbose = 1;
Expand All @@ -84,6 +88,12 @@ int main(int argc, char **argv)
case 'o':
outputname = strdup(optarg);
break;
case 'c':
if (sscanf(optarg, "%d", &coroutine_env_offset) != 1) {
printf("Error: can't parse jmpbuf offset for coroutines.\n");
usage();
}
break;
default: /* '?' */
usage();
}
Expand All @@ -109,5 +119,5 @@ int main(int argc, char **argv)
xerror("Can't open output file '%s'", outputname);
}

return make_file(fdo, buf, st.st_size, buildid);
return make_file(fdo, buf, st.st_size, buildid, coroutine_env_offset);
}