Browse Source

daemon: use readlink not realpath to find the exe

The realpath(3) will fail if the target does not exist (internally
realpath will stat all the components of the link target path).
This is a problem in the case of sandbox applications where
the exe points to the absolute path *inside* the sandbox, e.g. to
/app/bin/<name> in the case of flatpak. For these cases realpath(3)
will then fail. Therefore use readlink(3) instead.
Christian Kellner 5 years ago
parent
commit
70e601267b
1 changed files with 24 additions and 4 deletions
  1. 24 4
      daemon/gamemode.c

+ 24 - 4
daemon/gamemode.c

@@ -40,10 +40,14 @@ POSSIBILITY OF SUCH DAMAGE.
 #include "helpers.h"
 #include "helpers.h"
 #include "logging.h"
 #include "logging.h"
 
 
+#include <fcntl.h>
 #include <pthread.h>
 #include <pthread.h>
 #include <signal.h>
 #include <signal.h>
 #include <stdatomic.h>
 #include <stdatomic.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <systemd/sd-daemon.h>
 #include <systemd/sd-daemon.h>
+#include <unistd.h>
 
 
 /**
 /**
  * The GameModeClient encapsulates the remote connection, providing a list
  * The GameModeClient encapsulates the remote connection, providing a list
@@ -650,15 +654,31 @@ static char *game_mode_context_find_exe(pid_t pid)
 {
 {
 	char buffer[PATH_MAX];
 	char buffer[PATH_MAX];
 	char *proc_path = NULL, *wine_exe = NULL;
 	char *proc_path = NULL, *wine_exe = NULL;
+	autoclose_fd int pidfd = -1;
+	ssize_t r;
 
 
-	if (!(proc_path = buffered_snprintf(buffer, "/proc/%d/exe", pid)))
+	if (!(proc_path = buffered_snprintf(buffer, "/proc/%d", pid)))
 		goto fail;
 		goto fail;
 
 
-	/* Allocate the realpath if possible */
-	char *exe = realpath(proc_path, NULL);
-	if (!exe)
+	/* Translate /proc/<pid>/exe to the application binary */
+	pidfd = openat(AT_FDCWD, proc_path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOCTTY);
+	if (pidfd == -1)
 		goto fail;
 		goto fail;
 
 
+	r = readlinkat(pidfd, "exe", buffer, sizeof(buffer));
+
+	if (r == sizeof(buffer)) {
+		errno = ENAMETOOLONG;
+		r = -1;
+	}
+
+	if (r == -1)
+		goto fail;
+
+	buffer[r] = '\0';
+
+	char *exe = strdup(buffer);
+
 	/* Detect if the process is a wine loader process */
 	/* Detect if the process is a wine loader process */
 	if (game_mode_detect_wine_preloader(exe)) {
 	if (game_mode_detect_wine_preloader(exe)) {
 		LOG_MSG("Detected wine preloader for client %d [%s].\n", pid, exe);
 		LOG_MSG("Detected wine preloader for client %d [%s].\n", pid, exe);