DragonFlyBSD Kernel Audit
DF-2984 / fix.diff
← back to finding ↓ download raw
--- a/sys/kern/kern_syscalls.c
+++ b/sys/kern/kern_syscalls.c
@@ -72,9 +72,19 @@
 int
 syscall_deregister(int *offset, struct sysent *old_sysent)
 {
-       if (*offset)
-               sysent[*offset] = *old_sysent;
-       return 0;
+	/*
+	 * *offset and *old_sysent are only meaningful after a successful
+	 * syscall_register().  MOD_UNLOAD can nevertheless reach us with
+	 * never-initialized values (MOD_LOAD rollback via
+	 * module_register_init() -> module_unload(), kern_module.c:110-116),
+	 * so the index MUST be re-validated before writing the shared
+	 * dispatch table.  Note NO_SYSCALL is -1, which the old
+	 * "if (*offset)" test happily passed.
+	 */
+	if (*offset <= 0 || *offset >= SYS_MAXSYSCALL)
+		return EINVAL;
+	sysent[*offset] = *old_sysent;
+	return 0;
 }
 
 int
@@ -90,10 +100,16 @@
                                         &data->old_sysent);
                if (error)
                        return error;
+	       data->registered = 1;
 	       ms.intval = *data->offset;
 	       module_setspecific(mod, &ms);
                if (data->chainevh)
                        error = data->chainevh(mod, what, data->chainarg);
+               if (error == 0)
+                       return 0;
+	       /* init failed: roll our registration back before failing */
+	       syscall_deregister(data->offset, &data->old_sysent);
+	       data->registered = 0;
                return error;
 
        case MOD_UNLOAD :
@@ -102,7 +118,11 @@
                        if (error)
                                return error;
                }
-               error = syscall_deregister(data->offset, &data->old_sysent);
+               if (data->registered) {
+                       error = syscall_deregister(data->offset,
+                                                  &data->old_sysent);
+                       data->registered = 0;
+               }
                return error;
        }
 
--- a/sys/sys/sysent.h
+++ b/sys/sys/sysent.h
@@ -87,15 +87,16 @@
 
 struct syscall_module_data {
        int     (*chainevh)(struct module *, int, void *); /* next handler */
-       void    *chainarg;      /* arg for next event handler */
+       void    *chainarg;      /* arg for next event */
        int     *offset;         /* offset into sysent */
        struct  sysent *new_sysent; /* new sysent */
        struct  sysent old_sysent; /* old sysent */
+       int     registered;      /* syscall_register() succeeded */
 };
 
 #define SYSCALL_MODULE(name, offset, new_sysent, evh, arg)     \
 static struct syscall_module_data name##_syscall_mod = {       \
-       evh, arg, offset, new_sysent, { 0, NULL }               \
+       evh, arg, offset, new_sysent, { 0, 0, NULL, NULL }, 0   \
 };                                                             \
                                                                \
 static moduledata_t name##_mod = {                             \