tasklist-lock-redux-20060922
[openafs.git] / src / afs / LINUX / osi_probe.c
index 2c2de48..20acb39 100644 (file)
@@ -64,6 +64,9 @@
 #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>
 
 /* Allow the user to specify sys_call_table addresses */
 static unsigned long sys_call_table_addr[4] = { 0,0,0,0 };
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#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");
@@ -123,7 +126,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;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#ifdef module_param
 module_param(probe_carefully, int, 0);
 #else
 MODULE_PARM(probe_carefully, "i");
@@ -131,7 +134,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 };
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#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");
@@ -150,7 +153,7 @@ MODULE_PARM_DESC(probe_ignore_syscalls, "Syscalls to ignore in table checks");
  * 0x0040 - automatically ignore setgroups and afs_syscall
  */
 static int probe_debug = 0x41;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#ifdef module_param
 module_param(probe_debug, int, 0);
 #else
 MODULE_PARM(probe_debug, "i");
@@ -158,7 +161,7 @@ MODULE_PARM(probe_debug, "i");
 MODULE_PARM_DESC(probe_debug, "Debugging level");
 
 static unsigned long probe_debug_addr[4] = { 0,0,0,0 };
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#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");
@@ -166,7 +169,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;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#ifdef module_param
 module_param(probe_debug_range, long, 0);
 #else
 MODULE_PARM(probe_debug_range, "l");
@@ -174,7 +177,7 @@ MODULE_PARM(probe_debug_range, "l");
 MODULE_PARM_DESC(probe_debug_range, "Debug range length");
 
 static unsigned long probe_debug_tag = 0;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
+#ifdef module_param
 module_param(probe_debug_tag, long, 0);
 #else
 MODULE_PARM(probe_debug_tag, "l");
@@ -228,7 +231,9 @@ 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));
@@ -236,8 +241,11 @@ extern asmlinkage long sys_wait4(pid_t, int *, int, struct rusage *) __attribute
 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));
+extern rwlock_t tasklist_lock __attribute__((weak));
 
 
 /* Structures used to control probing.  We put all the details of which
@@ -272,6 +280,11 @@ typedef struct {
     unsigned long try_base_mask;    /* base address bits to force to zero */
     unsigned long try_length;       /* default length for scan */
 
+    unsigned long alt_try_sect_sym;     /* symbol in section to try scanning */
+    unsigned long alt_try_base;         /* default base address for scan */
+    unsigned long alt_try_base_mask;    /* base address bits to force to zero */
+    unsigned long alt_try_length;       /* default length for scan */
+
     int n_zapped_syscalls;          /* number of unimplemented system calls */
     int *zapped_syscalls;           /* list of unimplemented system calls */
 
@@ -292,14 +305,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 }
 };
 
@@ -533,6 +552,15 @@ static probectl main_probe = {
     16384,
 #endif
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size,
+    0x3ffff,
+    0x30000,
+#else
+    0, 0, 0, 0,
+#endif
+
     /* number and list of unimplemented system calls */
     ((sizeof(main_zapped_syscalls)/sizeof(main_zapped_syscalls[0])) - 1),
     main_zapped_syscalls,
@@ -573,8 +601,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 }
 };
 
