filter.c 14.8 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
 * Linux Socket Filter - Kernel level socket filtering
 *
 * Author:
 *     Jay Schulist <jschlst@samba.org>
 *
 * Based on the design of:
 *     - The Berkeley Packet Filter
 *
 * 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.
 *
 * Andi Kleen - Fix a few bad bugs and races.
16
 * Kris Katterjohn - Added many additional checks in sk_chk_filter()
Linus Torvalds's avatar
Linus Torvalds committed
17
18
19
20
21
22
23
24
25
26
27
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_packet.h>
28
#include <linux/gfp.h>
Linus Torvalds's avatar
Linus Torvalds committed
29
30
#include <net/ip.h>
#include <net/protocol.h>
31
#include <net/netlink.h>
Linus Torvalds's avatar
Linus Torvalds committed
32
33
34
35
36
37
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/uaccess.h>
38
#include <asm/unaligned.h>
Linus Torvalds's avatar
Linus Torvalds committed
39
40
#include <linux/filter.h>

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
enum {
	BPF_S_RET_K = 0,
	BPF_S_RET_A,
	BPF_S_ALU_ADD_K,
	BPF_S_ALU_ADD_X,
	BPF_S_ALU_SUB_K,
	BPF_S_ALU_SUB_X,
	BPF_S_ALU_MUL_K,
	BPF_S_ALU_MUL_X,
	BPF_S_ALU_DIV_X,
	BPF_S_ALU_AND_K,
	BPF_S_ALU_AND_X,
	BPF_S_ALU_OR_K,
	BPF_S_ALU_OR_X,
	BPF_S_ALU_LSH_K,
	BPF_S_ALU_LSH_X,
	BPF_S_ALU_RSH_K,
	BPF_S_ALU_RSH_X,
	BPF_S_ALU_NEG,
	BPF_S_LD_W_ABS,
	BPF_S_LD_H_ABS,
	BPF_S_LD_B_ABS,
	BPF_S_LD_W_LEN,
	BPF_S_LD_W_IND,
	BPF_S_LD_H_IND,
	BPF_S_LD_B_IND,
	BPF_S_LD_IMM,
	BPF_S_LDX_W_LEN,
	BPF_S_LDX_B_MSH,
	BPF_S_LDX_IMM,
	BPF_S_MISC_TAX,
	BPF_S_MISC_TXA,
	BPF_S_ALU_DIV_K,
	BPF_S_LD_MEM,
	BPF_S_LDX_MEM,
	BPF_S_ST,
	BPF_S_STX,
	BPF_S_JMP_JA,
	BPF_S_JMP_JEQ_K,
	BPF_S_JMP_JEQ_X,
	BPF_S_JMP_JGE_K,
	BPF_S_JMP_JGE_X,
	BPF_S_JMP_JGT_K,
	BPF_S_JMP_JGT_X,
	BPF_S_JMP_JSET_K,
	BPF_S_JMP_JSET_X,
};

Linus Torvalds's avatar
Linus Torvalds committed
89
/* No hurry in this branch */
90
static void *__load_pointer(struct sk_buff *skb, int k)
Linus Torvalds's avatar
Linus Torvalds committed
91
92
93
94
{
	u8 *ptr = NULL;

	if (k >= SKF_NET_OFF)
95
		ptr = skb_network_header(skb) + k - SKF_NET_OFF;
Linus Torvalds's avatar
Linus Torvalds committed
96
	else if (k >= SKF_LL_OFF)
97
		ptr = skb_mac_header(skb) + k - SKF_LL_OFF;
Linus Torvalds's avatar
Linus Torvalds committed
98

99
	if (ptr >= skb->head && ptr < skb_tail_pointer(skb))
Linus Torvalds's avatar
Linus Torvalds committed
100
101
102
103
		return ptr;
	return NULL;
}

104
static inline void *load_pointer(struct sk_buff *skb, int k,
105
				 unsigned int size, void *buffer)
106
107
108
109
110
111
112
113
114
115
{
	if (k >= 0)
		return skb_header_pointer(skb, k, size, buffer);
	else {
		if (k >= SKF_AD_OFF)
			return NULL;
		return __load_pointer(skb, k);
	}
}

