LINUX 5.3.0: Check for 'recurse' arg in keyring_search
[openafs.git] / src / afs / LINUX / osi_probe.c
index 3f27f0c..11a1be3 100644 (file)
 /* Code to find the Linux syscall table */
 
 #ifdef OSI_PROBE_STANDALONE
-#define OSI_PROBE_DEBUG
+# define OSI_PROBE_DEBUG
 #endif
 #ifndef OSI_PROBE_STANDALONE
-#include <afsconfig.h>
-#include "afs/param.h"
+# include <afsconfig.h>
+# include "afs/param.h"
 #endif
-#ifdef AFS_LINUX24_ENV
+
+#include <linux/version.h>
+#if defined(ENABLE_LINUX_SYSCALL_PROBING)
 #include <linux/module.h> /* early to avoid printf->printk mapping */
+#include <scsi/scsi.h> /* for scsi_command_size */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,26)
+/* Slightly kludgy, but too bad */
+#define scsi_command_size scsi_command_size_tbl
+#endif
 #ifndef OSI_PROBE_STANDALONE
-#include "afs/sysincludes.h"
-#include "afsincludes.h"
+# include "afs/sysincludes.h"
+# include "afsincludes.h"
+#endif
+#include <linux/sched.h>
+#ifdef HAVE_LINUX_CONFIG_H
+# include <linux/config.h>
 #endif
-#include <linux/version.h>
-#include <linux/config.h>
 #include <linux/linkage.h>
 #include <linux/init.h>
 #include <linux/unistd.h>
 #include <linux/mm.h>
-#ifdef AFS_LINUX26_ENV
-#include <scsi/scsi.h> /* for scsi_command_size */
-#endif
 
 #if defined(AFS_PPC64_LINUX26_ENV)
-#include <asm/abs_addr.h>
+# include <asm/abs_addr.h>
 #endif
 
 #ifdef AFS_AMD64_LINUX20_ENV
-#include <asm/ia32_unistd.h>
+# include <asm/ia32_unistd.h>
 #endif
 
 /* number of syscalls */
 
 /* Allow the user to specify sys_call_table addresses */
 static unsigned long sys_call_table_addr[4] = { 0,0,0,0 };
-#ifdef module_param_array
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param_array(sys_call_table_addr, long, NULL, 0);
 #else
 MODULE_PARM(sys_call_table_addr, "1-4l");
@@ -126,7 +132,7 @@ MODULE_PARM_DESC(sys_call_table_addr, "Location of system call tables");
 
 /* If this is set, we are more careful about avoiding duplicate matches */
 static int probe_carefully = 1;
-#ifdef module_param
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param(probe_carefully, int, 0);
 #else
 MODULE_PARM(probe_carefully, "i");
@@ -134,7 +140,7 @@ MODULE_PARM(probe_carefully, "i");
 MODULE_PARM_DESC(probe_carefully, "Probe for system call tables carefully");
 
 static int probe_ignore_syscalls[8] = { -1, -1, -1, -1, -1, -1, -1, -1 };
-#ifdef module_param_array
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param_array(probe_ignore_syscalls, int, NULL, 0);
 #else
 MODULE_PARM(probe_ignore_syscalls, "1-8i");
@@ -151,9 +157,10 @@ MODULE_PARM_DESC(probe_ignore_syscalls, "Syscalls to ignore in table checks");
  * 0x0010 - detail - check_harder
  * 0x0020 - detail - check_harder/zapped
  * 0x0040 - automatically ignore setgroups and afs_syscall
+ * 0x0080 - detail - check_table_readable
  */
 static int probe_debug = 0x41;
-#ifdef module_param
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param(probe_debug, int, 0);
 #else
 MODULE_PARM(probe_debug, "i");
@@ -161,7 +168,7 @@ MODULE_PARM(probe_debug, "i");
 MODULE_PARM_DESC(probe_debug, "Debugging level");
 
 static unsigned long probe_debug_addr[4] = { 0,0,0,0 };
-#ifdef module_param_array
+#if defined(module_param_array) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param_array(probe_debug_addr, long, NULL, 0);
 #else
 MODULE_PARM(probe_debug_addr, "1-4l");
@@ -169,7 +176,7 @@ MODULE_PARM(probe_debug_addr, "1-4l");
 MODULE_PARM_DESC(probe_debug_addr, "Debug range starting locations");
 
 static unsigned long probe_debug_range = 0;
-#ifdef module_param
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param(probe_debug_range, long, 0);
 #else
 MODULE_PARM(probe_debug_range, "l");
@@ -177,7 +184,7 @@ MODULE_PARM(probe_debug_range, "l");
 MODULE_PARM_DESC(probe_debug_range, "Debug range length");
 
 static unsigned long probe_debug_tag = 0;
-#ifdef module_param
+#if defined(module_param) && LINUX_VERSION_CODE > KERNEL_VERSION(2,6,9)
 module_param(probe_debug_tag, long, 0);
 #else
 MODULE_PARM(probe_debug_tag, "l");
@@ -231,15 +238,15 @@ extern SYSCALLTYPE sys_call_table_emu[] __attribute__((weak));
 
 extern asmlinkage ssize_t sys_read(unsigned int fd, char __user * buf, size_t count) __attribute__((weak));
 extern asmlinkage long sys_close(unsigned int) __attribute__((weak));
+#if defined(EXPORTED_SYS_CHDIR)
 extern asmlinkage long sys_chdir(const char *) __attribute__((weak));
+#endif
 extern asmlinkage ssize_t sys_write(unsigned int, const char *, size_t) __attribute__((weak));
-#ifdef AFS_LINUX26_ENV
 extern asmlinkage long sys_wait4(pid_t, int *, int, struct rusage *) __attribute__((weak));
-#else
-extern asmlinkage long sys_wait4(pid_t, unsigned int *, int, struct rusage *) __attribute__((weak));
-#endif
 extern asmlinkage long sys_exit (int) __attribute__((weak));
+#if defined(EXPORTED_SYS_OPEN)
 extern asmlinkage long sys_open (const char *, int, int) __attribute__((weak));
+#endif
 extern asmlinkage long sys_ioctl(unsigned int, unsigned int, unsigned long) __attribute__((weak));
 
 
@@ -292,6 +299,10 @@ typedef struct {
     int debug_ignore_NR[4];         /* syscalls to ignore for debugging */
 } probectl;
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+static int check_access(unsigned long, int);
+static int check_table_readable(probectl *, PROBETYPE *);
+#endif
 
 
 /********** Probing Configuration: sys_call_table **********/
