Skip to content

Commit 003c5c5

Browse files
gicmomartinpitt
authored andcommitted
Wrap openat{,64}() to accommodate for libudev changes (#70)
A recent change in libudev/systemd in device_set_syspath() changed the pattern of syscalls (by using chase_symlink) from: readlinkat (/sys/devices/XXX) canonicalize_file_name(/sys/devices/XXX) to: open64 (/) openat64 (sys) openat64 (devices) openat64 (XXX) This means it will not trigger trap_path and thus escape our faux sysfs tree. Modified by Martin Pitt: - More precise test for "sys" or "sys/..." - Add unit test for openat() behaviour - Turn into a macro and wrap openat() as well Fixes #69
1 parent 9bd0ba9 commit 003c5c5

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

src/libumockdev-preload.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,6 +1199,47 @@ WRAP_OPEN(, 64);
11991199
WRAP_OPEN2(__,_2);
12001200
WRAP_OPEN2(__,64_2);
12011201

1202+
/* wrapper template for openat family; intercept opening /sys from the root dir */
1203+
#define WRAP_OPENAT(prefix, suffix) \
1204+
int prefix ## openat ## suffix (int dirfd, const char *pathname, int flags, ...) \
1205+
{ \
1206+
const char *p = NULL; \
1207+
libc_func(prefix ## openat ## suffix, int, int, const char *, int, ...); \
1208+
libc_func(readlink, ssize_t, const char*, char *, size_t); \
1209+
int trapped = 0, ret; \
1210+
TRAP_PATH_LOCK; \
1211+
\
1212+
if (strncmp(pathname, "sys", 3) == 0 && (pathname[3] == '/' || pathname[3] == '\0')) { \
1213+
static char buf[PATH_MAX],link[PATH_MAX]; \
1214+
snprintf(buf, sizeof(buf), "/proc/self/fd/%d", dirfd); \
1215+
if (_readlink(buf, link, sizeof(link)) == 1 && link[0] == '/') { \
1216+
trapped = 1; \
1217+
strncpy(link + 1, pathname, sizeof(link) - 1); \
1218+
buf[sizeof(link) - 1] = 0; \
1219+
p = trap_path(link); \
1220+
} \
1221+
} \
1222+
\
1223+
if (!trapped) \
1224+
p = trap_path(pathname); \
1225+
DBG(DBG_PATH, "testbed wrapped " #suffix "openat" #suffix "(%s) -> %s\n", pathname, p); \
1226+
if (p == NULL) { TRAP_PATH_UNLOCK; return -1; } \
1227+
if (flags & (O_CREAT | O_TMPFILE)) { \
1228+
mode_t mode; \
1229+
va_list ap; \
1230+
va_start(ap, flags); \
1231+
mode = va_arg(ap, mode_t); \
1232+
va_end(ap); \
1233+
ret = _ ## prefix ## openat ## suffix(dirfd, p, flags, mode); \
1234+
} else \
1235+
ret = _ ## prefix ## openat ## suffix(dirfd, p, flags); \
1236+
TRAP_PATH_UNLOCK; \
1237+
return ret; \
1238+
}
1239+
1240+
WRAP_OPENAT(,);
1241+
WRAP_OPENAT(, 64);
1242+
12021243
int
12031244
inotify_add_watch(int fd, const char *path, uint32_t mask)
12041245
{

tests/test-umockdev.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,6 +1007,7 @@ t_testbed_libc(UMockdevTestbedFixture * fixture, gconstpointer data)
10071007
gboolean success;
10081008
GError *error = NULL;
10091009
char *path;
1010+
int dirfd, fd;
10101011

10111012
/* start with adding one device */
10121013
success = umockdev_testbed_add_from_string(fixture->testbed,
@@ -1031,6 +1032,38 @@ t_testbed_libc(UMockdevTestbedFixture * fixture, gconstpointer data)
10311032
/* nonexisting */
10321033
g_assert(canonicalize_file_name("/sys/devices/xxnoexist") == NULL);
10331034
g_assert_cmpint(errno, ==, ENOENT);
1035+
1036+
/* openat */
1037+
1038+
/* sys/ in root dir should be trapped */
1039+
dirfd = open("/", O_RDONLY | O_DIRECTORY);
1040+
g_assert_cmpint(dirfd, >=, 0);
1041+
fd = openat(dirfd, "sys/devices/dev1/simple_attr", O_RDONLY);
1042+
if (fd < 0)
1043+
perror("openat");
1044+
g_assert_cmpint(fd, >=, 0);
1045+
close(fd);
1046+
1047+
fd = openat64(dirfd, "sys/devices/dev1/simple_attr", O_RDONLY);
1048+
if (fd < 0)
1049+
perror("openat64");
1050+
g_assert_cmpint(fd, >=, 0);
1051+
close(fd);
1052+
1053+
close(dirfd);
1054+
1055+
/* sys/ in other dir should not be trapped */
1056+
errno = 0;
1057+
dirfd = open("/run", O_RDONLY | O_DIRECTORY);
1058+
g_assert_cmpint(openat(dirfd, "sys", O_RDONLY), <, 0);
1059+
g_assert_cmpint(errno, ==, ENOENT);
1060+
g_assert_cmpint(openat64(dirfd, "sys", O_RDONLY), <, 0);
1061+
close(dirfd);
1062+
1063+
errno = 0;
1064+
g_assert_cmpint(openat(AT_FDCWD, "sys/devices", O_RDONLY), <, 0);
1065+
g_assert_cmpint(errno, ==, ENOENT);
1066+
g_assert_cmpint(openat64(AT_FDCWD, "sys/devices", O_RDONLY), <, 0);
10341067
}
10351068

10361069
static void

0 commit comments

Comments
 (0)