Stephen Hemminger's avatar
Stephen Hemminger committed
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
 *	sk_filter - run a packet through a socket filter
 *	@sk: sock associated with &sk_buff
 *	@skb: buffer to filter
 *
 * Run the filter code and then cut skb->data to correct size returned by
 * sk_run_filter. If pkt_len is 0 we toss packet. If skb->len is smaller
 * than pkt_len we keep whole skb->data. This is the socket level
 * wrapper to sk_run_filter. It returns 0 if the packet should
 * be accepted or -EPERM if the packet should be tossed.
 *
 */
int sk_filter(struct sock *sk, struct sk_buff *skb)
{
	int err;
	struct sk_filter *filter;

	err = security_sock_rcv_skb(sk, skb);
	if (err)
		return err;

	rcu_read_lock_bh();
138
	filter = rcu_dereference_bh(sk->sk_filter);
Stephen Hemminger's avatar
Stephen Hemminger committed
139
	if (filter) {
140
141
		unsigned int pkt_len = sk_run_filter(skb, filter->insns, filter->len);

Stephen Hemminger's avatar
Stephen Hemminger committed
142
143
144
145
146
147
148
149
		err = pkt_len ? pskb_trim(skb, pkt_len) : -EPERM;
	}
	rcu_read_unlock_bh();

	return err;
}
EXPORT_SYMBOL(sk_filter);

Linus Torvalds's avatar
Linus Torvalds committed
150
/**
151
 *	sk_run_filter - run a filter on a socket
Linus Torvalds's avatar
Linus Torvalds committed
152
153
154
155
156
157
158
159
160
 *	@skb: buffer to run the filter on
 *	@filter: filter to apply
 *	@flen: length of filter
 *
 * Decode and apply filter instructions to the skb->data.
 * Return length to keep, 0 for none. skb is the data we are
 * filtering, filter is the array of filter instructions, and
 * len is the number of filter blocks in the array.
 */