@@ -620,6 +650,16 @@ static probectl ia32_probe = {
     0,
     (0x180000 / sizeof(unsigned long *)),
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size,
+    0x3ffff,
+    0x30000,
+#else
+    0, 0, 0, 0,
+#endif
+
+
     /* number and list of unimplemented system calls */
     ((sizeof(ia32_zapped_syscalls)/sizeof(ia32_zapped_syscalls[0])) - 1),
     ia32_zapped_syscalls,
@@ -749,6 +789,15 @@ static probectl sct32_probe = {
     16384,
 #endif
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size,
+    0x3ffff,
+    0x30000,
+#else
+    0, 0, 0, 0,
+#endif
+
     /* number and list of unimplemented system calls */
     ((sizeof(sct32_zapped_syscalls)/sizeof(sct32_zapped_syscalls[0])) - 1),
     sct32_zapped_syscalls,
@@ -835,6 +884,15 @@ static probectl emu_probe = {
     0xfffff,
     0x20000,
 
+#ifdef AFS_LINUX26_ENV
+    (unsigned long)scsi_command_size,
+    (unsigned long)scsi_command_size,
+    0x3ffff,
+    0x30000,
+#else
+    0, 0, 0, 0,
+#endif
+
     /* number and list of unimplemented system calls */
     ((sizeof(emu_zapped_syscalls)/sizeof(emu_zapped_syscalls[0])) - 1),
     emu_zapped_syscalls,
@@ -1119,6 +1177,66 @@ static void *try_harder(probectl *P, PROBETYPE *ptr, unsigned long datalen)
     }                           \
 } while (0)
 #endif
+static void *scan_for_syscall_table(probectl *P, PROBETYPE *B, unsigned long L)
+{
+    tryctl *T;
+    void *answer;
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+    void *answer2;
+#endif
+#ifdef OSI_PROBE_DEBUG
+    void *final_answer = 0;
+#endif
+#ifdef OSI_PROBE_DEBUG
+    if (probe_debug & 0x0007)
+       printk("<7>osi_probe: %s                      base=0x%lx, len=0x%lx\n",
+              P->symbol, (unsigned long)B, L);
+    if (probe_debug & 0x0009) {
+       printk("<7>osi_probe: %s                      ktxt_lower_bound=0x%lx\n",
+              P->symbol, ktxt_lower_bound);
+       printk("<7>osi_probe: %s                      NR_syscalls=%d\n",
+              P->symbol, NR_syscalls);
+    }
+#endif
+
+    for (T = P->trylist; T->name; T++) {
+       answer = try(P, T, B, L);
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+       answer2 = try(P, T, (PROBETYPE *)(2 + (void *)B), L);
+#ifdef OSI_PROBE_DEBUG
+       if (probe_debug & 0x0003) {
+           printk("<7>osi_probe: %s = 0x%016lx %s (even)\n",
+                  P->symbol, (unsigned long)(answer), T->name);
+           printk("<7>osi_probe: %s = 0x%016lx %s (odd)\n",
+                  P->symbol, (unsigned long)(answer2), T->name);
+       }
+#endif
+       if (answer && answer2) answer = 0;
+       else if (answer2) answer = answer2;
+#endif
+       if (answer)
+           return answer;
+    }
+
+    /* XXX more checks here */
+
+    answer = try_harder(P, B, L);
+#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
+    answer2 = try_harder(P, (PROBETYPE *)(2 + (void *)B), L);
+#ifdef OSI_PROBE_DEBUG
+    if (probe_debug & 0x0005) {
+       printk("<7>osi_probe: %s = 0x%016lx pattern scan (even)\n",
+              P->symbol, (unsigned long)(answer));
+       printk("<7>osi_probe: %s = 0x%016lx pattern scan (odd)\n",
+              P->symbol, (unsigned long)(answer2));
+    }
+#endif
+    if (answer && answer2) answer = 0;
+    else if (answer2) answer = answer2;
+#endif
+    return answer;
+}
+
 static void *do_find_syscall_table(probectl *P, char **method)
 {
 #ifdef OSI_PROBE_KALLSYMS
@@ -1131,11 +1249,7 @@ static void *do_find_syscall_table(probectl *P, char **method)
 #endif
     PROBETYPE *B;
     unsigned long L;
-    tryctl *T;
     void *answer;
-#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
-    void *answer2;
-#endif
 #ifdef OSI_PROBE_DEBUG
     void *final_answer = 0;
 #endif
@@ -1183,61 +1297,66 @@ static void *do_find_syscall_table(probectl *P, char **method)
        }
     }
 #endif
