Commit 9c789e74 authored by Michael Ellerman's avatar Michael Ellerman Committed by Liao Chen
Browse files

powerpc/pseries: Fix dtl_access_lock to be a rw_semaphore

stable inclusion
from stable-v5.10.231
commit 6956c0e7346ce1bbfc726755aa8da10d26e84276
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/IBEDPE
CVE: CVE-2024-56701

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=6956c0e7346ce1bbfc726755aa8da10d26e84276



--------------------------------

[ Upstream commit cadae3a45d23aa4f6485938a67cbc47aaaa25e38 ]

The dtl_access_lock needs to be a rw_sempahore, a sleeping lock, because
the code calls kmalloc() while holding it, which can sleep:

  # echo 1 > /proc/powerpc/vcpudispatch_stats
  BUG: sleeping function called from invalid context at include/linux/sched/mm.h:337
  in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 199, name: sh
  preempt_count: 1, expected: 0
  3 locks held by sh/199:
   #0: c00000000a0743f8 (sb_writers#3){.+.+}-{0:0}, at: vfs_write+0x324/0x438
   #1: c0000000028c7058 (dtl_enable_mutex){+.+.}-{3:3}, at: vcpudispatch_stats_write+0xd4/0x5f4
   #2: c0000000028c70b8 (dtl_access_lock){+.+.}-{2:2}, at: vcpudispatch_stats_write+0x220/0x5f4
  CPU: 0 PID: 199 Comm: sh Not tainted 6.10.0-rc4 #152
  Hardware name: IBM pSeries (emulated by qemu) POWER9 (raw) 0x4e1202 0xf000005 of:SLOF,HEAD hv:linux,kvm pSeries
  Call Trace:
    dump_stack_lvl+0x130/0x148 (unreliable)
    __might_resched+0x174/0x410
    kmem_cache_alloc_noprof+0x340/0x3d0
    alloc_dtl_buffers+0x124/0x1ac
    vcpudispatch_stats_write+0x2a8/0x5f4
    proc_reg_write+0xf4/0x150
    vfs_write+0xfc/0x438
    ksys_write+0x88/0x148
    system_call_exception+0x1c4/0x5a0
    system_call_common+0xf4/0x258

Fixes: 06220d78 ("powerpc/pseries: Introduce rwlock to gatekeep DTLB usage")
Tested-by: default avatarKajol Jain <kjain@linux.ibm.com>
Reviewed-by: default avatarNysal Jan K.A <nysal@linux.ibm.com>
Reviewed-by: default avatarKajol Jain <kjain@linux.ibm.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://patch.msgid.link/20240819122401.513203-1-mpe@ellerman.id.au


Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarLiao Chen <liaochen4@huawei.com>
parent d51fb86c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
#ifndef _ASM_POWERPC_DTL_H
#define _ASM_POWERPC_DTL_H

#include <linux/rwsem.h>
#include <asm/lppaca.h>
#include <linux/spinlock_types.h>

/*
 * Layout of entries in the hypervisor's dispatch trace log buffer.
@@ -35,7 +35,7 @@ struct dtl_entry {
#define DTL_LOG_ALL		(DTL_LOG_CEDE | DTL_LOG_PREEMPT | DTL_LOG_FAULT)

extern struct kmem_cache *dtl_cache;
extern rwlock_t dtl_access_lock;
extern struct rw_semaphore dtl_access_lock;

/*
 * When CONFIG_VIRT_CPU_ACCOUNTING_NATIVE = y, the cpu accounting code controls
+4 −4
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static int dtl_enable(struct dtl *dtl)
		return -EBUSY;

	/* ensure there are no other conflicting dtl users */
	if (!read_trylock(&dtl_access_lock))
	if (!down_read_trylock(&dtl_access_lock))
		return -EBUSY;

	n_entries = dtl_buf_entries;
@@ -189,7 +189,7 @@ static int dtl_enable(struct dtl *dtl)
	if (!buf) {
		printk(KERN_WARNING "%s: buffer alloc failed for cpu %d\n",
				__func__, dtl->cpu);
		read_unlock(&dtl_access_lock);
		up_read(&dtl_access_lock);
		return -ENOMEM;
	}

@@ -207,7 +207,7 @@ static int dtl_enable(struct dtl *dtl)
	spin_unlock(&dtl->lock);

	if (rc) {
		read_unlock(&dtl_access_lock);
		up_read(&dtl_access_lock);
		kmem_cache_free(dtl_cache, buf);
	}

@@ -222,7 +222,7 @@ static void dtl_disable(struct dtl *dtl)
	dtl->buf = NULL;
	dtl->buf_entries = 0;
	spin_unlock(&dtl->lock);
	read_unlock(&dtl_access_lock);
	up_read(&dtl_access_lock);
}

/* file interface */
+4 −4
Original line number Diff line number Diff line
@@ -166,7 +166,7 @@ struct vcpu_dispatch_data {
 */
#define NR_CPUS_H	NR_CPUS

DEFINE_RWLOCK(dtl_access_lock);
DECLARE_RWSEM(dtl_access_lock);
static DEFINE_PER_CPU(struct vcpu_dispatch_data, vcpu_disp_data);
static DEFINE_PER_CPU(u64, dtl_entry_ridx);
static DEFINE_PER_CPU(struct dtl_worker, dtl_workers);
@@ -460,7 +460,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
{
	int rc = 0, state;

	if (!write_trylock(&dtl_access_lock)) {
	if (!down_write_trylock(&dtl_access_lock)) {
		rc = -EBUSY;
		goto out;
	}
@@ -476,7 +476,7 @@ static int dtl_worker_enable(unsigned long *time_limit)
		pr_err("vcpudispatch_stats: unable to setup workqueue for DTL processing\n");
		free_dtl_buffers(time_limit);
		reset_global_dtl_mask();
		write_unlock(&dtl_access_lock);
		up_write(&dtl_access_lock);
		rc = -EINVAL;
		goto out;
	}
@@ -491,7 +491,7 @@ static void dtl_worker_disable(unsigned long *time_limit)
	cpuhp_remove_state(dtl_worker_state);
	free_dtl_buffers(time_limit);
	reset_global_dtl_mask();
	write_unlock(&dtl_access_lock);
	up_write(&dtl_access_lock);
}

static ssize_t vcpudispatch_stats_write(struct file *file, const char __user *p,