diff --git a/_service b/_service index 20111a0..4e9a0a0 100644 --- a/_service +++ b/_service @@ -2,7 +2,7 @@ https://github.com/FeralInteractive/gamemode.git git - 1.2 + 1.3.1 @PARENT_TAG@ enable diff --git a/_servicedata b/_servicedata index 663746b..a7eaa35 100644 --- a/_servicedata +++ b/_servicedata @@ -1,4 +1,4 @@ https://github.com/FeralInteractive/gamemode.git - ceb476052d10b945d16bc4bcf5381e69c6722c96 \ No newline at end of file + bc20bef283fb947d7cee23306860af04f723ae9f diff --git a/gamemode-1.2.tar.xz b/gamemode-1.2.tar.xz deleted file mode 100644 index a8af55a..0000000 --- a/gamemode-1.2.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6ea1f7709ab8a7a1c8d0a4e6dfa7858e3a530c4bde1edd4728bec5b617f03bbb -size 38336 diff --git a/gamemode-1.3.1.tar.xz b/gamemode-1.3.1.tar.xz new file mode 100644 index 0000000..7ed7118 --- /dev/null +++ b/gamemode-1.3.1.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b84ed18d7df4a35ba5a73659b5ee9504c3361e04c1d23ba78bf6507d11224fa4 +size 55908 diff --git a/gamemode.changes b/gamemode.changes index 6716ebe..5687893 100644 --- a/gamemode.changes +++ b/gamemode.changes @@ -1,3 +1,14 @@ +------------------------------------------------------------------- +Tue Apr 9 19:39:24 UTC 2019 - Matthias Bach + +- Update to version 1.3.1 + * Disables screensaver when the game is running. + * New `gamemoderun` script to run games in GameMode which don't + support it themselves. + * Add GPU managment capabilities. +- Removed hardening.patch already included in 1.3. +- Add gpuctl-fixes.patch hardening the new GPU features. + ------------------------------------------------------------------- Sat Mar 16 19:34:04 UTC 2019 - Matthias Bach diff --git a/gamemode.spec b/gamemode.spec index 2ea7671..5e3b9e1 100644 --- a/gamemode.spec +++ b/gamemode.spec @@ -18,7 +18,7 @@ Name: gamemode -Version: 1.2 +Version: 1.3.1 Release: 0 Summary: Daemon/library combo for changing Linux system performance on demand License: BSD-3-Clause @@ -28,7 +28,7 @@ Source0: gamemode-%{version}.tar.xz Source1: gamemode-rpmlintrc Source2: README.openSUSE Source3: baselibs.conf -Patch0: hardening.patch +Patch1: gpuctl-fixes.patch BuildRequires: meson BuildRequires: ninja BuildRequires: pkg-config @@ -109,7 +109,7 @@ built-in GameMode support. %prep %setup -q -%patch -P 0 -p1 +%patch1 -p1 cp %{SOURCE2} . %build @@ -136,7 +136,9 @@ export CC=gcc-7 # gcc4.8 does not work because of https://gcc.gnu.org/bugzilla/ %files -n gamemoded %defattr(-,root,root) %{_bindir}/gamemoded +%{_bindir}/gamemoderun %{_libexecdir}/cpugovctl +%{_libexecdir}/gpuclockctl %{_userunitdir}/gamemoded.service %{_datadir}/polkit-1/actions/com.feralinteractive.GameMode.policy %{_datadir}/dbus-1/services/com.feralinteractive.GameMode.service diff --git a/gpuctl-fixes.patch b/gpuctl-fixes.patch new file mode 100644 index 0000000..451f049 --- /dev/null +++ b/gpuctl-fixes.patch @@ -0,0 +1,644 @@ +From cbc6ce7e2677f9c3759423bfef5a03ff4ad7575c Mon Sep 17 00:00:00 2001 +From: Matthias Gerstner +Date: Wed, 3 Apr 2019 16:18:36 +0200 +Subject: [PATCH 1/4] gpuclockctl: fix return value of get_gpu_state_amd() + +--- + daemon/gpuclockctl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c +index 73c17c5..8598a9f 100644 +--- a/daemon/gpuclockctl.c ++++ b/daemon/gpuclockctl.c +@@ -303,7 +303,7 @@ static int get_gpu_state_amd(struct GameModeGPUInfo *info) + /* Copy in the value from the file */ + strncpy(info->amd_performance_level, buff, CONFIG_VALUE_MAX - 1); + info->amd_performance_level[CONFIG_VALUE_MAX - 1] = '\0'; +- return info == NULL; ++ return 0; + } + + /* + +From fd576682a3720a22e12e63014ec9f01621c6ed0f Mon Sep 17 00:00:00 2001 +From: Matthias Gerstner +Date: Wed, 3 Apr 2019 16:27:12 +0200 +Subject: [PATCH 2/4] daemon: fix file descriptor leaks + +--- + daemon/daemon_config.c | 1 + + daemon/governors-query.c | 1 + + daemon/gpu-control.c | 5 ++++- + daemon/gpuclockctl.c | 26 +++++++++++++++++--------- + 4 files changed, 23 insertions(+), 10 deletions(-) + +diff --git a/daemon/daemon_config.c b/daemon/daemon_config.c +index 675c195..23e0e76 100644 +--- a/daemon/daemon_config.c ++++ b/daemon/daemon_config.c +@@ -362,6 +362,7 @@ static void load_config_files(GameModeConfig *self) + if (error) { + LOG_MSG("Failed to parse config file - error on line %d!\n", error); + } ++ fclose(f); + } + free(path); + } +diff --git a/daemon/governors-query.c b/daemon/governors-query.c +index 9e0af0f..9d483c0 100644 +--- a/daemon/governors-query.c ++++ b/daemon/governors-query.c +@@ -133,6 +133,7 @@ const char *get_gov_state(void) + /* Don't handle the mixed case, this shouldn't ever happen + * But it is a clear sign we shouldn't carry on */ + LOG_ERROR("Governors malformed: got \"%s\", expected \"%s\"", contents, governor); ++ fclose(f); + return "malformed"; + } + +diff --git a/daemon/gpu-control.c b/daemon/gpu-control.c +index 147cdd1..a2699c0 100644 +--- a/daemon/gpu-control.c ++++ b/daemon/gpu-control.c +@@ -50,7 +50,10 @@ enum GPUVendor gamemode_get_gpu_vendor(long device) + return Vendor_Invalid; + } + char buff[64]; +- if (fgets(buff, 64, file) != NULL) { ++ bool got_line = fgets(buff, 64, file) != NULL; ++ fclose(file); ++ ++ if (got_line) { + vendor = strtol(buff, NULL, 0); + } else { + LOG_ERROR("Coudn't read contents of file %s, will not apply optimisations!\n", path); +diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c +index 8598a9f..873ab54 100644 +--- a/daemon/gpuclockctl.c ++++ b/daemon/gpuclockctl.c +@@ -289,21 +289,27 @@ static int get_gpu_state_amd(struct GameModeGPUInfo *info) + return -1; + } + ++ int ret = 0; ++ + char buff[CONFIG_VALUE_MAX] = { 0 }; + if (!fgets(buff, CONFIG_VALUE_MAX, file)) { + LOG_ERROR("Could not read file %s (%s)!\n", path, strerror(errno)); +- return -1; ++ ret = -1; + } + + if (fclose(file) != 0) { + LOG_ERROR("Could not close %s after reading (%s)!\n", path, strerror(errno)); +- return -1; ++ ret = -1; + } + +- /* Copy in the value from the file */ +- strncpy(info->amd_performance_level, buff, CONFIG_VALUE_MAX - 1); +- info->amd_performance_level[CONFIG_VALUE_MAX - 1] = '\0'; +- return 0; ++ if( ret == 0 ) ++ { ++ /* Copy in the value from the file */ ++ strncpy(info->amd_performance_level, buff, CONFIG_VALUE_MAX - 1); ++ info->amd_performance_level[CONFIG_VALUE_MAX - 1] = '\0'; ++ } ++ ++ return ret; + } + + /* +@@ -320,17 +326,19 @@ static int set_gpu_state_amd_file(const char *filename, long device, const char + return -1; + } + ++ int ret = 0; ++ + if (fprintf(file, "%s", value) < 0) { + LOG_ERROR("Could not write to %s (%s)!\n", path, strerror(errno)); +- return -1; ++ ret = -1; + } + + if (fclose(file) != 0) { + LOG_ERROR("Could not close %s after writing (%s)!\n", path, strerror(errno)); +- return -1; ++ ret = -1; + } + +- return 0; ++ return ret; + } + + /** + +From 54212fa81d80b7a6981faed09777e55ce8e86963 Mon Sep 17 00:00:00 2001 +From: Matthias Gerstner +Date: Wed, 3 Apr 2019 16:46:38 +0200 +Subject: [PATCH 3/4] gpuclockctl: refactor buffer size specification and avoid + redundancies + +--- + daemon/gpuclockctl.c | 116 ++++++++++++++++++++++++------------------- + 1 file changed, 64 insertions(+), 52 deletions(-) + +diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c +index 873ab54..cac49bd 100644 +--- a/daemon/gpuclockctl.c ++++ b/daemon/gpuclockctl.c +@@ -36,6 +36,8 @@ POSSIBILITY OF SUCH DAMAGE. + #include "external-helper.h" + #include "gpu-control.h" + ++#include ++ + /* NV constants */ + #define NV_CORE_OFFSET_ATTRIBUTE "GPUGraphicsClockOffset" + #define NV_MEM_OFFSET_ATTRIBUTE "GPUMemoryTransferRateOffset" +@@ -44,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE. + #define NV_PCIDEVICE_ATTRIBUTE "PCIDevice" + #define NV_ATTRIBUTE_FORMAT "[gpu:%ld]/%s" + #define NV_PERF_LEVEL_FORMAT "[%ld]" ++#define NV_ARG_MAX 128 + + /* AMD constants */ + #define AMD_DRM_PATH "/sys/class/drm/card%ld/device/%s" +@@ -64,6 +67,30 @@ static void print_usage_and_exit(void) + exit(EXIT_FAILURE); + } + ++static const char* get_nv_attr(const char *attr) ++{ ++ static char out[EXTERNAL_BUFFER_MAX]; ++ const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", attr, "-t", NULL }; ++ if (run_external_process(exec_args, out, -1) != 0) { ++ LOG_ERROR("Failed to get %s!\n", attr); ++ out[0] = 0; ++ return NULL; ++ } ++ ++ return &out[0]; ++} ++ ++static int set_nv_attr(const char *attr) ++{ ++ const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", attr, NULL }; ++ if (run_external_process(exec_args_core, NULL, -1) != 0) { ++ LOG_ERROR("Failed to set %s!\n", attr); ++ return -1; ++ } ++ ++ return 0; ++} ++ + /* Get the nvidia driver index for the current GPU */ + static long get_gpu_index_id_nv(struct GameModeGPUInfo *info) + { +@@ -108,23 +135,21 @@ static long get_max_perf_level_nv(struct GameModeGPUInfo *info) + if (!getenv("DISPLAY")) + LOG_ERROR("Getting Nvidia parameters requires DISPLAY to be set - will likely fail!\n"); + +- char arg[128] = { 0 }; +- char buf[EXTERNAL_BUFFER_MAX] = { 0 }; ++ char arg[NV_ARG_MAX] = { 0 }; ++ const char *attr; + +- snprintf(arg, 128, NV_ATTRIBUTE_FORMAT, info->device, NV_PERFMODES_ATTRIBUTE); +- const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; +- if (run_external_process(exec_args, buf, -1) != 0) { +- LOG_ERROR("Failed to get %s!\n", arg); ++ snprintf(arg, NV_ARG_MAX, NV_ATTRIBUTE_FORMAT, info->device, NV_PERFMODES_ATTRIBUTE); ++ if ((attr = get_nv_attr(arg)) == NULL) { + return -1; + } + +- char *ptr = strrchr(buf, ';'); ++ char *ptr = strrchr(attr, ';'); + long level = -1; + if (!ptr || sscanf(ptr, "; perf=%ld", &level) != 1) { + LOG_ERROR( + "Output didn't match expected format, couldn't discern highest perf level from " + "nvidia-settings!\n"); +- LOG_ERROR("Output:%s\n", buf); ++ LOG_ERROR("Output:%s\n", attr); + return -1; + } + +@@ -142,59 +167,53 @@ static int get_gpu_state_nv(struct GameModeGPUInfo *info) + + long perf_level = get_max_perf_level_nv(info); + +- char arg[128] = { 0 }; +- char buf[EXTERNAL_BUFFER_MAX] = { 0 }; ++ char arg[NV_ARG_MAX] = { 0 }; ++ const char *attr; + char *end; + + /* Get the GPUGraphicsClockOffset parameter */ + snprintf(arg, +- 128, ++ NV_ARG_MAX, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, + info->device, + NV_CORE_OFFSET_ATTRIBUTE, + perf_level); +- const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; +- if (run_external_process(exec_args_core, buf, -1) != 0) { +- LOG_ERROR("Failed to get %s!\n", arg); ++ if ((attr = get_nv_attr(arg)) == NULL) { + return -1; + } + +- info->nv_core = strtol(buf, &end, 10); +- if (end == buf) { +- LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); ++ info->nv_core = strtol(attr, &end, 10); ++ if (end == attr) { ++ LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, attr); + return -1; + } + + /* Get the GPUMemoryTransferRateOffset parameter */ + snprintf(arg, +- 128, ++ NV_ARG_MAX, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT, + info->device, + NV_MEM_OFFSET_ATTRIBUTE, + perf_level); +- const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; +- if (run_external_process(exec_args_mem, buf, -1) != 0) { +- LOG_ERROR("Failed to get %s!\n", arg); ++ if ((attr = get_nv_attr(arg)) == NULL) { + return -1; + } + +- info->nv_mem = strtol(buf, &end, 10); +- if (end == buf) { +- LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); ++ info->nv_mem = strtol(attr, &end, 10); ++ if (end == attr) { ++ LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, attr); + return -1; + } + + /* Get the GPUPowerMizerMode parameter */ +- snprintf(arg, 128, NV_ATTRIBUTE_FORMAT, info->device, NV_POWERMIZER_MODE_ATTRIBUTE); +- const char *exec_args_pm[] = { "/usr/bin/nvidia-settings", "-q", arg, "-t", NULL }; +- if (run_external_process(exec_args_pm, buf, -1) != 0) { +- LOG_ERROR("Failed to get %s!\n", arg); ++ snprintf(arg, NV_ARG_MAX, NV_ATTRIBUTE_FORMAT, info->device, NV_POWERMIZER_MODE_ATTRIBUTE); ++ if ((attr = get_nv_attr(arg)) == NULL) { + return -1; + } + +- info->nv_powermizer_mode = strtol(buf, &end, 10); +- if (end == buf) { +- LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, buf); ++ info->nv_powermizer_mode = strtol(attr, &end, 10); ++ if (end == attr) { ++ LOG_ERROR("Failed to parse output for \"%s\" output was \"%s\"!\n", arg, attr); + return -1; + } + +@@ -218,20 +237,18 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) + + long perf_level = get_max_perf_level_nv(info); + +- char arg[128] = { 0 }; ++ char arg[NV_ARG_MAX] = { 0 }; + + /* Set the GPUGraphicsClockOffset parameter */ + if (info->nv_core != -1) { + snprintf(arg, +- 128, ++ NV_ARG_MAX, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", + info->device, + NV_CORE_OFFSET_ATTRIBUTE, + perf_level, + info->nv_core); +- const char *exec_args_core[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; +- if (run_external_process(exec_args_core, NULL, -1) != 0) { +- LOG_ERROR("Failed to set %s!\n", arg); ++ if (set_nv_attr(arg) != 0) { + status = -1; + } + } +@@ -239,15 +256,13 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) + /* Set the GPUMemoryTransferRateOffset parameter */ + if (info->nv_mem != -1) { + snprintf(arg, +- 128, ++ NV_ARG_MAX, + NV_ATTRIBUTE_FORMAT NV_PERF_LEVEL_FORMAT "=%ld", + info->device, + NV_MEM_OFFSET_ATTRIBUTE, + perf_level, + info->nv_mem); +- const char *exec_args_mem[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; +- if (run_external_process(exec_args_mem, NULL, -1) != 0) { +- LOG_ERROR("Failed to set %s!\n", arg); ++ if (set_nv_attr(arg) != 0) { + status = -1; + } + } +@@ -255,14 +270,12 @@ static int set_gpu_state_nv(struct GameModeGPUInfo *info) + /* Set the GPUPowerMizerMode parameter if requested */ + if (info->nv_powermizer_mode != -1) { + snprintf(arg, +- 128, ++ NV_ARG_MAX, + NV_ATTRIBUTE_FORMAT "=%ld", + info->device, + NV_POWERMIZER_MODE_ATTRIBUTE, + info->nv_powermizer_mode); +- const char *exec_args_pm[] = { "/usr/bin/nvidia-settings", "-a", arg, NULL }; +- if (run_external_process(exec_args_pm, NULL, -1) != 0) { +- LOG_ERROR("Failed to set %s!\n", arg); ++ if (set_nv_attr(arg) != 0) { + status = -1; + } + } +@@ -280,8 +293,8 @@ static int get_gpu_state_amd(struct GameModeGPUInfo *info) + return -1; + + /* Get the contents of power_dpm_force_performance_level */ +- char path[64]; +- snprintf(path, 64, AMD_DRM_PATH, info->device, "power_dpm_force_performance_level"); ++ char path[PATH_MAX]; ++ snprintf(path, PATH_MAX, AMD_DRM_PATH, info->device, "power_dpm_force_performance_level"); + + FILE *file = fopen(path, "r"); + if (!file) { +@@ -317,8 +330,8 @@ static int get_gpu_state_amd(struct GameModeGPUInfo *info) + */ + static int set_gpu_state_amd_file(const char *filename, long device, const char *value) + { +- char path[64]; +- snprintf(path, 64, AMD_DRM_PATH, device, filename); ++ char path[PATH_MAX]; ++ snprintf(path, PATH_MAX, AMD_DRM_PATH, device, filename); + + FILE *file = fopen(path, "w"); + if (!file) { +@@ -398,10 +411,11 @@ static long get_generic_value(const char *val) + */ + int main(int argc, char *argv[]) + { ++ struct GameModeGPUInfo info; ++ memset(&info, 0, sizeof(info)); ++ + if (argc == 3 && strncmp(argv[2], "get", 3) == 0) { + /* Get and verify the vendor and device */ +- struct GameModeGPUInfo info; +- memset(&info, 0, sizeof(info)); + info.device = get_device(argv[1]); + info.vendor = gamemode_get_gpu_vendor(info.device); + +@@ -428,8 +442,6 @@ int main(int argc, char *argv[]) + + } else if (argc >= 4 && argc <= 7 && strncmp(argv[2], "set", 3) == 0) { + /* Get and verify the vendor and device */ +- struct GameModeGPUInfo info; +- memset(&info, 0, sizeof(info)); + info.device = get_device(argv[1]); + info.vendor = gamemode_get_gpu_vendor(info.device); + + +From 9320a94638bcc216104ffe822299def89eaf8945 Mon Sep 17 00:00:00 2001 +From: Matthias Gerstner +Date: Thu, 4 Apr 2019 11:39:17 +0200 +Subject: [PATCH 4/4] external-helper: improve run_external_process() + robustness + +run_external_process() contained pipe file descriptors leaks (e.g. one +pipe end was never closed). Also the stdout might have been captured +incomplete, since only a single read() was performed on the pipe. +Furthermore should a child process try to write a larger amount of data +onto the pipe then it will become stuck, because the parent process +isn't consuming the data. Thus the timeout would trigger in these cases +although the child process does nothing wrong. + +This commit changes the implementation to follow a select() based +approach that continually reads from the pipe, but discards data that +doesn't fit in the provided buffer. +--- + daemon/external-helper.c | 136 ++++++++++++++++++++++++++------------- + daemon/gpuclockctl.c | 5 +- + 2 files changed, 93 insertions(+), 48 deletions(-) + +diff --git a/daemon/external-helper.c b/daemon/external-helper.c +index 52361f9..62cf29a 100644 +--- a/daemon/external-helper.c ++++ b/daemon/external-helper.c +@@ -36,11 +36,75 @@ POSSIBILITY OF SUCH DAMAGE. + + #include + #include ++#include ++#include + #include +-#include + #include + +-static const int default_timeout = 5; ++static const int DEFAULT_TIMEOUT = 5; ++ ++static int read_child_stdout(int pipe_fd, char buffer[EXTERNAL_BUFFER_MAX], int tsec) ++{ ++ fd_set fds; ++ struct timeval timeout; ++ int num_readable = 0; ++ ssize_t buffer_bytes_read = 0; ++ ssize_t just_read = 0; ++ bool buffer_full = false; ++ char discard_buffer[EXTERNAL_BUFFER_MAX]; ++ ++ /* Set up the timout */ ++ timeout.tv_sec = tsec; ++ timeout.tv_usec = 0; ++ ++ FD_ZERO(&fds); ++ ++ /* Wait for the child to finish up with a timout */ ++ while (true) { ++ FD_SET(pipe_fd, &fds); ++ num_readable = select(pipe_fd + 1, &fds, NULL, NULL, &timeout); ++ ++ if (num_readable < 0) { ++ if (errno == EINTR) { ++ continue; ++ } else { ++ LOG_ERROR("sigtimedwait failed: %s\n", strerror(errno)); ++ return -1; ++ } ++ } else if (num_readable == 0) { ++ return -2; ++ } ++ ++ if (!buffer_full) { ++ just_read = read(pipe_fd, ++ buffer + buffer_bytes_read, ++ EXTERNAL_BUFFER_MAX - (size_t)buffer_bytes_read - 1); ++ } else { ++ just_read = read(pipe_fd, discard_buffer, EXTERNAL_BUFFER_MAX - 1); ++ } ++ ++ if (just_read < 0) { ++ return -1; ++ } else if (just_read == 0) { ++ // EOF encountered ++ break; ++ } ++ ++ if (!buffer_full) { ++ buffer_bytes_read += just_read; ++ ++ if (buffer_bytes_read == EXTERNAL_BUFFER_MAX - 1) { ++ // our buffer is exhausted, discard the rest ++ // of the output ++ buffer_full = true; ++ } ++ } ++ } ++ ++ buffer[buffer_bytes_read] = 0; ++ ++ return 0; ++} + + /** + * Call an external process +@@ -50,6 +114,7 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF + pid_t p; + int status = 0; + int pipes[2]; ++ int ret = 0; + char internal[EXTERNAL_BUFFER_MAX] = { 0 }; + + if (pipe(pipes) == -1) { +@@ -59,20 +124,12 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF + + /* Set the default timeout */ + if (tsec == -1) { +- tsec = default_timeout; +- } +- +- /* set up our signaling for the child and the timout */ +- sigset_t mask; +- sigset_t omask; +- sigemptyset(&mask); +- sigaddset(&mask, SIGCHLD); +- if (sigprocmask(SIG_BLOCK, &mask, &omask) < 0) { +- LOG_ERROR("sigprocmask failed: %s\n", strerror(errno)); +- return -1; ++ tsec = DEFAULT_TIMEOUT; + } + + if ((p = fork()) < 0) { ++ close(pipes[0]); ++ close(pipes[1]); + LOG_ERROR("Failed to fork(): %s\n", strerror(errno)); + return false; + } else if (p == 0) { +@@ -91,39 +148,31 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF + LOG_ERROR("Failed to execute external process: %s %s\n", exec_args[0], strerror(errno)); + exit(EXIT_FAILURE); + } +- _exit(EXIT_SUCCESS); +- } +- +- /* Set up the timout */ +- struct timespec timeout; +- timeout.tv_sec = tsec; +- timeout.tv_nsec = 0; +- +- /* Wait for the child to finish up with a timout */ +- while (true) { +- if (sigtimedwait(&mask, NULL, &timeout) < 0) { +- if (errno == EINTR) { +- continue; +- } else if (errno == EAGAIN) { +- LOG_ERROR("Child process timed out for %s, killing and returning\n", exec_args[0]); +- kill(p, SIGKILL); +- } else { +- LOG_ERROR("sigtimedwait failed: %s\n", strerror(errno)); +- return -1; +- } +- } +- break; ++ // should never be reached ++ abort(); + } + ++ // close the write end of the pipe so we get signaled EOF once the ++ // child exits + close(pipes[1]); +- ssize_t output_size = read(pipes[0], internal, EXTERNAL_BUFFER_MAX - 1); +- if (output_size < 0) { +- LOG_ERROR("Failed to read from process %s: %s\n", exec_args[0], strerror(errno)); +- return -1; ++ ret = read_child_stdout(pipes[0], internal, tsec); ++ close(pipes[0]); ++ ++ if (ret != 0) { ++ if (ret == -2) { ++ LOG_ERROR("Child process timed out for %s, killing and returning\n", exec_args[0]); ++ kill(p, SIGKILL); ++ } else { ++ LOG_ERROR("Failed to read from process %s: %s\n", exec_args[0], strerror(errno)); ++ } ++ if (buffer) { ++ // make sure the buffer is a terminated empty string on error ++ buffer[0] = 0; ++ } ++ } else if (buffer) { ++ memcpy(buffer, internal, EXTERNAL_BUFFER_MAX); + } + +- internal[output_size] = 0; +- + if (waitpid(p, &status, 0) < 0) { + LOG_ERROR("Failed to waitpid(%d): %s\n", (int)p, strerror(errno)); + return -1; +@@ -134,12 +183,9 @@ int run_external_process(const char *const *exec_args, char buffer[EXTERNAL_BUFF + LOG_ERROR("Child process '%s' exited abnormally\n", exec_args[0]); + } else if (WEXITSTATUS(status) != 0) { + LOG_ERROR("External process failed with exit code %d\n", WEXITSTATUS(status)); +- LOG_ERROR("Output was: %s\n", buffer ? buffer : internal); ++ LOG_ERROR("Output was: %s\n", internal); + return -1; + } + +- if (buffer) +- memcpy(buffer, internal, EXTERNAL_BUFFER_MAX); +- + return 0; + } +diff --git a/daemon/gpuclockctl.c b/daemon/gpuclockctl.c +index cac49bd..850bd05 100644 +--- a/daemon/gpuclockctl.c ++++ b/daemon/gpuclockctl.c +@@ -67,7 +67,7 @@ static void print_usage_and_exit(void) + exit(EXIT_FAILURE); + } + +-static const char* get_nv_attr(const char *attr) ++static const char *get_nv_attr(const char *attr) + { + static char out[EXTERNAL_BUFFER_MAX]; + const char *exec_args[] = { "/usr/bin/nvidia-settings", "-q", attr, "-t", NULL }; +@@ -315,8 +315,7 @@ static int get_gpu_state_amd(struct GameModeGPUInfo *info) + ret = -1; + } + +- if( ret == 0 ) +- { ++ if (ret == 0) { + /* Copy in the value from the file */ + strncpy(info->amd_performance_level, buff, CONFIG_VALUE_MAX - 1); + info->amd_performance_level[CONFIG_VALUE_MAX - 1] = '\0'; diff --git a/hardening.patch b/hardening.patch deleted file mode 100644 index 1956d46..0000000 --- a/hardening.patch +++ /dev/null @@ -1,165 +0,0 @@ -From: Matthias Gerstner -Date: 2018-06-30 17:44:00 +0200 -Subject: Bugfixes and hardening from security review for Factory inclusion -References: boo#1093979 -Upstream: submitted - -diff --git a/daemon/cpugovctl.c b/daemon/cpugovctl.c -index 51b2e58..b64b5b4 100644 ---- a/daemon/cpugovctl.c -+++ b/daemon/cpugovctl.c -@@ -35,15 +35,18 @@ POSSIBILITY OF SUCH DAMAGE. - #include "logging.h" - - #include -+#include - #include - - /** - * Sets all governors to a value - */ --static void set_gov_state(const char *value) -+static int set_gov_state(const char *value) - { - char governors[MAX_GOVERNORS][MAX_GOVERNOR_LENGTH] = { { 0 } }; - int num = fetch_governors(governors); -+ int retval = EXIT_SUCCESS; -+ int res = 0; - - LOG_MSG("Setting governors to %s\n", value); - for (int i = 0; i < num; i++) { -@@ -54,9 +57,15 @@ static void set_gov_state(const char *value) - continue; - } - -- fprintf(f, "%s\n", value); -+ res = fprintf(f, "%s\n", value); -+ if (res < 0) { -+ LOG_ERROR("Failed to set governor %s to %s: %s", gov, value, strerror(errno)); -+ retval = EXIT_FAILURE; -+ } - fclose(f); - } -+ -+ return retval; - } - - /** -@@ -64,14 +73,9 @@ static void set_gov_state(const char *value) - */ - int main(int argc, char *argv[]) - { -- if (argc < 2) { -- fprintf(stderr, "usage: cpugovctl [get] [set VALUE]\n"); -- return EXIT_FAILURE; -- } -- -- if (strncmp(argv[1], "get", 3) == 0) { -+ if (argc == 2 && strncmp(argv[1], "get", 3) == 0) { - printf("%s", get_gov_state()); -- } else if (strncmp(argv[1], "set", 3) == 0) { -+ } else if (argc == 3 && strncmp(argv[1], "set", 3) == 0) { - const char *value = argv[2]; - - /* Must be root to set the state */ -@@ -80,8 +84,9 @@ int main(int argc, char *argv[]) - return EXIT_FAILURE; - } - -- set_gov_state(value); -+ return set_gov_state(value); - } else { -+ fprintf(stderr, "usage: cpugovctl [get] [set VALUE]\n"); - return EXIT_FAILURE; - } - -diff --git a/daemon/daemonize.c b/daemon/daemonize.c -index 5a68cbf..cc475f3 100644 ---- a/daemon/daemonize.c -+++ b/daemon/daemonize.c -@@ -31,6 +31,7 @@ POSSIBILITY OF SUCH DAMAGE. - #include "daemonize.h" - #include "logging.h" - -+#include - #include - #include - #include -@@ -61,14 +62,20 @@ void daemonize(const char *name) - } - - /* Now continue execution */ -- umask(0); -+ umask(0022); - if (setsid() < 0) { - FATAL_ERRORNO("Failed to create process group\n"); - } - if (chdir("/") < 0) { - FATAL_ERRORNO("Failed to change to root directory\n"); - } -- close(STDIN_FILENO); -- close(STDOUT_FILENO); -- close(STDERR_FILENO); -+ -+ /* replace standard file descriptors by /dev/null */ -+ int devnull_r = open("/dev/null", O_RDONLY); -+ int devnull_w = open("/dev/null", O_WRONLY); -+ dup2(devnull_r, STDIN_FILENO); -+ dup2(devnull_w, STDOUT_FILENO); -+ dup2(devnull_w, STDERR_FILENO); -+ close(devnull_r); -+ close(devnull_w); - } -diff --git a/daemon/gamemode.c b/daemon/gamemode.c -index 9a8380d..63c02f7 100644 ---- a/daemon/gamemode.c -+++ b/daemon/gamemode.c -@@ -381,6 +381,12 @@ bool game_mode_context_register(GameModeContext *self, pid_t client) - /* Construct a new client if we can */ - GameModeClient *cl = NULL; - -+ /* Cap the total number of active clients */ -+ if (game_mode_context_num_clients(self) + 1 > MAX_GAMES) { -+ LOG_ERROR("Max games (%d) reached, not registering %d\n", MAX_GAMES, client); -+ return false; -+ } -+ - cl = game_mode_client_new(client); - if (!cl) { - fputs("OOM\n", stderr); -@@ -390,22 +396,16 @@ bool game_mode_context_register(GameModeContext *self, pid_t client) - - if (game_mode_context_has_client(self, client)) { - LOG_ERROR("Addition requested for already known process [%d]\n", client); -- return false; -- } -- -- /* Cap the total number of active clients */ -- if (game_mode_context_num_clients(self) + 1 > MAX_GAMES) { -- LOG_ERROR("Max games (%d) reached, not registering %d\n", MAX_GAMES, client); -- return false; -+ goto error_cleanup; - } - - /* Check our blacklist and whitelist */ - if (!config_get_client_whitelisted(self->config, cl->executable)) { - LOG_MSG("Client [%s] was rejected (not in whitelist)\n", cl->executable); -- return false; -+ goto error_cleanup; - } else if (config_get_client_blacklisted(self->config, cl->executable)) { - LOG_MSG("Client [%s] was rejected (in blacklist)\n", cl->executable); -- return false; -+ goto error_cleanup; - } - - /* Begin a write lock now to insert our new client at list start */ -@@ -427,6 +427,9 @@ bool game_mode_context_register(GameModeContext *self, pid_t client) - game_mode_apply_scheduler(self, client); - - return true; -+error_cleanup: -+ game_mode_client_free(cl); -+ return false; - } - - bool game_mode_context_unregister(GameModeContext *self, pid_t client)