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