x86, bts: fix wrmsr and spinlock over kmalloc

Impact: fix sleeping-with-spinlock-held bugs/crashes

- Turn a wrmsr to write the DS_AREA MSR into a wrmsrl.
- Use irqsave variants of spinlocks.
- Do not allocate memory while holding spinlocks.

Reported-by: Stephane Eranian <eranian@googlemail.com>
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

authored by Markus Metzger and committed by Ingo Molnar de90add3 c4858ffc

+43 -40
+43 -40
arch/x86/kernel/ds.c
··· 209 209 static inline struct ds_context *ds_get_context(struct task_struct *task) 210 210 { 211 211 struct ds_context *context; 212 + unsigned long irq; 212 213 213 - spin_lock(&ds_lock); 214 + spin_lock_irqsave(&ds_lock, irq); 214 215 215 216 context = (task ? task->thread.ds_ctx : this_system_context); 216 217 if (context) 217 218 context->count++; 218 219 219 - spin_unlock(&ds_lock); 220 + spin_unlock_irqrestore(&ds_lock, irq); 220 221 221 222 return context; 222 223 } ··· 225 224 /* 226 225 * Same as ds_get_context, but allocates the context and it's DS 227 226 * structure, if necessary; returns NULL; if out of memory. 228 - * 229 - * pre: requires ds_lock to be held 230 227 */ 231 228 static inline struct ds_context *ds_alloc_context(struct task_struct *task) 232 229 { 233 230 struct ds_context **p_context = 234 231 (task ? &task->thread.ds_ctx : &this_system_context); 235 232 struct ds_context *context = *p_context; 233 + unsigned long irq; 236 234 237 235 if (!context) { 238 - spin_unlock(&ds_lock); 239 - 240 236 context = kzalloc(sizeof(*context), GFP_KERNEL); 241 - 242 - if (!context) { 243 - spin_lock(&ds_lock); 237 + if (!context) 244 238 return NULL; 245 - } 246 239 247 240 context->ds = kzalloc(ds_cfg.sizeof_ds, GFP_KERNEL); 248 241 if (!context->ds) { 249 242 kfree(context); 250 - spin_lock(&ds_lock); 251 243 return NULL; 252 244 } 253 245 254 - spin_lock(&ds_lock); 255 - /* 256 - * Check for race - another CPU could have allocated 257 - * it meanwhile: 258 - */ 246 + spin_lock_irqsave(&ds_lock, irq); 247 + 259 248 if (*p_context) { 260 249 kfree(context->ds); 261 250 kfree(context); 262 - return *p_context; 251 + 252 + context = *p_context; 253 + } else { 254 + *p_context = context; 255 + 256 + context->this = p_context; 257 + context->task = task; 258 + 259 + if (task) 260 + set_tsk_thread_flag(task, TIF_DS_AREA_MSR); 261 + 262 + if (!task || (task == current)) 263 + wrmsrl(MSR_IA32_DS_AREA, 264 + (unsigned long)context->ds); 263 265 } 264 - 265 - *p_context = context; 266 - 267 - context->this = p_context; 268 - context->task = task; 269 - 270 - if (task) 271 - set_tsk_thread_flag(task, TIF_DS_AREA_MSR); 272 - 273 - if (!task || (task == current)) 274 - wrmsr(MSR_IA32_DS_AREA, (unsigned long)context->ds, 0); 275 - 276 - get_tracer(task); 266 + spin_unlock_irqrestore(&ds_lock, irq); 277 267 } 278 268 279 269 context->count++; ··· 278 286 */ 279 287 static inline void ds_put_context(struct ds_context *context) 280 288 { 289 + unsigned long irq; 290 + 281 291 if (!context) 282 292 return; 283 293 284 - spin_lock(&ds_lock); 294 + spin_lock_irqsave(&ds_lock, irq); 285 295 286 296 if (--context->count) 287 297 goto out; ··· 305 311 kfree(context->ds); 306 312 kfree(context); 307 313 out: 308 - spin_unlock(&ds_lock); 314 + spin_unlock_irqrestore(&ds_lock, irq); 309 315 } 310 316 311 317 ··· 376 382 struct ds_context *context; 377 383 unsigned long buffer, adj; 378 384 const unsigned long alignment = (1 << 3); 385 + unsigned long irq; 379 386 int error = 0; 380 387 381 388 if (!ds_cfg.sizeof_ds) ··· 391 396 return -EOPNOTSUPP; 392 397 393 398 394 - spin_lock(&ds_lock); 395 - 396 - error = -ENOMEM; 397 399 context = ds_alloc_context(task); 398 400 if (!context) 399 - goto out_unlock; 401 + return -ENOMEM; 402 + 403 + spin_lock_irqsave(&ds_lock, irq); 400 404 401 405 error = -EPERM; 402 406 if (!check_tracer(task)) 403 407 goto out_unlock; 404 408 409 + get_tracer(task); 410 + 405 411 error = -EALREADY; 406 412 if (context->owner[qual] == current) 407 - goto out_unlock; 413 + goto out_put_tracer; 408 414 error = -EPERM; 409 415 if (context->owner[qual] != NULL) 410 - goto out_unlock; 416 + goto out_put_tracer; 411 417 context->owner[qual] = current; 412 418 413 - spin_unlock(&ds_lock); 419 + spin_unlock_irqrestore(&ds_lock, irq); 414 420 415 421 416 422 error = -ENOMEM; ··· 459 463 out_release: 460 464 context->owner[qual] = NULL; 461 465 ds_put_context(context); 466 + put_tracer(task); 467 + return error; 468 + 469 + out_put_tracer: 470 + spin_unlock_irqrestore(&ds_lock, irq); 471 + ds_put_context(context); 472 + put_tracer(task); 462 473 return error; 463 474 464 475 out_unlock: 465 - spin_unlock(&ds_lock); 476 + spin_unlock_irqrestore(&ds_lock, irq); 466 477 ds_put_context(context); 467 478 return error; 468 479 }