161
unsigned int sk_run_filter(struct sk_buff *skb, struct sock_filter *filter, int flen)
Linus Torvalds's avatar
Linus Torvalds committed
162
{
163
	void *ptr;
164
165
	u32 A = 0;			/* Accumulator */
	u32 X = 0;			/* Index Register */
Linus Torvalds's avatar
Linus Torvalds committed
166
	u32 mem[BPF_MEMWORDS];		/* Scratch Memory Store */
167
	unsigned long memvalid = 0;
168
	u32 tmp;
Linus Torvalds's avatar
Linus Torvalds committed
169
170
171
	int k;
	int pc;

172
	BUILD_BUG_ON(BPF_MEMWORDS > BITS_PER_LONG);
Linus Torvalds's avatar
Linus Torvalds committed
173
174
175
176
	/*
	 * Process array of filter instructions.
	 */
	for (pc = 0; pc < flen; pc++) {
177
178
		const struct sock_filter *fentry = &filter[pc];
		u32 f_k = fentry->k;
179

Linus Torvalds's avatar
Linus Torvalds committed
180
		switch (fentry->code) {
181
		case BPF_S_ALU_ADD_X:
Linus Torvalds's avatar
Linus Torvalds committed
182
183
			A += X;
			continue;
184
		case BPF_S_ALU_ADD_K:
185
			A += f_k;
Linus Torvalds's avatar
Linus Torvalds committed
186
			continue;
187
		case BPF_S_ALU_SUB_X:
Linus Torvalds's avatar
Linus Torvalds committed
188
189
			A -= X;
			continue;
190
		case BPF_S_ALU_SUB_K:
191
			A -= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
192
			continue;
193
		case BPF_S_ALU_MUL_X:
Linus Torvalds's avatar
Linus Torvalds committed
194
195
			A *= X;
			continue;
196
		case BPF_S_ALU_MUL_K:
197
			A *= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
198
			continue;
199
		case BPF_S_ALU_DIV_X:
Linus Torvalds's avatar
Linus Torvalds committed
200
201
202
203
			if (X == 0)
				return 0;
			A /= X;
			continue;
204
		case BPF_S_ALU_DIV_K:
205
			A /= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
206
			continue;
207
		case BPF_S_ALU_AND_X:
Linus Torvalds's avatar
Linus Torvalds committed
208
209
			A &= X;
			continue;
210
		case BPF_S_ALU_AND_K:
211
			A &= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
212
			continue;
213
		case BPF_S_ALU_OR_X:
Linus Torvalds's avatar
Linus Torvalds committed
214
215
			A |= X;
			continue;
216
		case BPF_S_ALU_OR_K:
217
			A |= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
218
			continue;
219
		case BPF_S_ALU_LSH_X:
Linus Torvalds's avatar
Linus Torvalds committed
220
221
			A <<= X;
			continue;
222
		case BPF_S_ALU_LSH_K:
223
			A <<= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
224
			continue;
225
		case BPF_S_ALU_RSH_X:
Linus Torvalds's avatar
Linus Torvalds committed
226
227
			A >>= X;
			continue;
228
		case BPF_S_ALU_RSH_K:
229
			A >>= f_k;
Linus Torvalds's avatar
Linus Torvalds committed
230
			continue;
231
		case BPF_S_ALU_NEG:
Linus Torvalds's avatar
Linus Torvalds committed
232
233
			A = -A;
			continue;
234
		case BPF_S_JMP_JA:
235
			pc += f_k;
Linus Torvalds's avatar
Linus Torvalds committed
236
			continue;
237
		case BPF_S_JMP_JGT_K:
238
			pc += (A > f_k) ? fentry->jt : fentry->jf;
Linus Torvalds's avatar
Linus Torvalds committed
239
			continue;
240
		case BPF_S_JMP_JGE_K:
241
			pc += (A >= f_k) ? fentry->jt : fentry->jf;
Linus Torvalds's avatar
Linus Torvalds committed
242
			continue;
243
		case BPF_S_JMP_JEQ_K:
244
			pc += (A == f_k) ? fentry->jt : fentry->jf;
Linus Torvalds's avatar
Linus Torvalds committed
245
			continue;
246
		case BPF_S_JMP_JSET_K:
247
			pc += (A & f_k) ? fentry->jt : fentry->jf;
Linus Torvalds's avatar
Linus Torvalds committed
248
			continue;
249
		case BPF_S_JMP_JGT_X:
Linus Torvalds's avatar
Linus Torvalds committed
250
251
			pc += (A > X) ? fentry->jt : fentry->jf;
			continue;
252
		case BPF_S_JMP_JGE_X:
Linus Torvalds's avatar
Linus Torvalds committed
253
254
			pc += (A >= X) ? fentry->jt : fentry->jf;
			continue;
255
		case BPF_S_JMP_JEQ_X:
Linus Torvalds's avatar
Linus Torvalds committed
256
257
			pc += (A == X) ? fentry->jt : fentry->jf;
			continue;
258
		case BPF_S_JMP_JSET_X:
Linus Torvalds's avatar
Linus Torvalds committed
259
260
			pc += (A & X) ? fentry->jt : fentry->jf;
			continue;
261
		case BPF_S_LD_W_ABS:
262
			k = f_k;
263
load_w:
264
265
			ptr = load_pointer(skb, k, 4, &tmp);
			if (ptr != NULL) {
266
				A = get_unaligned_be32(ptr);
267
				continue;
Linus Torvalds's avatar
Linus Torvalds committed
268
			}
269
			break;
270
		case BPF_S_LD_H_ABS:
271
			k = f_k;
272
load_h:
273
274
			ptr = load_pointer(skb, k, 2, &tmp);
			if (ptr != NULL) {
275
				A = get_unaligned_be16(ptr);
276
				continue;
Linus Torvalds's avatar
Linus Torvalds committed
277
			}
278
			break;
279
		case BPF_S_LD_B_ABS:
280
			k = f_k;
Linus Torvalds's avatar
Linus Torvalds committed
281
load_b:
282
283
284
285
			ptr = load_pointer(skb, k, 1, &tmp);
			if (ptr != NULL) {
				A = *(u8 *)ptr;
				continue;
Linus Torvalds's avatar
Linus Torvalds committed
286
			}
287
			break;
288
		case BPF_S_LD_W_LEN:
289
			A = skb->len;
Linus Torvalds's avatar
Linus Torvalds committed
290
			continue;
291
		case BPF_S_LDX_W_LEN:
292
			X = skb->len;
Linus Torvalds's avatar
Linus Torvalds committed
293
			continue;
294
		case BPF_S_LD_W_IND:
295
			k = X + f_k;
Linus Torvalds's avatar
Linus Torvalds committed
296
			goto load_w;
297
		case BPF_S_LD_H_IND:
298
			k = X + f_k;
Linus Torvalds's avatar
Linus Torvalds committed
299
			goto load_h;
300
		case BPF_S_LD_B_IND:
301
			k = X + f_k;
Linus Torvalds's avatar
Linus Torvalds committed
302
			goto load_b;
303
		case BPF_S_LDX_B_MSH:
304
			ptr = load_pointer(skb, f_k, 1, &tmp);
305
306
307
308
309
			if (ptr != NULL) {
				X = (*(u8 *)ptr & 0xf) << 2;
				continue;
			}
			return 0;
310
		case BPF_S_LD_IMM:
311
			A = f_k;
Linus Torvalds's avatar
Linus Torvalds committed
312
			continue;
313
		case BPF_S_LDX_IMM:
314
			X = f_k;
Linus Torvalds's avatar
Linus Torvalds committed
315
			continue;
316
		case BPF_S_LD_MEM:
317
318
			A = (memvalid & (1UL << f_k)) ?
				mem[f_k] : 0;
Linus Torvalds's avatar
Linus Torvalds committed
319
			continue;
320
		case BPF_S_LDX_MEM:
321
322
			X = (memvalid & (1UL << f_k)) ?
				mem[f_k] : 0;
Linus Torvalds's avatar
Linus Torvalds committed
323
			continue;
324
		case BPF_S_MISC_TAX:
Linus Torvalds's avatar
Linus Torvalds committed
325
326
			X = A;
			continue;
327
		case BPF_S_MISC_TXA:
Linus Torvalds's avatar
Linus Torvalds committed
328
329
			A = X;
			continue;
330
		case BPF_S_RET_K:
331
			return f_k;
332
		case BPF_S_RET_A:
333
			return A;
334
		case BPF_S_ST:
335
336
			memvalid |= 1UL << f_k;
			mem[f_k] = A;
Linus Torvalds's avatar
Linus Torvalds committed
337
			continue;
338
		case BPF_S_STX:
339
340
			memvalid |= 1UL << f_k;
			mem[f_k] = X;
Linus Torvalds's avatar
Linus Torvalds committed
341
342
			continue;
		default:
343
			WARN_ON(1);
Linus Torvalds's avatar
Linus Torvalds committed
344
345
346
347
348
349
350
351
352
			return 0;
		}

		/*
		 * Handle ancillary data, which are impossible
		 * (or very difficult) to get parsing packet contents.
		 */
		switch (k-SKF_AD_OFF) {
		case SKF_AD_PROTOCOL:
Al Viro's avatar
Al Viro committed
353
			A = ntohs(skb->protocol);
Linus Torvalds's avatar
Linus Torvalds committed
354
355
356
357
358
			continue;
		case SKF_AD_PKTTYPE:
			A = skb->pkt_type;
			continue;
		case SKF_AD_IFINDEX:
359
360
			if (!skb->dev)
				return 0;
Linus Torvalds's avatar
Linus Torvalds committed
361
362
			A = skb->dev->ifindex;
			continue;
363
364
365
		case SKF_AD_MARK:
			A = skb->mark;
			continue;
366
367
368
		case SKF_AD_QUEUE:
			A = skb->queue_mapping;
			continue;
369
370
371
372
373
		case SKF_AD_HATYPE:
			if (!skb->dev)
				return 0;
			A = skb->dev->type;
			continue;
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
		case SKF_AD_NLATTR: {
			struct nlattr *nla;

			if (skb_is_nonlinear(skb))
				return 0;
			if (A > skb->len - sizeof(struct nlattr))
				return 0;

			nla = nla_find((struct nlattr *)&skb->data[A],
				       skb->len - A, X);
			if (nla)
				A = (void *)nla - (void *)skb->data;
			else
				A = 0;
			continue;
		}
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
		case SKF_AD_NLATTR_NEST: {
			struct nlattr *nla;

			if (skb_is_nonlinear(skb))
				return 0;
			if (A > skb->len - sizeof(struct nlattr))
				return 0;

			nla = (struct nlattr *)&skb->data[A];
			if (nla->nla_len > A - skb->len)
				return 0;

			nla = nla_find_nested(nla, X);
			if (nla)
				A = (void *)nla - (void *)skb->data;
			else
				A = 0;
			continue;
		}
Linus Torvalds's avatar
Linus Torvalds committed
409
410
411
412
413
414
415
		default:
			return 0;
		}
	}

	return 0;
}
416
EXPORT_SYMBOL(sk_run_filter);
Linus Torvalds's avatar
Linus Torvalds committed
417
418
419
420
421
422
423
424

