Skip to content

Commit bb46d5a

Browse files
author
CKI KWF Bot
committed
Merge: kernel: add support to rh_waived in RHEL-9
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/7534 JIRA: https://issues.redhat.com/browse/RHEL-122981 Upstream status: RHEL-only Add rh_waived to RHEL-9, its dependencies and the new concept of "Waived Items" to the rh_waived original machinery so we can leverage the mechanism to allow customers waiving off issues other then just features, like performance impacting CVE mitigations, through the same interface in a consistent manner. Signed-off-by: Ricardo Robaina <rrobaina@redhat.com> Approved-by: Rafael Aquini <raquini@redhat.com> Approved-by: Wander Lairson Costa <wander@redhat.com> Approved-by: CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> Merged-by: CKI GitLab Kmaint Pipeline Bot <26919896-cki-kmaint-pipeline-bot@users.noreply.gitlab.com>
2 parents 8860546 + 60fb217 commit bb46d5a

File tree

8 files changed

+366
-1
lines changed

8 files changed

+366
-1
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5734,6 +5734,20 @@
57345734
rhash_entries= [KNL,NET]
57355735
Set number of hash buckets for route cache
57365736

5737+
rh_waived=
5738+
Enable waived items in RHEL.
5739+
5740+
Some specific features, or security mitigations, can be
5741+
waived (toggled on/off) on demand in RHEL. However,
5742+
waiving any of these items should be used judiciously,
5743+
as it generally means the system might end up being
5744+
considered insecure or even out-of-scope for support.
5745+
5746+
Format: <item-1>,<item-2>...<item-n>
5747+
5748+
Use 'rh_waived' to enable all waived features listed at
5749+
Documentation/admin-guide/rh-waived-features.rst
5750+
57375751
ring3mwait=disable
57385752
[KNL] Disable ring 3 MONITOR/MWAIT feature on supported
57395753
CPUs.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
.. _rh_waived_items:
2+
3+
====================
4+
Red Hat Waived Items
5+
====================
6+
7+
Waived Items is a mechanism offered by Red Hat which allows customers to "waive"
8+
and utilize features that are not enabled by default as these are considered as
9+
unmaintained, insecure, rudimentary, or deprecated, but are shipped with the
10+
RHEL kernel for customer's convinience only.
11+
Waived Items can range from features that can be enabled on demand to specific
12+
security mitigations that can be disabled on demand.
13+
14+
To explicitly "waive" any of these items, RHEL offers the ``rh_waived``
15+
kernel boot parameter. To allow set of waived items, append
16+
``rh_waived=<item name>,...,<item name>`` to the kernel
17+
cmdline.
18+
Appending ``rh_waived=features`` will waive all features listed below,
19+
and appending ``rh_waived=cves`` will waive all security mitigations
20+
listed below.
21+
22+
The waived items listed in the next session follow the pattern below:
23+
24+
- item name
25+
item description
26+
27+
List of Red Hat Waived Items
28+
============================
29+

include/linux/rh_flags.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* rh_flags.h -- Red Hat flags tracking
4+
*
5+
* Copyright (c) 2018 Red Hat, Inc. -- Jiri Benc <jbenc@redhat.com>
6+
*
7+
* The intent of the flag tracking is to provide better and more focused
8+
* support. Only those flags that are of a special interest for customer
9+
* support should be tracked.
10+
*
11+
* THE FLAGS DO NOT EXPRESS ANY SUPPORT POLICIES.
12+
*/
13+
14+
#ifndef _LINUX_RH_FLAGS_H
15+
#define _LINUX_RH_FLAGS_H
16+
17+
#if defined CONFIG_RHEL_DIFFERENCES
18+
bool __rh_add_flag(const char *flag_name);
19+
void rh_print_flags(void);
20+
21+
#define rh_add_flag(flag_name) \
22+
({ \
23+
static bool __mark_once __read_mostly; \
24+
bool __ret_mark_once = !__mark_once; \
25+
\
26+
if (!__mark_once) \
27+
__mark_once = __rh_add_flag(flag_name); \
28+
unlikely(__ret_mark_once); \
29+
})
30+
#else
31+
void rh_print_flags(void) { };
32+
void rh_add_flag(const char *flag_name) { };
33+
#endif
34+
#endif

