fwupd/libfwupdplugin/fu-kernel.c
Richard Hughes 01994a993a trivial: Fix impossible crash when the firmware search path is NULL
PVS: The 'contents' pointer was used unsafely after it was verified against nullptr.
2022-09-09 19:23:29 +01:00

277 lines
6.5 KiB
C

/*
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#define G_LOG_DOMAIN "FuCommon"
#include "config.h"
#include <errno.h>
#include <glib/gstdio.h>
#ifdef HAVE_UTSNAME_H
#include <sys/utsname.h>
#endif
#include "fu-common.h"
#include "fu-kernel.h"
#include "fu-path.h"
#include "fu-string.h"
#include "fu-version-common.h"
/**
* fu_kernel_locked_down:
*
* Determines if kernel lockdown in effect
*
* Since: 1.8.2
**/
gboolean
fu_kernel_locked_down(void)
{
#ifdef __linux__
gsize len = 0;
g_autofree gchar *dir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY);
g_autofree gchar *fname = g_build_filename(dir, "lockdown", NULL);
g_autofree gchar *data = NULL;
g_auto(GStrv) options = NULL;
if (!g_file_test(fname, G_FILE_TEST_EXISTS))
return FALSE;
if (!g_file_get_contents(fname, &data, &len, NULL))
return FALSE;
if (len < 1)
return FALSE;
options = g_strsplit(data, " ", -1);
for (guint i = 0; options[i] != NULL; i++) {
if (g_strcmp0(options[i], "[none]") == 0)
return FALSE;
}
return TRUE;
#else
return FALSE;
#endif
}
/**
* fu_kernel_check_version:
* @minimum_kernel: (not nullable): The minimum kernel version to check against
* @error: (nullable): optional return location for an error
*
* Determines if the system is running at least a certain required kernel version
*
* Since: 1.8.2
**/
gboolean
fu_kernel_check_version(const gchar *minimum_kernel, GError **error)
{
#ifdef HAVE_UTSNAME_H
struct utsname name_tmp;
g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
g_return_val_if_fail(minimum_kernel != NULL, FALSE);
memset(&name_tmp, 0, sizeof(struct utsname));
if (uname(&name_tmp) < 0) {
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to read kernel version");
return FALSE;
}
if (fu_version_compare(name_tmp.release, minimum_kernel, FWUPD_VERSION_FORMAT_TRIPLET) <
0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"kernel %s doesn't meet minimum %s",
name_tmp.release,
minimum_kernel);
return FALSE;
}
return TRUE;
#else
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"platform doesn't support checking for minimum Linux kernel");
return FALSE;
#endif
}
/**
* fu_kernel_get_firmware_search_path:
* @error: (nullable): optional return location for an error
*
* Reads the FU_PATH_KIND_FIRMWARE_SEARCH and
* returns its contents
*
* Returns: a pointer to a gchar array
*
* Since: 1.8.2
**/
gchar *
fu_kernel_get_firmware_search_path(GError **error)
{
gsize sz = 0;
g_autofree gchar *sys_fw_search_path = NULL;
g_autofree gchar *contents = NULL;
sys_fw_search_path = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
if (!g_file_get_contents(sys_fw_search_path, &contents, &sz, error))
return NULL;
/* sanity check */
if (contents == NULL || sz == 0) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"failed to get firmware search path from %s",
sys_fw_search_path);
return NULL;
}
/* remove newline character */
if (contents[sz - 1] == '\n')
contents[sz - 1] = 0;
g_debug("read firmware search path (%" G_GSIZE_FORMAT "): %s", sz, contents);
return g_steal_pointer(&contents);
}
/**
* fu_kernel_set_firmware_search_path:
* @path: NUL-terminated string
* @error: (nullable): optional return location for an error
*
* Writes path to the FU_PATH_KIND_FIRMWARE_SEARCH
*
* Returns: %TRUE if successful
*
* Since: 1.8.2
**/
gboolean
fu_kernel_set_firmware_search_path(const gchar *path, GError **error)
{
#if GLIB_CHECK_VERSION(2, 66, 0)
g_autofree gchar *sys_fw_search_path_prm = NULL;
g_return_val_if_fail(path != NULL, FALSE);
g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE);
sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path);
return g_file_set_contents_full(sys_fw_search_path_prm,
path,
strlen(path),
G_FILE_SET_CONTENTS_NONE,
0644,
error);
#else
FILE *fd;
gsize res;
g_autofree gchar *sys_fw_search_path_prm = NULL;
g_return_val_if_fail(path != NULL, FALSE);
g_return_val_if_fail(strlen(path) < PATH_MAX, FALSE);
sys_fw_search_path_prm = fu_path_from_kind(FU_PATH_KIND_FIRMWARE_SEARCH);
/* g_file_set_contents will try to create backup files in sysfs, so use fopen here */
fd = fopen(sys_fw_search_path_prm, "w");
if (fd == NULL) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_PERMISSION_DENIED,
"Failed to open %s: %s",
sys_fw_search_path_prm,
g_strerror(errno));
return FALSE;
}
g_debug("writing firmware search path (%" G_GSIZE_FORMAT "): %s", strlen(path), path);
res = fwrite(path, sizeof(gchar), strlen(path), fd);
fclose(fd);
if (res != strlen(path)) {
g_set_error(error,
FWUPD_ERROR,
FWUPD_ERROR_WRITE,
"Failed to write firmware search path: %s",
g_strerror(errno));
return FALSE;
}
return TRUE;
#endif
}
/**
* fu_kernel_reset_firmware_search_path:
* @error: (nullable): optional return location for an error
*
* Resets the FU_PATH_KIND_FIRMWARE_SEARCH to an empty string
*
* Returns: %TRUE if successful
*
* Since: 1.8.2
**/
gboolean
fu_kernel_reset_firmware_search_path(GError **error)
{
const gchar *contents = " ";
return fu_kernel_set_firmware_search_path(contents, error);
}
/**
* fu_kernel_get_cmdline:
* @error: (nullable): optional return location for an error
*
* Loads all the kernel /proc/cmdline key/values into a hash table.
*
* Returns: (transfer container) (element-type utf8 utf8): keys from the kernel command line
*
* Since: 1.8.5
**/
GHashTable *
fu_kernel_get_cmdline(GError **error)
{
#ifdef __linux__
gsize bufsz = 0;
g_autofree gchar *buf = NULL;
g_autoptr(GHashTable) hash = NULL;
g_return_val_if_fail(error == NULL || *error == NULL, NULL);
if (!g_file_get_contents("/proc/cmdline", &buf, &bufsz, error))
return NULL;
hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
if (bufsz > 0) {
g_auto(GStrv) tokens = fu_strsplit(buf, bufsz - 1, " ", -1);
for (guint i = 0; tokens[i] != NULL; i++) {
g_auto(GStrv) kv = NULL;
if (strlen(tokens[i]) == 0)
continue;
kv = g_strsplit(tokens[i], "=", 2);
g_hash_table_insert(hash, g_strdup(kv[0]), g_strdup(kv[1]));
}
}
/* success */
return g_steal_pointer(&hash);
#else
g_set_error_literal(error,
FWUPD_ERROR,
FWUPD_ERROR_INTERNAL,
"platform does not support getting the kernel cmdline");
return NULL;
#endif
}