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

Configure Feed

Select the types of activity you want to include in your feed.

mm: memcontrol: correct the type of stats_updates to unsigned long

Patch series "fix unexpected type conversions and potential overflows",
v3.

As Harry Yoo pointed out [1], in scenarios where massive state updates
occur (e.g., during the reparenting of LRU folios), the values passed to
memcg stat update functions can accumulate and exceed the upper limit of a
32-bit integer.

If the parameter types are not large enough (like 'int') or are handled
incorrectly, it can lead to severe truncation, potential overflow issues,
and unexpected type conversion bugs.

This series aims to address these issues by correcting the parameter types
in the relevant functions, and by fixing an implicit conversion bug in
memcg_state_val_in_pages().


This patch (of 3):

The memcg_rstat_updated() tracks updates for vmstats_percpu->state and
lruvec_stats_percpu->state. Since these state values are of type long,
change the val parameter passed to memcg_rstat_updated() to long as well.

Correspondingly, change the type of stats_updates in struct
memcg_vmstats_percpu and struct memcg_vmstats from unsigned int and
atomic_t to unsigned long and atomic_long_t respectively to prevent
potential overflow when handling large state updates during the
reparenting of LRU folios.

Link: https://lore.kernel.org/cover.1774604356.git.zhengqi.arch@bytedance.com
Link: https://lore.kernel.org/a5b0b468e7b4fe5f26c50e36d5d016f16d92f98f.1774604356.git.zhengqi.arch@bytedance.com
Link: https://lore.kernel.org/all/acDxaEgnqPI-Z4be@hyeyoo/ [1]
Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
Reviewed-by: Lorenzo Stoakes (Oracle) <ljs@kernel.org>
Reviewed-by: Harry Yoo (Oracle) <harry@kernel.org>
Cc: Allen Pais <apais@linux.microsoft.com>
Cc: Axel Rasmussen <axelrasmussen@google.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: David Hildenbrand <david@kernel.org>
Cc: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com>
Cc: Hugh Dickins <hughd@google.com>
Cc: Imran Khan <imran.f.khan@oracle.com>
Cc: Johannes Weiner <hannes@cmpxchg.org>
Cc: Kamalesh Babulal <kamalesh.babulal@oracle.com>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: Michal Hocko <mhocko@kernel.org>
Cc: Michal Koutný <mkoutny@suse.com>
Cc: Muchun Song <muchun.song@linux.dev>
Cc: Roman Gushchin <roman.gushchin@linux.dev>
Cc: Shakeel Butt <shakeel.butt@linux.dev>
Cc: Usama Arif <usamaarif642@gmail.com>
Cc: Wei Xu <weixugc@google.com>
Cc: Yuanchu Xie <yuanchu@google.com>
Cc: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Qi Zheng and committed by
Andrew Morton
616795d7 0a98e139

+9 -9
+9 -9
mm/memcontrol.c
··· 608 608 609 609 struct memcg_vmstats_percpu { 610 610 /* Stats updates since the last flush */ 611 - unsigned int stats_updates; 611 + unsigned long stats_updates; 612 612 613 613 /* Cached pointers for fast iteration in memcg_rstat_updated() */ 614 614 struct memcg_vmstats_percpu __percpu *parent_pcpu; ··· 639 639 unsigned long events_pending[NR_MEMCG_EVENTS]; 640 640 641 641 /* Stats updates since the last flush */ 642 - atomic_t stats_updates; 642 + atomic_long_t stats_updates; 643 643 }; 644 644 645 645 /* ··· 665 665 666 666 static bool memcg_vmstats_needs_flush(struct memcg_vmstats *vmstats) 667 667 { 668 - return atomic_read(&vmstats->stats_updates) > 668 + return atomic_long_read(&vmstats->stats_updates) > 669 669 MEMCG_CHARGE_BATCH * num_online_cpus(); 670 670 } 671 671 672 - static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val, 672 + static inline void memcg_rstat_updated(struct mem_cgroup *memcg, long val, 673 673 int cpu) 674 674 { 675 675 struct memcg_vmstats_percpu __percpu *statc_pcpu; 676 676 struct memcg_vmstats_percpu *statc; 677 - unsigned int stats_updates; 677 + unsigned long stats_updates; 678 678 679 679 if (!val) 680 680 return; ··· 697 697 continue; 698 698 699 699 stats_updates = this_cpu_xchg(statc_pcpu->stats_updates, 0); 700 - atomic_add(stats_updates, &statc->vmstats->stats_updates); 700 + atomic_long_add(stats_updates, &statc->vmstats->stats_updates); 701 701 } 702 702 } 703 703 ··· 705 705 { 706 706 bool needs_flush = memcg_vmstats_needs_flush(memcg->vmstats); 707 707 708 - trace_memcg_flush_stats(memcg, atomic_read(&memcg->vmstats->stats_updates), 708 + trace_memcg_flush_stats(memcg, atomic_long_read(&memcg->vmstats->stats_updates), 709 709 force, needs_flush); 710 710 711 711 if (!force && !needs_flush) ··· 4413 4413 } 4414 4414 WRITE_ONCE(statc->stats_updates, 0); 4415 4415 /* We are in a per-cpu loop here, only do the atomic write once */ 4416 - if (atomic_read(&memcg->vmstats->stats_updates)) 4417 - atomic_set(&memcg->vmstats->stats_updates, 0); 4416 + if (atomic_long_read(&memcg->vmstats->stats_updates)) 4417 + atomic_long_set(&memcg->vmstats->stats_updates, 0); 4418 4418 } 4419 4419 4420 4420 static void mem_cgroup_fork(struct task_struct *task)