This patch prevents an OOPS when you pass a NULL buffer address
to recvfrom(). The __copy_user() was not protecting a stq with an
exception
table entry so when it chewed on a bad address the kernel did an OOPS
instead of properly handling the fault.
Attached is: 1.) the one-line kernel patch, 2.) the server that passes
the
NULL buffer to recvfrom(), and 3.) the client that sends junk to the
server.
To recreate the problem run the server in the background then run the
client. The server will OOPS without the patch.
Larry Woodman
http://www.missioncriticallinux.com
diff -u --recursive linux-2.3.99-pre3.vanilla/arch/alpha/lib/copy_user.S
linux/arch/alpha/lib/copy_user.S
--- linux-2.3.99-pre3.vanilla/arch/alpha/lib/copy_user.S Fri Apr 30 11:22:19
1999
+++ linux/arch/alpha/lib/copy_user.S Mon Apr 27 08:11:24 2020
@@ -80,7 +80,7 @@
extql $3,$7,$3
extqh $2,$7,$1
bis $3,$1,$1
- stq $1,0($6)
+ EXO( stq $1,0($6) )
addq $7,8,$7
subq $0,8,$0
addq $6,8,$6
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <getopt.h>
#define BUFLEN 8192
/* defaults */
int port = 1300;
main()
{
int sock, bytes;
struct sockaddr_in s;
char *buf[BUFLEN];
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
perror("socket");
exit(1);
}
bzero((void*)&s, sizeof(s));
s.sin_family = AF_INET;
s.sin_port = htons(port);
s.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr*)&s, sizeof(s)) < 0) {
perror("bind");
exit(2);
}
if ((bytes = recvfrom(sock, NULL, BUFLEN, 0, NULL, NULL)) < 0) {
perror("recvfrom");
exit(4);
}
printf("recvfrom returned %d bytes\n", bytes);
exit(0);
}
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <getopt.h>
#define BUFLEN 8192
/* defaults */
int port = 1300;
char *buf = "abcdefghijklmnopqrstuvwxyz";
main()
{
int sock, bytes;
struct sockaddr_in to;
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
perror("socket");
exit(1);
}
bzero((void*)&to, sizeof(to));
to.sin_family = AF_INET;
to.sin_port = htons(port);
to.sin_addr.s_addr = inet_addr("127.0.0.1");
if ((bytes = sendto(sock, buf, strlen(buf), 0, &to, sizeof(to))) < 0) {
perror("sendto");
exit(4);
}
printf("sendto returned %d bytes\n", bytes);
exit(0);
}