aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuresh Siddha <suresh.b.siddha@intel.com>2008-08-13 08:02:26 -0400
committerHerbert Xu <herbert@gondor.apana.org.au>2008-08-13 08:02:26 -0400
commite49140120c88eb99db1a9172d9ac224c0f2bbdd2 (patch)
tree2436693f64f185f45d67d4299b879091b2301f4b
parent318e5313923197e71a94f7b18835151649384b7f (diff)
crypto: padlock - fix VIA PadLock instruction usage with irq_ts_save/restore()
Wolfgang Walter reported this oops on his via C3 using padlock for AES-encryption: ################################################################## BUG: unable to handle kernel NULL pointer dereference at 000001f0 IP: [<c01028c5>] __switch_to+0x30/0x117 *pde = 00000000 Oops: 0002 [#1] PREEMPT Modules linked in: Pid: 2071, comm: sleep Not tainted (2.6.26 #11) EIP: 0060:[<c01028c5>] EFLAGS: 00010002 CPU: 0 EIP is at __switch_to+0x30/0x117 EAX: 00000000 EBX: c0493300 ECX: dc48dd00 EDX: c0493300 ESI: dc48dd00 EDI: c0493530 EBP: c04cff8c ESP: c04cff7c DS: 007b ES: 007b FS: 0000 GS: 0033 SS: 0068 Process sleep (pid: 2071, ti=c04ce000 task=dc48dd00 task.ti=d2fe6000) Stack: dc48df30 c0493300 00000000 00000000 d2fe7f44 c03b5b43 c04cffc8 00000046 c0131856 0000005a dc472d3c c0493300 c0493470 d983ae00 00002696 00000000 c0239f54 00000000 c04c4000 c04cffd8 c01025fe c04f3740 00049800 c04cffe0 Call Trace: [<c03b5b43>] ? schedule+0x285/0x2ff [<c0131856>] ? pm_qos_requirement+0x3c/0x53 [<c0239f54>] ? acpi_processor_idle+0x0/0x434 [<c01025fe>] ? cpu_idle+0x73/0x7f [<c03a4dcd>] ? rest_init+0x61/0x63 ======================= Wolfgang also found out that adding kernel_fpu_begin() and kernel_fpu_end() around the padlock instructions fix the oops. Suresh wrote: These padlock instructions though don't use/touch SSE registers, but it behaves similar to other SSE instructions. For example, it might cause DNA faults when cr0.ts is set. While this is a spurious DNA trap, it might cause oops with the recent fpu code changes. This is the code sequence that is probably causing this problem: a) new app is getting exec'd and it is somewhere in between start_thread() and flush_old_exec() in the load_xyz_binary() b) At pont "a", task's fpu state (like TS_USEDFPU, used_math() etc) is cleared. c) Now we get an interrupt/softirq which starts using these encrypt/decrypt routines in the network stack. This generates a math fault (as cr0.ts is '1') which sets TS_USEDFPU and restores the math that is in the task's xstate. d) Return to exec code path, which does start_thread() which does free_thread_xstate() and sets xstate pointer to NULL while the TS_USEDFPU is still set. e) At the next context switch from the new exec'd task to another task, we have a scenarios where TS_USEDFPU is set but xstate pointer is null. This can cause an oops during unlazy_fpu() in __switch_to() Now: 1) This should happen with or with out pre-emption. Viro also encountered similar problem with out CONFIG_PREEMPT. 2) kernel_fpu_begin() and kernel_fpu_end() will fix this problem, because kernel_fpu_begin() will manually do a clts() and won't run in to the situation of setting TS_USEDFPU in step "c" above. 3) This was working before the fpu changes, because its a spurious math fault which doesn't corrupt any fpu/sse registers and the task's math state was always in an allocated state. With out the recent lazy fpu allocation changes, while we don't see oops, there is a possible race still present in older kernels(for example, while kernel is using kernel_fpu_begin() in some optimized clear/copy page and an interrupt/softirq happens which uses these padlock instructions generating DNA fault). This is the failing scenario that existed even before the lazy fpu allocation changes: 0. CPU's TS flag is set 1. kernel using FPU in some optimized copy routine and while doing kernel_fpu_begin() takes an interrupt just before doing clts() 2. Takes an interrupt and ipsec uses padlock instruction. And we take a DNA fault as TS flag is still set. 3. We handle the DNA fault and set TS_USEDFPU and clear cr0.ts 4. We complete the padlock routine 5. Go back to step-1, which resumes clts() in kernel_fpu_begin(), finishes the optimized copy routine and does kernel_fpu_end(). At this point, we have cr0.ts again set to '1' but the task's TS_USEFPU is stilll set and not cleared. 6. Now kernel resumes its user operation. And at the next context switch, kernel sees it has do a FP save as TS_USEDFPU is still set and then will do a unlazy_fpu() in __switch_to(). unlazy_fpu() will take a DNA fault, as cr0.ts is '1' and now, because we are in __switch_to(), math_state_restore() will get confused and will restore the next task's FP state and will save it in prev tasks's FP state. Remember, in __switch_to() we are already on the stack of the next task but take a DNA fault for the prev task. This causes the fpu leakage. Fix the padlock instruction usage by calling them inside the context of new routines irq_ts_save/restore(), which clear/restore cr0.ts manually in the interrupt context. This will not generate spurious DNA in the context of the interrupt which will fix the oops encountered and the possible FPU leakage issue. Reported-and-bisected-by: Wolfgang Walter <wolfgang.walter@stwm.de> Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
-rw-r--r--drivers/char/hw_random/via-rng.c8
-rw-r--r--drivers/crypto/padlock-aes.c28
-rw-r--r--drivers/crypto/padlock-sha.c9
-rw-r--r--include/asm-x86/i387.h32
4 files changed, 76 insertions, 1 deletions
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
index f7feae4ebb5e..128202e18fc9 100644
--- a/drivers/char/hw_random/via-rng.c
+++ b/drivers/char/hw_random/via-rng.c
@@ -31,6 +31,7 @@
31#include <asm/io.h> 31#include <asm/io.h>
32#include <asm/msr.h> 32#include <asm/msr.h>
33#include <asm/cpufeature.h> 33#include <asm/cpufeature.h>
34#include <asm/i387.h>
34 35
35 36
36#define PFX KBUILD_MODNAME ": " 37#define PFX KBUILD_MODNAME ": "
@@ -67,16 +68,23 @@ enum {
67 * Another possible performance boost may come from simply buffering 68 * Another possible performance boost may come from simply buffering
68 * until we have 4 bytes, thus returning a u32 at a time, 69 * until we have 4 bytes, thus returning a u32 at a time,
69 * instead of the current u8-at-a-time. 70 * instead of the current u8-at-a-time.
71 *
72 * Padlock instructions can generate a spurious DNA fault, so
73 * we have to call them in the context of irq_ts_save/restore()
70 */ 74 */
71 75
72static inline u32 xstore(u32 *addr, u32 edx_in) 76static inline u32 xstore(u32 *addr, u32 edx_in)
73{ 77{
74 u32 eax_out; 78 u32 eax_out;
79 int ts_state;
80
81 ts_state = irq_ts_save();
75 82
76 asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" 83 asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
77 :"=m"(*addr), "=a"(eax_out) 84 :"=m"(*addr), "=a"(eax_out)
78 :"D"(addr), "d"(edx_in)); 85 :"D"(addr), "d"(edx_in));
79 86
87 irq_ts_restore(ts_state);
80 return eax_out; 88 return eax_out;
81} 89}
82 90
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 54a2a166e566..bf2917d197a0 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -16,6 +16,7 @@
16#include <linux/interrupt.h> 16#include <linux/interrupt.h>
17#include <linux/kernel.h> 17#include <linux/kernel.h>
18#include <asm/byteorder.h> 18#include <asm/byteorder.h>
19#include <asm/i387.h>
19#include "padlock.h" 20#include "padlock.h"
20 21
21/* Control word. */ 22/* Control word. */
@@ -141,6 +142,12 @@ static inline void padlock_reset_key(void)
141 asm volatile ("pushfl; popfl"); 142 asm volatile ("pushfl; popfl");
142} 143}
143 144
145/*
146 * While the padlock instructions don't use FP/SSE registers, they
147 * generate a spurious DNA fault when cr0.ts is '1'. These instructions
148 * should be used only inside the irq_ts_save/restore() context
149 */
150
144static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key, 151static inline void padlock_xcrypt(const u8 *input, u8 *output, void *key,
145 void *control_word) 152 void *control_word)
146{ 153{
@@ -205,15 +212,23 @@ static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key,
205static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) 212static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
206{ 213{
207 struct aes_ctx *ctx = aes_ctx(tfm); 214 struct aes_ctx *ctx = aes_ctx(tfm);
215 int ts_state;
208 padlock_reset_key(); 216 padlock_reset_key();
217
218 ts_state = irq_ts_save();
209 aes_crypt(in, out, ctx->E, &ctx->cword.encrypt); 219 aes_crypt(in, out, ctx->E, &ctx->cword.encrypt);
220 irq_ts_restore(ts_state);
210} 221}
211 222
212static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) 223static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
213{ 224{
214 struct aes_ctx *ctx = aes_ctx(tfm); 225 struct aes_ctx *ctx = aes_ctx(tfm);
226 int ts_state;
215 padlock_reset_key(); 227 padlock_reset_key();
228
229 ts_state = irq_ts_save();
216 aes_crypt(in, out, ctx->D, &ctx->cword.decrypt); 230 aes_crypt(in, out, ctx->D, &ctx->cword.decrypt);
231 irq_ts_restore(ts_state);
217} 232}
218 233
219static struct crypto_alg aes_alg = { 234static struct crypto_alg aes_alg = {
@@ -244,12 +259,14 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
244 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm); 259 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm);
245 struct blkcipher_walk walk; 260 struct blkcipher_walk walk;
246 int err; 261 int err;
262 int ts_state;
247 263
248 padlock_reset_key(); 264 padlock_reset_key();
249 265
250 blkcipher_walk_init(&walk, dst, src, nbytes); 266 blkcipher_walk_init(&walk, dst, src, nbytes);
251 err = blkcipher_walk_virt(desc, &walk); 267 err = blkcipher_walk_virt(desc, &walk);
252 268
269 ts_state = irq_ts_save();
253 while ((nbytes = walk.nbytes)) { 270 while ((nbytes = walk.nbytes)) {
254 padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr, 271 padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr,
255 ctx->E, &ctx->cword.encrypt, 272 ctx->E, &ctx->cword.encrypt,
@@ -257,6 +274,7 @@ static int ecb_aes_encrypt(struct blkcipher_desc *desc,
257 nbytes &= AES_BLOCK_SIZE - 1; 274 nbytes &= AES_BLOCK_SIZE - 1;
258 err = blkcipher_walk_done(desc, &walk, nbytes); 275 err = blkcipher_walk_done(desc, &walk, nbytes);
259 } 276 }
277 irq_ts_restore(ts_state);
260 278
261 return err; 279 return err;
262} 280}
@@ -268,12 +286,14 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
268 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm); 286 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm);
269 struct blkcipher_walk walk; 287 struct blkcipher_walk walk;
270 int err; 288 int err;
289 int ts_state;
271 290
272 padlock_reset_key(); 291 padlock_reset_key();
273 292
274 blkcipher_walk_init(&walk, dst, src, nbytes); 293 blkcipher_walk_init(&walk, dst, src, nbytes);
275 err = blkcipher_walk_virt(desc, &walk); 294 err = blkcipher_walk_virt(desc, &walk);
276 295
296 ts_state = irq_ts_save();
277 while ((nbytes = walk.nbytes)) { 297 while ((nbytes = walk.nbytes)) {
278 padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr, 298 padlock_xcrypt_ecb(walk.src.virt.addr, walk.dst.virt.addr,
279 ctx->D, &ctx->cword.decrypt, 299 ctx->D, &ctx->cword.decrypt,
@@ -281,7 +301,7 @@ static int ecb_aes_decrypt(struct blkcipher_desc *desc,
281 nbytes &= AES_BLOCK_SIZE - 1; 301 nbytes &= AES_BLOCK_SIZE - 1;
282 err = blkcipher_walk_done(desc, &walk, nbytes); 302 err = blkcipher_walk_done(desc, &walk, nbytes);
283 } 303 }
284 304 irq_ts_restore(ts_state);
285 return err; 305 return err;
286} 306}
287 307
@@ -314,12 +334,14 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
314 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm); 334 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm);
315 struct blkcipher_walk walk; 335 struct blkcipher_walk walk;
316 int err; 336 int err;
337 int ts_state;
317 338
318 padlock_reset_key(); 339 padlock_reset_key();
319 340
320 blkcipher_walk_init(&walk, dst, src, nbytes); 341 blkcipher_walk_init(&walk, dst, src, nbytes);
321 err = blkcipher_walk_virt(desc, &walk); 342 err = blkcipher_walk_virt(desc, &walk);
322 343
344 ts_state = irq_ts_save();
323 while ((nbytes = walk.nbytes)) { 345 while ((nbytes = walk.nbytes)) {
324 u8 *iv = padlock_xcrypt_cbc(walk.src.virt.addr, 346 u8 *iv = padlock_xcrypt_cbc(walk.src.virt.addr,
325 walk.dst.virt.addr, ctx->E, 347 walk.dst.virt.addr, ctx->E,
@@ -329,6 +351,7 @@ static int cbc_aes_encrypt(struct blkcipher_desc *desc,
329 nbytes &= AES_BLOCK_SIZE - 1; 351 nbytes &= AES_BLOCK_SIZE - 1;
330 err = blkcipher_walk_done(desc, &walk, nbytes); 352 err = blkcipher_walk_done(desc, &walk, nbytes);
331 } 353 }
354 irq_ts_restore(ts_state);
332 355
333 return err; 356 return err;
334} 357}
@@ -340,12 +363,14 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
340 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm); 363 struct aes_ctx *ctx = blk_aes_ctx(desc->tfm);
341 struct blkcipher_walk walk; 364 struct blkcipher_walk walk;
342 int err; 365 int err;
366 int ts_state;
343 367
344 padlock_reset_key(); 368 padlock_reset_key();
345 369
346 blkcipher_walk_init(&walk, dst, src, nbytes); 370 blkcipher_walk_init(&walk, dst, src, nbytes);
347 err = blkcipher_walk_virt(desc, &walk); 371 err = blkcipher_walk_virt(desc, &walk);
348 372
373 ts_state = irq_ts_save();
349 while ((nbytes = walk.nbytes)) { 374 while ((nbytes = walk.nbytes)) {
350 padlock_xcrypt_cbc(walk.src.virt.addr, walk.dst.virt.addr, 375 padlock_xcrypt_cbc(walk.src.virt.addr, walk.dst.virt.addr,
351 ctx->D, walk.iv, &ctx->cword.decrypt, 376 ctx->D, walk.iv, &ctx->cword.decrypt,
@@ -354,6 +379,7 @@ static int cbc_aes_decrypt(struct blkcipher_desc *desc,
354 err = blkcipher_walk_done(desc, &walk, nbytes); 379 err = blkcipher_walk_done(desc, &walk, nbytes);
355 } 380 }
356 381
382 irq_ts_restore(ts_state);
357 return err; 383 return err;
358} 384}
359 385
diff --git a/drivers/crypto/padlock-sha.c b/drivers/crypto/padlock-sha.c
index 40d5680fa013..a7fbadebf623 100644
--- a/drivers/crypto/padlock-sha.c
+++ b/drivers/crypto/padlock-sha.c
@@ -22,6 +22,7 @@
22#include <linux/interrupt.h> 22#include <linux/interrupt.h>
23#include <linux/kernel.h> 23#include <linux/kernel.h>
24#include <linux/scatterlist.h> 24#include <linux/scatterlist.h>
25#include <asm/i387.h>
25#include "padlock.h" 26#include "padlock.h"
26 27
27#define SHA1_DEFAULT_FALLBACK "sha1-generic" 28#define SHA1_DEFAULT_FALLBACK "sha1-generic"
@@ -102,6 +103,7 @@ static void padlock_do_sha1(const char *in, char *out, int count)
102 * PadLock microcode needs it that big. */ 103 * PadLock microcode needs it that big. */
103 char buf[128+16]; 104 char buf[128+16];
104 char *result = NEAREST_ALIGNED(buf); 105 char *result = NEAREST_ALIGNED(buf);
106 int ts_state;
105 107
106 ((uint32_t *)result)[0] = SHA1_H0; 108 ((uint32_t *)result)[0] = SHA1_H0;
107 ((uint32_t *)result)[1] = SHA1_H1; 109 ((uint32_t *)result)[1] = SHA1_H1;
@@ -109,9 +111,12 @@ static void padlock_do_sha1(const char *in, char *out, int count)
109 ((uint32_t *)result)[3] = SHA1_H3; 111 ((uint32_t *)result)[3] = SHA1_H3;
110 ((uint32_t *)result)[4] = SHA1_H4; 112 ((uint32_t *)result)[4] = SHA1_H4;
111 113
114 /* prevent taking the spurious DNA fault with padlock. */
115 ts_state = irq_ts_save();
112 asm volatile (".byte 0xf3,0x0f,0xa6,0xc8" /* rep xsha1 */ 116 asm volatile (".byte 0xf3,0x0f,0xa6,0xc8" /* rep xsha1 */
113 : "+S"(in), "+D"(result) 117 : "+S"(in), "+D"(result)
114 : "c"(count), "a"(0)); 118 : "c"(count), "a"(0));
119 irq_ts_restore(ts_state);
115 120
116 padlock_output_block((uint32_t *)result, (uint32_t *)out, 5); 121 padlock_output_block((uint32_t *)result, (uint32_t *)out, 5);
117} 122}
@@ -123,6 +128,7 @@ static void padlock_do_sha256(const char *in, char *out, int count)
123 * PadLock microcode needs it that big. */ 128 * PadLock microcode needs it that big. */
124 char buf[128+16]; 129 char buf[128+16];
125 char *result = NEAREST_ALIGNED(buf); 130 char *result = NEAREST_ALIGNED(buf);
131 int ts_state;
126 132
127 ((uint32_t *)result)[0] = SHA256_H0; 133 ((uint32_t *)result)[0] = SHA256_H0;
128 ((uint32_t *)result)[1] = SHA256_H1; 134 ((uint32_t *)result)[1] = SHA256_H1;
@@ -133,9 +139,12 @@ static void padlock_do_sha256(const char *in, char *out, int count)
133 ((uint32_t *)result)[6] = SHA256_H6; 139 ((uint32_t *)result)[6] = SHA256_H6;
134 ((uint32_t *)result)[7] = SHA256_H7; 140 ((uint32_t *)result)[7] = SHA256_H7;
135 141
142 /* prevent taking the spurious DNA fault with padlock. */
143 ts_state = irq_ts_save();
136 asm volatile (".byte 0xf3,0x0f,0xa6,0xd0" /* rep xsha256 */ 144 asm volatile (".byte 0xf3,0x0f,0xa6,0xd0" /* rep xsha256 */
137 : "+S"(in), "+D"(result) 145 : "+S"(in), "+D"(result)
138 : "c"(count), "a"(0)); 146 : "c"(count), "a"(0));
147 irq_ts_restore(ts_state);
139 148
140 padlock_output_block((uint32_t *)result, (uint32_t *)out, 8); 149 padlock_output_block((uint32_t *)result, (uint32_t *)out, 8);
141} 150}
diff --git a/include/asm-x86/i387.h b/include/asm-x86/i387.h
index 96fa8449ff11..6d3b21063419 100644
--- a/include/asm-x86/i387.h
+++ b/include/asm-x86/i387.h
@@ -13,6 +13,7 @@
13#include <linux/sched.h> 13#include <linux/sched.h>
14#include <linux/kernel_stat.h> 14#include <linux/kernel_stat.h>
15#include <linux/regset.h> 15#include <linux/regset.h>
16#include <linux/hardirq.h>
16#include <asm/asm.h> 17#include <asm/asm.h>
17#include <asm/processor.h> 18#include <asm/processor.h>
18#include <asm/sigcontext.h> 19#include <asm/sigcontext.h>
@@ -236,6 +237,37 @@ static inline void kernel_fpu_end(void)
236 preempt_enable(); 237 preempt_enable();
237} 238}
238 239
240/*
241 * Some instructions like VIA's padlock instructions generate a spurious
242 * DNA fault but don't modify SSE registers. And these instructions
243 * get used from interrupt context aswell. To prevent these kernel instructions
244 * in interrupt context interact wrongly with other user/kernel fpu usage, we
245 * should use them only in the context of irq_ts_save/restore()
246 */
247static inline int irq_ts_save(void)
248{
249 /*
250 * If we are in process context, we are ok to take a spurious DNA fault.
251 * Otherwise, doing clts() in process context require pre-emption to
252 * be disabled or some heavy lifting like kernel_fpu_begin()
253 */
254 if (!in_interrupt())
255 return 0;
256
257 if (read_cr0() & X86_CR0_TS) {
258 clts();
259 return 1;
260 }
261
262 return 0;
263}
264
265static inline void irq_ts_restore(int TS_state)
266{
267 if (TS_state)
268 stts();
269}
270
239#ifdef CONFIG_X86_64 271#ifdef CONFIG_X86_64
240 272
241static inline void save_init_fpu(struct task_struct *tsk) 273static inline void save_init_fpu(struct task_struct *tsk)