Android - sdcardfs Changes current->fs Without Proper Locking

EDB-ID:

45558




Platform:

Android

Date:

2018-10-08


Tested on a Pixel 2 (walleye):
[ro.build.ab_update]: [true]
[ro.build.characteristics]: [nosdcard]
[ro.build.date]: [Mon Jun  4 22:10:18 UTC 2018]
[ro.build.date.utc]: [1528150218]
[ro.build.description]: [walleye-user 8.1.0 OPM2.171026.006.G1 4820017 release-keys]
[ro.build.display.id]: [OPM2.171026.006.G1]
[ro.build.expect.baseband]: [g8998-00202-1802061358]
[ro.build.expect.bootloader]: [mw8998-002.0069.00]
[ro.build.fingerprint]: [google/walleye/walleye:8.1.0/OPM2.171026.006.G1/4820017:user/release-keys]
[ro.build.flavor]: [walleye-user]
[ro.build.host]: [wprd10.hot.corp.google.com]
[ro.build.id]: [OPM2.171026.006.G1]
[ro.build.product]: [walleye]
[ro.build.system_root_image]: [true]
[ro.build.tags]: [release-keys]
[ro.build.type]: [user]
[ro.build.user]: [android-build]
[ro.build.version.all_codenames]: [REL]
[ro.build.version.base_os]: []
[ro.build.version.codename]: [REL]
[ro.build.version.incremental]: [4820017]
[ro.build.version.preview_sdk]: [0]
[ro.build.version.release]: [8.1.0]
[ro.build.version.sdk]: [27]
[ro.build.version.security_patch]: [2018-07-05]


Android used to use a FUSE filesystem to emulate external storage, but nowadays
an in-kernel filesystem called "sdcardfs" is used instead. This filesystem does
not exist in the upstream Linux kernel, but does exist in the AOSP common kernel
tree.

