lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Mon, 1 Mar 2010 10:29:44 +0100
From:	Mikael Pettersson <mikpe@...uu.se>
To:	Roel Kluin <roel.kluin@...il.com>
Cc:	Andi Kleen <andi@...stfloor.org>,
	"David S. Miller" <davem@...emloft.net>,
	Mikael Pettersson <mikpe@...uu.se>, penberg@...helsinki.fi,
	Brian Gerst <brgerst@...il.com>,
	Andrew Morton <akpm@...ux-foundation.org>,
	LKML <linux-kernel@...r.kernel.org>,
	linux-crypto@...r.kernel.org, Herbert@...dor.apana.org.au
Subject: Re: [PATCH v2] compiler: prevent dead store elimination

Roel Kluin writes:
 > Due to optimization A call to memset() may be removed as a dead store when

_______________________^ lower-case a

 > the buffer is not used after its value is overwritten. The new function
 > secure_bzero() ensures a section of memory is padded with zeroes.
 > 
 > >From the GCC manual, section 5.37:
 > If your assembler instructions access memory in an unpredictable
 > fashion, add `memory' to the list of clobbered registers. This will
 > cause GCC to not keep memory values cached in registers across the
 > assembler instruction and not optimize stores or loads to that memory.

This paragraph, while accurate, is unrelated to the contents of this
patch. Note that in an asm(), a "memory" clobber (see barrier()) is
not at all the same thing as a memory operand "m"(...), which is what
we're using here. Just drop this bit.

 > Every byte in the [p,p+n[ range must be used. If you only use the
 > first byte, via e.g. asm("" :: "m"(*(char*)p)), then the compiler
 > _will_ skip scrubbing bytes beyond the first.
and then
 > This works with
 > gcc-3.2.3 up to gcc-4.4.3.

You've edited my comments and put them together in a way that doesn't
quite make sense. In particular, "This works" doesn't refer to the
previous text but the local-struct-of-variable-size trick. The text
should read something like:

To prevent a memset() from being eliminated, the compiler must belive
that the memory area is used after the memset(). Also, every byte in
the memory area must be used. If only the first byte is used, via e.g.
asm("" :: "m"(*(char*)p)), then the compiler _will_ skip scrubbing
bytes beyond the first.

The trick is to define a local type of the same size as the memory
area, and use it for the "m" operand in a dummy asm():

	static inline void fake_memory_use(void *p, size_t n)
	{
		struct __scrub { char c[n]; }
		asm("" : : "m"(*(struct __scrub *)p));
	}

This looks strange, n being a variable, but has been confirmed to
work with gcc-3.2.3 up to gcc-4.4.3.

 > diff --git a/include/linux/compiler.h b/include/linux/compiler.h
 > index 188fcae..2f14199 100644
 > --- a/include/linux/compiler.h
 > +++ b/include/linux/compiler.h
 > @@ -302,4 +302,15 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
 >   */
 >  #define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
 >  
 > +/*
 > + * Dead store elimination is an optimization that may remove a write to a
 > + * buffer that is not used anymore. Use ARRAY_KEEP_STORE after a write when
 > + * the scrub is required for security reasons.
 > + */
 > +#define ARRAY_KEEP_STORE(p, n)				\

1. This doesn't need to be a macro. Please make it a static inline function.
2. Its function is to "fake" a use of a memory area, which allows us to prevent
   unwanted DSE elsewhere. So this should be fake_memory_use() or something.

 > +	do {						\
 > +		struct __scrub { typeof(*p) c[n]; };	\

The typeof(*p) suggestion doesn't work. It would require p to always be
a pointer type with an accurate (for memset) sizeof(*p). In general however
we'll memset some array described by a void*/size_t pair, and typeof in
that case is useless. The original'struct __scrub { char c[n]; };' was Ok.

 >  extern char *kstrndup(const char *s, size_t len, gfp_t gfp);
 >  extern void *kmemdup(const void *src, size_t len, gfp_t gfp);
 > +extern void secure_bzero(void *, __kernel_size_t);

Why is this __kernel_size_t and not just plain size_t? It's not
a user-space API.
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