[PATCH bpf-next v1 1/1] libbpf: Correctly set the kernel code version in Debian kernel.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In a previous commit, Ubuntu kernel code version is correctly set by
retrieving the information from /proc/version_signature.

   commit<5b3d72987701d51bf31823b39db49d10970f5c2d>
   (libbpf: Improve LINUX_VERSION_CODE detection)

However, the /proc/version_signature file doesn't exist in at least the
older versions of Debian distributions (eg, Debian 9, 10). The Debian
kernel has a similar issue where the release information from uname()
syscall doesn't give the kernel code version that matches what the kernel
actually expects. Below is an example content from Debian 10.

   release: 4.19.0-23-amd64
   version: #1 SMP Debian 4.19.269-1 (2022-12-20) x86_64

Debian reports incorrect kernel version in utsname::release returned
by uname() syscall, which in older kernels (Debian 9, 10) leads to
kprobe BPF programs failing to load due to the version check mismatch.

Fortunately, the correct kernel code version presents in the
utsname::version returned by uname() syscall in Debian kernels.
This change adds another get kernel version function to handle
Debian in addition to the previously added get kernel version function
to handle Ubuntu. Some minor refactoring work is also done to make
the code more readable.

Signed-off-by: Hao Xiang <hao.xiang@xxxxxxxxxxxxx>
Signed-off-by: Ho-Ren (Jack) Chuang <horenchuang@xxxxxxxxxxxxx>
---
tools/lib/bpf/libbpf.c | 92 +++++++++++++++++++++++++++++++++++-------
1 file changed, 78 insertions(+), 14 deletions(-)

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index eed5cec6f510..bc022d0cd71f 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -870,21 +870,20 @@ bpf_object__add_programs(struct bpf_object *obj,
Elf_Data *sec_data,
        return 0;
}

-__u32 get_kernel_version(void)
+/* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release,
+ * but Ubuntu provides /proc/version_signature file, as described at
+ * https://ubuntu.com/kernel, with an example contents below, which we
+ * can use to get a proper LINUX_VERSION_CODE.
+ *
+ *   Ubuntu 5.4.0-12.15-generic 5.4.8
+ *
+ * In the above, 5.4.8 is what kernel is actually expecting, while
+ * uname() call will return 5.4.0 in info.release.
+ */
+static __u32 get_ubuntu_kernel_version(void)
{
-        /* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release,
-         * but Ubuntu provides /proc/version_signature file, as described at
-         * https://ubuntu.com/kernel, with an example contents below, which we
-         * can use to get a proper LINUX_VERSION_CODE.
-         *
-         *   Ubuntu 5.4.0-12.15-generic 5.4.8
-         *
-         * In the above, 5.4.8 is what kernel is actually expecting, while
-         * uname() call will return 5.4.0 in info.release.
-         */
        const char *ubuntu_kver_file = "/proc/version_signature";
        __u32 major, minor, patch;
-        struct utsname info;

        if (faccessat(AT_FDCWD, ubuntu_kver_file, R_OK, AT_EACCESS) == 0) {
                FILE *f;
@@ -900,12 +899,77 @@ __u32 get_kernel_version(void)
                /* something went wrong, fall back to uname() approach */
        }

-        uname(&info);
-        if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
+        return 0;
+}
+
+#define VERSION_PREFIX_DEBIAN "Debian "
+
+/* On Debian LINUX_VERSION_CODE doesn't correspond to info.release.
+ * Instead, it is provided in info.version. An example content of
+ * Debian 10 looks like the below.
+ *
+ *   utsname::release   4.19.0-22-amd64
+ *   utsname::version   #1 SMP Debian 4.19.260-1 (2022-09-29)
+ *
+ * In the above, 4.19.260 is what kernel is actually expecting, while
+ * uname() call will return 4.19.0 in info.release.
+ */
+static __u32 get_debian_kernel_version(struct utsname *info)
+{
+        __u32 major, minor, patch;
+        size_t len;
+        char *version;
+        char *find;
+
+        version = info->version;
+
+        find = strstr(version, VERSION_PREFIX_DEBIAN);
+        if (!find) {
+              /* This is not a Debian kernel. */
                return 0;
+        }
+
+        len = strlen(version);
+        find += strlen(VERSION_PREFIX_DEBIAN);
+        if (find - version >= len)
+                return 0;
+
+        if (sscanf(find, "%u.%u.%u", &major, &minor, &patch) != 3)
+                return 0;
+
+        return KERNEL_VERSION(major, minor, patch);
+}
+
+static __u32 get_general_kernel_version(struct utsname *info)
+{
+        __u32 major, minor, patch;
+
+        if (sscanf(info->release, "%u.%u.%u", &major, &minor, &patch) != 3)
+                return 0;
+
        return KERNEL_VERSION(major, minor, patch);
}

+__u32 get_kernel_version(void)
+{
+        __u32 version;
+        struct utsname info;
+
+        /* Check if this is an Ubuntu kernel. */
+        version = get_ubuntu_kernel_version();
+        if (version != 0)
+                return version;
+
+        uname(&info);
+
+        /* Check if this is a Debian kernel. */
+        version = get_debian_kernel_version(&info);
+        if (version != 0)
+                return version;
+
+        return get_general_kernel_version(&info);
+}
+
static const struct btf_member *
find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
{
-- 
2.30.2



[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux