2002-10-03 12:08:26

by Vamsi Krishna S .

[permalink] [raw]
Subject: [rfc] [patch] kernel hooks

The Kernel Hooks Interface is a generalised facility for placing
hooks in arbitrary kernel locations. A hook is a location in the
kernel that calls out of the kernel to a kernel module routine -
a hook exit routine. It enables many kernel enhancements, which
are otherwise self-contained, to become loadable kernel modules
and retain a substantial degree of independence from the kernel
source.

Hooks are implemented as fast and slim-line insertions to kernel
space code. When not active that have practically no overhead to
the kernel. Optimized versions are implemented on ia32, ppc,
ppc64, s390, s390x archs, where they expand to:
movl $0, %reg
^x
testl %reg, %reg
jz out
call out exits
out:

Hooks are armed by editing the number at ^x above to a non-zero
value so that the exits will be now be called out.

These hooks are useful especially for invasive facilities like
security hooks and tracing hooks, which should be compiled in, but
could be inactive most of the time. The overhead of these hooks
is very little when they are not armed. These are used by the
Dynamic Probes (http://www.ibm.com/linux/ltc/projects/dprobes/)
and the Linux Kernel State Tracer at (http://lkst.sourceforge.net/)

The patch given below is a slimmed down version of what is at the
project website http://www.ibm.com/linux/ltc/projects/kernelhooks/.
I have removed from the full package:
- /proc interface for arming/disarming hooks
- documentation and sample code (could be posted as a seperate patch)
- some extra paranoid checks
- hook exit priorities
- open coded exit lists in favour of using include/linux/list.h
implementation
- kill typedefs and a number of other cleanups

Todo:
- find some way to eliminate the need for creating a symbol at the
hook location so as not to confuse disassembers. We should write
the address of the hook location in a seperate section, may be,
and set a variable to that location.

Note that the hook structure defined at each hook location is
exported for use by GPL'ed modules only, just like the security hooks.

Here is an example illustrating how hooks are placed and used:
Defining a hook:
In the source file where you want to place the hook:
DECLARE_HOOK(my_hook);

and at the location where the hook is desired:

...
HOOK(my_hook, parm1, parm2);
...
Now, for this location, exit functions will be called out
when they are registered and enabled.

Before a hook can be used, it needs to be initialised:
...
hook_initialise(my_hook);

...
hook_terminate(my_hook, force);

To register an exit, say in a different module:

struct hook_rec rec = {
.hook_exit = exit_fn;
}
...
hook_exit_register(my_hook, &rec);

...
hook_exit_deregister(&rec);

To enable/disable a hook exit:
...
hook_exit_arm(&rec);

...
hook_exit_disarm(&rec);

Any ideas, suggestions, comments are welcome.

Thanks,
Vamsi.
--
Vamsi Krishna S.
Linux Technology Center,
IBM Software Lab, Bangalore.
Ph: +91 80 5044959
Internet: [email protected]
--
arch/alpha/config.in | 1
arch/arm/config.in | 1
arch/cris/config.in | 1
arch/i386/Config.help | 9 +
arch/i386/config.in | 4
arch/ia64/config.in | 1
arch/m68k/config.in | 1
arch/mips/config.in | 1
arch/mips64/config.in | 1
arch/parisc/config.in | 1
arch/ppc/config.in | 4
arch/ppc64/config.in | 4
arch/s390/config.in | 4
arch/s390x/config.in | 4
arch/sh/config.in | 1
arch/sparc/config.in | 2
arch/sparc64/config.in | 1
arch/x86_64/config.in | 1
include/asm-generic/hook.h | 25 +++++
include/asm-i386/hook.h | 47 +++++++++
include/asm-ppc/hook.h | 46 +++++++++
include/asm-ppc64/hook.h | 46 +++++++++
include/asm-s390/hook.h | 48 +++++++++
include/asm-s390x/hook.h | 48 +++++++++
include/linux/hook.h | 225 +++++++++++++++++++++++++++++++++++++++++++++
kernel/Makefile | 3
kernel/hook.c | 165 +++++++++++++++++++++++++++++++++
27 files changed, 693 insertions(+), 2 deletions(-)
--
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/alpha/config.in 40-hooks/arch/alpha/config.in
--- /usr/src/40-pure/arch/alpha/config.in 2002-10-01 12:36:24.000000000 +0530
+++ 40-hooks/arch/alpha/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -394,6 +394,7 @@
define_tristate CONFIG_MATHEMU y
fi

+tristate ' Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/arm/config.in 40-hooks/arch/arm/config.in
--- /usr/src/40-pure/arch/arm/config.in 2002-10-01 12:36:22.000000000 +0530
+++ 40-hooks/arch/arm/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -546,6 +546,7 @@
dep_bool ' Kernel low-level debugging functions' CONFIG_DEBUG_LL $CONFIG_DEBUG_KERNEL
dep_bool ' Kernel low-level debugging messages via footbridge serial port' CONFIG_DEBUG_DC21285_PORT $CONFIG_DEBUG_LL $CONFIG_FOOTBRIDGE
dep_bool ' Kernel low-level debugging messages via UART2' CONFIG_DEBUG_CLPS711X_UART2 $CONFIG_DEBUG_LL $CONFIG_ARCH_CLPS711X
+tristate ' Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/cris/config.in 40-hooks/arch/cris/config.in
--- /usr/src/40-pure/arch/cris/config.in 2002-10-01 12:37:03.000000000 +0530
+++ 40-hooks/arch/cris/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -213,6 +213,7 @@
if [ "$CONFIG_SOUND" != "n" ]; then
source sound/Config.in
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source drivers/usb/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/i386/Config.help 40-hooks/arch/i386/Config.help
--- /usr/src/40-pure/arch/i386/Config.help 2002-10-01 12:36:17.000000000 +0530
+++ 40-hooks/arch/i386/Config.help 2002-10-03 16:10:17.000000000 +0530
@@ -1058,3 +1058,12 @@
absence of features.

For more information take a look at Documentation/swsusp.txt.
+
+Kernel Hook Support
+CONFIG_HOOK
+ The Kernel Hooks Interface is a generalised facility for placing hooks in
+ arbitrary kernel locations. A hook is a location in the kernel that
+ calls out of the kernel to a kernel module routine - a hook exit routine.
+ http://www-124.ibm.com/linux/projects/kernelhooks for more details.
+ If in doubt, say N.
+
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/i386/config.in 40-hooks/arch/i386/config.in
--- /usr/src/40-pure/arch/i386/config.in 2002-10-01 12:36:28.000000000 +0530
+++ 40-hooks/arch/i386/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -461,6 +461,10 @@
define_bool CONFIG_X86_MPPARSE y
fi

+tristate 'Kernel Hook support' CONFIG_HOOK
+ if [ "$CONFIG_HOOK" != "n" ]; then
+ define_bool CONFIG_ASM_HOOK y
+ fi
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/ia64/config.in 40-hooks/arch/ia64/config.in
--- /usr/src/40-pure/arch/ia64/config.in 2002-10-01 12:37:56.000000000 +0530
+++ 40-hooks/arch/ia64/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -281,6 +281,7 @@
bool ' Turn on irq debug checks (slow!)' CONFIG_IA64_DEBUG_IRQ
fi

+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/m68k/config.in 40-hooks/arch/m68k/config.in
--- /usr/src/40-pure/arch/m68k/config.in 2002-10-01 12:37:33.000000000 +0530
+++ 40-hooks/arch/m68k/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -543,6 +543,7 @@
bool ' Verbose BUG() reporting' CONFIG_DEBUG_BUGVERBOSE
fi

+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/mips/config.in 40-hooks/arch/mips/config.in
--- /usr/src/40-pure/arch/mips/config.in 2002-10-01 12:37:10.000000000 +0530
+++ 40-hooks/arch/mips/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -492,6 +492,7 @@
if [ "$CONFIG_SMP" != "y" ]; then
bool 'Run uncached' CONFIG_MIPS_UNCACHED
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/mips64/config.in 40-hooks/arch/mips64/config.in
--- /usr/src/40-pure/arch/mips64/config.in 2002-10-01 12:37:12.000000000 +0530
+++ 40-hooks/arch/mips64/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -246,6 +246,7 @@
if [ "$CONFIG_SMP" != "y" ]; then
bool 'Run uncached' CONFIG_MIPS_UNCACHED
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/parisc/config.in 40-hooks/arch/parisc/config.in
--- /usr/src/40-pure/arch/parisc/config.in 2002-10-01 12:37:48.000000000 +0530
+++ 40-hooks/arch/parisc/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -188,6 +188,7 @@

#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/ppc/config.in 40-hooks/arch/ppc/config.in
--- /usr/src/40-pure/arch/ppc/config.in 2002-10-01 12:37:02.000000000 +0530
+++ 40-hooks/arch/ppc/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -612,6 +612,10 @@
-o "$CONFIG_SANDPOINT" = "y" -o "$CONFIG_ZX4500" = "y" ]; then
bool 'Support for early boot texts over serial port' CONFIG_SERIAL_TEXT_DEBUG
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
+ if [ "$CONFIG_HOOK" != "n" ]; then
+ define_bool CONFIG_ASM_HOOK y
+ fi
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/ppc64/config.in 40-hooks/arch/ppc64/config.in
--- /usr/src/40-pure/arch/ppc64/config.in 2002-10-01 12:36:18.000000000 +0530
+++ 40-hooks/arch/ppc64/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -202,6 +202,10 @@
fi
bool ' Include PPCDBG realtime debugging' CONFIG_PPCDBG
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
+ if [ "$CONFIG_HOOK" != "n" ]; then
+ define_bool CONFIG_ASM_HOOK y
+ fi
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/s390/config.in 40-hooks/arch/s390/config.in
--- /usr/src/40-pure/arch/s390/config.in 2002-10-01 12:37:35.000000000 +0530
+++ 40-hooks/arch/s390/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -73,6 +73,10 @@
# bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
#fi
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+tristate 'Kernel Hook support' CONFIG_HOOK
+ if [ "$CONFIG_HOOK" != "n" ]; then
+ define_bool CONFIG_ASM_HOOK y
+ fi
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/s390x/config.in 40-hooks/arch/s390x/config.in
--- /usr/src/40-pure/arch/s390x/config.in 2002-10-01 12:36:17.000000000 +0530
+++ 40-hooks/arch/s390x/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -76,6 +76,10 @@
# bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
#fi
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
+tristate 'Kernel Hook support' CONFIG_HOOK
+ if [ "$CONFIG_HOOK" != "n" ]; then
+ define_bool CONFIG_ASM_HOOK y
+ fi
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/sh/config.in 40-hooks/arch/sh/config.in
--- /usr/src/40-pure/arch/sh/config.in 2002-10-01 12:37:00.000000000 +0530
+++ 40-hooks/arch/sh/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -366,6 +366,7 @@
if [ "$CONFIG_SH_STANDARD_BIOS" = "y" ]; then
bool 'Early printk support' CONFIG_SH_EARLY_PRINTK
fi
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/sparc/config.in 40-hooks/arch/sparc/config.in
--- /usr/src/40-pure/arch/sparc/config.in 2002-10-01 12:36:16.000000000 +0530
+++ 40-hooks/arch/sparc/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -244,7 +244,7 @@
bool 'Debug memory allocations' CONFIG_DEBUG_SLAB
bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ
bool 'Spinlock debugging' CONFIG_DEBUG_SPINLOCK
-
+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/sparc64/config.in 40-hooks/arch/sparc64/config.in
--- /usr/src/40-pure/arch/sparc64/config.in 2002-10-01 12:36:28.000000000 +0530
+++ 40-hooks/arch/sparc64/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -288,6 +288,7 @@
define_bool CONFIG_MCOUNT y
fi

+tristate 'Kernel Hook support' CONFIG_HOOK
endmenu

source security/Config.in
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/arch/x86_64/config.in 40-hooks/arch/x86_64/config.in
--- /usr/src/40-pure/arch/x86_64/config.in 2002-10-01 12:36:25.000000000 +0530
+++ 40-hooks/arch/x86_64/config.in 2002-10-03 16:10:17.000000000 +0530
@@ -223,6 +223,7 @@
bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK
bool ' Additional run-time checks' CONFIG_CHECKING
bool ' Debug __init statements' CONFIG_INIT_DEBUG
+ tristate ' Kernel Hook support' CONFIG_HOOK
fi
endmenu

diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-generic/hook.h 40-hooks/include/asm-generic/hook.h
--- /usr/src/40-pure/include/asm-generic/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-generic/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,25 @@
+#ifndef __ASM_GENERIC_HOOK_H
+#define __ASM_GENERIC_HOOK_H
+/*
+ * Kernel Hooks common code for many archs.
+ *
+ * Authors: Vamsi Krishna S. <[email protected]>
+ */
+#include <asm/cacheflush.h>
+
+static inline void deactivate_asm_hook(struct hook *hook)
+{
+ unsigned char *addr = (unsigned char *) (hook->hook_addr);
+ addr[2] = 0;
+ flush_icache_range(addr + 2, addr + 2);
+ return;
+}
+
+static inline void activate_asm_hook(struct hook *hook)
+{
+ unsigned char *addr = (unsigned char *) (hook->hook_addr);
+ addr[2] = 1;
+ flush_icache_range(addr + 2, addr + 2);
+ return;
+}
+#endif /* __ASM_GENERIC_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-i386/hook.h 40-hooks/include/asm-i386/hook.h
--- /usr/src/40-pure/include/asm-i386/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-i386/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,47 @@
+#ifndef __ASM_I386_HOOK_H
+#define __ASM_I386_HOOK_H
+/*
+ * Kernel Hooks optimized for ia32.
+ *
+ * Authors: Richard J Moore <[email protected]>
+ * Vamsi Krishna S. <[email protected]>
+ */
+#include <asm-generic/hook.h>
+
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define IF_HOOK_ENABLED(h, h1) \
+ register int tmp; \
+ __asm__ __volatile__ (".global "h"; "h":movl $0, %0":"=r"(tmp)); \
+ if (unlikely(tmp))
+
+#endif /* CONFIG_HOOK || CONFIG_HOOK_MODULE */
+
+/*
+ * Sanity check the hook location for expected instructions at hook location.
+ * movl $0, %reg, testl %reg, %reg
+ * test doesn't have to follow movl, so don't test for that.
+ */
+#define OPCODE_MOV1 0xb0
+#define OPCODE_MOV1_MASK 0xf0
+#define OPCODE_MOV2_1 0xc6 /* first byte */
+#define OPCODE_MOV2_2 0xc0 /* second byte */
+#define OPCODE_MOV2_1_MASK 0xfe
+#define OPCODE_MOV2_2_MASK 0xf8
+
+static inline int is_asm_hook(unsigned char *addr)
+{
+ if (!addr)
+ return 0;
+ if((addr[0] & OPCODE_MOV1_MASK) == OPCODE_MOV1) {
+ if (*((unsigned long *)(addr+1)) == 0)
+ return 1;
+ } else if (((addr[0] & OPCODE_MOV2_1_MASK) == OPCODE_MOV2_1) &&
+ ((addr[1] & OPCODE_MOV2_2_MASK) == OPCODE_MOV2_2)) {
+ if (*((unsigned long *)(addr+2)) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+#endif /* __ASM_I386_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-ppc/hook.h 40-hooks/include/asm-ppc/hook.h
--- /usr/src/40-pure/include/asm-ppc/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-ppc/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,46 @@
+#ifndef __ASM_PPC_HOOK_H
+#define __ASM_PPC_HOOK_H
+/*
+ * Kernel Hooks optimized for PPC.
+ *
+ * Authors: Mike Grundy <[email protected]> s390x
+ */
+#include <asm-generic/hook.h>
+
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define IF_HOOK_ENABLED(h, h1) \
+ register int tmp; \
+ __asm__ __volatile__ (".global "h"; "h":li %0, 0x00":"=r"(tmp)); \
+ if (unlikely(tmp))
+
+#endif /* CONFIG_HOOK || CONFIG_HOOK_MODULE */
+
+
+/*
+ * Sanity check the hook location for valid instructions at hook location.
+ * At hook location, we should find these instructions:
+ * 38 00 00 00 li r0,0
+ * 2c 00 00 00 cmpwi r0,0
+ *
+ * We can check for li and cmpwi instructions. As these instructions encode
+ * the register name in the second byte and the register cannot be predicted,
+ * we mask out the bits corresponding to registers in the opcode before comparing.
+ * PPC opcodes are six bits, hence mask of 0xFC
+ */
+#define OPCODE_MOV1 0x38 /* LI (really an extended mnemonic for addi */
+#define OPCODE_MOV1_MASK 0xFC
+/* Compiler generates 2c 00 00 00 cmpwi r0,0 */
+
+static inline int is_asm_hook(unsigned char * addr)
+{
+ if (!addr)
+ return 0;
+
+ if((addr[0] & OPCODE_MOV1_MASK) == OPCODE_MOV1) {
+ if (*((unsigned short *)(addr+1)) == 0)
+ return 1;
+ }
+ return 0;
+}
+#endif /* __ASM_PPC_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-ppc64/hook.h 40-hooks/include/asm-ppc64/hook.h
--- /usr/src/40-pure/include/asm-ppc64/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-ppc64/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,46 @@
+#ifndef __ASM_PPC_HOOK_H
+#define __ASM_PPC_HOOK_H
+/*
+ * Kernel Hooks optimized for PPC64.
+ *
+ * Authors: Mike Grundy <[email protected]> PPC64
+ */
+#include <asm-generic/hook.h>
+
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define IF_HOOK_ENABLED(h, h1) \
+ register int tmp; \
+ __asm__ __volatile__ (".global "h"; "h":li %0, 0x00":"=r"(tmp)); \
+ if (unlikely(tmp))
+
+#endif /* CONFIG_HOOK || CONFIG_HOOK_MODULE */
+
+
+/*
+ * Sanity check the hook location for valid instructions at hook location.
+ * At hook location, we should find these instructions:
+ * 38 00 00 00 li r0,0
+ * 2c 00 00 00 cmpwi r0,0
+ *
+ * We can check for li and cmpwi instructions. As these instructions encode
+ * the register name in the second byte and the register cannot be predicted,
+ * we mask out the bits corresponding to registers in the opcode before comparing.
+ * PPC opcodes are six bits, hence mask of 0xFC
+ */
+#define OPCODE_MOV1 0x38 /* LI (really an extended mnemonic for addi */
+#define OPCODE_MOV1_MASK 0xFC
+/* Compiler generates 2c 00 00 00 cmpwi r0,0 */
+
+static inline int is_asm_hook(unsigned char * addr)
+{
+ if (!addr)
+ return 0;
+
+ if((addr[0] & OPCODE_MOV1_MASK) == OPCODE_MOV1) {
+ if (*((unsigned short *)(addr+1)) == 0)
+ return 1;
+ }
+ return 0;
+}
+#endif /* __ASM_PPC_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-s390/hook.h 40-hooks/include/asm-s390/hook.h
--- /usr/src/40-pure/include/asm-s390/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-s390/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,48 @@
+#ifndef __ASM_S390_HOOK_H
+#define __ASM_S390_HOOK_H
+/*
+ * Kernel Hooks optimized for s390.
+ *
+ * Authors: Mike Grundy <[email protected]> s390
+ */
+#include <asm-generic/hook.h>
+
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define IF_HOOK_ENABLED(h, h1) \
+ register int tmp; \
+ __asm__ __volatile__ (".global "h"; "h":lhi %0, 0x00":"=r"(tmp)); \
+ if (unlikely(tmp))
+
+#endif /* CONFIG_HOOK || CONFIG_HOOK_MODULE */
+
+/*
+ * Sanity check the hook location for valid instructions at hook location.
+ * At hook location, we should find these instructions:
+ * a7 18 00 00 lhi %r1,0
+ * 12 11 ltr %r1,%r1
+ * We can check for the lhi and ltr instructions. As the lhi instruction encodes
+ * the register name in it, and we can't guarantee which register will be used,
+ * we'll mask out the bits corresponding to the target register.
+ */
+#define OPCODE_MOV2_1 0xa7 /* LHI first byte */
+#define OPCODE_MOV2_2 0x08 /* LHI second byte */
+#define OPCODE_MOV2_1_MASK 0xff
+#define OPCODE_MOV2_2_MASK 0x0f
+/* Compiler generates LTR opcode 12, but second op not tested */
+
+static inline int is_asm_hook(unsigned char * addr)
+{
+ if (!addr){
+ return 0;
+ }
+ if (((addr[0] & OPCODE_MOV2_1_MASK) == OPCODE_MOV2_1) &&
+ ((addr[1] & OPCODE_MOV2_2_MASK) == OPCODE_MOV2_2)) {
+ /* was checking a 32bit val, need to check 16, cheated with 8+8 */
+ if (addr[2]== 0 && addr[3]== 0){
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif /* __ASM_S390_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/asm-s390x/hook.h 40-hooks/include/asm-s390x/hook.h
--- /usr/src/40-pure/include/asm-s390x/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/asm-s390x/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,48 @@
+#ifndef __ASM_S390X_HOOK_H
+#define __ASM_S390X_HOOK_H
+/*
+ * Kernel Hooks optimized for s390x(64bit).
+ *
+ * Authors: Mike Grundy <[email protected]> s390x
+ */
+#include <asm-generic/hook.h>
+
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define IF_HOOK_ENABLED(h, h1) \
+ register int tmp; \
+ __asm__ __volatile__ (".global "h"; "h":lhi %0, 0x00":"=r"(tmp)); \
+ if (unlikely(tmp))
+
+#endif /* CONFIG_HOOK || CONFIG_HOOK_MODULE */
+
+/*
+ * Sanity check the hook location for valid instructions at hook location.
+ * At hook location, we should find these instructions:
+ * a7 18 00 00 lhi %r1,0
+ * 12 11 ltr %r1,%r1
+ * We can check for the lhi and ltr instructions. As the lhi instruction encodes
+ * the register name in it, and we can't guarantee which register will be used,
+ * we'll mask out the bits corresponding to the target register.
+ */
+#define OPCODE_MOV2_1 0xa7 /* LHI first byte */
+#define OPCODE_MOV2_2 0x08 /* LHI second byte */
+#define OPCODE_MOV2_1_MASK 0xff
+#define OPCODE_MOV2_2_MASK 0x0f
+/* Compiler generates LTR opcode 12, but second op not tested */
+
+static inline int is_asm_hook(unsigned char * addr)
+{
+ if (!addr){
+ return 0;
+ }
+ if (((addr[0] & OPCODE_MOV2_1_MASK) == OPCODE_MOV2_1) &&
+ ((addr[1] & OPCODE_MOV2_2_MASK) == OPCODE_MOV2_2)) {
+ /* was checking a 32bit val, need to check 16, cheated with 8+8 */
+ if (addr[2]== 0 && addr[3]== 0){
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif /* __ASM_S390X_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/include/linux/hook.h 40-hooks/include/linux/hook.h
--- /usr/src/40-pure/include/linux/hook.h 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/include/linux/hook.h 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,225 @@
+#ifndef __LINUX_HOOK_H
+#define __LINUX_HOOK_H
+/*
+ * Kernel Hooks Interface.
+ *
+ * Authors: Richard J Moore <[email protected]>
+ * Vamsi Krishna S. <[email protected]>
+ */
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/list.h>
+
+/* define the user (kernel module) hook structure */
+struct hook_rec;
+struct hook;
+
+struct hook_rec {
+ void *hook_exit;
+ struct list_head exit_list;
+ unsigned int hook_flags;
+ struct hook *hook_head;
+};
+
+struct hook {
+ struct list_head exit_list;
+ struct list_head hook_list;
+ unsigned int hook_flags;
+ void *hook_addr;
+ char *hook_name;
+ void *hook_ex_exit;
+};
+
+/* hook flags */
+#define HOOK_ARMED 0x00000001
+#define HOOK_REGISTERED 0x00000002
+#define HOOK_EXCLUSIVE 0x0000000c
+
+/* flag groupings */
+#define HOOK_STATUS (HOOK_ARMED | HOOK_REGISTERED)
+#define HOOK_OPTIONS (HOOK_OPTION_DISABLE_IRQS | HOOK_OPTION_STOP_CPUS)
+
+/*
+ * Use the DECLARE_HOOK macro to define the hook structure in global
+ * memory of a kernel module that is implementing a hook.
+ */
+#if defined(CONFIG_HOOK) || defined(CONFIG_HOOK_MODULE)
+
+#define EXPORT_SYMBOL_NOVERS_GPL(var) __EXPORT_SYMBOL_GPL(var, __MODULE_STRING(var))
+
+#define DECLARE_HOOK(x) _DECLARE_HOOK(x)
+#define _DECLARE_HOOK(hk) \
+extern void hk; \
+struct hook hk ## _head = { \
+ hook_addr: &(hk), \
+ hook_name: #hk \
+}; \
+EXPORT_SYMBOL_NOVERS_GPL(hk##_head);
+
+#define DECLARE_EXCLUSIVE_HOOK(x) _DECLARE_EXCLUSIVE_HOOK(x)
+#define _DECLARE_EXCLUSIVE_HOOK(hk) \
+extern void hk; \
+struct hook hk ## _head = { \
+ hook_addr: &(hk), \
+ hook_name: #hk, \
+ hook_flags: HOOK_EXCLUSIVE \
+}; \
+EXPORT_SYMBOL_NOVERS_GPL(hk##_head);
+
+/*
+ * Generic hooks are the same in all architectures and may be used to
+ * place hooks even in inline functions. They don't define a symbol at hook
+ * location.
+ */
+#define DECLARE_GENERIC_HOOK(x) _DECLARE_GENERIC_HOOK(x)
+#define _DECLARE_GENERIC_HOOK(hk) \
+struct hook hk ## _head = { \
+ hook_addr: NULL, \
+ hook_name: #hk \
+}; \
+EXPORT_SYMBOL_NOVERS_GPL(hk##_head);
+
+#define USE_HOOK(x) _USE_HOOK(x)
+#define _USE_HOOK(hk) extern struct hook hk##_head
+
+/* define head record only flags */
+#define HOOK_ACTIVE 0x80000000
+#define HOOK_ASM_HOOK 0x40000000
+#define HOOK_EXCLUSIVE 0x20000000
+
+typedef int (*hook_fn_t)(struct hook *, ...);
+
+#ifdef CONFIG_ASM_HOOK
+#include <asm/hook.h>
+#else
+static inline int is_asm_hook(unsigned char *addr) {return 0;}
+static inline void activate_asm_hook(struct hook *hook) { }
+static inline void deactivate_asm_hook(struct hook *hook) { }
+#endif
+
+#ifndef IF_HOOK_ENABLED
+#define IF_HOOK_ENABLED(h, h1) \
+ __asm__ __volatile__ (".global "h"; "h":"); \
+ if (unlikely(h1##_head.hook_flags & HOOK_ACTIVE))
+#endif
+
+#define HOOK_TEST(h) \
+ extern struct hook h##_head; \
+ IF_HOOK_ENABLED(#h, h)
+
+#define CALL_EXIT(fn, parm, args...) ((hook_fn_t)(fn)(parm , ##args))
+
+#define DISPATCH_NORMAL(fn, parm, dsprc, args...) \
+ dsprc = CALL_EXIT(fn, parm , ##args);
+
+#define DISPATCH_RET(fn, parm, dsprc, args...) { \
+ int rc; \
+ dsprc = CALL_EXIT(fn, parm, &rc , ##args); \
+ if (dsprc == HOOK_RETURN) \
+ return rc; \
+}
+
+#define DISPATCH_RET_NORC(fn, parm, dsprc, args...) { \
+ dsprc = CALL_EXIT(fn, parm , ##args); \
+ if (dsprc == HOOK_RETURN) \
+ return; \
+}
+
+#define HOOK_DISP_LOOP(h, dispatch, args...) { \
+ register struct hook_rec *rec; \
+ list_for_each_entry(rec, &h##_head.exit_list, exit_list) { \
+ register int dsprc; \
+ if (rec->hook_flags & HOOK_ARMED) { \
+ dispatch(rec->hook_exit, rec->hook_head, dsprc , ##args) \
+ if (dsprc == HOOK_TERMINATE) \
+ break; \
+ } \
+ } \
+}
+
+#define HOOK(h, args...) { \
+ HOOK_TEST(h) \
+ HOOK_DISP_LOOP(h, DISPATCH_NORMAL , ##args); \
+}
+
+#define HOOK_RET(h, args...) { \
+ HOOK_TEST(h) \
+ HOOK_DISP_LOOP(h, DISPATCH_RET , ##args); \
+}
+
+#define HOOK_RET_NORC(h, args...) { \
+ HOOK_TEST(h) \
+ HOOK_DISP_LOOP(h, DISPATCH_RET_NORC , ##args); \
+}
+
+#define EXCLUSIVE_HOOK(h, args...) { \
+ HOOK_TEST(h) \
+ DISPATCH_NORMAL(h##_head.hook_ex_exit, &h##_head , ##args); \
+}
+
+#define EXCLUSIVE_HOOK_RET(h, args...) { \
+ HOOK_TEST(h) \
+ DISPATCH_RET(h##_head.hook_ex_exit, &h##_head , ##args); \
+}
+
+#define EXCLUSIVE_HOOK_RET_NORC(h, args...) { \
+ HOOK_TEST(h) \
+ DISPATCH_RET_NORC(h##_head.hook_ex_exit, &h##_head , ##args); \
+}
+
+#define GENERIC_HOOK(h, args...) { \
+ extern struct hook h##_head; \
+ if (unlikely(h##_head.hook_flags & HOOK_ACTIVE)) { \
+ HOOK_DISP_LOOP(h, DISPATCH_NORMAL , ##args); \
+ } \
+}
+
+/* exported functions wrapped in a macro */
+extern int _hook_initialise(struct hook *);
+extern int _hook_terminate(struct hook *, int);
+extern int _hook_exit_register(struct hook *, struct hook_rec *);
+
+/* exported function prototypes */
+extern void hook_exit_deregister(struct hook_rec *);
+extern void hook_exit_arm(struct hook_rec *);
+extern void hook_exit_disarm(struct hook_rec *);
+
+/* exported functions error codes */
+#define EPRIORITY 1 /* reqd. priority not possible */
+
+/* Return values from Hook Exit routines */
+#define HOOK_CONTINUE 0
+#define HOOK_TERMINATE 1
+#define HOOK_RETURN -1
+
+/*
+ * public interfaces requiring a macro wrapper
+ *
+ * int hook_exit_register(hook, struct hook_rec *);
+ * int _hook_terminate(hook, int force_flag);/
+ * int hook_initialise(hook);
+ *
+ * where hook is a typeless name
+ */
+#define hook_exit_register(x, y) dummy_hook_exit_register(x, y)
+#define dummy_hook_exit_register(x, y) _hook_exit_register(&(x##_head), y)
+#define hook_terminate(x, y) dummy_hook_terminate(x, y)
+#define dummy_hook_terminate(x, y) _hook_terminate(&(x ## _head), y)
+#define hook_initialise(x) dummy_hook_initialise(x)
+#define dummy_hook_initialise(x) _hook_initialise(&(x ## _head))
+
+#else
+/* dummy macros when hooks are not compiled in */
+#define DECLARE_HOOK(x)
+#define DECLARE_GENERIC_HOOK(x)
+#define USE_HOOK(x)
+#define HOOK(h, args...)
+#define HOOK_RET(h, args...)
+#define HOOK_RET_NORC(h, args...)
+#define EXCLUSIVE_HOOK(h, args...)
+#define EXCLUSIVE_HOOK_RET(h, args...)
+#define EXCLUSIVE_HOOK_RET_NORC(h, args...)
+#define GENERIC_HOOK(h, args...)
+#endif /* !(CONFIG_HOOK || CONFIG_HOOK_MODULE) */
+
+#endif /* __LINUX_HOOK_H */
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/kernel/hook.c 40-hooks/kernel/hook.c
--- /usr/src/40-pure/kernel/hook.c 1970-01-01 05:30:00.000000000 +0530
+++ 40-hooks/kernel/hook.c 2002-10-03 16:10:17.000000000 +0530
@@ -0,0 +1,165 @@
+/*
+ * Kernel Hooks Interface.
+ *
+ * Authors: Richard J Moore <[email protected]>
+ * Vamsi Krishna S. <[email protected]>
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/hook.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+static spinlock_t hook_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(hook_list);
+
+static inline void deactivate_hook(struct hook *hook)
+{
+ hook->hook_flags &= ~HOOK_ACTIVE;
+ if(hook->hook_flags & HOOK_ASM_HOOK)
+ deactivate_asm_hook(hook);
+}
+
+static inline void activate_hook(struct hook *hook)
+{
+ hook->hook_flags |= HOOK_ACTIVE;
+ if(hook->hook_flags & HOOK_ASM_HOOK)
+ activate_asm_hook(hook);
+}
+
+static void disarm_one_hook_exit(struct hook_rec *hook_rec)
+{
+ struct hook_rec *rec;
+
+ hook_rec->hook_flags &= ~HOOK_ARMED;
+ list_for_each_entry(rec, &hook_rec->hook_head->exit_list, exit_list) {
+ if(rec->hook_flags & HOOK_ARMED) {
+ return;
+ }
+ }
+ deactivate_hook(hook_rec->hook_head);
+}
+
+int _hook_exit_register(struct hook *hook, struct hook_rec *hook_rec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+ if(hook->hook_flags & HOOK_EXCLUSIVE) {
+ if (!list_empty(&hook->exit_list)) {
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return -EPRIORITY;
+ }
+ hook->hook_ex_exit = hook_rec->hook_exit;
+ }
+ list_add(&hook_rec->exit_list, &hook->exit_list);
+ hook_rec->hook_head = hook;
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return 0;
+}
+
+void hook_exit_deregister(struct hook_rec *rec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+ if(rec->hook_flags & HOOK_ARMED)
+ disarm_one_hook_exit(rec);
+ if(rec->hook_head->hook_flags & HOOK_EXCLUSIVE)
+ rec->hook_head->hook_ex_exit = NULL;
+ list_del(&rec->exit_list);
+ spin_unlock_irqrestore(&hook_lock, flags);
+}
+
+void hook_exit_arm(struct hook_rec *rec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+ rec->hook_flags |= HOOK_ARMED;
+ if(!(rec->hook_head->hook_flags & HOOK_ACTIVE))
+ activate_hook(rec->hook_head);
+ spin_unlock_irqrestore(&hook_lock, flags);
+}
+
+void hook_exit_disarm(struct hook_rec *rec)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+ disarm_one_hook_exit(rec);
+ spin_unlock_irqrestore(&hook_lock, flags);
+}
+
+int _hook_initialise(struct hook *hook)
+{
+ struct hook *h;
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+ list_for_each_entry(h, &hook_list, hook_list) {
+ if(strcmp(hook->hook_name, h->hook_name) == 0) {
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return -EEXIST;
+ }
+ }
+
+ INIT_LIST_HEAD(&hook->exit_list);
+ if(is_asm_hook(hook->hook_addr)) {
+ hook->hook_flags |= HOOK_ASM_HOOK;
+ }
+
+ list_add(&hook->hook_list, &hook_list);
+
+ MOD_INC_USE_COUNT;
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return 0;
+}
+
+int _hook_terminate(struct hook *hook, int force)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hook_lock, flags);
+
+ if(hook->hook_flags & HOOK_ACTIVE) {
+ if(force) {
+ deactivate_hook(hook);
+ } else {
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return -EBUSY;
+ }
+ }
+ list_del(&hook->hook_list);
+
+ MOD_DEC_USE_COUNT;
+ spin_unlock_irqrestore(&hook_lock, flags);
+ return 0;
+}
+
+static int __init hook_init_module(void)
+{
+ printk(KERN_INFO "Kernel Hooks Interface installed.\n");
+ return 0;
+}
+
+static void __exit hook_cleanup_module(void)
+{
+ printk(KERN_INFO "Kernel Hooks Interface terminated.\n");
+}
+
+module_init(hook_init_module);
+module_exit(hook_cleanup_module);
+
+EXPORT_SYMBOL(hook_exit_deregister);
+EXPORT_SYMBOL(hook_exit_arm);
+EXPORT_SYMBOL(hook_exit_disarm);
+EXPORT_SYMBOL(_hook_initialise);
+EXPORT_SYMBOL(_hook_terminate);
+EXPORT_SYMBOL(_hook_exit_register);
+
+MODULE_LICENSE("GPL");
diff -urN -X /home/vamsi/.dontdiff /usr/src/40-pure/kernel/Makefile 40-hooks/kernel/Makefile
--- /usr/src/40-pure/kernel/Makefile 2002-10-01 12:36:22.000000000 +0530
+++ 40-hooks/kernel/Makefile 2002-10-03 16:10:53.000000000 +0530
@@ -3,13 +3,14 @@
#

export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
- printk.o platform.o suspend.o dma.o module.o cpufreq.o
+ printk.o platform.o suspend.o dma.o module.o cpufreq.o hook.o

obj-y = sched.o fork.o exec_domain.o panic.o printk.o \
module.o exit.o itimer.o time.o softirq.o resource.o \
sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o context.o futex.o platform.o pid.o

+obj-$(CONFIG_HOOK) += hook.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += cpu.o
obj-$(CONFIG_UID16) += uid16.o


2002-10-03 12:35:41

by Mikael Pettersson

[permalink] [raw]
Subject: Re: [rfc] [patch] kernel hooks

Vamsi Krishna S . writes:
> Hooks are implemented as fast and slim-line insertions to kernel
> space code. When not active that have practically no overhead to
> the kernel. Optimized versions are implemented on ia32, ppc,
> ppc64, s390, s390x archs, where they expand to:
> movl $0, %reg
> ^x
> testl %reg, %reg
> jz out
> call out exits
> out:
>
> Hooks are armed by editing the number at ^x above to a non-zero
> value so that the exits will be now be called out.
...
> +static inline void activate_asm_hook(struct hook *hook)
> +{
> + unsigned char *addr = (unsigned char *) (hook->hook_addr);
> + addr[2] = 1;
> + flush_icache_range(addr + 2, addr + 2);
> + return;
> +}

You just triggered Intel's "Unsynchronised Cross-Modifying Code
Operations Can Cause Unexpected Instruction Execution Results"
Erratum, which affects _every_ multiprocessor Intel P6 box.
It's Pentium 3 Erratum E49 if you want to look it up; the other
Intel P6s have it too, but with different numbers.

In short, the only safe way to do this sort of thing is to force
all other processors to wait on a barrier first, then modify the
code, then release the barrier.

On a general note, I wish people would pay more attention to Intel's
and AMD's errata documents. There are a number of things documented
in Intel's IA32 manual set which simply do not work in the real world
due to errata. Unsync cross-modifying code is one, the PAT is another.

/Mikael

2002-10-03 12:50:42

by Alan

[permalink] [raw]
Subject: Re: [rfc] [patch] kernel hooks

On Thu, 2002-10-03 at 13:40, Mikael Pettersson wrote:
> You just triggered Intel's "Unsynchronised Cross-Modifying Code
> Operations Can Cause Unexpected Instruction Execution Results"
> Erratum, which affects _every_ multiprocessor Intel P6 box.
> It's Pentium 3 Erratum E49 if you want to look it up; the other
> Intel P6s have it too, but with different numbers.
>
> In short, the only safe way to do this sort of thing is to force
> all other processors to wait on a barrier first, then modify the
> code, then release the barrier.

You must also ensure that the code you are modifying isnt on an IRQ path
(if it is you must do spin locks and then be very careful about cross
cpu tlb deadlocks). Finally you have no choice but to ensure you never
use it on the NMI path

2002-10-03 13:31:35

by Richard J Moore

[permalink] [raw]
Subject: Re: [rfc] [patch] kernel hooks


> You must also ensure that the code you are modifying isnt on an IRQ path
> (if it is you must do spin locks and then be very careful about cross
> cpu tlb deadlocks). Finally you have no choice but to ensure you never
> use it on the NMI path

Why do we need a spinlock? We change one byte, we are not concered about
when exactly that takes effect, only that there are always valid
instructions in the pipeline.

Richard.

2002-10-03 13:49:16

by Alan

[permalink] [raw]
Subject: Re: [rfc] [patch] kernel hooks

On Thu, 2002-10-03 at 14:32, Richard J Moore wrote:
>
> > You must also ensure that the code you are modifying isnt on an IRQ path
> > (if it is you must do spin locks and then be very careful about cross
> > cpu tlb deadlocks). Finally you have no choice but to ensure you never
> > use it on the NMI path
>
> Why do we need a spinlock? We change one byte, we are not concered about
> when exactly that takes effect, only that there are always valid
> instructions in the pipeline.

Because you are programming for real silicon not for the imaginary
perfect processor. Read the x86 errata

2002-10-04 09:25:27

by Richard J Moore

[permalink] [raw]
Subject: Re: [rfc] [patch] kernel hooks


>> Why do we need a spinlock? We change one byte, we are not concerned
about
>> when exactly that takes effect, only that there are always valid
>> instructions in the pipeline.
>
> Because you are programming for real silicon not for the imaginary
> perfect processor. Read the x86 errata

OK I see what you're getting at here -you're talking about the XMC
algorithm.

However, I'm not convinced that we will hit E49 in our specific case - we
looked at this some time ago and felt that because we are not altering
instruction length or boundary, and that even if there is a score-boarding
effect on the register value stored, we still wouldn't generate exceptions
from intermediates. There are apparent inconsistencies in the
architecture manuals; in the past when I've found these and queried the
processor behaviour with Intel's microarchitecture guys they've provided
clarification. I'll do the same here and see what they say. It's no big
deal whatever their response as kernel hooks has two mechanisms: generic,
which is architecturally independent and doesn't use self-modifying code;
and architecturally specific, which does. We can always restrict the IA32
mechanism for processors < P3 to use the generic hook or implement the XMC
algorithm.

Richard