include/linux/rh_waived.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* include/linux/rh_waived.h
4+
*
5+
* rh_waived cmdline parameter interface.
6+
*
7+
* Copyright (C) 2024, Red Hat, Inc. Ricardo Robaina <rrobaina@redhat.com>
8+
*/
9+
#ifndef _RH_WAIVED_H
10+
#define _RH_WAIVED_H
11+
12+
enum rh_waived_items {
13+
/* RH_WAIVED_ITEMS must always be the last item in the enum */
14+
RH_WAIVED_ITEMS,
15+
};
16+
17+
bool is_rh_waived(enum rh_waived_items feat);
18+
19+
#endif /* _RH_WAIVED_H */

kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ obj-y = fork.o exec_domain.o panic.o \
1212
notifier.o ksysfs.o cred.o reboot.o \
1313
async.o range.o smpboot.o ucount.o regset.o ksyms_common.o
1414

15-
obj-$(CONFIG_RHEL_DIFFERENCES) += rh_messages.o rh_shadowman.o
15+
obj-$(CONFIG_RHEL_DIFFERENCES) += rh_messages.o rh_flags.o rh_waived.o rh_shadowman.o
1616
obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
1717
obj-$(CONFIG_MULTIUSER) += groups.o
1818

kernel/module/main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@
6262
#define CREATE_TRACE_POINTS
6363
#include <trace/events/module.h>
6464

