| /* |
| * Copyright (c) 2017 Richard Palethorpe <rpalethorpe@suse.com> |
| * Original POC by Daniel Jiang |
| * |
| * This program is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| /* |
| * Test for CVE-2017-2671 faulty locking on ping socket |
| * |
| * When sys_connect() is called with sockaddr.sin_family set to AF_UNSPEC on a |
| * ping socket; __udp_disconnect() gets called, which in turn calls the buggy |
| * function ping_unhashed(). This function does not obtain a rwlock before |
| * checking if the socket is hashed allowing the socket data to be pulled from |
| * underneath it in the time between calling sk_hashed() and gaining the write |
| * lock. |
| * |
| * Fixed in commit 43a6684519ab0a6c52024b5e25322476cabad893 |
| * |
| * This test repeatedly 'connects' a ping socket correctly then calls |
| * connect() with AF_UNSPEC in two seperate threads to trigger the race |
| * condition. If the bug is present, then the test will most likely crash the |
| * system. |
| * |
| * The test requests root privileges so that it can ensure ping sockets are |
| * enabled. On distributions (including Android) where ping sockets are |
| * enabled by default, root privileges are not required. |
| */ |
| |
| #include <stdio.h> |
| #include <sys/socket.h> |
| #include <arpa/inet.h> |
| #include <stdlib.h> |
| |
| #include "tst_test.h" |
| #include "tst_safe_net.h" |
| #include "tst_safe_pthread.h" |
| |
| #include "tst_fuzzy_sync.h" |
| |
| #define ATTEMPTS 0xFFFF |
| #define PING_SYSCTL_PATH "/proc/sys/net/ipv4/ping_group_range" |
| |
| static int sockfd; |
| static unsigned int ping_min_grp = 1, ping_max_grp; |
| static struct tst_fzsync_pair fzsync_pair = TST_FZSYNC_PAIR_INIT; |
| static struct sockaddr_in iaddr, uaddr; |
| |
| static void setup(void) |
| { |
| iaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
| uaddr = iaddr; |
| iaddr.sin_family = AF_INET; |
| uaddr.sin_family = AF_UNSPEC; |
| fzsync_pair.delay_inc = 1; |
| |
| SAFE_FILE_SCANF(PING_SYSCTL_PATH, "%u %u", |
| &ping_min_grp, &ping_max_grp); |
| SAFE_FILE_PRINTF(PING_SYSCTL_PATH, "0 0"); |
| |
| sockfd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); |
| tst_res(TINFO, "Created ping socket, attempting to race..."); |
| } |
| |
| static void cleanup(void) |
| { |
| SAFE_CLOSE(sockfd); |
| if (ping_min_grp | ping_max_grp) |
| SAFE_FILE_PRINTF(PING_SYSCTL_PATH, "%u %u", |
| ping_min_grp, ping_max_grp); |
| } |
| |
| static void *connect_b(void * param LTP_ATTRIBUTE_UNUSED) |
| { |
| tst_fzsync_delay_b(&fzsync_pair); |
| connect(sockfd, (struct sockaddr *)&uaddr, sizeof(uaddr)); |
| tst_fzsync_time_b(&fzsync_pair); |
| |
| return 0; |
| } |
| |
| static void run(void) |
| { |
| pthread_t thrd; |
| int i; |
| |
| for (i = 0; i < ATTEMPTS; i++) { |
| SAFE_CONNECT(sockfd, |
| (struct sockaddr *)&iaddr, sizeof(iaddr)); |
| SAFE_PTHREAD_CREATE(&thrd, 0, connect_b, 0); |
| |
| tst_fzsync_delay_a(&fzsync_pair); |
| connect(sockfd, (struct sockaddr *)&uaddr, sizeof(uaddr)); |
| tst_fzsync_time_a(&fzsync_pair); |
| |
| SAFE_PTHREAD_JOIN(thrd, 0); |
| tst_fzsync_pair_update(i, &fzsync_pair); |
| |
| if (!(i & 0x7FFF)) |
| tst_fzsync_pair_info(&fzsync_pair); |
| } |
| |
| tst_res(TPASS, "We didn't crash"); |
| } |
| |
| static struct tst_test test = { |
| .setup = setup, |
| .test_all = run, |
| .cleanup = cleanup, |
| .needs_root = 1, |
| }; |