Skip to content

Commit 2e736fb

Browse files
authored
[SYCL] Check access to /dev/dri/renderD* in sycl-ls (#19520)
This patch adds a check in sycl-ls to verify that the user has permission to access /dev/dri/renderD* device nodes, if they are present. If a device node exists but cannot be opened due to insufficient permissions (typically because the user is not in the render group), a clear warning is printed. This helps users quickly identify missing group membership as the cause of device enumeration failures, instead of silently failing and requiring time-consuming troubleshooting.
1 parent 22ee417 commit 2e736fb

File tree

3 files changed

+95
-1
lines changed

3 files changed

+95
-1
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#define _GNU_SOURCE
2+
3+
#include <errno.h>
4+
#include <stdarg.h>
5+
#include <stdlib.h>
6+
#include <string.h>
7+
#include <sys/stat.h>
8+
9+
int stat(const char *pathname, struct stat *statbuf) {
10+
const char *mock_mode = getenv("MOCK_STAT_MODE");
11+
if (strstr(pathname, "renderD128")) {
12+
if (mock_mode && strcmp(mock_mode, "notfound") == 0) {
13+
errno = ENOENT;
14+
return -1;
15+
}
16+
if (mock_mode && strcmp(mock_mode, "exists") == 0) {
17+
memset(statbuf, 0, sizeof(*statbuf));
18+
statbuf->st_mode = S_IFCHR | 0666;
19+
return 0;
20+
}
21+
}
22+
// Default: file does not exist
23+
errno = ENOENT;
24+
return -1;
25+
}
26+
27+
int open(const char *pathname, int flags, ...) {
28+
const char *mock_mode = getenv("MOCK_OPEN_MODE");
29+
if (strstr(pathname, "renderD128")) {
30+
if (mock_mode && strcmp(mock_mode, "deny") == 0) {
31+
errno = EACCES;
32+
return -1;
33+
}
34+
if (mock_mode && strcmp(mock_mode, "allow") == 0) {
35+
return 3; // Dummy fd
36+
}
37+
}
38+
// Default: permission denied
39+
errno = EACCES;
40+
return -1;
41+
}

sycl/test/tools/render-group.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// REQUIRES: linux
2+
3+
// Compile Inputs/mock_renderd_access.c to a shared library and then use
4+
// LD_PRELOAD to mock the stat and open functions.
5+
// RUN: %clang -shared -fPIC -o %t-mock_stat.so %S/Inputs/mock_renderd_access.c
6+
7+
// Check the case when /dev/dri/renderD128 does not exist.
8+
// RUN: env MOCK_STAT_MODE=notfound LD_PRELOAD=%t-mock_stat.so sycl-ls --verbose --ignore-device-selectors 2>&1 | FileCheck %s -check-prefix=CHECK-NOTFOUND
9+
// We don't expect any warning about permissions in this case.
10+
// CHECK-NOTFOUND-NOT: WARNING: Unable to access /dev/dri/renderD128 due to permissions (EACCES).
11+
12+
// Check the case when /dev/dri/renderD128 exists but is not accessible.
13+
// RUN: env MOCK_STAT_MODE=exists MOCK_OPEN_MODE=deny LD_PRELOAD=%t-mock_stat.so sycl-ls --verbose --ignore-device-selectors 2>&1 | FileCheck %s --check-prefix=CHECK-DENY
14+
// CHECK-DENY: WARNING: Unable to access /dev/dri/renderD128 due to permissions (EACCES).
15+
// CHECK-DENY-NEXT: You might be missing the 'render' group locally.
16+
// CHECK-DENY-NEXT: Try: sudo usermod -a -G render $USER
17+
// CHECK-DENY-NEXT: Then log out and log back in.
18+
19+
// Check the case when /dev/dri/renderD128 exists and is accessible.
20+
// RUN: env MOCK_STAT_MODE=exists MOCK_OPEN_MODE=allow LD_PRELOAD=%t-mock_stat.so sycl-ls --verbose --ignore-device-selectors 2>&1 | FileCheck %s --check-prefix=CHECK-GRANT
21+
// CHECK-GRANT-NOT: WARNING: Unable to access /dev/dri/renderD128 due to permissions (EACCES).

sycl/tools/sycl-ls/sycl-ls.cpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@
2626
#include <string>
2727
#include <vector>
2828

29+
#ifdef __linux__
30+
#include <errno.h>
31+
#include <fcntl.h>
32+
#include <sys/stat.h>
33+
#include <sys/types.h>
34+
#include <unistd.h>
35+
#endif
2936
#ifdef _WIN32
3037
#include <system_error>
3138
#include <windows.h>
@@ -344,8 +351,30 @@ static int unsetFilterEnvVarsAndFork() {
344351
}
345352
#endif
346353

347-
int main(int argc, char **argv) {
354+
static void checkRenderGroupPermission() {
355+
#ifdef __linux__
356+
// Check for /dev/dri/render* devices
357+
for (int i = 128; i < 256; ++i) {
358+
std::string path = std::string("/dev/dri/renderD") + std::to_string(i);
359+
struct stat st;
360+
if (stat(path.c_str(), &st) == 0) {
361+
int fd = open(path.c_str(), O_RDWR);
362+
if (fd < 0 && errno == EACCES) {
363+
std::cerr << "WARNING: Unable to access " << path
364+
<< " due to permissions (EACCES).\n"
365+
<< "You might be missing the 'render' group locally.\n"
366+
<< "Try: sudo usermod -a -G render $USER\n"
367+
<< "Then log out and log back in.\n";
368+
break;
369+
}
370+
if (fd >= 0)
371+
close(fd);
372+
}
373+
}
374+
#endif
375+
}
348376

377+
int main(int argc, char **argv) {
349378
if (argc == 1) {
350379
verbose = false;
351380
DiscardFilters = false;
@@ -361,6 +390,9 @@ int main(int argc, char **argv) {
361390
}
362391
}
363392

393+
if (verbose)
394+
checkRenderGroupPermission();
395+
364396
bool SuppressNumberPrinting = false;
365397
// Print warning and suppress printing device ids if any of
366398
// the filter environment variable is set.

0 commit comments

Comments
 (0)