65+
#include <linux/rh_flags.h>
66+
6567
/*
6668
* Mutex protects:
6769
* 1) List of modules (also safely readable with preempt_disable),
@@ -3393,6 +3395,10 @@ void print_modules(void)
33933395
pr_cont(" [last unloaded: %s%s]", last_unloaded_module.name,
33943396
last_unloaded_module.taints);
33953397
pr_cont("\n");
3398+
3399+
#ifdef CONFIG_RHEL_DIFFERENCES
3400+
rh_print_flags();
3401+
#endif
33963402
}
33973403

33983404
#ifdef CONFIG_MODULE_DEBUGFS

kernel/rh_flags.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include <linux/kernel.h>
2+
#include <linux/list.h>
3+
#include <linux/proc_fs.h>
4+
#include <linux/seq_file.h>
5+
#include <linux/slab.h>
6+
#include <linux/spinlock.h>
7+
#include <linux/rh_flags.h>
8+
9+
#define RH_FLAG_NAME_LEN 32
10+
#define MAX_RH_FLAGS 128
11+
#define MAX_RH_FLAG_NAME_LEN (MAX_RH_FLAGS * RH_FLAG_NAME_LEN)
12+
13+
struct rh_flag {
14+
struct list_head list;
15+
char name[RH_FLAG_NAME_LEN];
16+
};
17+
18+
static LIST_HEAD(rh_flag_list);
19+
static DEFINE_SPINLOCK(rh_flag_lock);
20+
21+
bool __rh_add_flag(const char *flag_name)
22+
{
23+
struct rh_flag *feat, *iter;
24+
25+
BUG_ON(in_interrupt());
26+
feat = kzalloc(sizeof(*feat), GFP_ATOMIC);
27+
if (WARN(!feat, "Adding Red Hat flag %s.\n", flag_name))
28+
return false;
29+
strscpy(feat->name, flag_name, RH_FLAG_NAME_LEN);
30+
31+
spin_lock(&rh_flag_lock);
32+
list_for_each_entry_rcu(iter, &rh_flag_list, list) {
33+
if (!strcmp(iter->name, flag_name)) {
34+
kfree(feat);
35+
feat = NULL;
36+
break;
37+
}
38+
}
39+
if (feat)
40+
list_add_rcu(&feat->list, &rh_flag_list);
41+
spin_unlock(&rh_flag_lock);
42+
43+
if (feat)
44+
pr_info("Adding Red Hat flag %s.\n", flag_name);
45+
return true;
46+
}
47+
EXPORT_SYMBOL(__rh_add_flag);
48+
49+
void rh_print_flags(void)
50+
{
51+
struct rh_flag *feat;
52+
53+
/*
54+
* This function cannot do any locking, we're oopsing. Traversing
55+
* rh_flag_list is okay, though, even without the rcu_read_lock
56+
* taken: we never delete from that list and thus don't need the
57+
* delayed free. All we need are the smp barriers invoked by the rcu
58+
* list manipulation routines.
59+
*/
60+
if (list_empty(&rh_flag_list))
61+
return;
62+
printk(KERN_DEFAULT "Red Hat flags:");
63+
list_for_each_entry_lockless(feat, &rh_flag_list, list) {
64+
pr_cont(" %s", feat->name);
65+
}
66+
pr_cont("\n");
67+
}
68+
EXPORT_SYMBOL(rh_print_flags);
69+
70+
#ifdef CONFIG_SYSCTL
71+
static int rh_flags_show(struct ctl_table *ctl, int write,
72+
void __user *buffer, size_t *lenp,
73+
loff_t *ppos)
74+
{
75+
struct ctl_table tbl = { .maxlen = MAX_RH_FLAG_NAME_LEN, };
76+
struct rh_flag *feat;
77+
size_t offs = 0;
78+
int ret;
79+
80+
tbl.data = kmalloc(tbl.maxlen, GFP_KERNEL);
81+
if (!tbl.data)
82+
return -ENOMEM;
83+
((char *)tbl.data)[0] = '\0';
84+
85+
rcu_read_lock();
86+
list_for_each_entry_rcu(feat, &rh_flag_list, list) {
87+
offs += scnprintf(tbl.data + offs, tbl.maxlen - offs, "%s%s",
88+
offs == 0 ? "" : " ", feat->name);
89+
}
90+
rcu_read_unlock();
91+
92+
ret = proc_dostring(&tbl, write, buffer, lenp, ppos);
93+
kfree(tbl.data);
94+
return ret;
95+
}
96+
97+
static struct ctl_table rh_flags_table[] = {
98+
{
99+
.procname = "rh_flags",
100+
.data = &rh_flag_list,
101+
.maxlen = MAX_RH_FLAG_NAME_LEN,
102+
.mode = 0444,
103+
.proc_handler = rh_flags_show,
104+
},
105+
{ }
106+
};
107+
#endif
108+
109+
static __init int rh_flags_init(void)
110+
{
111+
#ifdef CONFIG_SYSCTL
112+
register_sysctl_init("kernel", rh_flags_table);
113+
#endif
114+
return 0;
115+
}
116+
subsys_initcall(rh_flags_init);

