Skip to content

Implement workaround for HARDENED_USERCOPY #18

@jpenalbae

Description

@jpenalbae

Latest kernels implement a new hardening mechanism HARDENED_USERCOPY. When enabled every call to copy_from_user() and copy_to_user() calls to __check_object_size() prior performing the copy operation. This makes several checks as if the orig/destination is a valid heap or stack area, it is not a kernel text area, etc....

For example, when reading from kernel text area the responsible process gets killed and we get this output:

[ 1045.117123] ------------[ cut here ]------------
[ 1045.117222] kernel BUG at /build/linux-v2K05A/linux-4.8.5/mm/usercopy.c:75!
[ 1045.117361] invalid opcode: 0000 [#1] SMP
[ 1045.117481] Modules linked in: r2kmod(OE) vboxsf(OE) vboxvideo(OE) vboxguest(OE) ttm drm_kms_helper drm ppdev sg i2c_piix4 evdev serio_raw battery parport_pc pcspkr parport acpi_cpufreq sb_edac video tpm_tis tpm_tis_core button tpm ac edac_core intel_powerclamp crct10dif_pclmul crc32_pclmul ghash_clmulni_intel loop ip_tables x_tables autofs4 ext4 crc16 jbd2 crc32c_generic fscrypto ecb mbcache sr_mod cdrom sd_mod ata_generic crc32c_intel ata_piix ahci libahci e1000 libata mptspi aesni_intel scsi_transport_spi mptscsih psmouse mptbase aes_x86_64 glue_helper lrw scsi_mod gf128mul ablk_helper cryptd fjes
[ 1045.119039] CPU: 2 PID: 16995 Comm: r2 Tainted: G           OE   4.8.0-1-amd64 #1 Debian 4.8.5-1
[ 1045.119325] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 1045.119603] task: ffff96dbd533e140 task.stack: ffff96dbd5378000
[ 1045.119954] RIP: 0010:[<ffffffffa31fe489>]  [<ffffffffa31fe489>] __check_object_size+0x69/0x1e8
[ 1045.120272] RSP: 0018:ffff96dbd537bdb8  EFLAGS: 00010286
[ 1045.120602] RAX: 0000000000000063 RBX: ffffffffa35ed270 RCX: 0000000000000000
[ 1045.120704] RDX: 0000000000000000 RSI: ffff96dbdfc8dd48 RDI: ffff96dbdfc8dd48
[ 1045.120704] RBP: 0000000000000100 R08: 0000000000000000 R09: 0000000000000000
[ 1045.120704] R10: 0000000000000000 R11: ffffffffffffffff R12: 0000000000000001
[ 1045.120704] R13: ffffffffa35ed370 R14: ffff96dbd5feb080 R15: 0000000000000000
[ 1045.120704] FS:  00007f649c6ef700(0000) GS:ffff96dbdfc80000(0000) knlGS:0000000000000000
[ 1045.120704] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1045.120704] CR2: 00007ff524d52000 CR3: 000000021225d000 CR4: 00000000000406e0
[ 1045.120704] Stack:
[ 1045.120704]  0000000000000100 ffffffffa35ed270 0000561871e5bd10 ffffffffffffffea
[ 1045.120704]  ffffffffc05db2b5 ffff96dbd1984a38 0000000000000246 ffffffffa3408d01
[ 1045.120704]  ffff96dbd19848d8 ffff96dbd2764000 00000001d533e140 00000001d1984a30
[ 1045.120704] Call Trace:
[ 1045.120704]  [<ffffffffa35ed270>] ? __mutex_lock_slowpath+0x130/0x130
[ 1045.120704]  [<ffffffffc05db2b5>] ? io_ioctl+0xd5/0xc70 [r2kmod]
[ 1045.120704]  [<ffffffffa3408d01>] ? n_tty_write+0x261/0x490
[ 1045.120704]  [<ffffffffa3215dbf>] ? do_vfs_ioctl+0x9f/0x5f0
[ 1045.120704]  [<ffffffffa320232f>] ? vfs_write+0x14f/0x1a0
[ 1045.120704]  [<ffffffffa32021c8>] ? vfs_read+0x118/0x130
[ 1045.120704]  [<ffffffffa3216384>] ? SyS_ioctl+0x74/0x80
[ 1045.120704]  [<ffffffffa35ef8f6>] ? system_call_fast_compare_end+0xc/0x96
[ 1045.120704] Code: 48 0f 44 d1 48 c7 c6 5d b9 7f a3 48 c7 c1 ce 54 80 a3 48 0f 45 f1 49 89 e9 49 89 c0 48 89 d9 48 c7 c7 c8 24 80 a3 e8 a5 c5 f7 ff <0f> 0b 48 83 ff 10 0f 86 57 01 00 00 e8 36 68 fc ff 85 c0 0f 85
[ 1045.120704] RIP  [<ffffffffa31fe489>] __check_object_size+0x69/0x1e8
[ 1045.120704]  RSP <ffff96dbd537bdb8>
[ 1045.120704] fbcon_switch: detected unhandled fb_set_par error, error code -16
[ 1045.120704] fbcon_switch: detected unhandled fb_set_par error, error code -16
[ 1045.134036] ---[ end trace 492eb760cc8f1369 ]---

As a workaround we could create a new a #ifdef CONFIG_HARDENED_USERCOPY, and in case it has been enabled, then there are two options, reimplement the copy operations by ourselves or directly call to the appropriate copy operation per arch:

  • x86: __copy_from_user_ll_nozero(), __copy_to_user_ll()
  • x86_64: copy_user_generic()
  • arm64: __arch_copy_from_user(), __arch_copy_to_user()
  • arm: arm_copy_to_user(), arm_copy_from_user()

This might imply some performance hit for example on x86_64 due to always using copy_user_generic() instead of the optimized copy operations for each size. This might also require extra checks when accessing memory or pinning the accessed pages...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions