Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'cgroup-for-6.19-rc8-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup

Pull cgroup fixes from Tejun Heo:
"Three dmem fixes from Chen Ridong addressing use-after-free, RCU
warning, and NULL pointer dereference issues introduced with the dmem
controller.

All changes are confined to kernel/cgroup/dmem.c and can only affect
dmem controller users"

* tag 'cgroup-for-6.19-rc8-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/cgroup:
cgroup/dmem: avoid pool UAF
cgroup/dmem: avoid rcu warning when unregister region
cgroup/dmem: fix NULL pointer dereference when setting max

+63 -7
+63 -7
kernel/cgroup/dmem.c
··· 14 14 #include <linux/mutex.h> 15 15 #include <linux/page_counter.h> 16 16 #include <linux/parser.h> 17 + #include <linux/refcount.h> 17 18 #include <linux/rculist.h> 18 19 #include <linux/slab.h> 19 20 ··· 72 71 struct rcu_head rcu; 73 72 74 73 struct page_counter cnt; 74 + struct dmem_cgroup_pool_state *parent; 75 75 76 + refcount_t ref; 76 77 bool inited; 77 78 }; 78 79 ··· 90 87 */ 91 88 static DEFINE_SPINLOCK(dmemcg_lock); 92 89 static LIST_HEAD(dmem_cgroup_regions); 90 + 91 + static void dmemcg_free_region(struct kref *ref); 92 + static void dmemcg_pool_free_rcu(struct rcu_head *rcu); 93 93 94 94 static inline struct dmemcg_state * 95 95 css_to_dmemcs(struct cgroup_subsys_state *css) ··· 110 104 return cg->css.parent ? css_to_dmemcs(cg->css.parent) : NULL; 111 105 } 112 106 107 + static void dmemcg_pool_get(struct dmem_cgroup_pool_state *pool) 108 + { 109 + refcount_inc(&pool->ref); 110 + } 111 + 112 + static bool dmemcg_pool_tryget(struct dmem_cgroup_pool_state *pool) 113 + { 114 + return refcount_inc_not_zero(&pool->ref); 115 + } 116 + 117 + static void dmemcg_pool_put(struct dmem_cgroup_pool_state *pool) 118 + { 119 + if (!refcount_dec_and_test(&pool->ref)) 120 + return; 121 + 122 + call_rcu(&pool->rcu, dmemcg_pool_free_rcu); 123 + } 124 + 125 + static void dmemcg_pool_free_rcu(struct rcu_head *rcu) 126 + { 127 + struct dmem_cgroup_pool_state *pool = container_of(rcu, typeof(*pool), rcu); 128 + 129 + if (pool->parent) 130 + dmemcg_pool_put(pool->parent); 131 + kref_put(&pool->region->ref, dmemcg_free_region); 132 + kfree(pool); 133 + } 134 + 113 135 static void free_cg_pool(struct dmem_cgroup_pool_state *pool) 114 136 { 115 137 list_del(&pool->region_node); 116 - kfree(pool); 138 + dmemcg_pool_put(pool); 117 139 } 118 140 119 141 static void ··· 376 342 page_counter_init(&pool->cnt, 377 343 ppool ? &ppool->cnt : NULL, true); 378 344 reset_all_resource_limits(pool); 345 + refcount_set(&pool->ref, 1); 346 + kref_get(&region->ref); 347 + if (ppool && !pool->parent) { 348 + pool->parent = ppool; 349 + dmemcg_pool_get(ppool); 350 + } 379 351 380 352 list_add_tail_rcu(&pool->css_node, &dmemcs->pools); 381 353 list_add_tail(&pool->region_node, &region->pools); ··· 429 389 430 390 /* Fix up parent links, mark as inited. */ 431 391 pool->cnt.parent = &ppool->cnt; 392 + if (ppool && !pool->parent) { 393 + pool->parent = ppool; 394 + dmemcg_pool_get(ppool); 395 + } 432 396 pool->inited = true; 433 397 434 398 pool = ppool; ··· 467 423 */ 468 424 void dmem_cgroup_unregister_region(struct dmem_cgroup_region *region) 469 425 { 470 - struct list_head *entry; 426 + struct dmem_cgroup_pool_state *pool, *next; 471 427 472 428 if (!region) 473 429 return; ··· 477 433 /* Remove from global region list */ 478 434 list_del_rcu(&region->region_node); 479 435 480 - list_for_each_rcu(entry, &region->pools) { 481 - struct dmem_cgroup_pool_state *pool = 482 - container_of(entry, typeof(*pool), region_node); 483 - 436 + list_for_each_entry_safe(pool, next, &region->pools, region_node) { 484 437 list_del_rcu(&pool->css_node); 438 + list_del(&pool->region_node); 439 + dmemcg_pool_put(pool); 485 440 } 486 441 487 442 /* ··· 561 518 */ 562 519 void dmem_cgroup_pool_state_put(struct dmem_cgroup_pool_state *pool) 563 520 { 564 - if (pool) 521 + if (pool) { 565 522 css_put(&pool->cs->css); 523 + dmemcg_pool_put(pool); 524 + } 566 525 } 567 526 EXPORT_SYMBOL_GPL(dmem_cgroup_pool_state_put); 568 527 ··· 578 533 pool = find_cg_pool_locked(cg, region); 579 534 if (pool && !READ_ONCE(pool->inited)) 580 535 pool = NULL; 536 + if (pool && !dmemcg_pool_tryget(pool)) 537 + pool = NULL; 581 538 rcu_read_unlock(); 582 539 583 540 while (!pool) { ··· 588 541 pool = get_cg_pool_locked(cg, region, &allocpool); 589 542 else 590 543 pool = ERR_PTR(-ENODEV); 544 + if (!IS_ERR(pool)) 545 + dmemcg_pool_get(pool); 591 546 spin_unlock(&dmemcg_lock); 592 547 593 548 if (pool == ERR_PTR(-ENOMEM)) { ··· 625 576 626 577 page_counter_uncharge(&pool->cnt, size); 627 578 css_put(&pool->cs->css); 579 + dmemcg_pool_put(pool); 628 580 } 629 581 EXPORT_SYMBOL_GPL(dmem_cgroup_uncharge); 630 582 ··· 677 627 if (ret_limit_pool) { 678 628 *ret_limit_pool = container_of(fail, struct dmem_cgroup_pool_state, cnt); 679 629 css_get(&(*ret_limit_pool)->cs->css); 630 + dmemcg_pool_get(*ret_limit_pool); 680 631 } 632 + dmemcg_pool_put(pool); 681 633 ret = -EAGAIN; 682 634 goto err; 683 635 } ··· 752 700 if (!region_name[0]) 753 701 continue; 754 702 703 + if (!options || !*options) 704 + return -EINVAL; 705 + 755 706 rcu_read_lock(); 756 707 region = dmemcg_get_region_by_name(region_name); 757 708 rcu_read_unlock(); ··· 774 719 775 720 /* And commit */ 776 721 apply(pool, new_limit); 722 + dmemcg_pool_put(pool); 777 723 778 724 out_put: 779 725 kref_put(&region->ref, dmemcg_free_region);