kernel/rh_waived.c

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* kernel/rh_waived.c
4+
*
5+
* rh_waived cmdline parameter support.
6+
*
7+
* Copyright (C) 2024, Red Hat, Inc. Ricardo Robaina <rrobaina@redhat.com>
8+
*/
9+
#include <linux/types.h>
10+
#include <linux/init.h>
11+
#include <linux/printk.h>
12+
#include <linux/string.h>
13+
#include <linux/panic.h>
14+
#include <linux/module.h>
15+
#include <linux/kernel.h>
16+
#include <linux/rh_flags.h>
17+
#include <linux/rh_waived.h>
18+
19+
/*
20+
* * RH_INSERT_WAIVED_ITEM
21+
* This macro is intended to be used to insert items into the
22+
* rh_waived_list array. It expects to get an item from
23+
* enum rh_waived_items as its first argument, and a string
24+
* holding the feature name as its second argument.
25+
*
26+
* The feature name is also utilized as the token for the
27+
* boot parameter parser.
28+
*
29+
* Example usage:
30+
* struct rh_waived_item foo[RH_WAIVED_FEAT_ITEMS] = {
31+
* RH_INSERT_WAIVED_ITEM(FOO_FEAT, "foo_feat_short_str", "alias", RH_WAIVED_FEAT),
32+
* };
33+
*/
34+
#define RH_INSERT_WAIVED_ITEM(enum_item, item, item_alt, class) \
35+
[(enum_item)] = { .name = (item), .alias = (item_alt), \
36+
.type = (class), .waived = 0, }
37+
38+
/* Indicates if the rh_flag 'rh_waived' should be added. */
39+
bool __initdata add_rh_flag = false;
40+
41+
typedef enum {
42+
RH_WAIVED_FEAT,
43+
RH_WAIVED_CVE,
44+
RH_WAIVED_ANY
45+
} rh_waived_t;
46+
47+
struct rh_waived_item {
48+
char *name, *alias;
49+
rh_waived_t type;
50+
unsigned int waived;
51+
52+
};
53+
54+
/* Always use the marco RH_INSERT_WAIVED to insert items to this array. */
55+
struct rh_waived_item rh_waived_list[RH_WAIVED_ITEMS] = {
56+
};
57+
58+
/*
59+
* is_rh_waived() - Checks if a given item has been marked as waived.
60+
*
61+
* @item: waived item.
62+
*/
63+
__inline__ bool is_rh_waived(enum rh_waived_items item)
64+
{
65+
return !!rh_waived_list[item].waived;
66+
}
67+
EXPORT_SYMBOL(is_rh_waived);
68+
69+
static void __init rh_waived_parser(char *s, rh_waived_t type)
70+
{
71+
int i;
72+
char *token;
73+
74+
pr_info(KERN_CONT "rh_waived: ");
75+
76+
if (!s) {
77+
for (i = 0; i < RH_WAIVED_ITEMS; i++) {
78+
if (type != RH_WAIVED_ANY && rh_waived_list[i].type != type)
79+
continue;
80+
81+
rh_waived_list[i].waived = 1;
82+
pr_info(KERN_CONT "%s%s", rh_waived_list[i].name,
83+
i < RH_WAIVED_ITEMS - 1 ? " " : "\n");
84+
}
85+
86+
add_rh_flag = true;
87+
return;
88+
}
89+
90+
while ((token = strsep(&s, ",")) != NULL) {
91+
for (i = 0; i < RH_WAIVED_ITEMS; i++) {
92+
char *alias = rh_waived_list[i].alias;
93+
94+
if (type != RH_WAIVED_ANY && rh_waived_list[i].type != type)
95+
continue;
96+
97+
if (!strcmp(token, rh_waived_list[i].name) ||
98+
(alias && !strcmp(token, alias))) {
99+
rh_waived_list[i].waived = 1;
100+
pr_info(KERN_CONT "%s ", rh_waived_list[i].name);
101+
}
102+
}
103+
}
104+
105+
pr_info(KERN_CONT "\n");
106+
add_rh_flag = true;
107+
}
108+
109+
static int __init rh_waived_setup(char *s)
110+
{
111+
/*
112+
* originally, if no string was passed to the cmdline option
113+
* all listed features would be waived, so we keep that same
114+
* compromise with the new contract.
115+
*/
116+
if (!s || !strcmp(s, "features")) {
117+
rh_waived_parser(NULL, RH_WAIVED_FEAT);
118+
return 0;
119+
}
120+
121+
/* waive all possible mitigations in the list */
122+
if (!strcmp(s, "cves")) {
123+
rh_waived_parser(NULL, RH_WAIVED_CVE);
124+
return 0;
125+
}
126+
127+
/* otherwise, just deal with the enumerated waive list */
128+
rh_waived_parser(s, RH_WAIVED_ANY);
129+
130+
return 0;
131+
}
132+
early_param("rh_waived", rh_waived_setup);
133+
134+
/*
135+
* rh_flags is initialized at subsys_initcall, calling rh_add_flag()
136+
* from rh_waived_setup() would result in a can't boot situation.
137+
* Deffering the inclusion 'rh_waived' rh_flag to late_initcall to
138+
* avoid this issue.
139+
*/
140+
static int __init __add_rh_flag(void)
141+
{
142+
if (add_rh_flag)
143+
rh_add_flag("rh_waived");
144+
145+
return 0;
146+
}
147+
late_initcall(__add_rh_flag);

0 commit comments

Comments
 (0)