/**
 *	sk_chk_filter - verify socket filter code
 *	@filter: filter to verify
 *	@flen: length of filter
 *
 * Check the user's filter code. If we let some ugly
 * filter code slip through kaboom! The filter must contain
425
426
 * no references or jumps that are out of range, no illegal
 * instructions, and must end with a RET instruction.
Linus Torvalds's avatar
Linus Torvalds committed
427
 *
428
429
430
 * All jumps are forward as they are not signed.
 *
 * Returns 0 if the rule set is legal or -EINVAL if not.
Linus Torvalds's avatar
Linus Torvalds committed
431
432
433
 */
int sk_chk_filter(struct sock_filter *filter, int flen)
{
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
	/*
	 * Valid instructions are initialized to non-0.
	 * Invalid instructions are initialized to 0.
	 */
	static const u8 codes[] = {
		[BPF_ALU|BPF_ADD|BPF_K]  = BPF_S_ALU_ADD_K + 1,
		[BPF_ALU|BPF_ADD|BPF_X]  = BPF_S_ALU_ADD_X + 1,
		[BPF_ALU|BPF_SUB|BPF_K]  = BPF_S_ALU_SUB_K + 1,
		[BPF_ALU|BPF_SUB|BPF_X]  = BPF_S_ALU_SUB_X + 1,
		[BPF_ALU|BPF_MUL|BPF_K]  = BPF_S_ALU_MUL_K + 1,
		[BPF_ALU|BPF_MUL|BPF_X]  = BPF_S_ALU_MUL_X + 1,
		[BPF_ALU|BPF_DIV|BPF_X]  = BPF_S_ALU_DIV_X + 1,
		[BPF_ALU|BPF_AND|BPF_K]  = BPF_S_ALU_AND_K + 1,
		[BPF_ALU|BPF_AND|BPF_X]  = BPF_S_ALU_AND_X + 1,
		[BPF_ALU|BPF_OR|BPF_K]   = BPF_S_ALU_OR_K + 1,
		[BPF_ALU|BPF_OR|BPF_X]   = BPF_S_ALU_OR_X + 1,
		[BPF_ALU|BPF_LSH|BPF_K]  = BPF_S_ALU_LSH_K + 1,
		[BPF_ALU|BPF_LSH|BPF_X]  = BPF_S_ALU_LSH_X + 1,
		[BPF_ALU|BPF_RSH|BPF_K]  = BPF_S_ALU_RSH_K + 1,
		[BPF_ALU|BPF_RSH|BPF_X]  = BPF_S_ALU_RSH_X + 1,
		[BPF_ALU|BPF_NEG]        = BPF_S_ALU_NEG + 1,
		[BPF_LD|BPF_W|BPF_ABS]   = BPF_S_LD_W_ABS + 1,
		[BPF_LD|BPF_H|BPF_ABS]   = BPF_S_LD_H_ABS + 1,
		[BPF_LD|BPF_B|BPF_ABS]   = BPF_S_LD_B_ABS + 1,
		[BPF_LD|BPF_W|BPF_LEN]   = BPF_S_LD_W_LEN + 1,
		[BPF_LD|BPF_W|BPF_IND]   = BPF_S_LD_W_IND + 1,
		[BPF_LD|BPF_H|BPF_IND]   = BPF_S_LD_H_IND + 1,
		[BPF_LD|BPF_B|BPF_IND]   = BPF_S_LD_B_IND + 1,
		[BPF_LD|BPF_IMM]         = BPF_S_LD_IMM + 1,
		[BPF_LDX|BPF_W|BPF_LEN]  = BPF_S_LDX_W_LEN + 1,
		[BPF_LDX|BPF_B|BPF_MSH]  = BPF_S_LDX_B_MSH + 1,
		[BPF_LDX|BPF_IMM]        = BPF_S_LDX_IMM + 1,
		[BPF_MISC|BPF_TAX]       = BPF_S_MISC_TAX + 1,
		[BPF_MISC|BPF_TXA]       = BPF_S_MISC_TXA + 1,
		[BPF_RET|BPF_K]          = BPF_S_RET_K + 1,
		[BPF_RET|BPF_A]          = BPF_S_RET_A + 1,
		[BPF_ALU|BPF_DIV|BPF_K]  = BPF_S_ALU_DIV_K + 1,
		[BPF_LD|BPF_MEM]         = BPF_S_LD_MEM + 1,
		[BPF_LDX|BPF_MEM]        = BPF_S_LDX_MEM + 1,
		[BPF_ST]                 = BPF_S_ST + 1,
		[BPF_STX]                = BPF_S_STX + 1,
		[BPF_JMP|BPF_JA]         = BPF_S_JMP_JA + 1,
		[BPF_JMP|BPF_JEQ|BPF_K]  = BPF_S_JMP_JEQ_K + 1,
		[BPF_JMP|BPF_JEQ|BPF_X]  = BPF_S_JMP_JEQ_X + 1,
		[BPF_JMP|BPF_JGE|BPF_K]  = BPF_S_JMP_JGE_K + 1,
		[BPF_JMP|BPF_JGE|BPF_X]  = BPF_S_JMP_JGE_X + 1,
		[BPF_JMP|BPF_JGT|BPF_K]  = BPF_S_JMP_JGT_K + 1,
		[BPF_JMP|BPF_JGT|BPF_X]  = BPF_S_JMP_JGT_X + 1,
		[BPF_JMP|BPF_JSET|BPF_K] = BPF_S_JMP_JSET_K + 1,
		[BPF_JMP|BPF_JSET|BPF_X] = BPF_S_JMP_JSET_X + 1,
	};
Linus Torvalds's avatar
Linus Torvalds committed
485
486
	int pc;

487
	if (flen == 0 || flen > BPF_MAXINSNS)
Linus Torvalds's avatar
Linus Torvalds committed
488
489
490
491
		return -EINVAL;

	/* check the filter code now */
	for (pc = 0; pc < flen; pc++) {
492
493
		struct sock_filter *ftest = &filter[pc];
		u16 code = ftest->code;
494

495
496
497
498
499
500
		if (code >= ARRAY_SIZE(codes))
			return -EINVAL;
		code = codes[code];
		/* Undo the '+ 1' in codes[] after validation. */
		if (!code--)
			return -EINVAL;
501
		/* Some instructions need special checks */
502
503
		switch (code) {
		case BPF_S_ALU_DIV_K:
504
505
			/* check for division by zero */
			if (ftest->k == 0)
Linus Torvalds's avatar
Linus Torvalds committed
506
				return -EINVAL;
507
			break;
508
509
510
511
512
		case BPF_S_LD_MEM:
		case BPF_S_LDX_MEM:
		case BPF_S_ST:
		case BPF_S_STX:
			/* check for invalid memory addresses */
513
514
515
			if (ftest->k >= BPF_MEMWORDS)
				return -EINVAL;
			break;
516
		case BPF_S_JMP_JA:
517
518
519
520
521
522
523
			/*
			 * Note, the large ftest->k might cause loops.
			 * Compare this with conditional jumps below,
			 * where offsets are limited. --ANK (981016)
			 */
			if (ftest->k >= (unsigned)(flen-pc-1))
				return -EINVAL;
524
525
526
527
528
529
530
531
532
			break;
		case BPF_S_JMP_JEQ_K:
		case BPF_S_JMP_JEQ_X:
		case BPF_S_JMP_JGE_K:
		case BPF_S_JMP_JGE_X:
		case BPF_S_JMP_JGT_K:
		case BPF_S_JMP_JGT_X:
		case BPF_S_JMP_JSET_X:
		case BPF_S_JMP_JSET_K:
533
			/* for conditionals both must be safe */
534
			if (pc + ftest->jt + 1 >= flen ||
535
536
			    pc + ftest->jf + 1 >= flen)
				return -EINVAL;
537
			break;
538
		}
539
		ftest->code = code;
540
	}
541

542
543
544
545
546
	/* last instruction must be a RET code */
	switch (filter[flen - 1].code) {
	case BPF_S_RET_K:
	case BPF_S_RET_A:
		return 0;
547
548
	}
	return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
549
}
550
EXPORT_SYMBOL(sk_chk_filter);
Linus Torvalds's avatar
Linus Torvalds committed
551

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
/**
 * 	sk_filter_rcu_release: Release a socket filter by rcu_head
 *	@rcu: rcu_head that contains the sk_filter to free
 */
