linux-stable/arch/s390/hypfs/hypfs_dbfs.c
Peter Oberparleiter 13b8655986 s390/hypfs: Enable limited access during lockdown
[ Upstream commit 3868f91044 ]

When kernel lockdown is active, debugfs_locked_down() blocks access to
hypfs files that register ioctl callbacks, even if the ioctl interface
is not required for a function. This unnecessarily breaks userspace
tools that only rely on read operations.

Resolve this by registering a minimal set of file operations during
lockdown, avoiding ioctl registration and preserving access for affected
tooling.

Note that this change restores hypfs functionality when lockdown is
active from early boot (e.g. via lockdown=integrity kernel parameter),
but does not apply to scenarios where lockdown is enabled dynamically
while Linux is running.

Tested-by: Mete Durlu <meted@linux.ibm.com>
Reviewed-by: Vasily Gorbik <gor@linux.ibm.com>
Fixes: 5496197f9b ("debugfs: Restrict debugfs when the kernel is locked down")
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
2025-08-28 16:34:51 +02:00

129 lines
2.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Hypervisor filesystem for Linux on s390 - debugfs interface
*
* Copyright IBM Corp. 2010
* Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
*/
#include <linux/security.h>
#include <linux/slab.h>
#include "hypfs.h"
static struct dentry *dbfs_dir;
static struct hypfs_dbfs_data *hypfs_dbfs_data_alloc(struct hypfs_dbfs_file *f)
{
struct hypfs_dbfs_data *data;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return NULL;
data->dbfs_file = f;
return data;
}
static void hypfs_dbfs_data_free(struct hypfs_dbfs_data *data)
{
data->dbfs_file->data_free(data->buf_free_ptr);
kfree(data);
}
static ssize_t dbfs_read(struct file *file, char __user *buf,
size_t size, loff_t *ppos)
{
struct hypfs_dbfs_data *data;
struct hypfs_dbfs_file *df;
ssize_t rc;
if (*ppos != 0)
return 0;
df = file_inode(file)->i_private;
if (mutex_lock_interruptible(&df->lock))
return -ERESTARTSYS;
data = hypfs_dbfs_data_alloc(df);
if (!data) {
mutex_unlock(&df->lock);
return -ENOMEM;
}
rc = df->data_create(&data->buf, &data->buf_free_ptr, &data->size);
if (rc) {
mutex_unlock(&df->lock);
kfree(data);
return rc;
}
mutex_unlock(&df->lock);
rc = simple_read_from_buffer(buf, size, ppos, data->buf, data->size);
hypfs_dbfs_data_free(data);
return rc;
}
static long dbfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hypfs_dbfs_file *df = file_inode(file)->i_private;
long rc;
mutex_lock(&df->lock);
rc = df->unlocked_ioctl(file, cmd, arg);
mutex_unlock(&df->lock);
return rc;
}
static const struct file_operations dbfs_ops_ioctl = {
.read = dbfs_read,
.unlocked_ioctl = dbfs_ioctl,
};
static const struct file_operations dbfs_ops = {
.read = dbfs_read,
};
void hypfs_dbfs_create_file(struct hypfs_dbfs_file *df)
{
const struct file_operations *fops = &dbfs_ops;
if (df->unlocked_ioctl && !security_locked_down(LOCKDOWN_DEBUGFS))
fops = &dbfs_ops_ioctl;
df->dentry = debugfs_create_file(df->name, 0400, dbfs_dir, df, fops);
mutex_init(&df->lock);
}
void hypfs_dbfs_remove_file(struct hypfs_dbfs_file *df)
{
debugfs_remove(df->dentry);
}
static int __init hypfs_dbfs_init(void)
{
int rc = -ENODATA;
dbfs_dir = debugfs_create_dir("s390_hypfs", NULL);
if (hypfs_diag_init())
goto fail_dbfs_exit;
if (hypfs_vm_init())
goto fail_hypfs_diag_exit;
hypfs_sprp_init();
if (hypfs_diag0c_init())
goto fail_hypfs_sprp_exit;
rc = hypfs_fs_init();
if (rc)
goto fail_hypfs_diag0c_exit;
return 0;
fail_hypfs_diag0c_exit:
hypfs_diag0c_exit();
fail_hypfs_sprp_exit:
hypfs_sprp_exit();
hypfs_vm_exit();
fail_hypfs_diag_exit:
hypfs_diag_exit();
pr_err("Initialization of hypfs failed with rc=%i\n", rc);
fail_dbfs_exit:
debugfs_remove(dbfs_dir);
return rc;
}
device_initcall(hypfs_dbfs_init)