lkrg-org / lkrg

Linux Kernel Runtime Guard
https://lkrg.org
Other
404 stars 72 forks source link

CentOS Stream 9 kernel support #345

Closed solardiz closed 6 days ago

solardiz commented 6 days ago

Our CentOS Stream 9 CI job has just started failing, perhaps this is an issue we'll also face with RHEL9-based distros next (in 9.5?)

make[1]: Entering directory '/usr/src/kernels/5.14.0-472.el9.x86_64'
  CC [M]  /__w/lkrg/lkrg/src/modules/ksyms/p_resolve_ksym.o
  CC [M]  /__w/lkrg/lkrg/src/modules/hashing/p_lkrg_fast_hash.o
  CC [M]  /__w/lkrg/lkrg/src/modules/comm_channel/p_comm_channel.o
  CC [M]  /__w/lkrg/lkrg/src/modules/integrity_timer/p_integrity_timer.o
In file included from /__w/lkrg/lkrg/src/modules/ksyms/../../p_lkrg_main.h:408,
                 from /__w/lkrg/lkrg/src/modules/ksyms/p_resolve_ksym.c:19:
/__w/lkrg/lkrg/src/modules/ksyms/../../modules/wrap/p_struct_wrap.h: In function 'p_module_core':
/__w/lkrg/lkrg/src/modules/ksyms/../../modules/wrap/p_struct_wrap.h:120:16: error: 'struct module' has no member named 'core_layout'
  120 |    return p_mod->core_layout.base;
      |                ^~
solardiz commented 6 days ago

Source repo at https://gitlab.com/redhat/centos-stream/rpms/kernel branch c9s.

We may have a hint here:

* Mon Jun 24 2024 Lucas Zampieri <lzampier@redhat.com> [5.14.0-470.el9]
[...]
- scripts/gdb: use mem instead of core_layout to get the module address (Donald Dutile) [RHEL-28063]

Kernel tree tarballs may be downloaded e.g. from https://sources.stream.centos.org/sources/rpms/kernel/linux-5.14.0-475.el9.tar.xz/sha512/b305934f8ddf0ed87a846a92b0d8d045c706b14771a1ab6579e5a74938422909f04137003a444a4c8b730f4496c122e3cf9610404296512d3a0cf97d620fb8c2/linux-5.14.0-475.el9.tar.xz for current latest, where the filename and hash come from the sources file in the repo.

solardiz commented 6 days ago

Also helpful is https://github.com/rocky-linux/rocky-tools/tree/main/getsrc but I'll do this manually for now.

$ git show 15e00c17cf78b7215a52dba623285038d72a93aa sources | fgrep tar.xz
-SHA512 (linux-5.14.0-469.el9.tar.xz) = 0729d6a5e3aa3e52edadc36e0a8a19a0b2211b9723e0e63cb738385c3181d197f2e6852e07485dd788ccd94dac4f7d67ed9334d89fcb03a58cc95fe03653bc72
+SHA512 (linux-5.14.0-470.el9.tar.xz) = 64458e3e6ee4abc11f9f5dbcf03e333a3cdb2a6bf806703056ab6bdaf444914e517dc921f7e688009c3844d5c617d16118baaf9e21ac36c1e50852c3c4eaf0de
wget https://sources.stream.centos.org/sources/rpms/kernel/linux-5.14.0-470.el9.tar.xz/sha512/64458e3e6ee4abc11f9f5dbcf03e333a3cdb2a6bf806703056ab6bdaf444914e517dc921f7e688009c3844d5c617d16118baaf9e21ac36c1e50852c3c4eaf0de/linux-5.14.0-470.el9.tar.xz
wget https://sources.stream.centos.org/sources/rpms/kernel/linux-5.14.0-469.el9.tar.xz/sha512/0729d6a5e3aa3e52edadc36e0a8a19a0b2211b9723e0e63cb738385c3181d197f2e6852e07485dd788ccd94dac4f7d67ed9334d89fcb03a58cc95fe03653bc72/linux-5.14.0-469.el9.tar.xz
tar xvf linux-5.14.0-469.el9.tar.xz 
tar xvf linux-5.14.0-470.el9.tar.xz 
diff -urpN linux-5.14.0-469.el9 linux-5.14.0-470.el9 > 469to470.diff
solardiz commented 6 days ago

Looks like for some of the fields we've been using there's now a different way to access them, but some other fields are gone - do we need them?