static void sk_filter_rcu_release(struct rcu_head *rcu)
{
	struct sk_filter *fp = container_of(rcu, struct sk_filter, rcu);

	sk_filter_release(fp);
}

static void sk_filter_delayed_uncharge(struct sock *sk, struct sk_filter *fp)
{
	unsigned int size = sk_filter_len(fp);

	atomic_sub(size, &sk->sk_omem_alloc);
	call_rcu_bh(&fp->rcu, sk_filter_rcu_release);
}

Linus Torvalds's avatar
Linus Torvalds committed
571
572
573
574
575
576
577
578
579
580
581
582
/**
 *	sk_attach_filter - attach a socket filter
 *	@fprog: the filter program
 *	@sk: the socket to use
 *
 * Attach the user's filter code. We first run some sanity checks on
 * it to make sure it does not explode on us later. If an error
 * occurs or there is insufficient memory for the filter a negative
 * errno code is returned. On success the return is zero.
 */
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
{
583
	struct sk_filter *fp, *old_fp;
Linus Torvalds's avatar
Linus Torvalds committed
584
585
586
587
	unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
	int err;

	/* Make sure new filter is there and in the right amounts. */
588
589
	if (fprog->filter == NULL)
		return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
590
591
592
593
594

	fp = sock_kmalloc(sk, fsize+sizeof(*fp), GFP_KERNEL);
	if (!fp)
		return -ENOMEM;
	if (copy_from_user(fp->insns, fprog->filter, fsize)) {
595
		sock_kfree_s(sk, fp, fsize+sizeof(*fp));
Linus Torvalds's avatar
Linus Torvalds committed
596
597
598
599
600
601
602
		return -EFAULT;
	}

	atomic_set(&fp->refcnt, 1);
	fp->len = fprog->len;

	err = sk_chk_filter(fp->insns, fp->len);
603
604
605
	if (err) {
		sk_filter_uncharge(sk, fp);
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
606
607
	}

608
609
	old_fp = rcu_dereference_protected(sk->sk_filter,
					   sock_owned_by_user(sk));
610
611
	rcu_assign_pointer(sk->sk_filter, fp);

612
613
	if (old_fp)
		sk_filter_delayed_uncharge(sk, old_fp);
614
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
615
}
616
EXPORT_SYMBOL_GPL(sk_attach_filter);
Linus Torvalds's avatar
Linus Torvalds committed
617

618
619
620
621
622
int sk_detach_filter(struct sock *sk)
{
	int ret = -ENOENT;
	struct sk_filter *filter;

623
624
	filter = rcu_dereference_protected(sk->sk_filter,
					   sock_owned_by_user(sk));
625
626
	if (filter) {
		rcu_assign_pointer(sk->sk_filter, NULL);
627
		sk_filter_delayed_uncharge(sk, filter);
628
629
630
631
		ret = 0;
	}
	return ret;
}
632
EXPORT_SYMBOL_GPL(sk_detach_filter);