In sdcardfs_create() and sdcardfs_mkdir()
(https://android.googlesource.com/kernel/common/+/android-4.14/fs/sdcardfs/inode.c),
the following code is used to temporarily override the umask while calling into
the lower filesystem:

        /* temporarily change umask for lower fs write */
        saved_fs = current->fs;
        copied_fs = copy_fs_struct(current->fs);
        if (!copied_fs) {
                err = -ENOMEM;
                goto out_unlock;
        }
        current->fs = copied_fs;
        current->fs->umask = 0;
        [... access lower filesystem ...]
        current->fs = saved_fs;
        free_fs_struct(copied_fs);

This is wrong; as a comment in include/linux/sched.h explains, ->fs must not be
accessed without holding the corresponding task lock:

        /* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */
        spinlock_t                      alloc_lock;

For example, the procfs per-task entries "root" and "cwd" access the ->fs member
of remote tasks under the task lock:

        static int proc_cwd_link(struct dentry *dentry, struct path *path)
        {
                struct task_struct *task = get_proc_task(d_inode(dentry));
                int result = -ENOENT;

                if (task) {
                        task_lock(task);
                        if (task->fs) {
                                get_fs_pwd(task->fs, path);
                                result = 0;
                        }
                        task_unlock(task);
                        put_task_struct(task);
                }
                return result;
        }

This bug can be triggered by any context that can create files in an sdcardfs
mount, so normal applications with zero permissions can hit it (by using
/sdcard/Android/data/{packagename}/, which does not require the external storage
permission).


To reproduce the bug in a simple way, compile the attached poc_viaadb.c:

$ /usr/local/google/home/jannh/my-android-toolchain/bin/aarch64-linux-android-gcc -static -o poc_viaadb poc_viaadb.c -pthread

Push the resulting binary to the device, and run it:

        $ adb push poc_viaadb /data/local/tmp/
        poc_viaadb: 1 file pushed. 13.5 MB/s (2640776 bytes in 0.187s)
        $ adb shell /data/local/tmp/poc_viaadb

Now you should see a lot of "target: [...]" messages, followed by the device
freezing and rebooting.

After rebooting, pull a bug report via ADB ("adb bugreport") and look for a
crash message in the "LAST KMSG" section. The type of crash you see might vary,
since there's a lot of different ways in which this code can crash, but here's
an example of how it might look - a crash inside the memory allocator:


================================================================================
[  997.010495] c7   1718 Unable to handle kernel paging request at virtual address fffffff2873e1180
[  997.010522] c7   1718 pgd = 0000000000000000
[  997.010537] [fffffff2873e1180] *pgd=0000000000000000, *pud=0000000000000000
[  997.010632] c7   1718 ------------[ cut here ]------------
[  997.010646] c7   1718 Kernel BUG at 0000000000000000 [verbose debug info unavailable]
[  997.010661] c7   1718 Internal error: Oops - BUG: 96000005 [#1] PREEMPT SMP
[  997.010675] Modules linked in: htc_battery synaptics_dsx_rmi_dev_htc synaptics_dsx_fw_update_htc synaptics_dsx_core_htc
[  997.010721] c7   1718 CPU: 7 PID: 1718 Comm: GLThread 41 Not tainted 4.4.88-g3acf2d53921d #1
[  997.010736] c7   1718 Hardware name: Qualcomm Technologies, Inc. MSM8998 v2.1 (DT)
[  997.010750] c7   1718 task: 0000000000000000 task.stack: 0000000000000000
[  997.010776] c7   1718 PC is at kmem_cache_alloc+0x88/0x228
[  997.010798] c7   1718 LR is at kgsl_drawobj_cmd_add_cmdlist+0x120/0x1e4
[  997.010812] c7   1718 pc : [<ffffff9f4d9df18c>] lr : [<ffffff9f4de4e054>] pstate: 60400145
[  997.010824] c7   1718 sp : fffffff2058ebbb0
[  997.010836] x29: fffffff2058ebbf0 x28: fffffff2089c9b80 
[  997.010868] x27: ffffff9f501f4000 x26: fffffff2089c9b80 
[  997.010893] x25: fffffff18e07a448 x24: 0000000000000001 
[  997.010912] x23: fffffff2089c9b80 x22: fffffff239402b00 
[  997.010930] x21: fffffff2873e1180 x20: 00000000024000c0 
[  997.010952] x19: ffffff9f4de4e054 x18: 0000000000001600 
[  997.010959] x17: 0000007ea408ae34 x16: 00000000b0000000 
[  997.010965] x15: 000000017e4c0000 x14: 0000000000000006 
[  997.010972] x13: ffffff9f5008f490 x12: 0000000000000000 
[  997.010978] x11: 000000000012abd7 x10: 000000000012abcf 
[  997.010984] x9 : 0000000000000000 x8 : 000000000012abcf 
[  997.010990] x7 : 00000007fdcf4000 x6 : fffffff2058ebc28 
[  997.010996] x5 : fffffff2058ebc28 x4 : 0000000000000001 
[  997.011002] x3 : 0000000082cb5000 x2 : 0000000000000018 
[  997.011008] x1 : 00000000024000c0 x0 : fffffff239402b00 
[  997.011015] c7   1718 
[  997.011015] c7   1718 PC: 0xffffff9f4d9df14c:
[  997.011019] f14c  b9401ae9 51000529 b9001ae9 35000069 f94002e9 37080449 f94002c9 d538d08a
[  997.011040] f16c  8b090149 f940052a eb0a011f 54fffdc1 f9400135 b4000bb5 b98022c9 9100210b
[  997.011060] f18c  f8696ab8 b9401ae9 11000529 b9001ae9 f94002c9 d538d08a 8b090149 f9800131
[  997.011080] f1ac  c87f652a ca15014a ca080339 aa190159 b5000079 c82a2d38 35ffff4a b9401ae8
[  997.011101] c7   1718 
[  997.011101] c7   1718 LR: 0xffffff9f4de4e014:
[  997.011105] e014  b40004ca aa1703e1 2a1f03e2 97ee702c 910023e0 aa1603e1 aa1703e2 97f50b24
[  997.011125] e034  b5000420 b94023e4 12000888 34000408 f9471360 52801801 72a04801 97ee442d
[  997.011145] e054  aa0003e8 b40005a8 f9400be9 11000718 2a1f03e0 6b14031f f9001109 f9400fe9
[  997.011164] e074  910082d6 f9001509 b94027e9 b9001109 f94007e9 f9000d09 b94023e9 a9037d09
[  997.011185] c7   1718 
[  997.011185] c7   1718 SP: 0xfffffff2058ebb70:
[  997.011189] bb70  4de4e054 ffffff9f 058ebbb0 fffffff2 4d9df18c ffffff9f 60400145 00000000
[  997.011209] bb90  4fe2f270 ffffff9f 4fe2fe98 ffffff9f 00000000 00000080 4fe2cee8 ffffff9f
[  997.011230] bbb0  8e07a448 fffffff1 a2257020 fffffff1 00000001 00000000 00000020 00000000
[  997.011250] bbd0  0a083ac8 0000007e 4fe2cee8 ffffff9f 00000002 00000000 8e07a400 fffffff1
[  997.011270] c7   1718 
[  997.011274] c7   1718 Process GLThread 41 (pid: 1718, stack limit = 0x0000000000000000)
[  997.011278] c7   1718 Call trace:
[  997.011283] c7   1718 Exception stack(0xfffffff2058eba80 to 0xfffffff2058ebbb0)
[  997.011288] c7   1718 ba80: fffffff239402b00 00000000024000c0 0000000000000018 0000000082cb5000
[  997.011292] c7   1718 baa0: 0000000000000001 fffffff2058ebc28 fffffff2058ebc28 00000007fdcf4000
[  997.011297] c7   1718 bac0: 000000000012abcf 0000000000000000 000000000012abcf 000000000012abd7
[  997.011301] c7   1718 bae0: 0000000000000000 ffffff9f5008f490 0000000000000006 000000017e4c0000
[  997.011306] c7   1718 bb00: 00000000b0000000 0000007ea408ae34 0000000000001600 ffffff9f4de4e054
[  997.011310] c7   1718 bb20: 00000000024000c0 fffffff2873e1180 fffffff239402b00 fffffff2089c9b80
[  997.011315] c7   1718 bb40: 0000000000000001 fffffff18e07a448 fffffff2089c9b80 ffffff9f501f4000
[  997.011319] c7   1718 bb60: fffffff2089c9b80 fffffff2058ebbf0 ffffff9f4de4e054 fffffff2058ebbb0
[  997.011323] c7   1718 bb80: ffffff9f4d9df18c 0000000060400145 ffffff9f4fe2f270 ffffff9f4fe2fe98
[  997.011327] c7   1718 bba0: 0000008000000000 ffffff9f4fe2cee8
[  997.011332] c7   1718 [<ffffff9f4d9df18c>] kmem_cache_alloc+0x88/0x228
[  997.011337] c7   1718 [<ffffff9f4de4e054>] kgsl_drawobj_cmd_add_cmdlist+0x120/0x1e4
[  997.011342] c7   1718 [<ffffff9f4de3f3d8>] kgsl_ioctl_gpu_command+0x114/0x288
[  997.011347] c7   1718 [<ffffff9f4de4eb4c>] kgsl_ioctl_helper+0x134/0x1b8
[  997.011351] c7   1718 [<ffffff9f4de4ec00>] kgsl_ioctl+0x30/0xbc
[  997.011357] c7   1718 [<ffffff9f4da00dd0>] do_vfs_ioctl+0x434/0x884
[  997.011361] c7   1718 [<ffffff9f4da012a8>] SyS_ioctl+0x88/0x94
[  997.011367] c7   1718 [<ffffff9f4d883b0c>] __sys_trace_return+0x0/0x4
[  997.011373] c7   1718 Code: f9400135 b4000bb5 b98022c9 9100210b (f8696ab8) 
[  997.011417] c7   1718 ---[ end trace aea07a0c0fb86e0d ]---
[  997.015389] c7   1718 Kernel panic - not syncing: Fatal exception
================================================================================

Note that another possible way in which the memory corruption can happen is
corruption of a spinlock - in that case, the phone won't panic, but messages
about a soft kernel lockup will start appearing in dmesg after some time.
Experimentally, that seems to happen if the chaos_worker thread is removed from
the PoC.



I have verified that this bug can also be triggered from a normal Android app.
To reproduce that, follow these steps:

 - install https://play.google.com/store/apps/details?id=org.connectbot on the
   phone
 - run "adb shell mkdir /sdcard/Android/data/org.connectbot"
 - run "/usr/local/google/home/jannh/my-android-toolchain/bin/aarch64-linux-android-gcc -static -o poc poc.c -pthread"
 - run "adb push poc /sdcard/Android/data/org.connectbot/"
 - on the phone, open a local terminal in connectbot
 - in the terminal:
       $ cd /data/data/org.connectbot
       $ cp /sdcard/Android/data/org.connectbot/poc .
       $ chmod +x poc
       $ ./poc


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/45558.zip