-
-#ifdef OSI_PROBE_DEBUG
-    if (probe_debug & 0x0007)
-       printk("<7>osi_probe: %s                      base=0x%lx, len=0x%lx\n",
-              P->symbol, (unsigned long)B, L);
-    if (probe_debug & 0x0009) {
-       printk("<7>osi_probe: %s                      ktxt_lower_bound=0x%lx\n",
-              P->symbol, ktxt_lower_bound);
-       printk("<7>osi_probe: %s                      NR_syscalls=%d\n",
-              P->symbol, NR_syscalls);
-    }
-#endif
-
-    for (T = P->trylist; T->name; T++) {
-       answer = try(P, T, B, L);
-#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
-       answer2 = try(P, T, (PROBETYPE *)(2 + (void *)B), L);
-#ifdef OSI_PROBE_DEBUG
-       if (probe_debug & 0x0003) {
-           printk("<7>osi_probe: %s = 0x%016lx %s (even)\n",
-                  P->symbol, (unsigned long)(answer), T->name);
-           printk("<7>osi_probe: %s = 0x%016lx %s (odd)\n",
-                  P->symbol, (unsigned long)(answer2), T->name);
+   
+    answer = scan_for_syscall_table(P, B, L);
+    check_result(answer, "pattern scan");
+    B = (PROBETYPE *)((P->alt_try_base) & ~(P->alt_try_base_mask));
+    L = P->alt_try_length;
+    /* Now, see if the kernel will tell us something better than the default */
+#ifdef OSI_PROBE_KALLSYMS
+    if (kallsyms_address_to_symbol && P->alt_try_sect_sym) {
+       ret = kallsyms_address_to_symbol(P->alt_try_sect_sym,
+                                        &mod_name, &mod_start, &mod_end,
+                                        &sec_name, &sec_start, &sec_end,
+                                        &sym_name, &sym_start, &sym_end);
+       if (ret) {
+           B = (PROBETYPE *)sec_start;
+           L = (sec_end - sec_start) / sizeof(unsigned long);
        }
+    }
 #endif
-       if (answer && answer2) answer = 0;
-       else if (answer2) answer = answer2;
-#endif
-        check_result(answer, T->name);
+    if (B && L) {
+       answer = scan_for_syscall_table(P, B, L);
+       check_result(answer, "pattern scan");
     }
-
-    /* XXX more checks here */
-
-    answer = try_harder(P, B, L);
-#if defined(AFS_S390_LINUX20_ENV) || defined(AFS_S390X_LINUX20_ENV)
-    answer2 = try_harder(P, (PROBETYPE *)(2 + (void *)B), L);
 #ifdef OSI_PROBE_DEBUG
-    if (probe_debug & 0x0005) {
-       printk("<7>osi_probe: %s = 0x%016lx pattern scan (even)\n",
-              P->symbol, (unsigned long)(answer));
-       printk("<7>osi_probe: %s = 0x%016lx pattern scan (odd)\n",
-              P->symbol, (unsigned long)(answer2));
-    }
+    return final_answer;
+#else
+    return 0;
 #endif
-    if (answer && answer2) answer = 0;
-    else if (answer2) answer = answer2;
+}
+
+#if defined(AFS_I386_LINUX26_ENV) || defined(AFS_AMD64_LINUX26_ENV)
+static int check_writable(unsigned long address) 
+{ 
+    pgd_t *pgd = pgd_offset_k(address);
+#ifdef PUD_SIZE
+    pud_t *pud;
 #endif
-    check_result(answer, "pattern scan");
+    pmd_t *pmd;
+    pte_t *pte;
 
-#ifdef OSI_PROBE_DEBUG
-    return final_answer;
+    if (pgd_none(*pgd))
+       return 0;
+#ifdef PUD_SIZE
+    pud = pud_offset(pgd, address);
+    if (pud_none(*pud))
+       return 0;
+    pmd = pmd_offset(pud, address);
 #else
-    return 0;
+    pmd = pmd_offset(pgd, address);
 #endif
+    if (pmd_none(*pmd))
+       return 0;
+    if (pmd_large(*pmd))
+       pte = (pte_t *)pmd;
+    else
+       pte = pte_offset_kernel(pmd, address);
+    if (pte_none(*pte) || !pte_present(*pte) || !pte_write(*pte))
+       return 0;
+    return 1;
 }
+#endif
 
 void *osi_find_syscall_table(int which)
 {
@@ -1263,6 +1382,13 @@ void *osi_find_syscall_table(int which)
        return 0;
     }
     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)) {
+       printk("Address 0x%lx is not writable.\n", (unsigned long)answer);
+       printk("System call hooks will not be installed; proceeding anyway\n");
+       return 0;
+    }
+#endif
     return answer;
 }