@@ -300,14 +311,20 @@ typedef struct {
 /* On PPC64 and SPARC64, we need to omit the ones that might match both tables */
 static tryctl main_try[] = {
 #if !defined(AFS_PPC64_LINUX20_ENV) && !defined(AFS_SPARC64_LINUX20_ENV)
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir+write", __NR_close, &sys_close, __NR_chdir, &sys_chdir, __NR_write, &sys_write },
 #endif
+#endif
     { "scan: close+wait4",       __NR_close, &sys_close, __NR_wait4, &sys_wait4, -1,         0          },
 #if !defined(AFS_PPC64_LINUX20_ENV) && !defined(AFS_SPARC64_LINUX20_ENV)
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir",       __NR_close, &sys_close, __NR_chdir, &sys_chdir, -1,         0          },
 #endif
+#endif
     { "scan: close+ioctl",       __NR_close, &sys_close, __NR_ioctl, &sys_ioctl, -1,         0          },
+#if defined(EXPORTED_SYS_OPEN)
     { "scan: exit+open",         __NR_exit,  &sys_exit,  __NR_open,  &sys_open,  -1,         0          },
+#endif
     { 0 }
 };
 
@@ -390,7 +407,6 @@ static int main_zapped_syscalls[] = {
  * The module-loading mechanism changed in Linux 2.6, and insmod's
  * loss is our gain: three new unimplemented system calls! 
  */
-#if defined(AFS_LINUX26_ENV)
 #ifdef __NR_
     __NR_create_module,
 #endif
@@ -400,7 +416,6 @@ static int main_zapped_syscalls[] = {
 #ifdef __NR_get_kernel_syms
     __NR_get_kernel_syms,
 #endif
-#endif /* AFS_LINUX26_ENV */
 
 /* 
  * On IA64, the old module-loading calls are indeed present and
@@ -500,9 +515,9 @@ static probectl main_probe = {
     /* symbol in section to try scanning */
 #if defined(AFS_SPARC64_LINUX20_ENV) || defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
     (unsigned long)&sys_close,
-#elif defined(AFS_AMD64_LINUX20_ENV)
+#elif defined(AFS_AMD64_LINUX26_ENV)
     /* On this platform, it's in a different section! */
-    (unsigned long)&tasklist_lock,
+    (unsigned long)&generic_ro_fops,
 #else
     (unsigned long)&init_mm,
 #endif
@@ -523,32 +538,24 @@ static probectl main_probe = {
     (unsigned long)(&init_mm),
     0x1fffff,
     0x30000,
-#elif defined(AFS_AMD64_LINUX20_ENV)
-    (unsigned long)(&tasklist_lock) - 0x30000,
+#elif defined(AFS_AMD64_LINUX26_ENV)
+    (unsigned long)(&generic_ro_fops) - 0x30000,
     0,
     0x6000,
 #elif defined(AFS_PPC64_LINUX26_ENV)
     (unsigned long)(&do_signal),
     0xfff,
     0x400,
-#elif defined(AFS_PPC_LINUX20_ENV) || defined(AFS_PPC_LINUX20_ENV)
-    (unsigned long)&init_mm,
-    0xffff,
-    16384,
 #else
     (unsigned long)&init_mm,
     0,
     16384,
 #endif
 
-#ifdef AFS_LINUX26_ENV
-    (unsigned long)scsi_command_size,
     (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
     0x3ffff,
-    0x30000,
-#else
-    0, 0, 0, 0,
-#endif
+    0x40000,
 
     /* number and list of unimplemented system calls */
     ((sizeof(main_zapped_syscalls)/sizeof(main_zapped_syscalls[0])) - 1),
@@ -590,8 +597,10 @@ static probectl main_probe = {
 
 /* syscall pairs/triplets to probe */
 static tryctl ia32_try[] = {
+#if defined(EXPORTED_SYS_CHDIR)
     { "scan: close+chdir+write", __NR_ia32_close, &sys_close, __NR_ia32_chdir, &sys_chdir,        __NR_ia32_write, &sys_write },
     { "scan: close+chdir",       __NR_ia32_close, &sys_close, __NR_ia32_chdir, &sys_chdir,        -1,              0          },
+#endif
     { 0 }
 };
 
@@ -637,14 +646,10 @@ static probectl ia32_probe = {
     0,
     (0x180000 / sizeof(unsigned long *)),
 
-#ifdef AFS_LINUX26_ENV
-    (unsigned long)scsi_command_size,
     (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
     0x3ffff,
-    0x30000,
-#else
-    0, 0, 0, 0
-#endif
+    0x40000,
 
 
     /* number and list of unimplemented system calls */
@@ -776,14 +781,10 @@ static probectl sct32_probe = {
     16384,
 #endif
 
-#ifdef AFS_LINUX26_ENV
-    (unsigned long)scsi_command_size,
     (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
     0x3ffff,
-    0x30000,
-#else
-    0, 0, 0, 0
-#endif
+    0x40000,
 
     /* number and list of unimplemented system calls */
     ((sizeof(sct32_zapped_syscalls)/sizeof(sct32_zapped_syscalls[0])) - 1),
@@ -871,14 +872,10 @@ static probectl emu_probe = {
     0xfffff,
     0x20000,
 
-#ifdef AFS_LINUX26_ENV
-    (unsigned long)scsi_command_size,
     (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size - 0x10000,
     0x3ffff,
-    0x30000,
-#else
-    0, 0, 0, 0
-#endif
+    0x40000,
 
     /* number and list of unimplemented system calls */
     ((sizeof(emu_zapped_syscalls)/sizeof(emu_zapped_syscalls[0])) - 1),
@@ -926,6 +923,11 @@ static int check_table(probectl *P, PROBETYPE *ptr)
     PROBETYPE *x;
     int i, j;
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+    i = check_table_readable(P, ptr);
+    if (i >= 0) return i;
+#endif
+
     for (x = ptr, i = 0; i < _SS(NR_syscalls); i++, x++) {
 #ifdef OSI_PROBE_DEBUG
        if (probe_debug & 0x0040) {
@@ -996,6 +998,17 @@ static void *try(probectl *P, tryctl *T, PROBETYPE *aptr,
 #else
        ptr = aptr;
 #endif
+       if ((unsigned long)ptr < init_mm.start_code ||
+#if defined(AFS_AMD64_LINUX20_ENV)
+               (unsigned long)ptr > init_mm.brk)
+#else
+               (unsigned long)ptr > init_mm.end_data)
+#endif
+       {
+/*          printk("address 0x%lx (from 0x%lx %d) is out of range in check_table. wtf?\n", (unsigned long)x, (unsigned long)ptr, i);*/
+            continue;
+       }
+
        ret = check_table(P, ptr);
        if (ret >= 0) {
            /* return value is number of entries to skip */
@@ -1037,6 +1050,11 @@ static int check_harder(probectl *P, PROBETYPE *p)
     unsigned long ip1;
     int i, s;
 
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+    i = check_table_readable(P, p);
+    if (i >= 0) return 0;
+#endif
+
     /* Check zapped syscalls */
     for (i = 1; i < P->n_zapped_syscalls; i++) {
        if (p[_SS(P->zapped_syscalls[i])] != p[_SS(P->zapped_syscalls[0])]) {
@@ -1100,6 +1118,16 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
        printk("<7>osi_probe: %s                      try_harder\n", P->symbol);
 #endif
     for (offset = 0; offset < datalen; offset++, ptr++) {
+        if ((unsigned long)ptr < init_mm.start_code ||
+#if defined(AFS_AMD64_LINUX20_ENV)
+               (unsigned long)ptr > init_mm.brk)
+#else
+               (unsigned long)ptr > init_mm.end_data)
+#endif
+       {
+/*          printk("address 0x%lx (from 0x%lx %d) is out of range in check_table. wtf?\n", (unsigned long)x, (unsigned long)ptr, i);*/
+            continue;
+       }
        ret = check_table(P, ptr);
         if (ret >= 0) {
             /* return value is number of entries to skip */
@@ -1151,14 +1179,14 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
     if (probe_debug & 0x0001) {                                                              \
        printk("<7>osi_probe: %s = 0x%016lx %s\n", P->symbol, (unsigned long)(x), (m)); \
     }                                                                                      \
-    if ((x)) {                                                                             \
+    if ((x) && ((int)(x)) != -ENOENT) {                                                    \
        *method = (m);                                                                     \
         final_answer = (void *)(x);                                                        \
     }                                                                                      \
 } while (0)
 #else
 #define check_result(x,m) do {  \
-    if ((x)) {                  \
+    if ((x) && ((int)(x)) != -ENOENT) { \
         *method = (m);          \
         return (void *)(x);     \
     }                           \
@@ -1314,7 +1342,7 @@ static void *do_find_syscall_table(probectl *P, char **method)
 }
 
 #if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
-static int check_writable(unsigned long address) 
+static int check_access(unsigned long address, int mode) 
 { 
     pgd_t *pgd = pgd_offset_k(address);
 #ifdef PUD_SIZE
@@ -1339,10 +1367,34 @@ static int check_writable(unsigned long address)
        pte = (pte_t *)pmd;
     else
        pte = pte_offset_kernel(pmd, address);
-    if (pte_none(*pte) || !pte_present(*pte) || !pte_write(*pte))
+    if (pte_none(*pte) || !pte_present(*pte))
+       return 0;
+    if (mode && !pte_write(*pte))
        return 0;
     return 1;
 }
+
+static int check_table_readable(probectl *P, PROBETYPE *ptr)
+{
+    PROBETYPE *next_page;
+    int i = 0, delta;
+
+    while (i < _SS(NR_syscalls)) {
+       next_page = (PROBETYPE *)PAGE_ALIGN((unsigned long)(ptr+1));
+       delta = next_page - ptr;
+       if (!check_access((unsigned long)ptr, 0)) {
+#ifdef OSI_PROBE_DEBUG
+           if (probe_debug & 0x0080)
+               printk("<7>osi_probe: %s                      0x%016lx not readable; delta=0x%lx\n",
+                      P->symbol, (unsigned long)ptr, delta);
+#endif
+           return delta - 1;
+       }
+       ptr += delta;
+       i += delta;
+    }
+    return -1;
+}
 #endif
 
 void *osi_find_syscall_table(int which)
@@ -1370,7 +1422,7 @@ void *osi_find_syscall_table(int which)
     }
     printk("Found %s at 0x%lx (%s)\n", P->desc, (unsigned long)answer, method);
 #if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
-    if (!check_writable((unsigned long)answer)) {
+    if (!check_access((unsigned long)answer, 1)) {
        printk("Address 0x%lx is not writable.\n", (unsigned long)answer);
        printk("System call hooks will not be installed; proceeding anyway\n");
        return 0;
@@ -1398,4 +1450,10 @@ void osi_probe_exit(void) { }
 module_init(osi_probe_init);
 module_exit(osi_probe_exit);
 #endif
-#endif
+
+#else
+void *osi_find_syscall_table(int which)
+{
+    return 0;
+}
+#endif /* EXPORTED_INIT_MM */