Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 10 Dec 2000 10:36:05 -0800
From:      Jake Burkholder <jburkhol@home.com>
To:        Alfred Perlstein <bright@wintelcom.net>
Cc:        hackers@FreeBSD.ORG
Subject:   Re: Patching live kernels 
Message-ID:  <20001210183605.5DD3FBA7D@io.yi.org>
In-Reply-To: Message from Alfred Perlstein <bright@wintelcom.net>  of "Sun, 10 Dec 2000 04:42:32 PST." <20001210044232.D16205@fw.wintelcom.net>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multipart MIME message.

--==_Exmh_-6582008560
Content-Type: text/plain; charset=us-ascii

> Ok, sometimes we find a bug in a particular release where what's
> needed is a function replaced with fixed code.
> 
> I'm wondering if it's possible to:
> 
> 1) look at the kernel symbol table for a particular function in a
>    particular object file (static functions would be even better?)
> 2) replace the first instruction in the function with a jmp to
>    our newly loaded code
> 3) have our newly loaded code be "anonymous" meaning no symbols
>    from it enter the kernel symbol namespace (i want to be able to
>    re-patch a patched kernel)
> 
> Is it possible?
> 
> Are there any takers? :)

io# kldload ./syscall.ko
Dec 10 10:09:14 io /boot/kernel/kernel: syscall loaded at 210
io# ./call
Dec 10 10:09:26 io /boot/kernel/kernel: bad code...

(kgdb) x/6i bad_code
0xc01235a0 <bad_code>:  push   %ebp
0xc01235a1 <bad_code+1>:        mov    %esp,%ebp
0xc01235a3 <bad_code+3>:        push   $0xc02cc6d0
0xc01235a8 <bad_code+8>:        call   0xc0198a30 <printf>
0xc01235ad <bad_code+13>:       leave  
0xc01235ae <bad_code+14>:       ret    
(kgdb) x/s 0xc02cc6d0
0xc02cc6d0 <__umoddi3+672>:      "bad code...\n"

io# kldload ./kpatch.ko
io# ./kpatch -f bad_code -t good_code
Dec 10 10:11:24 io /boot/kernel/kernel: patching from: 0xc01235a0 to: 
0xc0fbd4c4

(kgdb) x/i bad_code
0xc01235a0 <bad_code>:  jmp    0xc0fbd4c4
(kgdb) x/6i 0xc0fbd4c4
0xc0fbd4c4:     push   %ebp
0xc0fbd4c5:     mov    %esp,%ebp
0xc0fbd4c7:     push   $0xc0fbd528
0xc0fbd4cc:     call   0xc0198a30 <printf>
0xc0fbd4d1:     leave  
0xc0fbd4d2:     ret    
(kgdb) x/s 0xc0fbd528
0xc0fbd528:      "good code!!\n"

io# ./call
Dec 10 10:13:17 io /boot/kernel/kernel: good code!!

I'm not sure if static functions are in the kernel symbol table, but you
should be able to get the address with nm and pass it to kpatch instead of
the name, ./kpatch -f 0x... -t good_code; this is untested.  Whatever symbols
are in your module will still be in the kernel symbol table, removing them is
more involved but not impossible, all the magic is in kern/kern_linker.c.
Just rename whatever function you are loading, repatch, and then unload the
old module.  If you can ensure that nothing will call the loaded code while
you're repatching you should be able to unload first to avoid the renaming.
Use with extreme caution.

Release, pff, you don't fool me  :)

Jake

--==_Exmh_-6582008560
Content-Type: text/plain ; name="Makefile"; charset=us-ascii
Content-Description: Makefile
Content-Disposition: attachment; filename="Makefile"

# Makefile for building the sample syscall module


SRCS	= kpatch-sys.c
KMOD	= kpatch
KO	= ${KMOD}.ko
KLDMOD	= t

kpatch:
	cc -o kpatch kpatch.c

CLEANFILES+=	kpatch

.include <bsd.kmod.mk>

