From 085354f907969fb3ee33f236368f6e1dd4c74d62 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 Sep 2016 16:21:34 -0400 Subject: [PATCH 01/10] alpha: get rid of tail-zeroing in __copy_user() ... and adjust copy_from_user() accordingly Signed-off-by: Al Viro --- arch/alpha/include/asm/uaccess.h | 9 +++++---- arch/alpha/lib/copy_user.S | 16 +--------------- arch/alpha/lib/ev6-copy_user.S | 23 +---------------------- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h index 466e42e96bfaf2..94f587535deed9 100644 --- a/arch/alpha/include/asm/uaccess.h +++ b/arch/alpha/include/asm/uaccess.h @@ -396,11 +396,12 @@ copy_to_user(void __user *to, const void *from, long n) extern inline long copy_from_user(void *to, const void __user *from, long n) { + long res = n; if (likely(__access_ok((unsigned long)from, n, get_fs()))) - n = __copy_tofrom_user_nocheck(to, (__force void *)from, n); - else - memset(to, 0, n); - return n; + res = __copy_from_user_inatomic(to, from, n); + if (unlikely(res)) + memset(to + (n - res), 0, res); + return res; } extern void __do_clear_user(void); diff --git a/arch/alpha/lib/copy_user.S b/arch/alpha/lib/copy_user.S index 6f3fab9eb43444..ac9c3766ba8cb2 100644 --- a/arch/alpha/lib/copy_user.S +++ b/arch/alpha/lib/copy_user.S @@ -124,22 +124,8 @@ $65: bis $31,$31,$0 $41: $35: -$exitout: - ret $31,($28),1 - $exitin: - /* A stupid byte-by-byte zeroing of the rest of the output - buffer. This cures security holes by never leaving - random kernel data around to be copied elsewhere. */ - - mov $0,$1 -$101: - EXO ( ldq_u $2,0($6) ) - subq $1,1,$1 - mskbl $2,$6,$2 - EXO ( stq_u $2,0($6) ) - addq $6,1,$6 - bgt $1,$101 +$exitout: ret $31,($28),1 .end __copy_user diff --git a/arch/alpha/lib/ev6-copy_user.S b/arch/alpha/lib/ev6-copy_user.S index db42ffe9c350b6..c4d0689c3d26e7 100644 --- a/arch/alpha/lib/ev6-copy_user.S +++ b/arch/alpha/lib/ev6-copy_user.S @@ -227,33 +227,12 @@ $dirtyentry: bgt $0,$onebyteloop # U .. .. .. : U L U L $zerolength: +$exitin: $exitout: # Destination for exception recovery(?) nop # .. .. .. E nop # .. .. E .. nop # .. E .. .. ret $31,($28),1 # L0 .. .. .. : L U L U -$exitin: - - /* A stupid byte-by-byte zeroing of the rest of the output - buffer. This cures security holes by never leaving - random kernel data around to be copied elsewhere. */ - - nop - nop - nop - mov $0,$1 - -$101: - EXO ( stb $31,0($6) ) # L - subq $1,1,$1 # E - addq $6,1,$6 # E - bgt $1,$101 # U - - nop - nop - nop - ret $31,($28),1 # L0 - .end __copy_user From 7798bf2140ebcc36eafec6a4194fffd8d585d471 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 Sep 2016 16:31:04 -0400 Subject: [PATCH 02/10] arc: don't leak bits of kernel stack into coredump On faulting sigreturn we do get SIGSEGV, all right, but anything we'd put into pt_regs could end up in the coredump. And since __copy_from_user() never zeroed on arc, we'd better bugger off on its failure without copying random uninitialized bits of kernel stack into pt_regs... Cc: stable@vger.kernel.org Signed-off-by: Al Viro --- arch/arc/kernel/signal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index 6cb3736b6b8361..d347bbc086fed1 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -107,13 +107,13 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf) struct user_regs_struct uregs; err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set)); - if (!err) - set_current_blocked(&set); - err |= __copy_from_user(&uregs.scratch, &(sf->uc.uc_mcontext.regs.scratch), sizeof(sf->uc.uc_mcontext.regs.scratch)); + if (err) + return err; + set_current_blocked(&set); regs->bta = uregs.scratch.bta; regs->lp_start = uregs.scratch.lp_start; regs->lp_end = uregs.scratch.lp_end; @@ -138,7 +138,7 @@ static int restore_usr_regs(struct pt_regs *regs, struct rt_sigframe __user *sf) regs->r0 = uregs.scratch.r0; regs->sp = uregs.scratch.sp; - return err; + return 0; } static inline int is_do_ss_needed(unsigned int magic) From 91344493b7da9487a8dbc1109d7486e51c8e5235 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 Sep 2016 16:44:03 -0400 Subject: [PATCH 03/10] arm: don't zero in __copy_from_user_inatomic()/__copy_from_user() adjust copy_from_user(), obviously Signed-off-by: Al Viro --- arch/arm/include/asm/uaccess.h | 11 ++++++----- arch/arm/lib/copy_from_user.S | 9 +++------ 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h index a93c0f99acf776..1f59ea051bab81 100644 --- a/arch/arm/include/asm/uaccess.h +++ b/arch/arm/include/asm/uaccess.h @@ -533,11 +533,12 @@ __clear_user(void __user *addr, unsigned long n) static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) { - if (access_ok(VERIFY_READ, from, n)) - n = __copy_from_user(to, from, n); - else /* security hole - plug it */ - memset(to, 0, n); - return n; + unsigned long res = n; + if (likely(access_ok(VERIFY_READ, from, n))) + res = __copy_from_user(to, from, n); + if (unlikely(res)) + memset(to + (n - res), 0, res); + return res; } static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S index 1512bebfbf1b18..7a4b060490012d 100644 --- a/arch/arm/lib/copy_from_user.S +++ b/arch/arm/lib/copy_from_user.S @@ -98,12 +98,9 @@ ENDPROC(arm_copy_from_user) .pushsection .fixup,"ax" .align 0 copy_abort_preamble - ldmfd sp!, {r1, r2} - sub r3, r0, r1 - rsb r1, r3, r2 - str r1, [sp] - bl __memzero - ldr r0, [sp], #4 + ldmfd sp!, {r1, r2, r3} + sub r0, r0, r1 + rsb r0, r0, r2 copy_abort_end .popsection From 4855bd255f9fb429d296dc3c620f1b563d20353e Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 Sep 2016 16:50:00 -0400 Subject: [PATCH 04/10] arm64: don't zero in __copy_from_user{,_inatomic} Signed-off-by: Al Viro --- arch/arm64/include/asm/uaccess.h | 10 ++++++---- arch/arm64/lib/copy_from_user.S | 7 +------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h index c47257c91b77e3..bcaf6fba1b65bd 100644 --- a/arch/arm64/include/asm/uaccess.h +++ b/arch/arm64/include/asm/uaccess.h @@ -278,14 +278,16 @@ static inline unsigned long __must_check __copy_to_user(void __user *to, const v static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) { + unsigned long res = n; kasan_check_write(to, n); if (access_ok(VERIFY_READ, from, n)) { check_object_size(to, n, false); - n = __arch_copy_from_user(to, from, n); - } else /* security hole - plug it */ - memset(to, 0, n); - return n; + res = __arch_copy_from_user(to, from, n); + } + if (unlikely(res)) + memset(to + (n - res), 0, res); + return res; } static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S index 0b90497d4424c5..4fd67ea03bb054 100644 --- a/arch/arm64/lib/copy_from_user.S +++ b/arch/arm64/lib/copy_from_user.S @@ -79,11 +79,6 @@ ENDPROC(__arch_copy_from_user) .section .fixup,"ax" .align 2 -9998: - sub x0, end, dst -9999: - strb wzr, [dst], #1 // zero remaining buffer space - cmp dst, end - b.lo 9999b +9998: sub x0, end, dst // bytes not copied ret .previous From b065444286bed7ec49ee81c593a46f3031fbfc83 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Sat, 10 Sep 2016 18:53:30 -0400 Subject: [PATCH 05/10] blackfin: no access_ok() for __copy_{to,from}_user() callers have checked that already Signed-off-by: Al Viro --- arch/blackfin/include/asm/uaccess.h | 32 ++++++++++++++++++----------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/arch/blackfin/include/asm/uaccess.h b/arch/blackfin/include/asm/uaccess.h index 0a2a70096d8b2d..0eff88aa6d6ae6 100644 --- a/arch/blackfin/include/asm/uaccess.h +++ b/arch/blackfin/include/asm/uaccess.h @@ -163,18 +163,29 @@ static inline int bad_user_access_length(void) : "a" (__ptr(ptr))); \ }) -#define __copy_from_user(to, from, n) copy_from_user(to, from, n) -#define __copy_to_user(to, from, n) copy_to_user(to, from, n) #define __copy_to_user_inatomic __copy_to_user #define __copy_from_user_inatomic __copy_from_user +static inline unsigned long __must_check +__copy_from_user(void *to, const void __user *from, unsigned long n) +{ + memcpy(to, (const void __force *)from, n); + return 0; +} + +static inline unsigned long __must_check +__copy_to_user(void __user *to, const void *from, unsigned long n) +{ + memcpy((void __force *)to, from, n); + SSYNC(); + return 0; +} + static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n) { - if (likely(access_ok(VERIFY_READ, from, n))) { - memcpy(to, (const void __force *)from, n); - return 0; - } + if (likely(access_ok(VERIFY_READ, from, n))) + return __copy_from_user(to, from, n); memset(to, 0, n); return n; } @@ -182,12 +193,9 @@ copy_from_user(void *to, const void __user *from, unsigned long n) static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n) { - if (access_ok(VERIFY_WRITE, to, n)) - memcpy((void __force *)to, from, n); - else - return n; - SSYNC(); - return 0; + if (likely(access_ok(VERIFY_WRITE, to, n))) + return __copy_to_user(to, from, n); + return n; } /* From ffecee4f2442bb8cb6b34c3335fef4eb50c22fdd Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Sat, 8 Oct 2016 11:18:07 +0200 Subject: [PATCH 06/10] iov_iter: kernel-doc import_iovec() and rw_copy_check_uvector() Both import_iovec() and rw_copy_check_uvector() take an array (typically small and on-stack) which is used to hold an iovec array copy from userspace. This is to avoid an expensive memory allocation in the fast path (i.e. few iovec elements). The caller may have to check whether these functions actually used the provided buffer or allocated a new one -- but this differs between the too. Let's just add a kernel doc to clarify what the semantics are for each function. Signed-off-by: Vegard Nossum Signed-off-by: Al Viro --- fs/read_write.c | 29 +++++++++++++++++++++++++++++ lib/iov_iter.c | 22 ++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/fs/read_write.c b/fs/read_write.c index 66215a7b17cf14..190e0d362581a9 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -730,6 +730,35 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter, /* A write operation does a read from user space and vice versa */ #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) +/** + * rw_copy_check_uvector() - Copy an array of &struct iovec from userspace + * into the kernel and check that it is valid. + * + * @type: One of %CHECK_IOVEC_ONLY, %READ, or %WRITE. + * @uvector: Pointer to the userspace array. + * @nr_segs: Number of elements in userspace array. + * @fast_segs: Number of elements in @fast_pointer. + * @fast_pointer: Pointer to (usually small on-stack) kernel array. + * @ret_pointer: (output parameter) Pointer to a variable that will point to + * either @fast_pointer, a newly allocated kernel array, or NULL, + * depending on which array was used. + * + * This function copies an array of &struct iovec of @nr_segs from + * userspace into the kernel and checks that each element is valid (e.g. + * it does not point to a kernel address or cause overflow by being too + * large, etc.). + * + * As an optimization, the caller may provide a pointer to a small + * on-stack array in @fast_pointer, typically %UIO_FASTIOV elements long + * (the size of this array, or 0 if unused, should be given in @fast_segs). + * + * @ret_pointer will always point to the array that was used, so the + * caller must take care not to call kfree() on it e.g. in case the + * @fast_pointer array was used and it was allocated on the stack. + * + * Return: The total number of bytes covered by the iovec array on success + * or a negative error code on error. + */ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, unsigned long nr_segs, unsigned long fast_segs, struct iovec *fast_pointer, diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 7312e7784611d7..f0c7f1481baeef 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1139,6 +1139,28 @@ const void *dup_iter(struct iov_iter *new, struct iov_iter *old, gfp_t flags) } EXPORT_SYMBOL(dup_iter); +/** + * import_iovec() - Copy an array of &struct iovec from userspace + * into the kernel, check that it is valid, and initialize a new + * &struct iov_iter iterator to access it. + * + * @type: One of %READ or %WRITE. + * @uvector: Pointer to the userspace array. + * @nr_segs: Number of elements in userspace array. + * @fast_segs: Number of elements in @iov. + * @iov: (input and output parameter) Pointer to pointer to (usually small + * on-stack) kernel array. + * @i: Pointer to iterator that will be initialized on success. + * + * If the array pointed to by *@iov is large enough to hold all @nr_segs, + * then this function places %NULL in *@iov on return. Otherwise, a new + * array will be allocated and the result placed in *@iov. This means that + * the caller may call kfree() on *@iov regardless of whether the small + * on-stack array was used or not (and regardless of whether this function + * returns an error or not). + * + * Return: 0 on success or negative error code on error. + */ int import_iovec(int type, const struct iovec __user * uvector, unsigned nr_segs, unsigned fast_segs, struct iovec **iov, struct iov_iter *i) From 655042cc1406fcec20aa7ffd7d790ada18ac5211 Mon Sep 17 00:00:00 2001 From: Vivek Goyal Date: Fri, 14 Oct 2016 03:03:36 +0200 Subject: [PATCH 07/10] overlayfs: Fix setting IOP_XATTR flag ovl_fill_super calls ovl_new_inode to create a root inode for the new superblock before initializing sb->s_xattr. This wrongly causes IOP_XATTR to be cleared in i_opflags of the new inode, causing SELinux to log the following message: SELinux: (dev overlay, type overlay) has no xattr support Fix this by initializing sb->s_xattr and similar fields before calling ovl_new_inode. Signed-off-by: Andreas Gruenbacher Signed-off-by: Al Viro --- fs/overlayfs/super.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 7e3f0127fc1aa7..0ffc8da1aa3f4e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1292,6 +1292,12 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) if (!oe) goto out_put_cred; + sb->s_magic = OVERLAYFS_SUPER_MAGIC; + sb->s_op = &ovl_super_operations; + sb->s_xattr = ovl_xattr_handlers; + sb->s_fs_info = ufs; + sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK; + root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR)); if (!root_dentry) goto out_free_oe; @@ -1315,12 +1321,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) ovl_inode_init(d_inode(root_dentry), realinode, !!upperpath.dentry); ovl_copyattr(realinode, d_inode(root_dentry)); - sb->s_magic = OVERLAYFS_SUPER_MAGIC; - sb->s_op = &ovl_super_operations; - sb->s_xattr = ovl_xattr_handlers; sb->s_root = root_dentry; - sb->s_fs_info = ufs; - sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK; return 0; From 89f39af129382a40d7cd1f6914617282cfeee28e Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 26 Sep 2016 18:07:48 +0200 Subject: [PATCH 08/10] fs/super.c: fix race between freeze_super() and thaw_super() Change thaw_super() to check frozen != SB_FREEZE_COMPLETE rather than frozen == SB_UNFROZEN, otherwise it can race with freeze_super() which drops sb->s_umount after SB_FREEZE_WRITE to preserve the lock ordering. In this case thaw_super() will wrongly call s_op->unfreeze_fs() before it was actually frozen, and call sb_freeze_unlock() which leads to the unbalanced percpu_up_write(). Unfortunately lockdep can't detect this, so this triggers misc BUG_ON()'s in kernel/rcu/sync.c. Reported-and-tested-by: Nikolay Borisov Signed-off-by: Oleg Nesterov Cc: stable@vger.kernel.org Signed-off-by: Al Viro --- fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/super.c b/fs/super.c index c2ff475c1711f3..47d11e0462d0e8 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1379,8 +1379,8 @@ int freeze_super(struct super_block *sb) } } /* - * This is just for debugging purposes so that fs can warn if it - * sees write activity when frozen is set to SB_FREEZE_COMPLETE. + * For debugging purposes so that fs can warn if it sees write activity + * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super(). */ sb->s_writers.frozen = SB_FREEZE_COMPLETE; up_write(&sb->s_umount); @@ -1399,7 +1399,7 @@ int thaw_super(struct super_block *sb) int error; down_write(&sb->s_umount); - if (sb->s_writers.frozen == SB_UNFROZEN) { + if (sb->s_writers.frozen != SB_FREEZE_COMPLETE) { up_write(&sb->s_umount); return -EINVAL; } From f1a9622037cd370460fd06bb7e28d0f01ceb8ef1 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Mon, 26 Sep 2016 18:55:25 +0200 Subject: [PATCH 09/10] fs/super.c: don't fool lockdep in freeze_super() and thaw_super() paths sb_wait_write()->percpu_rwsem_release() fools lockdep to avoid the false-positives. Now that xfs was fixed by Dave's commit dbad7c993053 ("xfs: stop holding ILOCK over filldir callbacks") we can remove it and change freeze_super() and thaw_super() to run with s_writers.rw_sem locks held; we add two trivial helpers for that, lockdep_sb_freeze_release() and lockdep_sb_freeze_acquire(). xfstests-dev/check `grep -il freeze tests/*/???` does not trigger any warning from lockdep. Signed-off-by: Oleg Nesterov Signed-off-by: Al Viro --- fs/super.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/fs/super.c b/fs/super.c index 47d11e0462d0e8..c183835566c19c 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1269,25 +1269,34 @@ EXPORT_SYMBOL(__sb_start_write); static void sb_wait_write(struct super_block *sb, int level) { percpu_down_write(sb->s_writers.rw_sem + level-1); - /* - * We are going to return to userspace and forget about this lock, the - * ownership goes to the caller of thaw_super() which does unlock. - * - * FIXME: we should do this before return from freeze_super() after we - * called sync_filesystem(sb) and s_op->freeze_fs(sb), and thaw_super() - * should re-acquire these locks before s_op->unfreeze_fs(sb). However - * this leads to lockdep false-positives, so currently we do the early - * release right after acquire. - */ - percpu_rwsem_release(sb->s_writers.rw_sem + level-1, 0, _THIS_IP_); } -static void sb_freeze_unlock(struct super_block *sb) +/* + * We are going to return to userspace and forget about these locks, the + * ownership goes to the caller of thaw_super() which does unlock(). + */ +static void lockdep_sb_freeze_release(struct super_block *sb) +{ + int level; + + for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--) + percpu_rwsem_release(sb->s_writers.rw_sem + level, 0, _THIS_IP_); +} + +/* + * Tell lockdep we are holding these locks before we call ->unfreeze_fs(sb). + */ +static void lockdep_sb_freeze_acquire(struct super_block *sb) { int level; for (level = 0; level < SB_FREEZE_LEVELS; ++level) percpu_rwsem_acquire(sb->s_writers.rw_sem + level, 0, _THIS_IP_); +} + +static void sb_freeze_unlock(struct super_block *sb) +{ + int level; for (level = SB_FREEZE_LEVELS - 1; level >= 0; level--) percpu_up_write(sb->s_writers.rw_sem + level); @@ -1383,6 +1392,7 @@ int freeze_super(struct super_block *sb) * when frozen is set to SB_FREEZE_COMPLETE, and for thaw_super(). */ sb->s_writers.frozen = SB_FREEZE_COMPLETE; + lockdep_sb_freeze_release(sb); up_write(&sb->s_umount); return 0; } @@ -1409,11 +1419,14 @@ int thaw_super(struct super_block *sb) goto out; } + lockdep_sb_freeze_acquire(sb); + if (sb->s_op->unfreeze_fs) { error = sb->s_op->unfreeze_fs(sb); if (error) { printk(KERN_ERR "VFS:Filesystem thaw failed\n"); + lockdep_sb_freeze_release(sb); up_write(&sb->s_umount); return error; } From 7041c57709efdc1e31aaff663cfe17f0b21f4743 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 12 Oct 2016 13:42:23 -0700 Subject: [PATCH 10/10] score: traps: Add missing include file to fix build error score images fail to build as follows. arch/score/kernel/traps.c: In function 'show_stack': arch/score/kernel/traps.c:55:3: error: implicit declaration of function '__get_user' __get_user() is declared in asm/uaccess.h, which was previously included through asm/module.h. Cc: Al Viro Fixes: 88dd4a748da7 ("score: separate extable.h, switch module.h to it") Signed-off-by: Guenter Roeck Signed-off-by: Al Viro --- arch/score/kernel/traps.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/score/kernel/traps.c b/arch/score/kernel/traps.c index 1517a7dcd6d92a..5cea1e750ceca0 100644 --- a/arch/score/kernel/traps.c +++ b/arch/score/kernel/traps.c @@ -29,6 +29,7 @@ #include #include #include +#include unsigned long exception_handlers[32];