--- linux-5.14.0-469.el9/include/linux/module.h 2024-06-21 14:57:53.000000000 +0200
+++ linux-5.14.0-470.el9/include/linux/module.h 2024-06-24 14:20:16.000000000 +0200
@@ -323,17 +324,47 @@
        struct latch_tree_node node;
 };

-struct module_layout {
-       /* The actual code + data. */
+enum mod_mem_type {
+       MOD_TEXT = 0,
+       MOD_DATA,
+       MOD_RODATA,
+       MOD_RO_AFTER_INIT,
+       MOD_INIT_TEXT,
+       MOD_INIT_DATA,
+       MOD_INIT_RODATA,
+
+       MOD_MEM_NUM_TYPES,
+       MOD_INVALID = -1,
+};
+
+#define mod_mem_type_is_init(type)     \
+       ((type) == MOD_INIT_TEXT ||     \
+        (type) == MOD_INIT_DATA ||     \
+        (type) == MOD_INIT_RODATA)
+
+#define mod_mem_type_is_core(type) (!mod_mem_type_is_init(type))
+
+#define mod_mem_type_is_text(type)     \
+        ((type) == MOD_TEXT ||         \
+         (type) == MOD_INIT_TEXT)
+
+#define mod_mem_type_is_data(type) (!mod_mem_type_is_text(type))
+
+#define mod_mem_type_is_core_data(type)        \
+       (mod_mem_type_is_core(type) &&  \
+        mod_mem_type_is_data(type))
+
+#define for_each_mod_mem_type(type)                    \
+       for (enum mod_mem_type (type) = 0;              \
+            (type) < MOD_MEM_NUM_TYPES; (type)++)
+
+#define for_class_mod_mem_type(type, class)            \
+       for_each_mod_mem_type(type)                     \
+               if (mod_mem_type_is_##class(type))
+
+struct module_memory {
        void *base;
-       /* Total size. */
        unsigned int size;
-       /* The size of the executable code.  */
-       unsigned int text_size;
-       /* Size of RO section of the module (text+rodata) */
-       unsigned int ro_size;
-       /* Size of RO after init section */
-       unsigned int ro_after_init_size;

 #ifdef CONFIG_MODULES_TREE_LOOKUP
        struct mod_tree_node mtn;
@@ -429,9 +460,7 @@
        /* Startup function. */
        int (*init)(void);

-       /* Core layout: rbtree is accessed frequently, so keep together. */
-       struct module_layout core_layout __module_layout_align;
-       struct module_layout init_layout;
+       struct module_memory mem[MOD_MEM_NUM_TYPES] __module_memory_align;

        /* Arch-specific module values */
        struct mod_arch_specific arch;
@@ -554,6 +583,9 @@
        struct error_injection_entry *ei_funcs;
        unsigned int num_ei_funcs;
 #endif
+#ifdef CONFIG_DYNAMIC_DEBUG_CORE
+       struct _ddebug_info dyndbg_info;
+#endif
 } ____cacheline_aligned __randomize_layout;
 #ifndef MODULE_ARCH_INIT
 #define MODULE_ARCH_INIT {}
@@ -581,18 +613,35 @@
 bool is_module_percpu_address(unsigned long addr);
 bool is_module_text_address(unsigned long addr);

+static inline bool within_module_mem_type(unsigned long addr,
+                                         const struct module *mod,
+                                         enum mod_mem_type type)
+{
+       unsigned long base, size;
+
+       base = (unsigned long)mod->mem[type].base;
+       size = mod->mem[type].size;
+       return addr - base < size;
+}
+
 static inline bool within_module_core(unsigned long addr,
                                      const struct module *mod)
 {
-       return (unsigned long)mod->core_layout.base <= addr &&
-              addr < (unsigned long)mod->core_layout.base + mod->core_layout.size;
+       for_class_mod_mem_type(type, core) {
+               if (within_module_mem_type(addr, mod, type))
+                       return true;
+       }
+       return false;
 }

 static inline bool within_module_init(unsigned long addr,
                                      const struct module *mod)
 {
-       return (unsigned long)mod->init_layout.base <= addr &&
-              addr < (unsigned long)mod->init_layout.base + mod->init_layout.size;
+       for_class_mod_mem_type(type, init) {
+               if (within_module_mem_type(addr, mod, type))
+                       return true;
+       }
+       return false;
 }

 static inline bool within_module(unsigned long addr, const struct module *mod)
solardiz commented 6 days ago

Oh, I see we already support this for 6.4+. So we just need to add version detection. Since it's probably still tricky to check for exact RHEL revision, I suggest we instead check for defined(mod_mem_type_is_init). In fact, that would also work for mainline, and should cover other/future backports.