CFLAGS=	-D_KERNEL -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -fformat-extensions -DKLD_MODULE -nostdinc -I-   -I. -I@ -I@/../include  -mpreferred-stack-boundary=2

--==_Exmh_-6582008560
Content-Type: text/plain ; name="kpatch.c"; charset=us-ascii
Content-Description: kpatch.c
Content-Disposition: attachment; filename="kpatch.c"

#include <sys/param.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/syscall.h>
#include <err.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>

extern char *optarg;
extern int optind;

static void usage(void);

int
main(int ac, char **av)
{
	struct kld_sym_lookup from;
	struct kld_sym_lookup to;
	struct module_stat stat;
	int kfid;
	int mfid;
	int c;

	bzero(&from, sizeof from);
	bzero(&stat, sizeof stat);
	bzero(&to, sizeof to);

	from.version = sizeof from;
	stat.version = sizeof stat;
	to.version = sizeof to;

	while ((c = getopt(ac, av, "f:t:")) != -1)
		switch (c) {
		case 'f':
			if (bcmp(optarg, "0x", 2) == 0)
				from.symvalue = strtol(optarg, NULL, 16);
			else
				from.symname = optarg;
			break;
		case 't':
			if (bcmp(optarg, "0x", 2) == 0)
				to.symvalue = strtol(optarg, NULL, 16);
			else
				to.symname = optarg;
			break;
		case '?':
		default:
			usage();
		}

	if (from.symvalue == 0) {
		kfid = kldfind("kernel");
		kldsym(kfid, KLDSYM_LOOKUP, &from);
	}

	if (to.symvalue == 0) {
		for (mfid = 0; (mfid = kldnext(mfid)) != 0;)
			if (to.symvalue == 0 &&
			    kldsym(mfid, KLDSYM_LOOKUP, &to) == 0)
				break;
	}

	printf("from: 0x%lx to: 0x%lx\n", from.symvalue, to.symvalue);
	modstat(modfind("kpatch"), &stat);
	return syscall(stat.data.intval, from.symvalue, to.symvalue);
}

static void
usage(void)
{
	fprintf(stderr, "usage: kpatch -f <from> -t <to>\n");
	exit(EX_USAGE);
}

--==_Exmh_-6582008560
Content-Type: text/plain ; name="kpatch-sys.c"; charset=us-ascii
Content-Description: kpatch-sys.c
Content-Disposition: attachment; filename="kpatch-sys.c"

#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/module.h>
#include <sys/sysent.h>
#include <sys/kernel.h>
#include <sys/systm.h>

extern char do_jmp[];
extern char do_jmp_end[];

asm(
"	.globl	do_jmp ;			"
"do_jmp:					"
"	jmp	. + 1 << 16 ;			"
"	.globl	do_jmp_end ;			"
"do_jmp_end:					"
);

struct kpatch_args {
	char *	from;
	char *	to;
};

static int
kpatch(struct proc *p, struct kpatch_args *uap)
{

	printf("patching from: %p to: %p\n", uap->from, uap->to);
	bcopy(do_jmp, uap->from, do_jmp_end - do_jmp);
	uap->from++;
	*(int *)uap->from = uap->to - uap->from - 4;

	return 0;
}

static struct sysent kpatch_sysent = {
	2,
	(sy_call_t *)kpatch
};

static int offset = NO_SYSCALL;

static int
kpatch_load(struct module *module, int cmd, void *arg)
{
	int error = 0;

	switch (cmd) {
	case MOD_LOAD:
		printf("kpatch loaded at %d\n", offset);
		break;
	case MOD_UNLOAD:
		printf("kpatch unloaded from %d\n", offset);
		break;
	default:
		error = EINVAL;
		break;
	}
	return error;
}

SYSCALL_MODULE(kpatch, &offset, &kpatch_sysent, kpatch_load, NULL);

--==_Exmh_-6582008560--




To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-hackers" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001210183605.5DD3FBA7D>