From: Alireza Asgari Repeated nonblocking charges can accumulate deferred memory.high work before the charging task returns to userspace. The unsigned-int page counter can wrap, including to zero, and erase that pending work. Large virtual capacity reservations can reach this range without allocating corresponding physical memory, including on 64-bit kernels. Saturate the addition at UINT_MAX. Ordinary additions retain their value; very large debt retains the pressure signal instead of wrapping. This does not change memory.max checks or promise exact debt beyond the saturation point. Found during AI-assisted source review of virtual capacity accounting. The accompanying KUnit patch calls the real GFP_NOWAIT charging path with an isolated counter fixture and a 65-page charge. It seeds debt near UINT_MAX to cover exact-boundary, crossing, and already-saturated cases; it does not allocate the corresponding large amount of physical memory. The debt case passed on both 64-bit and 32-bit x86 KUnit kernels built from this series. The test exercises the production charging and debt update path; it does not reproduce end-to-end throttling. Fixes: b23afb93d317 ("memcg: punt high overage reclaim to return-to-userland path") Signed-off-by: Alireza Asgari --- mm/memcontrol.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 84f4a8fd4d16..1980fb7431a8 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -2894,8 +2894,12 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask, * Target some best-effort fairness between the tasks, * and distribute reclaim work and delay penalties * based on how much each task is actually allocating. + * Saturate so repeated nonblocking charges cannot erase + * pending work by wrapping the counter back to zero. */ - current->memcg_nr_pages_over_high += batch; + current->memcg_nr_pages_over_high += min( + batch, + UINT_MAX - current->memcg_nr_pages_over_high); set_notify_resume(current); break; } -- 2.34.1