Skip to content

Commit 62ed968

Browse files
committed
rtapi: warn on stock kernel, gate Xenomai/RTAI on setuid
Address two review concerns from @hdiethelm on PR #3964: 1. SCHED_FIFO probe was too generous on PREEMPT_DYNAMIC stock kernels. The probe correctly reports that SCHED_FIFO is achievable, but the resulting latency on a non-PREEMPT_RT kernel can be tens of milliseconds, surprising users who read 'POSIX realtime' and expect bounded scheduling. Restore detect_preempt_rt() (uname-based) and emit a one-shot warning at makeApp() when the SCHED_FIFO path is chosen but the kernel lacks PREEMPT_RT and there is no Xenomai/RTAI backend. Behavior is unchanged: SCHED_FIFO on stock is still strictly better than SCHED_OTHER, the warning just makes the tradeoff visible. 2. Xenomai/RTAI backends still need root for iopl() (RTAI) or RTDM device access (Xenomai/EVL) and were being selected for unprivileged users on a Xenomai kernel, leading to 'iopl() failed: Operation not permitted'. Gate detect_rtai/detect_xenomai/detect_xenomai_evl on geteuid()==0 so unprivileged callers fall through to the SCHED_FIFO probe and a clean POSIX path. This is a band-aid pending proper capability/group support (udev rules + 'xenomai'/'evl' group membership, the approach Xenomai's own docs recommend); marked with a FIXME pointing at @hdiethelm's planned follow-up. The probe-based rtapi_is_realtime() itself is unchanged: it remains a pure capability check, matching the convention in JACK, PipeWire, rtkit, and Klipper. Kernel quality is reported as a separate diagnostic, not folded into the boolean.
1 parent 54c3f57 commit 62ed968

2 files changed

Lines changed: 51 additions & 0 deletions

File tree

src/rtapi/uspace_common.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,46 @@ int rtapi_exit(int module_id)
353353
}
354354

355355
int rtapi_is_kernelspace() { return 0; }
356+
357+
#ifdef __linux__
358+
// detect_preempt_rt() inspects uname for the PREEMPT_RT marker. Used only
359+
// for diagnostic warning at startup; callers must not gate behavior on
360+
// the kernel string, since SCHED_FIFO on a PREEMPT_DYNAMIC kernel is still
361+
// useful (better than SCHED_OTHER, worse than PREEMPT_RT).
362+
// Marked unused because uspace_common.h is also included from ULAPI TUs
363+
// that do not reference it.
364+
__attribute__((unused))
365+
static int detect_preempt_rt() {
366+
struct utsname u;
367+
if(uname(&u) < 0) return 0;
368+
return strcasestr(u.version, "PREEMPT RT") != 0
369+
|| strcasestr(u.version, "PREEMPT_RT") != 0;
370+
}
371+
#else
372+
__attribute__((unused))
373+
static int detect_preempt_rt() {
374+
return 0;
375+
}
376+
#endif
377+
378+
// FIXME: detect_rtai/detect_xenomai/detect_xenomai_evl currently gate on
379+
// setuid because the RTAI/Xenomai backends still need root for iopl()
380+
// (RTAI) or RTDM device access (Xenomai/EVL). Long-term these should
381+
// probe the actual capability the way can_set_sched_fifo() does, paired
382+
// with udev rules + a 'xenomai'/'evl' group; @hdiethelm has a follow-up
383+
// planned. Until then, an unprivileged user on a Xenomai kernel cannot
384+
// claim the Xenomai backend, and falls back to the SCHED_FIFO probe.
385+
// Marked unused because the helper is called only when one of the
386+
// USPACE_RTAI / USPACE_XENOMAI / USPACE_XENOMAI_EVL macros is defined,
387+
// and the header is included from ULAPI TUs that define none of them.
388+
__attribute__((unused))
389+
static int has_setuid_root() {
390+
return geteuid() == 0;
391+
}
392+
356393
#ifdef USPACE_RTAI
357394
static int detect_rtai() {
395+
if(!has_setuid_root()) return 0;
358396
struct utsname u;
359397
uname(&u);
360398
return strcasestr (u.release, "-rtai") != 0;
@@ -366,6 +404,7 @@ static int detect_rtai() {
366404
#endif
367405
#ifdef USPACE_XENOMAI
368406
static int detect_xenomai() {
407+
if(!has_setuid_root()) return 0;
369408
struct stat sb;
370409
//Running xenomai has /proc/xenomai
371410
return stat("/proc/xenomai", &sb) == 0;
@@ -377,6 +416,7 @@ static int detect_xenomai() {
377416
#endif
378417
#ifdef USPACE_XENOMAI_EVL
379418
static int detect_xenomai_evl() {
419+
if(!has_setuid_root()) return 0;
380420
struct stat sb;
381421
//Running xenomai evl has /dev/evl but no /proc/xenomai
382422
return stat("/dev/evl", &sb) == 0;

src/rtapi/uspace_rtapi_main.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,17 @@ static RtapiApp *makeApp() {
995995
} else if (detect_rtai()) {
996996
app = makeDllApp("liblinuxcnc-uspace-rtai.so.0", SCHED_FIFO);
997997
} else {
998+
// SCHED_FIFO available but no Xenomai/RTAI backend. Warn if the
999+
// kernel is not PREEMPT_RT: SCHED_FIFO still beats SCHED_OTHER,
1000+
// but latency on a PREEMPT_DYNAMIC stock kernel can be tens of
1001+
// milliseconds, which will surprise users who expect the same
1002+
// bounds as a PREEMPT_RT or Xenomai setup.
1003+
if (!detect_preempt_rt()) {
1004+
rtapi_print_msg(RTAPI_MSG_WARN,
1005+
"Note: SCHED_FIFO available but kernel is not PREEMPT_RT. "
1006+
"Latency may be unbounded; install a PREEMPT_RT kernel "
1007+
"for hard realtime guarantees.\n");
1008+
}
9981009
app = makeDllApp("liblinuxcnc-uspace-posix.so.0", SCHED_FIFO);
9991010
}
10001011
}

0 commit comments

Comments
 (0)