PAX_USERCOPY
Subsystems (7)
Hi,
This is an initial version of PAX_USERCOPY port on Linux 3.0.
It lacks:
1) white list of allowed slabs (SLAB_USERCOPY).
2) current task termination and stack unrolling on a failed check. (I think it will be rejected in upstream anyway).
3) mem/kmem handling. As they read/write into the raw memory, it should be copied to/from temporary buffer before userspace interaction.
The basic tests are passed. BTW, a stack protector catches the smaller overflows, however it is bypassable.
arch/x86/include/asm/uaccess.h +52 -0
@@ -10,6 +10,9 @@#include <asm/asm.h> #include <asm/page.h> +extern bool slab_access_ok (const void *ptr,unsigned long len); +extern bool stack_access_ok (const void *ptr,unsigned long len); +#define VERIFY_READ 0#define VERIFY_WRITE 1
@@ -78,6 +81,55 @@*/ #define access_ok (type, addr, size) (likely(__range_not_ok(addr, size) == 0)) +#if defined(CONFIG_FRAME_POINTER ) +/* MUST be always_inline to correctly count stack frame numbers */ +inline static __attribute__((always_inline)) +bool arch_check_object_on_stack_frame (const void *stack, +const void *stackend,const void *obj,unsigned long len) +{ +const void *frame =NULL ; +const void *oldframe; + +/* +low ----------------------------------------------> high +[saved bp][saved ip][args][local vars][saved bp][saved ip] +^----------------^ +allow copies only within here +*/ + +#if 0 + oldframe =__builtin_frame_address (1 ); +if (oldframe) + frame =__builtin_frame_address (2 ); +#endif + +/* +* Get the stack_access_ok() caller frame. +* __builtin_frame_address(0) returns stack_access_ok() frame +* as arch_ is inline and stack_ is noinline. +*/ + oldframe =__builtin_frame_address (0 ); + frame =__builtin_frame_address (1 ); + +/* if obj + len extends past the last frame, this +check won't pass and the next frame will be 0, +causing us to bail out and correctly report +the copy as invalid +*/ +while (stack <= frame && frame < stackend) { +if (obj + len <= frame) { +if (obj >= oldframe +2 *sizeof (void *)) +return true; +return false; + } + oldframe = frame; + frame = *(const void *const *)frame; + } +return false; +} +#define arch_check_object_on_stack_frame arch_check_object_on_stack_frame +#endif +/* * The exception table consists of pairs of addresses: the first is the * address of an instruction that is allowed to fault, and the second is
arch/x86/include/asm/uaccess_32.h +20 -1
@@ -61,6 +61,10 @@ __copy_to_user_inatomic(void __user *to, const void *from, unsigned long n)return ret; } } + +if (!slab_access_ok (from, n) || !stack_access_ok (from, n)) +return n; +return __copy_to_user_ll (to, from, n); }
@@ -108,6 +112,10 @@ __copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)return ret; } } + +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; +return __copy_from_user_ll_nozero (to, from, n); }
@@ -152,6 +160,10 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)return ret; } } + +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; +return __copy_from_user_ll (to, from, n); }
@@ -174,6 +186,10 @@ static __always_inline unsigned long __copy_from_user_nocache(void *to,return ret; } } + +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; +return __copy_from_user_ll_nocache (to, from, n); }
@@ -181,7 +197,10 @@ static __always_inline unsigned long __copy_from_user_inatomic_nocache(void *to, const void __user *from, unsigned long n) { -return __copy_from_user_ll_nocache_nozero (to, from, n); +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; + +return __copy_from_user_ll_nocache_nozero (to, from, n); } unsigned long __must_check copy_to_user(void __user *to,
arch/x86/include/asm/uaccess_64.h +21 -1
@@ -50,8 +50,11 @@ static inline unsigned long __must_check copy_from_user(void *to,int sz =__compiletime_object_size (to);might_fault (); -if (likely (sz ==-1 || sz >= n)) +if (likely (sz ==-1 || sz >= n)) { +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; n =_copy_from_user (to, from, n); + }#ifdef CONFIG_DEBUG_VM else WARN(1, "Buffer overflow detected!\n");
@@ -64,6 +67,9 @@ int copy_to_user(void __user *dst, const void *src, unsigned size) {might_fault (); +if (!slab_access_ok (src, size) || !stack_access_ok (src, size)) +return size; +return _copy_to_user (dst, src, size); }
@@ -73,6 +79,10 @@ int __copy_from_user(void *dst, const void __user *src, unsigned size)int ret =0 ;might_fault (); + +if (!slab_access_ok (dst, size) || !stack_access_ok (dst, size)) +return size; +if (!__builtin_constant_p (size))return copy_user_generic (dst, (__force void *)src, size);switch (size) {
@@ -117,6 +127,10 @@ int __copy_to_user(void __user *dst, const void *src, unsigned size)int ret =0 ;might_fault (); + +if (!slab_access_ok (src, size) || !stack_access_ok (src, size)) +return size; +if (!__builtin_constant_p (size))return copy_user_generic ((__force void *)dst, src, size);switch (size) {
@@ -221,12 +235,18 @@ __must_check unsigned long __clear_user(void __user *mem, unsigned long len); static __must_check __always_inline int__copy_from_user_inatomic (void *dst,const void __user *src,unsigned size) { +if (!slab_access_ok (dst, size) || !stack_access_ok (dst, size)) +return size; +return copy_user_generic (dst, (__force const void *)src, size); } static __must_check __always_inline int__copy_to_user_inatomic (void __user *dst,const void *src,unsigned size) { +if (!slab_access_ok (src, size) || !stack_access_ok (src, size)) +return size; +return copy_user_generic ((__force void *)dst, src, size); }
arch/x86/lib/usercopy_64.c +4 -0
@@ -42,6 +42,10 @@ long __strncpy_from_user(char *dst, const char __user *src, long count) {long res; + +if (!slab_access_ok (dst, count) || !stack_access_ok (dst, count)) +return count; +__do_strncpy_from_user (dst, src, count, res);return res; }
include/asm-generic/uaccess.h +19 -0
@@ -50,6 +50,15 @@ static inline int __access_ok(unsigned long addr, unsigned long size) }#endif +#ifndef arch_check_object_on_stack_frame +static inline bool arch_check_object_on_stack_frame (const void *stack, +const void *stackend,const void *obj,unsigned long len) +{ +return true; +} +#define arch_check_object_on_stack_frame arch_check_object_on_stack_frame +#endif /* arch_check_object_on_stack_frame */ +/* * The exception table consists of pairs of addresses: the first is the * address of an instruction that is allowed to fault, and the second is
@@ -99,6 +108,9 @@ static inline __must_check long __copy_from_user(void *to, } } +if (!slab_access_ok (to, n) || !stack_access_ok (to, n)) +return n; +memcpy (to, (const void __force *)from, n);return 0 ; }
@@ -129,6 +141,9 @@ static inline __must_check long __copy_to_user(void __user *to, } } +if (!slab_access_ok (from, n) || !stack_access_ok (from, n)) +return n; +memcpy ((void __force *)to, from, n);return 0 ; }
@@ -268,6 +283,10 @@ static inline long __strncpy_from_user(char *dst, const char __user *src, long count) {char *tmp; + +if (!slab_access_ok (dst, count) || !stack_access_ok (dst, count)) +return count; +strncpy (dst, (const char __force *)src, count);for (tmp = dst; *tmp && count >0 ; tmp++, count--) ;
include/linux/slab.h +9 -0
@@ -333,4 +333,13 @@ static inline void *kzalloc_node(size_t size, gfp_t flags, int node)void __init kmem_cache_init_late (void ); +/* +* slab_access_ok() checks whether ptr belongs to the slab cache and whether +* it fits in a single allocated area. +* +* Returns false only if ptr belongs to a slab cache and overflows allocated +* slab area. +*/ +extern bool slab_access_ok (const void *ptr,unsigned long len); +#endif /* _LINUX_SLAB_H */
include/linux/uaccess.h +1 -0
@@ -108,4 +108,5 @@ extern long __probe_kernel_read(void *dst, const void *src, size_t size);extern long notrace probe_kernel_write (void *dst,const void *src,size_t size);extern long notrace __probe_kernel_write (void *dst,const void *src,size_t size); +#endif /* __LINUX_UACCESS_H__ */
mm/maccess.c +41 -0
50 lines not on this page · raw message
mm/slab.c +29 -0
@@ -3843,6 +3843,35 @@ unsigned int kmem_cache_size(struct kmem_cache *cachep) }EXPORT_SYMBOL (kmem_cache_size); +bool slab_access_ok (const void *ptr,unsigned long len) +{ +struct page *page; +struct kmem_cache *cachep =NULL ; +struct slab *slabp; +unsigned int objnr; +unsigned long offset; + +if (!len) +return true; +if (!virt_addr_valid (ptr)) +return true; + page =virt_to_head_page (ptr); +if (!PageSlab (page)) +return true; + + cachep =page_get_cache (page); + slabp =page_get_slab (page); + objnr =obj_to_index (cachep, slabp, (void *)ptr); +BUG_ON (objnr >= cachep->num); + offset = ptr -index_to_obj (cachep, slabp, objnr) -obj_offset (cachep); +if (offset <=obj_size (cachep) && len <=obj_size (cachep) - offset) +return true; + +pr_err ("slab_access_ok failed (addr %p, len %lu)\n" , ptr, len); +return false; +} +EXPORT_SYMBOL (slab_access_ok); +/* * This initializes kmem_list3 or resizes various caches for all nodes. */
mm/slob.c +58 -0
64 lines not on this page · raw message
mm/slub.c +32 -0
@@ -2623,6 +2623,38 @@ unsigned int kmem_cache_size(struct kmem_cache *s) }EXPORT_SYMBOL (kmem_cache_size); +/* +* slab_access_ok() checks whether object is on the slab cache and whether +* it fits in a single allocated area. +* +* Returns true in cases: +* a) object is not a slab object +* b) object is located in the slab cache and fully fits into the allocated area +*/ +bool slab_access_ok (const void *ptr,unsigned long n) +{ +struct page *page; +struct kmem_cache *s =NULL ; +unsigned long offset; + +if (!n) +return true; +if (!virt_addr_valid (ptr)) +return true; + page =virt_to_head_page (ptr); +if (!PageSlab (page)) +return true; + + s = page->slab; + offset = (ptr -page_address (page)) % s->size; +if (offset <= s->objsize && n <= s->objsize - offset) +return true; + +pr_err ("slab_access_ok failed (addr %p, len %lu)\n" , ptr, n); +return false; +} +EXPORT_SYMBOL (slab_access_ok); +static void list_slab_objects (struct kmem_cache *s,struct page *page,const char *text) {
---
[kernel-hardening] PAX_USERCOPY testing
Hi,
This is a version of PAX_USERCOPY I want to send to LKML. However, the patch without performance testing would be incomplete. I have some syscalls measurements, the worst case is gethostname() with 6-7% penalty on x86-64 (Intel Core 2 Duo 2Ghz).
If someone is able to test and measure the penalty, especially on x86-32 (I have no such machine available for testing now, unfortunately), it would be great. Any comments about what workloads would suffer more/less than others are appreciated.
BTW, Tested-by: tag is a Good Thing (tm) when proposing a patch ;)
Changes since v1:
* moved all checks to CONFIG_DEBUG_RUNTIME_USER_COPY_CHECKS,
* added fast check if length and/or object length is known at the compile time,
* renamed _nocheck -> _unchecked as there is already put_user_nocheck stuff,
* removed some odd tests in rare pathes or when an overflow is unlikely.
The patch is rebased against 3.0-rc7.
arch/x86/include/asm/uaccess.h +49 -0
@@ -9,6 +9,7 @@#include <linux/string.h> #include <asm/asm.h> #include <asm/page.h> +#include <linux/uaccess-check.h> #define VERIFY_READ 0#define VERIFY_WRITE 1
@@ -78,6 +79,54 @@*/ #define access_ok (type, addr, size) (likely(__range_not_ok(addr, size) == 0)) +#if defined(CONFIG_FRAME_POINTER ) +/* +* MUST be always_inline to correctly count stack frame numbers. +* +* low ----------------------------------------------> high +* [saved bp][saved ip][args][local vars][saved bp][saved ip] +* ^----------------^ +* allow copies only within here +*/ +#undef arch_check_object_on_stack_frame +inline static __attribute__((always_inline)) +bool arch_check_object_on_stack_frame (const void *stack, +const void *stackend,const void *obj,unsigned long len) +{ +const void *frame =NULL ; +const void *oldframe; + +/* +* Get the kernel_access_ok() caller frame. +* __builtin_frame_address(0) returns kernel_access_ok() frame +* as arch_ and stack_ are inline and kernel_ is noinline. +*/ + oldframe =__builtin_frame_address (0 ); +if (oldframe) + frame =__builtin_frame_address (1 ); + +while (stack <= frame && frame < stackend) { +/* +* If obj + len extends past the last frame, this +* check won't pass and the next frame will be 0, +* causing us to bail out and correctly report +* the copy as invalid. +*/ +if (obj + len <= frame) { +/* EBP + EIP */ +int protected_regs_size =2 *sizeof (void *); + +if (obj >= oldframe + protected_regs_size) +return true; +return false; + } + oldframe = frame; + frame = *(const void *const *)frame; + } +return false; +} +#endif +/* * The exception table consists of pairs of addresses: the first is the * address of an instruction that is allowed to fault, and the second is
arch/x86/include/asm/uaccess_32.h +26 -3
@@ -82,6 +82,8 @@ static __always_inline unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n) {might_fault (); +if (!kernel_access_ok (from, n)) +return n;return __copy_to_user_inatomic (to, from, n); }
@@ -152,6 +154,8 @@ __copy_from_user(void *to, const void __user *from, unsigned long n)return ret; } } +if (!kernel_access_ok (to, n)) +return n;return __copy_from_user_ll (to, from, n); }
@@ -205,11 +209,30 @@ static inline unsigned long __must_check copy_from_user(void *to, {int sz =__compiletime_object_size (to); -if (likely (sz ==-1 || sz >= n)) - n =_copy_from_user (to, from, n); -else +if (likely (sz ==-1 || sz >= n)) { +if (kernel_access_ok (to, n)) + n =_copy_from_user (to, from, n); + }else {copy_from_user_overflow (); + } + +return n; +} + +#undef copy_from_user_uncheched +static inline unsigned long __must_check copy_from_user_uncheched (void *to, +const void __user *from, +unsigned long n) +{ +return _copy_from_user (to, from, n); +} +#undef copy_to_user_uncheched +static inline unsigned long copy_to_user_unchecked (void __user *to, +const void *from,unsigned long n) +{ +if (access_ok (VERIFY_WRITE , to, n)) + n =__copy_to_user (to, from, n);return n; }
arch/x86/include/asm/uaccess_64.h +34 -2
@@ -50,8 +50,10 @@ static inline unsigned long __must_check copy_from_user(void *to,int sz =__compiletime_object_size (to);might_fault (); -if (likely (sz ==-1 || sz >= n)) - n =_copy_from_user (to, from, n); +if (likely (sz ==-1 || sz >= n)) { +if (kernel_access_ok (to, n)) + n =_copy_from_user (to, from, n); + }#ifdef CONFIG_DEBUG_VM else WARN(1, "Buffer overflow detected!\n");
@@ -59,11 +61,33 @@ static inline unsigned long __must_check copy_from_user(void *to,return n; } +#undef copy_from_user_unchecked +static inline unsigned long __must_check copy_from_user_unchecked (void *to, +const void __user *from, +unsigned long n) +{ +might_fault (); + +return _copy_from_user (to, from, n); +} +static __always_inline __must_check int copy_to_user(void __user *dst, const void *src, unsigned size) {might_fault (); +if (!kernel_access_ok (src, size)) +return size; + +return _copy_to_user (dst, src, size); +} + +#undef copy_to_user_unchecked +static __always_inline __must_check +int copy_to_user_unchecked(void __user *dst, const void *src, unsigned size) +{ +might_fault (); +return _copy_to_user (dst, src, size); }
@@ -73,8 +97,12 @@ int __copy_from_user(void *dst, const void __user *src, unsigned size)int ret =0 ;might_fault (); +if (!kernel_access_ok (dst, size)) +return size; +if (!__builtin_constant_p (size))return copy_user_generic (dst, (__force void *)src, size); +switch (size) {case 1 :__get_user_asm (*(u8 *)dst, (u8 __user *)src, ret,"b" ,"b" ,"=q" ,1 );
@@ -117,8 +145,12 @@ int __copy_to_user(void __user *dst, const void *src, unsigned size)int ret =0 ;might_fault (); +if (!kernel_access_ok (dst, size)) +return size; +if (!__builtin_constant_p (size))return copy_user_generic ((__force void *)dst, src, size); +switch (size) {case 1 :__put_user_asm (*(u8 *)src, (u8 __user *)dst, ret,"b" ,"b" ,"iq" ,1 );
arch/x86/lib/usercopy_32.c +1 -1
@@ -851,7 +851,7 @@ EXPORT_SYMBOL(__copy_from_user_ll_nocache_nozero);unsigned long copy_to_user (void __user *to,const void *from,unsigned long n) { -if (access_ok (VERIFY_WRITE , to, n)) +if (access_ok (VERIFY_WRITE , to, n) &&kernel_access_ok (from, n)) n =__copy_to_user (to, from, n);return n; }
drivers/char/mem.c +4 -4
@@ -132,7 +132,7 @@ static ssize_t read_mem(struct file *file, char __user *buf,if (!ptr)return -EFAULT ; - remaining =copy_to_user (buf, ptr, sz); + remaining =copy_to_user_unchecked (buf, ptr, sz);unxlate_dev_mem_ptr (p, ptr);if (remaining)return -EFAULT ;
@@ -190,7 +190,7 @@ static ssize_t write_mem(struct file *file, const char __user *buf,return -EFAULT ; } - copied =copy_from_user (ptr, buf, sz); + copied =copy_from_user_unchecked (ptr, buf, sz);unxlate_dev_mem_ptr (p, ptr);if (copied) { written += sz - copied;
@@ -428,7 +428,7 @@ static ssize_t read_kmem(struct file *file, char __user *buf,*/ kbuf =xlate_dev_kmem_ptr ((char *)p); -if (copy_to_user (buf, kbuf, sz)) +if (copy_to_user_unchecked (buf, kbuf, sz))return -EFAULT ; buf += sz; p += sz;
@@ -498,7 +498,7 @@ static ssize_t do_write_kmem(unsigned long p, const char __user *buf,*/ ptr =xlate_dev_kmem_ptr ((char *)p); - copied =copy_from_user (ptr, buf, sz); + copied =copy_from_user_unchecked (ptr, buf, sz);if (copied) { written += sz - copied;if (written)
include/linux/uaccess-check.h new file +37 -0
@@ -0,0 +1,37 @@ +#ifndef __LINUX_UACCESS_CHECK_H__ +#define __LINUX_UACCESS_CHECK_H__ + +#ifdef CONFIG_DEBUG_RUNTIME_USER_COPY_CHECKS +extern bool __kernel_access_ok (const void *ptr,unsigned long len); +static inline bool kernel_access_ok (const void *ptr,unsigned long len) +{ +size_t sz =__compiletime_object_size (ptr); + +if (sz != (size_t )-1 ) { +if (sz >= len) +return true; +pr_err ("kernel_access_ok(static) failed, ptr = %p, length = %lu\n" , + ptr, len); +dump_stack (); +return false; + } + +/* We care about "len" overflows only. */ +if (__builtin_constant_p (len)) +return true; + +return __kernel_access_ok (ptr, len); +} +#else +static inline bool kernel_access_ok (const void *ptr,unsigned long len) +{ +return true; +} +#endif /* CONFIG_DEBUG_RUNTIME_USER_COPY_CHECKS */ + +#define copy_to_user_unchecked copy_to_user +#define copy_from_user_unchecked copy_from_user + +#define arch_check_object_on_stack_frame (s,se,o,len) true + +#endif /* __LINUX_UACCESS_CHECK_H__ */
lib/Kconfig.debug +21 -0
@@ -679,6 +679,27 @@ config DEBUG_STACK_USAGE This option will slow down process creation somewhat. +config DEBUG_RUNTIME_USER_COPY_CHECKS + bool "Runtime copy size checks" + default n + depends on DEBUG_KERNEL && X86 + ---help--- + Enabling this option adds additional runtime checks into copy_from_user() + and similar functions. + + Specifically, this checking prevents information leaking from the kernel + heap/stack during kernel to userland copies (if the kernel object is + otherwise fully initialized, including padding bytes) and prevents kernel + heap/stack overflows during userland to kernel copies. + + If frame pointers are enabled on x86, this option will also restrict + copies into and out of the kernel stack to local variables within a + single frame. + + The option has a performance drawback (up to 7% on small syscalls like + gethostname), so don't turn it on unless you care enough about possible + exploitation of buffer overflows. + config DEBUG_KOBJECT bool "kobject debugging" depends on DEBUG_KERNEL
mm/maccess.c +56 -0
67 lines not on this page · raw message
mm/slab.c +34 -0
@@ -3844,6 +3844,40 @@ unsigned int kmem_cache_size(struct kmem_cache *cachep)EXPORT_SYMBOL (kmem_cache_size);/* +* Returns false if and only if [ptr; ptr+len) touches the slab, +* but breaks objects boundaries. It doesn't check whether the +* accessed object is actually allocated. +*/ +bool slab_access_ok (const void *ptr,unsigned long len) +{ +struct page *page; +struct kmem_cache *cachep =NULL ; +struct slab *slabp; +unsigned int objnr; +unsigned long offset; + +if (!len) +return true; +if (!virt_addr_valid (ptr)) +return true; + page =virt_to_head_page (ptr); +if (!PageSlab (page)) +return true; + + cachep =page_get_cache (page); + slabp =page_get_slab (page); + objnr =obj_to_index (cachep, slabp, (void *)ptr); +BUG_ON (objnr >= cachep->num); + offset = (const char *)ptr -obj_offset (cachep) - + (const char *)index_to_obj (cachep, slabp, objnr); +if (offset <=obj_size (cachep) && len <=obj_size (cachep) - offset) +return true; + +return false; +} +EXPORT_SYMBOL (slab_access_ok); + +/* * This initializes kmem_list3 or resizes various caches for all nodes. */ static int alloc_kmemlist (struct kmem_cache *cachep,gfp_t gfp)
mm/slob.c +10 -0
@@ -666,6 +666,16 @@ unsigned int kmem_cache_size(struct kmem_cache *c) }EXPORT_SYMBOL (kmem_cache_size); +bool slab_access_ok (const void *ptr,unsigned long len) +{ +/* +* TODO: is it worth checking? We have to gain a lock and +* walk through all chunks. +*/ +return true; +} +EXPORT_SYMBOL (slab_access_ok); +int kmem_cache_shrink (struct kmem_cache *d) {return 0 ;
mm/slub.c +28 -0
@@ -2623,6 +2623,34 @@ unsigned int kmem_cache_size(struct kmem_cache *s) }EXPORT_SYMBOL (kmem_cache_size); +/* +* Returns false if and only if [ptr; ptr+len) touches the slab, +* but breaks objects boundaries. It doesn't check whether the +* accessed object is actually allocated. +*/ +bool slab_access_ok (const void *ptr,unsigned long len) +{ +struct page *page; +struct kmem_cache *s =NULL ; +unsigned long offset; + +if (len ==0 ) +return true; +if (!virt_addr_valid (ptr)) +return true; + page =virt_to_head_page (ptr); +if (!PageSlab (page)) +return true; + + s = page->slab; + offset = ((const char *)ptr - (const char *)page_address (page)) % s->size; +if (offset <= s->objsize && len <= s->objsize - offset) +return true; + +return false; +} +EXPORT_SYMBOL (slab_access_ok); +static void list_slab_objects (struct kmem_cache *s,struct page *page,const char *text) {
Signature · 2 lines
Vasiliy Kulikov http://www.openwall.com - bringing security into open computing environments
[kernel-hardening] Re: PAX_USERCOPY testing
Hi,
Quoted 4 lines · Vasiliy Kulikov
This is a version of
PAX_USERCOPYI want to send to LKML. However, the patch without performance testing would be incomplete. I have some syscalls measurements, the worst case isgethostname()with 6-7% penalty on x86-64 (Intel Core 2 Duo 2Ghz).
The previous benchmarks are totally wrong. I didn't disable background daemons, which created significant error, and used on-demand CPU governor, which finally smashed results.
Measurements with maximum governor and with minimum processes showed great "boost":
The worst case of tiny syscalls, gethostname(), has 0,9% slowdown.
Real workflows - find /usr, git log -Sredirect, make in kernel tree, files copying - are not affected by the feature at all (the slowdown is so low that I cannot effectively measure it, so it is less than 0,1%).
So, I've sent RFCv2 to LKML as-is :-)
I suppose no sysctl to control the behaviour is needed - the permormance is acceptable for real workflows IMO.
Thanks,
Signature · 2 lines
Vasiliy Kulikov http://www.openwall.com - bringing security into open computing environments